Skip to content

Add opcode-level execution tracing #89

@mw2000

Description

@mw2000

Summary

Add optional step-by-step execution tracing that logs each opcode execution with stack, memory, gas, and storage state. Essential for debugging test failures and comparing execution traces against reference implementations.

Specification

Trace entry per step:

%TraceStep{
  pc: non_neg_integer(),
  op: atom(),               # e.g., :ADD, :SSTORE
  gas_remaining: non_neg_integer(),
  gas_cost: non_neg_integer(),
  stack: [non_neg_integer()],
  memory_size: non_neg_integer(),
  depth: non_neg_integer(),  # call depth
  return_data: binary(),
  error: atom() | nil
}

Output Format

Compatible with evm --json trace format for easy diffing against geth/revm traces.

Implementation Guide

  1. Create lib/eevm/tracer.ex — trace collector (functional, no GenServer)
  2. Add optional tracer field to MachineStatenil (disabled) or %Tracer{}
  3. Hook into Executor.step/1 — before each opcode, record trace entry if tracer enabled
  4. Add EEVM.execute/3 variant that accepts trace options
  5. JSON outputTracer.to_json/1 for geth-compatible format
  6. Tests: trace a simple program, verify gas costs per step, verify stack states

Acceptance Criteria

  • Tracing is opt-in (zero overhead when disabled)
  • Each step records PC, opcode, gas, stack, memory size, depth
  • JSON output compatible with geth trace format
  • Works with nested calls (tracks depth)
  • Tests pass

Reference

  • geth --json trace format
  • revm TracerEip3155

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestinfraInfrastructure and configuration

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions