Skip to main content

revm_inspectors/tracing/
arena.rs

1use super::types::{CallTrace, CallTraceNode, TraceMemberOrder};
2use alloc::vec::Vec;
3use alloy_primitives::Address;
4
5/// An arena of recorded traces.
6///
7/// This type will be populated via the [TracingInspector](super::TracingInspector).
8#[derive(Clone, Debug, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct CallTraceArena {
11    /// The arena of recorded trace nodes
12    pub(crate) arena: Vec<CallTraceNode>,
13}
14
15impl Default for CallTraceArena {
16    fn default() -> Self {
17        let mut this = Self { arena: Vec::with_capacity(8) };
18        this.clear();
19        this
20    }
21}
22
23impl CallTraceArena {
24    /// Returns the nodes in the arena.
25    #[inline]
26    pub fn nodes(&self) -> &[CallTraceNode] {
27        &self.arena
28    }
29
30    /// Returns a mutable reference to the nodes in the arena.
31    #[inline]
32    pub fn nodes_mut(&mut self) -> &mut Vec<CallTraceNode> {
33        &mut self.arena
34    }
35
36    /// Consumes the arena and returns the nodes.
37    #[inline]
38    pub fn into_nodes(self) -> Vec<CallTraceNode> {
39        self.arena
40    }
41
42    /// Clears the arena
43    ///
44    /// Note that this method has no effect on the allocated capacity of the arena.
45    pub fn clear(&mut self) {
46        self.arena.clear();
47        self.arena.push(Default::default());
48    }
49
50    /// Returns __all__ addresses in the recorded traces, that is addresses of the trace and the
51    /// caller address.
52    pub fn trace_addresses(&self) -> impl Iterator<Item = Address> + '_ {
53        self.nodes().iter().flat_map(|node| [node.trace.address, node.trace.caller].into_iter())
54    }
55
56    /// Pushes a new trace into the arena, returning the trace ID
57    ///
58    /// This appends a new trace to the arena, and also inserts a new entry in the node's parent
59    /// node children set if `attach_to_parent` is `true`. E.g. if calls to precompiles should
60    /// not be included in the call graph this should be called with [PushTraceKind::PushOnly].
61    pub(crate) fn push_trace(
62        &mut self,
63        mut entry: usize,
64        kind: PushTraceKind,
65        new_trace: CallTrace,
66    ) -> usize {
67        // The entry node, just update it.
68        if new_trace.depth == 0 {
69            self.arena[0].trace = new_trace;
70            return 0;
71        }
72
73        // Otherwise, we need to find the parent node and add the new trace as a child.
74        while self.arena[entry].trace.depth != new_trace.depth - 1 {
75            entry = *self.arena[entry].children.last().expect("Disconnected trace");
76        }
77
78        let idx = self.arena.len();
79        self.arena.push(CallTraceNode {
80            parent: Some(entry),
81            trace: new_trace,
82            idx,
83            ..Default::default()
84        });
85
86        // Also track the child in the parent node.
87        if kind.is_attach_to_parent() {
88            let parent = &mut self.arena[entry];
89            let trace_location = parent.children.len();
90            parent.ordering.push(TraceMemberOrder::Call(trace_location));
91            parent.children.push(idx);
92        }
93
94        idx
95    }
96}
97
98/// How to push a trace into the arena
99#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub(crate) enum PushTraceKind {
101    /// This will _only_ push the trace into the arena.
102    PushOnly,
103    /// This will push the trace into the arena, and also insert a new entry in the node's parent
104    /// node children set.
105    PushAndAttachToParent,
106}
107
108impl PushTraceKind {
109    #[inline]
110    const fn is_attach_to_parent(&self) -> bool {
111        matches!(self, Self::PushAndAttachToParent)
112    }
113}