stylus_trace_core/aggregator/
stack_builder.rs1use crate::parser::{HostIoType, ParsedTrace};
10use log::debug;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct CollapsedStack {
19 pub stack: String,
21
22 pub weight: u64,
24
25 pub last_pc: Option<u64>,
27}
28
29impl CollapsedStack {
30 pub fn new(stack: String, weight: u64, last_pc: Option<u64>) -> Self {
34 Self {
35 stack,
36 weight,
37 last_pc,
38 }
39 }
40}
41
42pub fn build_collapsed_stacks(parsed_trace: &ParsedTrace) -> Vec<CollapsedStack> {
58 debug!(
59 "Building collapsed stacks from {} execution steps",
60 parsed_trace.execution_steps.len()
61 );
62
63 let mut stack_map: HashMap<String, (u64, u64)> = HashMap::new();
65
66 let mut call_stack: Vec<String> = Vec::new();
68
69 for step in &parsed_trace.execution_steps {
71 let raw_op = step
73 .function
74 .as_deref()
75 .or(step.op.as_deref())
76 .unwrap_or("unknown");
77
78 let op_part = raw_op.split(';').next_back().unwrap_or(raw_op);
80
81 let operation = HostIoType::from_opcode(op_part)
82 .map(map_hostio_to_label)
83 .unwrap_or(raw_op);
84
85 let current_depth = step.depth as usize;
87
88 if current_depth < call_stack.len() {
90 call_stack.truncate(current_depth);
91 }
92
93 while call_stack.len() < current_depth {
96 call_stack.push("call".to_string());
97 }
98
99 let stack_str = if call_stack.is_empty() {
101 operation.to_string()
102 } else {
103 format!("{};{}", call_stack.join(";"), operation)
104 };
105
106 let entry = stack_map.entry(stack_str).or_insert((0, 0));
108 entry.0 += step.gas_cost;
109 entry.1 = step.pc;
110 }
111
112 let mut stacks: Vec<CollapsedStack> = stack_map
114 .into_iter()
115 .map(|(stack, (weight, pc))| CollapsedStack::new(stack, weight, Some(pc)))
116 .collect();
117
118 stacks.sort_by(|a, b| b.weight.cmp(&a.weight));
119 debug!("Built {} unique collapsed stacks", stacks.len());
120
121 stacks
122}
123
124pub fn map_hostio_to_label(io_type: HostIoType) -> &'static str {
126 match io_type {
127 HostIoType::StorageLoad => "storage_load_bytes32",
128 HostIoType::StorageStore => "storage_store_bytes32",
129 HostIoType::StorageFlush => "storage_flush_cache",
130 HostIoType::StorageCache => "storage_cache",
131 HostIoType::Call => "call",
132 HostIoType::StaticCall => "staticcall",
133 HostIoType::DelegateCall => "delegatecall",
134 HostIoType::Create => "create",
135 HostIoType::Log => "emit_log",
136 HostIoType::SelfDestruct => "selfdestruct",
137 HostIoType::AccountBalance => "account_balance",
138 HostIoType::BlockHash => "block_hash",
139 HostIoType::NativeKeccak256 => "native_keccak256",
140 HostIoType::ReadArgs => "read_args",
141 HostIoType::WriteResult => "write_result",
142 HostIoType::MsgValue => "msg_value",
143 HostIoType::MsgSender => "msg_sender",
144 HostIoType::MsgReentrant => "msg_reentrant",
145 HostIoType::Other => "other",
146 }
147}