Skip to content

Commit 623f852

Browse files
zerosnacksmattsse
andauthored
feat(anvil): add support for trace logging in Anvil (#9895)
* add basic tracing, at the moment insufficient because we dont access local artifacts in Anvil yet * only enable printing conditionally * fix * fix * prefer using node_info! macro to stay consistent with how logging is performed in Anvil * touchups * add decoding support --------- Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
1 parent a2ecefc commit 623f852

File tree

6 files changed

+93
-6
lines changed

6 files changed

+93
-6
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/anvil/src/cmd.rs

+5
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ impl NodeArgs {
259259
.with_genesis(self.init)
260260
.with_steps_tracing(self.evm.steps_tracing)
261261
.with_print_logs(!self.evm.disable_console_log)
262+
.with_print_traces(self.evm.print_traces)
262263
.with_auto_impersonate(self.evm.auto_impersonate)
263264
.with_ipc(self.ipc)
264265
.with_code_size_limit(self.evm.code_size_limit)
@@ -557,6 +558,10 @@ pub struct AnvilEvmArgs {
557558
#[arg(long, visible_alias = "no-console-log")]
558559
pub disable_console_log: bool,
559560

561+
/// Enable printing of traces for executed transactions and `eth_call` to stdout.
562+
#[arg(long, visible_alias = "enable-trace-printing")]
563+
pub print_traces: bool,
564+
560565
/// Enables automatic impersonation on startup. This allows any transaction sender to be
561566
/// simulated as different accounts, which is useful for testing contract behavior.
562567
#[arg(long, visible_alias = "auto-unlock")]

crates/anvil/src/config.rs

+11
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ pub struct NodeConfig {
155155
pub enable_steps_tracing: bool,
156156
/// Enable printing of `console.log` invocations.
157157
pub print_logs: bool,
158+
/// Enable printing of traces.
159+
pub print_traces: bool,
158160
/// Enable auto impersonation of accounts on startup
159161
pub enable_auto_impersonate: bool,
160162
/// Configure the code size limit
@@ -446,6 +448,7 @@ impl Default for NodeConfig {
446448
enable_tracing: true,
447449
enable_steps_tracing: false,
448450
print_logs: true,
451+
print_traces: false,
449452
enable_auto_impersonate: false,
450453
no_storage_caching: false,
451454
server_config: Default::default(),
@@ -879,6 +882,13 @@ impl NodeConfig {
879882
self
880883
}
881884

885+
/// Sets whether to print traces to stdout.
886+
#[must_use]
887+
pub fn with_print_traces(mut self, print_traces: bool) -> Self {
888+
self.print_traces = print_traces;
889+
self
890+
}
891+
882892
/// Sets whether to enable autoImpersonate
883893
#[must_use]
884894
pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
@@ -1069,6 +1079,7 @@ impl NodeConfig {
10691079
Arc::new(RwLock::new(fork)),
10701080
self.enable_steps_tracing,
10711081
self.print_logs,
1082+
self.print_traces,
10721083
self.odyssey,
10731084
self.prune_history,
10741085
self.max_persisted_states,

crates/anvil/src/eth/backend/executor.rs

+8
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
108108
pub enable_steps_tracing: bool,
109109
pub odyssey: bool,
110110
pub print_logs: bool,
111+
pub print_traces: bool,
111112
/// Precompiles to inject to the EVM.
112113
pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
113114
}
@@ -312,6 +313,9 @@ impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExec
312313
if self.print_logs {
313314
inspector = inspector.with_log_collector();
314315
}
316+
if self.print_traces {
317+
inspector = inspector.with_trace_printer();
318+
}
315319

316320
let exec_result = {
317321
let mut evm = new_evm_with_inspector(&mut *self.db, env, &mut inspector, self.odyssey);
@@ -345,6 +349,10 @@ impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExec
345349
}
346350
}
347351
};
352+
353+
if self.print_traces {
354+
inspector.print_traces();
355+
}
348356
inspector.print_logs();
349357

350358
let (exit_reason, gas_used, out, logs) = match exec_result {

crates/anvil/src/eth/backend/mem/inspector.rs

+50-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Anvil specific [`revm::Inspector`] implementation
22
3-
use crate::revm::Database;
3+
use crate::{eth::macros::node_info, revm::Database};
44
use alloy_primitives::{Address, Log};
55
use foundry_evm::{
66
call_inspectors,
@@ -13,14 +13,17 @@ use foundry_evm::{
1313
primitives::U256,
1414
EvmContext,
1515
},
16-
traces::TracingInspectorConfig,
16+
traces::{
17+
render_trace_arena_inner, CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig,
18+
},
1719
};
1820

1921
/// The [`revm::Inspector`] used when transacting in the evm
2022
#[derive(Clone, Debug, Default)]
2123
pub struct Inspector {
24+
/// Collects all traces
2225
pub tracer: Option<TracingInspector>,
23-
/// collects all `console.sol` logs
26+
/// Collects all `console.sol` logs
2427
pub log_collector: Option<LogCollector>,
2528
}
2629

@@ -34,6 +37,21 @@ impl Inspector {
3437
}
3538
}
3639

40+
/// Consumes the type and prints the traces.
41+
pub fn into_print_traces(mut self) {
42+
if let Some(a) = self.tracer.take() {
43+
print_traces(a)
44+
}
45+
}
46+
47+
/// Called after the inspecting the evm
48+
/// This will log all traces
49+
pub fn print_traces(&self) {
50+
if let Some(a) = self.tracer.clone() {
51+
print_traces(a)
52+
}
53+
}
54+
3755
/// Configures the `Tracer` [`revm::Inspector`]
3856
pub fn with_tracing(mut self) -> Self {
3957
self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
@@ -51,11 +69,39 @@ impl Inspector {
5169
self
5270
}
5371

54-
/// Configures the `Tracer` [`revm::Inspector`]
72+
/// Configures the `Tracer` [`revm::Inspector`] with a log collector
5573
pub fn with_log_collector(mut self) -> Self {
5674
self.log_collector = Some(Default::default());
5775
self
5876
}
77+
78+
/// Configures the `Tracer` [`revm::Inspector`] with a trace printer
79+
pub fn with_trace_printer(mut self) -> Self {
80+
self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
81+
self
82+
}
83+
}
84+
85+
/// Prints the traces for the inspector
86+
///
87+
/// Caution: This blocks on call trace decoding
88+
///
89+
/// # Panics
90+
///
91+
/// If called outside tokio runtime
92+
fn print_traces(tracer: TracingInspector) {
93+
let arena = tokio::task::block_in_place(move || {
94+
tokio::runtime::Handle::current().block_on(async move {
95+
let mut arena = tracer.into_traces();
96+
let decoder = CallTraceDecoder::new();
97+
decoder.populate_traces(arena.nodes_mut()).await;
98+
arena
99+
})
100+
});
101+
102+
let traces = SparsedTraceArena { arena, ignored: Default::default() };
103+
node_info!("Traces:");
104+
node_info!("{}", render_trace_arena_inner(&traces, false, true));
59105
}
60106

61107
impl<DB: Database> revm::Inspector<DB> for Inspector {

crates/anvil/src/eth/backend/mem/mod.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ pub struct Backend {
204204
active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
205205
enable_steps_tracing: bool,
206206
print_logs: bool,
207+
print_traces: bool,
207208
odyssey: bool,
208209
/// How to keep history state
209210
prune_state_history_config: PruneStateHistoryConfig,
@@ -232,6 +233,7 @@ impl Backend {
232233
fork: Arc<RwLock<Option<ClientFork>>>,
233234
enable_steps_tracing: bool,
234235
print_logs: bool,
236+
print_traces: bool,
235237
odyssey: bool,
236238
prune_state_history_config: PruneStateHistoryConfig,
237239
max_persisted_states: Option<usize>,
@@ -336,6 +338,7 @@ impl Backend {
336338
active_state_snapshots: Arc::new(Mutex::new(Default::default())),
337339
enable_steps_tracing,
338340
print_logs,
341+
print_traces,
339342
odyssey,
340343
prune_state_history_config,
341344
transaction_block_keeper,
@@ -1092,6 +1095,10 @@ impl Backend {
10921095
drop(evm);
10931096
inspector.print_logs();
10941097

1098+
if self.print_traces {
1099+
inspector.print_traces();
1100+
}
1101+
10951102
Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default()))
10961103
}
10971104

@@ -1132,6 +1139,7 @@ impl Backend {
11321139
blob_gas_used: 0,
11331140
enable_steps_tracing: self.enable_steps_tracing,
11341141
print_logs: self.print_logs,
1142+
print_traces: self.print_traces,
11351143
precompile_factory: self.precompile_factory.clone(),
11361144
odyssey: self.odyssey,
11371145
};
@@ -1215,6 +1223,7 @@ impl Backend {
12151223
blob_gas_used: 0,
12161224
enable_steps_tracing: self.enable_steps_tracing,
12171225
print_logs: self.print_logs,
1226+
print_traces: self.print_traces,
12181227
odyssey: self.odyssey,
12191228
precompile_factory: self.precompile_factory.clone(),
12201229
};
@@ -1461,13 +1470,16 @@ impl Backend {
14611470
env
14621471
}
14631472

1464-
/// Builds [`Inspector`] with the configured options
1473+
/// Builds [`Inspector`] with the configured options.
14651474
fn build_inspector(&self) -> Inspector {
14661475
let mut inspector = Inspector::default();
14671476

14681477
if self.print_logs {
14691478
inspector = inspector.with_log_collector();
14701479
}
1480+
if self.print_traces {
1481+
inspector = inspector.with_trace_printer();
1482+
}
14711483

14721484
inspector
14731485
}
@@ -1708,6 +1720,11 @@ impl Backend {
17081720
};
17091721
drop(evm);
17101722
inspector.print_logs();
1723+
1724+
if self.print_traces {
1725+
inspector.into_print_traces();
1726+
}
1727+
17111728
Ok((exit_reason, out, gas_used as u128, state))
17121729
}
17131730

0 commit comments

Comments
 (0)