function parse(text) { // https://github.com/shioyadan/Konata/blob/master/docs/kanata-log-format.md const before = performance.now(); let line_start = 0; let line_index = 0; let c = -1; const instructions = {}; //console.log(text); for (let i = 0; i < text.length; ++i) { if (text[i] === '\n') { // TODO: speed const line_copy = text.substring(line_start, i); const line_parts = line_copy.split(/\t/); if (line_parts.length === 0) { console.error('Parser error: empty line'); } const command = line_parts[0]; switch (command) { case 'C=': { // Specify the number of cycles since the start of simulation. if (!assert_arglen(line_parts, 1, 'C=')) return false; const cycles = Number(line_parts[1]); c = cycles; break; } case 'C': { // Specifies the number of elapsed cycles since the last output of any commands. if (!assert_arglen(line_parts, 1, 'C')) return false; const cycles = Number(line_parts[1]); c += cycles; break; } case 'I': { if (!assert_arglen(line_parts, 3, 'I')) return false; const insn_id_in_file = Number(line_parts[1]); const insn_id_in_sim = Number(line_parts[2]); const thread_id = Number(line_parts[3]); instructions[insn_id_in_file] = { 'cycle': c, 'file_id': insn_id_in_file, 'sim_id': insn_id_in_sim, 'thread_id': thread_id, 'text': '', 'popover_text': '', 'lanes': {'0': [], '1': []}, }; break; } case 'L': { if (!assert_arglenmin(line_parts, 3, 'L')) return false; const id = Number(line_parts[1]); const type = Number(line_parts[2]); const text = line_parts[3].replaceAll('\\n', '\n'); if (id in instructions) { if (type === 0) { instructions[id].text += text; } else { instructions[id].popover_text += text; } } else { console.error('Parser error: label for an instruction that does not exist'); } break; } case 'S': { if (!assert_arglen(line_parts, 3, 'S')) return false; const id = Number(line_parts[1]); const lane = Number(line_parts[2]); const stage = line_parts[3]; if (id in instructions) { if (!(lane in instructions[id].lanes)) { instructions[id].lanes[lane] = []; } instructions[id].lanes[lane].push({ 'name': stage, 'c': c, }); } else { console.error('Parser error: pipeline start for an instruction that does not exist'); } break; } case 'E': { if (!assert_arglen(line_parts, 3, 'E')) return false; const id = Number(line_parts[1]); const lane = Number(line_parts[2]); const stage = line_parts[3]; // This command can be ommited break; } case 'R': { if (!assert_arglen(line_parts, 3, 'R')) return false; const id = Number(line_parts[1]); const retire_id = Number(line_parts[2]); const type = Number(line_parts[3]); if (id in instructions) { if (type === 0) { instructions[id].retired = true; } else { instructions[id].retired = false; } instructions[id].retcyc = c; } else { console.error('Parser error: retire for an instruction that does not exist'); } break; } case 'W': { if (!assert_arglen(line_parts, 3, 'W')) return false; const consumer_id = Number(line_parts[1]); const producer_id = Number(line_parts[2]); const type = Number(line_parts[3]); // TODO break; } default: { console.error('Parser error: unexpected command', command); } } line_start = i + 1; line_index += 1; } } traces['0'] = { // The fact these are sorted is used extensively over the code 'raw': Object.values(instructions).sort((a, b) => a.cycle - b.cycle), }; traces['0'].geo = generate('0'); const after = performance.now(); console.log(`Parsed in ${Math.round(after - before)}ms`); return true; } function assert_arglen(args, arglen, command) { if (args.length !== arglen + 1) { console.error(`Parser error: command ${command} requires ${arglen} argument(s)`); return false; } return true; } function assert_arglenmin(args, arglen, command) { if (args.length < arglen + 1) { console.error(`Parser error: command ${command} requires at least ${arglen} argument(s)`); return false; } return true; }