sp1_core_executor/
report.rs

1use 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::{events::generate_execution_report, syscalls::SyscallCode, Opcode};
10
11/// An execution report.
12#[derive(Default, Debug, Clone, PartialEq, Eq)]
13pub struct ExecutionReport {
14    /// The opcode counts.
15    pub opcode_counts: Box<EnumMap<Opcode, u64>>,
16    /// The syscall counts.
17    pub syscall_counts: Box<EnumMap<SyscallCode, u64>>,
18    /// The cycle tracker counts.
19    pub cycle_tracker: HashMap<String, u64>,
20    /// Tracker for the number of `cycle-tracker-report-*` invocations for a specific label.
21    pub invocation_tracker: HashMap<String, u64>,
22    /// The unique memory address counts.
23    pub touched_memory_addresses: u64,
24    /// The gas, if it was calculated.
25    pub gas: Option<u64>,
26}
27
28impl ExecutionReport {
29    /// Compute the total number of instructions run during the execution.
30    #[must_use]
31    pub fn total_instruction_count(&self) -> u64 {
32        self.opcode_counts.values().sum()
33    }
34
35    /// Compute the total number of syscalls made during the execution.
36    #[must_use]
37    pub fn total_syscall_count(&self) -> u64 {
38        self.syscall_counts.values().sum()
39    }
40}
41
42/// Combines two `HashMap`s together. If a key is in both maps, the values are added together.
43fn counts_add_assign<K, V>(lhs: &mut EnumMap<K, V>, rhs: EnumMap<K, V>)
44where
45    K: EnumArray<V>,
46    V: AddAssign,
47{
48    for (k, v) in rhs {
49        lhs[k] += v;
50    }
51}
52
53impl AddAssign for ExecutionReport {
54    fn add_assign(&mut self, rhs: Self) {
55        counts_add_assign(&mut self.opcode_counts, *rhs.opcode_counts);
56        counts_add_assign(&mut self.syscall_counts, *rhs.syscall_counts);
57        self.touched_memory_addresses += rhs.touched_memory_addresses;
58    }
59}
60
61impl Add for ExecutionReport {
62    type Output = Self;
63
64    fn add(mut self, rhs: Self) -> Self::Output {
65        self += rhs;
66        self
67    }
68}
69
70impl Display for ExecutionReport {
71    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
72        if let Some(gas) = self.gas {
73            writeln!(f, "gas: {gas}")?;
74        }
75        writeln!(f, "opcode counts ({} total instructions):", self.total_instruction_count())?;
76        for line in generate_execution_report(self.opcode_counts.as_ref()) {
77            writeln!(f, "  {line}")?;
78        }
79
80        writeln!(f, "syscall counts ({} total syscall instructions):", self.total_syscall_count())?;
81        for line in generate_execution_report(self.syscall_counts.as_ref()) {
82            writeln!(f, "  {line}")?;
83        }
84
85        Ok(())
86    }
87}