foundry_evm/inspectors/
custom_printer.rs

1//! Custom print inspector, it has step level information of execution.
2//! It is a great tool if some debugging is needed.
3
4use foundry_common::sh_println;
5use foundry_evm_core::backend::DatabaseError;
6use revm::{
7    bytecode::opcode::OpCode,
8    context::{ContextTr, JournalTr},
9    inspector::{inspectors::GasInspector, JournalExt},
10    interpreter::{
11        interpreter::EthInterpreter,
12        interpreter_types::{Jumps, LoopControl, MemoryTr},
13        CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
14    },
15    primitives::{Address, U256},
16    Database, Inspector,
17};
18
19/// Custom print [Inspector], it has step level information of execution.
20///
21/// It is a great tool if some debugging is needed.
22#[derive(Clone, Debug, Default)]
23pub struct CustomPrintTracer {
24    gas_inspector: GasInspector,
25}
26
27impl<CTX, D> Inspector<CTX, EthInterpreter> for CustomPrintTracer
28where
29    D: Database<Error = DatabaseError>,
30    CTX: ContextTr<Db = D>,
31    CTX::Journal: JournalExt,
32{
33    fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
34        self.gas_inspector.initialize_interp(&interp.control.gas);
35    }
36
37    // get opcode by calling `interp.contract.opcode(interp.program_counter())`.
38    // all other information can be obtained from interp.
39    fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) {
40        let opcode = interp.bytecode.opcode();
41        let name = OpCode::name_by_op(opcode);
42
43        let gas_remaining = self.gas_inspector.gas_remaining();
44
45        let memory_size = interp.memory.size();
46
47        let _ = sh_println!(
48            "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?})  refund:{:#x}({}) Stack:{:?}, Data size:{}",
49            context.journal().depth(),
50            interp.bytecode.pc(),
51            gas_remaining,
52            gas_remaining,
53            name,
54            opcode,
55            interp.control.gas.refunded(),
56            interp.control.gas.refunded(),
57            interp.stack.data(),
58            memory_size,
59        );
60
61        self.gas_inspector.step(&interp.control.gas);
62    }
63
64    fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
65        self.gas_inspector.step_end(interp.control.gas_mut());
66    }
67
68    fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) {
69        self.gas_inspector.call_end(outcome)
70    }
71
72    fn create_end(
73        &mut self,
74        _context: &mut CTX,
75        _inputs: &CreateInputs,
76        outcome: &mut CreateOutcome,
77    ) {
78        self.gas_inspector.create_end(outcome)
79    }
80
81    fn call(&mut self, _context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
82        let _ = sh_println!(
83            "SM Address: {:?}, caller:{:?},target:{:?} is_static:{:?}, transfer:{:?}, input_size:{:?}",
84            inputs.bytecode_address,
85            inputs.caller,
86            inputs.target_address,
87            inputs.is_static,
88            inputs.value,
89            inputs.input.len(),
90        );
91        None
92    }
93
94    fn create(&mut self, _context: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
95        let _ = sh_println!(
96            "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}",
97            inputs.caller,
98            inputs.scheme,
99            inputs.value,
100            inputs.init_code,
101            inputs.gas_limit
102        );
103        None
104    }
105
106    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
107        let _ = sh_println!(
108            "SELFDESTRUCT: contract: {:?}, refund target: {:?}, value {:?}",
109            contract,
110            target,
111            value
112        );
113    }
114}