|
|
|
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') {
|
|
|
|
if (line_index === 0) {
|
|
|
|
line_start = i + 1;
|
|
|
|
line_index += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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': {},
|
|
|
|
};
|
|
|
|
|
|
|
|
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');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
line_start = i + 1;
|
|
|
|
line_index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const after = performance.now();
|
|
|
|
|
|
|
|
console.log(`Parsed in ${Math.round(after - before)}ms`);
|
|
|
|
|
|
|
|
traces['0'] = instructions;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|