From c200bc529a3c3590effc787a07e4d58b3ac4bfac Mon Sep 17 00:00:00 2001 From: dbanks12 Date: Mon, 28 Oct 2024 13:55:42 +0000 Subject: [PATCH] feat: tally AVM opcodes executed in simulator --- .../src/protocol_contract_data.ts | 4 +- .../simulator/src/avm/avm_simulator.ts | 67 ++++++++++++++++++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index 11810f96b37..8512d948c85 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -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', ); diff --git a/yarn-project/simulator/src/avm/avm_simulator.ts b/yarn-project/simulator/src/avm/avm_simulator.ts index 9a6012ef566..a6dd4c636a1 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.ts @@ -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, @@ -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 = new Map(); + public pcTallies: Map = new Map(); constructor(private context: AvmContext) { assert( @@ -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); @@ -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) { @@ -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`, + ); + } + } }