sp1_core_executor/
report.rs1use std::{
2 fmt::{Display, Formatter, Result as FmtResult},
3 ops::{Add, AddAssign},
4};
5
6use enum_map::{EnumArray, EnumMap};
7use hashbrown::HashMap;
8
9use crate::{
10 events::{generate_execution_report, MemInstrEvent, PrecompileEvent, SyscallEvent},
11 ITypeRecord, Opcode, SyscallCode,
12};
13
14const GAS_NORMALIZATION_FACTOR: u64 = 191;
17
18#[derive(Default, Debug, Clone, PartialEq, Eq)]
20pub struct ExecutionReport {
21 pub opcode_counts: Box<EnumMap<Opcode, u64>>,
23 pub syscall_counts: Box<EnumMap<SyscallCode, u64>>,
25 pub cycle_tracker: HashMap<String, u64>,
27 pub invocation_tracker: HashMap<String, u64>,
29 pub touched_memory_addresses: u64,
31 pub exit_code: u64,
33 pub(crate) gas: Option<u64>,
35}
36
37impl ExecutionReport {
38 #[must_use]
40 pub fn total_instruction_count(&self) -> u64 {
41 self.opcode_counts.values().sum()
42 }
43
44 #[must_use]
46 pub fn total_syscall_count(&self) -> u64 {
47 self.syscall_counts.values().sum()
48 }
49
50 #[must_use]
52 pub fn total_record_size(&self) -> u64 {
53 let avg_opcode_record_size = std::mem::size_of::<(MemInstrEvent, ITypeRecord)>();
57 let total_opcode_records_size_bytes =
58 self.opcode_counts.values().sum::<u64>() * avg_opcode_record_size as u64;
59
60 let syscall_avg_record_size = std::mem::size_of::<(SyscallEvent, PrecompileEvent)>() + 512;
63 let total_syscall_records_size_bytes =
64 self.syscall_counts.values().sum::<u64>() * syscall_avg_record_size as u64;
65
66 total_opcode_records_size_bytes + total_syscall_records_size_bytes
67 }
68
69 #[must_use]
72 pub fn gas(&self) -> Option<u64> {
73 self.gas.map(|g| g * 10 / GAS_NORMALIZATION_FACTOR)
75 }
76}
77
78fn counts_add_assign<K, V>(lhs: &mut EnumMap<K, V>, rhs: EnumMap<K, V>)
80where
81 K: EnumArray<V>,
82 V: AddAssign,
83{
84 for (k, v) in rhs {
85 lhs[k] += v;
86 }
87}
88
89impl AddAssign for ExecutionReport {
90 fn add_assign(&mut self, rhs: Self) {
91 counts_add_assign(&mut self.opcode_counts, *rhs.opcode_counts);
92 counts_add_assign(&mut self.syscall_counts, *rhs.syscall_counts);
93 self.touched_memory_addresses += rhs.touched_memory_addresses;
94
95 for (label, count) in rhs.cycle_tracker {
97 *self.cycle_tracker.entry(label).or_insert(0) += count;
98 }
99 for (label, count) in rhs.invocation_tracker {
100 *self.invocation_tracker.entry(label).or_insert(0) += count;
101 }
102
103 self.gas = match (self.gas, rhs.gas) {
105 (Some(c1), Some(c2)) => Some(c1 + c2),
106 (Some(g), None) | (None, Some(g)) => Some(g),
107 (None, None) => None,
108 };
109
110 self.exit_code |= rhs.exit_code;
112 }
113}
114
115impl Add for ExecutionReport {
116 type Output = Self;
117
118 fn add(mut self, rhs: Self) -> Self::Output {
119 self += rhs;
120 self
121 }
122}
123
124impl Display for ExecutionReport {
125 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
126 if let Some(gas) = self.gas() {
127 writeln!(f, "gas: {gas:?}")?;
128 }
129 writeln!(f, "opcode counts ({} total instructions):", self.total_instruction_count())?;
130 for line in generate_execution_report(self.opcode_counts.as_ref()) {
131 writeln!(f, " {line}")?;
132 }
133
134 writeln!(f, "syscall counts ({} total syscall instructions):", self.total_syscall_count())?;
135 for line in generate_execution_report(self.syscall_counts.as_ref()) {
136 writeln!(f, " {line}")?;
137 }
138
139 Ok(())
140 }
141}