The Soroban Debugger now supports instruction-by-instruction stepping through WASM bytecode, providing fine-grained control over contract execution.
Instruction-level debugging allows you to:
- Step through individual WASM instructions
- Examine the exact execution flow at the bytecode level
- Understand performance characteristics at the instruction level
- Debug complex contract logic with precision
- Correlate high-level code with low-level WASM operations
Enable instruction-level debugging with the --instruction-debug flag:
soroban-debug run --contract token.wasm --function transfer --args '["Alice", "Bob", 100]' --instruction-debugStart with instruction stepping enabled:
soroban-debug run --contract token.wasm --function transfer --args '["Alice", "Bob", 100]' --instruction-debug --step-instructionsThis will enter an interactive stepping mode where you can control execution instruction by instruction.
You can specify different step modes:
# Step into every instruction (default)
soroban-debug run --contract token.wasm --function transfer --instruction-debug --step-instructions --step-mode into
# Step over function calls
soroban-debug run --contract token.wasm --function transfer --instruction-debug --step-instructions --step-mode over
# Step to next basic block
soroban-debug run --contract token.wasm --function transfer --instruction-debug --step-instructions --step-mode blockWhen in instruction stepping mode, the following commands are available:
n,next- Step to the next instructions,step,into- Step into the next instruction (same as next in instruction mode)o,over- Step over function calls (don't step into)u,out- Step out of the current functionb,block- Step to the next basic block (control flow instruction)p,prev,back- Step back to the previous instruction
i,info- Show detailed instruction and execution statectx,context- Display instruction context (prompts for context size)h,help- Show all available commands
c,continue- Continue execution until completionq,quit,exit- Exit instruction stepping mode
=== Instruction Stepping Mode ===
Type 'help' for available commands
┌─ Instruction Context ─────────────────────────────┐
│ 0: ► 00000100: i32.const 42 │
│ 1: 00000105: local.set $0 │
│ 2: 00000107: local.get $0 │
│ 3: 0000010a: i32.const 1 │
│ 4: 0000010f: i32.add │
└───────────────────────────────────────────────────┘
(step) > n
Stepped to: 00000105: local.set $0
(step) > info
┌─ Instruction Pointer ─────────────────────────────┐
│ Current Index: 1 │
│ Call Depth: 0 │
│ Step Mode: Step Into │
│ Stepping: Active │
└───────────────────────────────────────────────────┘
┌─ Execution Progress ──────────────────────────────┐
│ Total Instructions: 126 │
│ Current Position: 1 │
│ Instructions Executed: 2 │
│ Progress: 0.8% │
│ [█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] │
└───────────────────────────────────────────────────┘
(step) > continue
Continuing execution...
Execution completed. Result: "Success"
Parses WASM bytecode and extracts individual instructions with their metadata:
- Instruction struct: Represents a single WASM instruction with offset, opcode, and context
- InstructionParser: Parses WASM modules and extracts all instructions
- Instruction formatting: Human-readable display of instructions
Manages execution position and stepping state:
- InstructionPointer: Tracks current position, call stack depth, and execution history
- StepMode enum: Defines different stepping modes (into, over, out, block)
- History management: Maintains execution history for backward stepping
Extended to support instruction-level information:
- Instruction storage: Holds all parsed instructions for the contract
- Current instruction tracking: Maintains reference to currently executing instruction
- Stepping state management: Tracks whether instruction debugging is enabled
Implements instruction-level stepping logic:
- Step modes: Implements different stepping strategies
- Execution control: Manages when to pause and continue
- State coordination: Works with debug state to track execution
Core orchestrator with instruction-level capabilities:
- Instruction debugging control: Enable/disable instruction-level debugging
- Stepping interface: Provides step_into, step_over, step_out, step_back methods
- Execution coordination: Integrates with WASM execution to enable stepping
- WASM Parsing:
InstructionParserextracts all instructions from WASM bytecode - State Initialization:
DebugStatestores instructions and initializes instruction pointer - Instrumentation:
Instrumenter(future) will add callbacks for execution control - Stepping Control: User commands trigger stepping operations via
Stepper - Execution Management:
DebuggerEnginecoordinates between stepping and execution - Display:
Formatterprovides user-friendly instruction and state display
The current implementation provides a foundation for instruction-level debugging:
- Parsing-based: Extracts instructions from WASM for display and analysis
- Simulation: Steps through instructions conceptually without runtime integration
- Interactive UI: Provides full stepping interface and state display
For full runtime integration, the instrumentation system will:
- WASM Modification: Use
walrusto inject debug callbacks before each instruction - Runtime Hooks: Add callback functions that pause execution and return control to debugger
- State Synchronization: Keep debugger state synchronized with actual execution state
- Performance Optimization: Provide debug mode toggle to minimize overhead
Current limitations and future improvements:
- Simulation vs. Runtime: Current implementation simulates stepping rather than controlling actual execution
- WASM Integration: Full integration requires hooking into Soroban's WASM execution environment
- Performance: Runtime instrumentation may impact execution speed
- Source Mapping: Future versions could correlate instructions with Rust source code
Instruction-level debugging can be toggled on/off to minimize performance impact:
# Normal execution (fast)
soroban-debug run --contract token.wasm --function transfer
# With instruction debugging (slower but detailed)
soroban-debug run --contract token.wasm --function transfer --instruction-debug- Lazy Instrumentation: Only instrument when debug mode is enabled
- Basic Block Stepping: Step through groups of instructions for better performance
- Selective Instrumentation: Only instrument functions of interest
- History Limits: Limit instruction history to prevent memory bloat
# Debug a simple token transfer with instruction stepping
soroban-debug run \
--contract examples/token.wasm \
--function transfer \
--args '["ALICE", "BOB", 1000]' \
--instruction-debug \
--step-instructions# Combine function breakpoints with instruction stepping
soroban-debug run \
--contract examples/complex.wasm \
--function process \
--args '{"data": [1,2,3,4,5]}' \
--breakpoint validate \
--breakpoint compute \
--instruction-debug# Use block stepping for performance analysis
soroban-debug run \
--contract examples/heavy.wasm \
--function compute_heavy \
--instruction-debug \
--step-instructions \
--step-mode block \
--verboseComprehensive tests ensure reliability:
- Instruction parsing accuracy
- Step mode behavior
- Instruction pointer management
- State synchronization
- Full stepping workflow
- Command interface
- Error handling
- Performance benchmarks
- Instruction parsing speed
- Memory usage with large contracts
- Step operation latency
Run tests with:
cargo test instruction_stepping
cargo test --test instruction_stepping_tests- Source Code Mapping: Correlate WASM instructions with Rust source code
- Runtime Integration: Full integration with Soroban execution environment
- Visual Debugger: GUI interface for instruction stepping
- Advanced Breakpoints: Instruction-level and conditional breakpoints
- Execution Recording: Record and replay execution sessions
- Conditional Stepping: Step only when certain conditions are met
- Instruction Breakpoints: Break at specific instruction addresses
- Memory Watches: Track memory changes during execution
- Gas/Budget Analysis: Correlate instructions with resource usage
This instruction-level stepping feature provides a powerful foundation for detailed contract debugging and analysis, enabling developers to understand their code at the most granular level possible.