Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions tests/zkasm/helpers/InstructionTracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@ const dataDump = require("./data-dump");
* Handles the generation of traces of instructions executed at runtime.
*/
class InstructionTracer {
constructor() {
/**
* @param {Object} config
* @param {boolean} [config.aggregateTrace = false] - An aggregated trace
* maps each instruction to the number of times it was executed at runtime.
*/
constructor({ aggregateTrace = false }) {
// Settings
this.isAggregatingTrace = aggregateTrace;

// State
// Contains executed instructions in order of execution.
this.rawTrace = [];
// Maps instructions to their number of executions at runtime.
this.aggregatedTrace = {};
}

setup() {
Expand All @@ -27,24 +38,36 @@ class InstructionTracer {
*/
eval_traceInstruction(ctx, tag) {
const instruction = tag.params[0].varName;
this.rawTrace.push(instruction);
if (this.isAggregatingTrace) {
const storedCount = this.aggregatedTrace[instruction];
let currentCount = storedCount ? storedCount : 0;
this.aggregatedTrace[instruction] = currentCount + 1;
} else {
this.rawTrace.push(instruction);
}
}

/**
* Writes the raw trace to `path`.
* Writes the trace to `path`.
*
* @param {string} path
*/
writeRawTrace(path) {
writeTrace(path) {
if (typeof path !== "string") {
// Writing to a descriptor will append instead of replace content,
// which might result in invalid traces, see
// https://nodejs.org/api/fs.html#using-fswritefile-with-file-descriptors
throw new Error("provide a file name (not descriptor) to write to");
}
// Writing in chunks of 1000 instructions to limit memory usage,
// as raw traces might grow big.
dataDump.writeToFileInChunks(this.rawTrace, path, 1000);

if (this.isAggregatingTrace) {
const orderedTrace = dataDump.orderObjectByValues(this.aggregatedTrace);
dataDump.writeJsonToFile(orderedTrace, path);
} else {
// Writing in chunks of 1000 instructions to limit memory usage,
// as raw traces might grow big.
dataDump.writeToFileInChunks(this.rawTrace, path, 1000);
}
}
}

Expand Down
37 changes: 36 additions & 1 deletion tests/zkasm/helpers/data-dump.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,41 @@ function writeToFileInChunks(lines, filePath, chunkSize) {
}
}

/**
* Serializes `obj` as JSON string and writes that string to `filePath`.
*
* @param {Object} obj
* @param {string} filePath
*/
function writeJsonToFile(obj, filePath) {
const jsonString = JSON.stringify(obj, null, 4);
fs.writeFileSync(filePath, jsonString);
}

/**
* Returns a shallow clone of `obj` with fields ordered by value (descending).
*
* Object key order can be relied upon in recent versions of Node.js, see
* https://node.green/#ES2015-misc-own-property-order
*
* @param {Object} obj
* @returns {Object}
* @example
* // {a: 2, b: 4} will be ordered to {b: 4, a: 2}
*/
function orderObjectByValues(obj) {
const compareFn = ([_key1, val1], [_key2, val2]) => val1 > val2 ? -1:1;
return Object.entries(obj).sort(compareFn).reduce(
(accumulator, [key, val]) => {
accumulator[key] = val;
return accumulator;
},
{}
);
}

module.exports = {
writeToFileInChunks
orderObjectByValues,
writeToFileInChunks,
writeJsonToFile
}
26 changes: 22 additions & 4 deletions tests/zkasm/run-tests-zkasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,21 @@ async function main() {
describe: "If provided, results are written to this file. Otherwise they are printed to stdout.",
type: "string"
})
yargs.options({
"aggregate": {
type: "boolean",
default: false,
describe: "Summarize how often each instruction was executed instead of collecting the raw trace."
}
})
},
handler: (argv) => profileInstructions(argv.path, argv.outfile)
handler: (argv) => profileInstructions(
argv.path,
{
aggregate: argv.aggregate,
},
argv.outfile
)
})
.parse();
}
Expand Down Expand Up @@ -199,18 +212,23 @@ async function runTest(pathTest, cmPols) {
* trace of executed instructions.
*
* @param {string} zkasmFile - Path to the zkASM file.
* @param {Object} settings
* @param {boolean} settings.aggregate - Summarize how often each instruction
* was executed instead of collecting the raw trace.
* @param {string} [outfile] - Path to a file where output is written. If not
* given, the trace is written to `stdout`.
*/
async function profileInstructions(zkasmFile, outfile) {
async function profileInstructions(zkasmFile, { aggregate } ,outfile) {
const configZkasm = {
defines: [],
allowUndefinedLabels: true,
allowOverwriteLabels: true,
};

// Construct helper classes.
const instructionTracer = new InstructionTracer();
const instructionTracer = new InstructionTracer({
aggregateTrace: aggregate
});

// Compile rom.
const config = {
Expand All @@ -235,7 +253,7 @@ async function profileInstructions(zkasmFile, outfile) {
}

if (outfile) {
instructionTracer.writeRawTrace(outfile);
instructionTracer.writeTrace(outfile);
} else {
console.log(instructionTracer.rawTrace);
}
Expand Down