Skip to content

Commit

Permalink
feat: tally AVM opcodes executed in simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
dbanks12 committed Oct 28, 2024
1 parent 2823cbb commit c200bc5
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 4 deletions.
4 changes: 2 additions & 2 deletions yarn-project/protocol-contracts/src/protocol_contract_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export const ProtocolContractLeaf = {
ContractClassRegisterer: Fr.fromString('0x147ba3294403576dbad10f86d3ffd4eb83fb230ffbcd5c8b153dd02942d0611f'),
MultiCallEntrypoint: Fr.fromString('0x154b701b41d6cf6da7204fef36b2ee9578b449d21b3792a9287bf45eba48fd26'),
FeeJuice: Fr.fromString('0x21c9ab2e339c9b3394e4e1ff3b7cf37be4e0fc0bc177a192d287d98963b9b254'),
Router: Fr.fromString('0x2779d7e4ccba389da358a0c9362364d0c65e14cd4d9df929e6722187e808e068'),
Router: Fr.fromString('0x2b0b558e92b7a13cde0a2ecc7570c181a6fbae2bdc6f966cacfc39a784635394'),
};

export const protocolContractTreeRoot = Fr.fromString(
'0x00724e4de088411c873c3d6975491eb48889bfa51bc854744a4fcc307ee9abd8',
'0x0d560ad12f14dd5026070bc037ac343535db339212f0904dfc96c4aea4dcc8ab',
);
67 changes: 65 additions & 2 deletions yarn-project/simulator/src/avm/avm_simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { strict as assert } from 'assert';
import { SideEffectLimitReachedError } from '../public/side_effect_errors.js';
import type { AvmContext } from './avm_context.js';
import { AvmContractCallResult } from './avm_contract_call_result.js';
import { type Gas } from './avm_gas.js';
import { isAvmBytecode } from './bytecode_utils.js';
import {
AvmExecutionError,
Expand All @@ -17,9 +18,21 @@ import {
import type { Instruction } from './opcodes/index.js';
import { decodeFromBytecode } from './serialization/bytecode_serialization.js';

type OpcodeTally = {
count: number;
gas: Gas;
};
type PcTally = {
opcode: string;
count: number;
gas: Gas;
};

export class AvmSimulator {
private log: DebugLogger;
private bytecode: Buffer | undefined;
public opcodeTallies: Map<string, OpcodeTally> = new Map();
public pcTallies: Map<number, PcTally> = new Map();

constructor(private context: AvmContext) {
assert(
Expand Down Expand Up @@ -79,13 +92,24 @@ export class AvmSimulator {
'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
);

const gasLeft = `l2=${machineState.l2GasLeft} da=${machineState.daGasLeft}`;
this.log.debug(`@${machineState.pc} ${instruction.toString()} (${gasLeft})`);
const instrStartGas = machineState.gasLeft; // Save gas before executing instruction (for profiling)
const instrPc = machineState.pc; // Save PC before executing instruction (for profiling)

this.log.debug(
`@${machineState.pc} ${instruction.toString()} (l2=${machineState.l2GasLeft} da=${machineState.daGasLeft})`,
);
// Execute the instruction.
// Normal returns and reverts will return normally here.
// "Exceptional halts" will throw.
await instruction.execute(this.context);

// gas used by this instruction - used for profiling/tallying
const gasUsed: Gas = {
l2Gas: instrStartGas.l2Gas - machineState.l2GasLeft,
daGas: instrStartGas.daGas - machineState.daGasLeft,
};
this.tallyInstruction(instrPc, instruction.constructor.name, gasUsed);

if (machineState.pc >= instructions.length) {
this.log.warn('Passed end of program');
throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
Expand All @@ -97,6 +121,8 @@ export class AvmSimulator {
const revertReason = reverted ? revertReasonFromExplicitRevert(output, this.context) : undefined;
const results = new AvmContractCallResult(reverted, output, revertReason);
this.log.debug(`Context execution results: ${results.toString()}`);

this.printOpcodeTallies();
// Return results for processing by calling context
return results;
} catch (err: any) {
Expand All @@ -110,8 +136,45 @@ export class AvmSimulator {
// Note: "exceptional halts" cannot return data, hence []
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], revertReason);
this.log.debug(`Context execution results: ${results.toString()}`);

this.printOpcodeTallies();
// Return results for processing by calling context
return results;
}
}

private tallyInstruction(pc: number, opcode: string, gasUsed: Gas) {
const opcodeTally = this.opcodeTallies.get(opcode) || ({ count: 0, gas: { l2Gas: 0, daGas: 0 } } as OpcodeTally);
opcodeTally.count++;
opcodeTally.gas.l2Gas += gasUsed.l2Gas;
opcodeTally.gas.daGas += gasUsed.daGas;
this.opcodeTallies.set(opcode, opcodeTally);

const pcTally = this.pcTallies.get(pc) || ({ opcode: opcode, count: 0, gas: { l2Gas: 0, daGas: 0 } } as PcTally);
pcTally.count++;
pcTally.gas.l2Gas += gasUsed.l2Gas;
pcTally.gas.daGas += gasUsed.daGas;
this.pcTallies.set(pc, pcTally);
}

private printOpcodeTallies() {
this.log.debug(`Printing tallies per opcode sorted by gas...`);
// sort descending by L2 gas consumed
const sortedOpcodes = Array.from(this.opcodeTallies.entries()).sort((a, b) => b[1].gas.l2Gas - a[1].gas.l2Gas);
for (const [opcode, tally] of sortedOpcodes) {
// NOTE: don't care to clutter the logs with DA gas for now
this.log.debug(`${opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`);
}

this.log.debug(`Printing tallies per PC sorted by #times each PC was executed...`);
const sortedPcs = Array.from(this.pcTallies.entries())
.sort((a, b) => b[1].count - a[1].count)
.filter((_, i) => i < 20);
for (const [pc, tally] of sortedPcs) {
// NOTE: don't care to clutter the logs with DA gas for now
this.log.debug(
`PC:${pc} containing opcode ${tally.opcode} executed ${tally.count} times consuming a total of ${tally.gas.l2Gas} L2 gas`,
);
}
}
}

0 comments on commit c200bc5

Please sign in to comment.