reth_evm/
metrics.rs

1//! Executor metrics.
2use alloy_consensus::BlockHeader;
3use metrics::{Counter, Gauge, Histogram};
4use reth_metrics::Metrics;
5use reth_primitives_traits::{Block, RecoveredBlock};
6use std::time::Instant;
7
8/// Executor metrics.
9// TODO(onbjerg): add sload/sstore
10#[derive(Metrics, Clone)]
11#[metrics(scope = "sync.execution")]
12pub struct ExecutorMetrics {
13    /// The total amount of gas processed.
14    pub gas_processed_total: Counter,
15    /// The instantaneous amount of gas processed per second.
16    pub gas_per_second: Gauge,
17    /// The Histogram for amount of gas used.
18    pub gas_used_histogram: Histogram,
19
20    /// The Histogram for amount of time taken to execute blocks.
21    pub execution_histogram: Histogram,
22    /// The total amount of time it took to execute the latest block.
23    pub execution_duration: Gauge,
24
25    /// The Histogram for number of accounts loaded when executing the latest block.
26    pub accounts_loaded_histogram: Histogram,
27    /// The Histogram for number of storage slots loaded when executing the latest block.
28    pub storage_slots_loaded_histogram: Histogram,
29    /// The Histogram for number of bytecodes loaded when executing the latest block.
30    pub bytecodes_loaded_histogram: Histogram,
31
32    /// The Histogram for number of accounts updated when executing the latest block.
33    pub accounts_updated_histogram: Histogram,
34    /// The Histogram for number of storage slots updated when executing the latest block.
35    pub storage_slots_updated_histogram: Histogram,
36    /// The Histogram for number of bytecodes updated when executing the latest block.
37    pub bytecodes_updated_histogram: Histogram,
38}
39
40impl ExecutorMetrics {
41    /// Helper function for metered execution
42    fn metered<F, R>(&self, f: F) -> R
43    where
44        F: FnOnce() -> (u64, R),
45    {
46        // Execute the block and record the elapsed time.
47        let execute_start = Instant::now();
48        let (gas_used, output) = f();
49        let execution_duration = execute_start.elapsed().as_secs_f64();
50
51        // Update gas metrics.
52        self.gas_processed_total.increment(gas_used);
53        self.gas_per_second.set(gas_used as f64 / execution_duration);
54        self.gas_used_histogram.record(gas_used as f64);
55        self.execution_histogram.record(execution_duration);
56        self.execution_duration.set(execution_duration);
57
58        output
59    }
60
61    /// Execute a block and update basic gas/timing metrics.
62    ///
63    /// This is a simple helper that tracks execution time and gas usage.
64    /// For more complex metrics tracking (like state changes), use the
65    /// metered execution functions in the engine/tree module.
66    pub fn metered_one<F, R, B>(&self, block: &RecoveredBlock<B>, f: F) -> R
67    where
68        F: FnOnce(&RecoveredBlock<B>) -> R,
69        B: Block,
70        B::Header: BlockHeader,
71    {
72        self.metered(|| (block.header().gas_used(), f(block)))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use alloy_consensus::Header;
80    use alloy_primitives::B256;
81    use reth_ethereum_primitives::Block;
82    use reth_primitives_traits::Block as BlockTrait;
83
84    fn create_test_block_with_gas(gas_used: u64) -> RecoveredBlock<Block> {
85        let header = Header { gas_used, ..Default::default() };
86        let block = Block { header, body: Default::default() };
87        // Use a dummy hash for testing
88        let hash = B256::default();
89        let sealed = block.seal_unchecked(hash);
90        RecoveredBlock::new_sealed(sealed, Default::default())
91    }
92
93    #[test]
94    fn test_metered_one_updates_metrics() {
95        let metrics = ExecutorMetrics::default();
96        let block = create_test_block_with_gas(1000);
97
98        // Execute with metered_one
99        let result = metrics.metered_one(&block, |b| {
100            // Simulate some work
101            std::thread::sleep(std::time::Duration::from_millis(10));
102            b.header().gas_used()
103        });
104
105        // Verify result
106        assert_eq!(result, 1000);
107    }
108
109    #[test]
110    fn test_metered_helper_tracks_timing() {
111        let metrics = ExecutorMetrics::default();
112
113        let result = metrics.metered(|| {
114            // Simulate some work
115            std::thread::sleep(std::time::Duration::from_millis(10));
116            (500, "test_result")
117        });
118
119        assert_eq!(result, "test_result");
120    }
121}