miden_core/mast/debuginfo/
mod.rs

1//! Debug information management for MAST forests.
2//!
3//! This module provides the [`DebugInfo`] struct which consolidates all debug-related
4//! information for a MAST forest in a single location. This includes:
5//!
6//! - All decorators (debug, trace, and assembly operation metadata)
7//! - Operation-indexed decorator mappings for efficient lookup
8//! - Node-level decorator storage (before_enter/after_exit)
9//! - Error code mappings for descriptive error messages
10//!
11//! The debug info is always available at the `MastForest` level (as per issue #1821),
12//! but may be conditionally included during assembly to maintain backward compatibility.
13//! Decorators are only executed when the processor is running in debug mode, allowing
14//! debug information to be available for debugging and error reporting without
15//! impacting performance in production execution.
16//!
17//! # Debug Mode Semantics
18//!
19//! Debug mode is controlled via [`ExecutionOptions`](air::options::ExecutionOptions):
20//! - `with_debugging(true)` enables debug mode explicitly
21//! - `with_tracing()` automatically enables debug mode (tracing requires debug info)
22//! - By default, debug mode is disabled for maximum performance
23//!
24//! When debug mode is disabled:
25//! - Debug decorators are not executed
26//! - Trace decorators are not executed
27//! - Assembly operation decorators are not recorded
28//! - before_enter/after_exit decorators are not executed
29//!
30//! When debug mode is enabled:
31//! - All decorator types are executed according to their semantics
32//! - Debug decorators trigger host callbacks for breakpoints
33//! - Trace decorators trigger host callbacks for tracing
34//! - Assembly operation decorators provide source mapping information
35//! - before_enter/after_exit decorators execute around node execution
36//!
37//! # Production Builds
38//!
39//! The `DebugInfo` can be stripped for production builds using the [`clear()`](Self::clear)
40//! method, which removes decorators while preserving critical information. This allows
41//! backward compatibility while enabling size optimization for deployment.
42
43use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
44
45use miden_utils_indexing::{Idx, IndexVec};
46#[cfg(feature = "serde")]
47use serde::{Deserialize, Serialize};
48
49use super::{Decorator, DecoratorId, MastForestError, MastNodeId};
50
51mod decorator_storage;
52pub use decorator_storage::{
53    DecoratedLinks, DecoratedLinksIter, DecoratorIndexError, OpToDecoratorIds,
54};
55
56mod node_decorator_storage;
57pub use node_decorator_storage::NodeToDecoratorIds;
58
59// DEBUG INFO
60// ================================================================================================
61
62/// Debug information for a MAST forest, containing decorators and error messages.
63#[derive(Debug, Clone, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65pub struct DebugInfo {
66    /// All decorators in the MAST forest.
67    decorators: IndexVec<DecoratorId, Decorator>,
68
69    /// Efficient access to decorators per operation per node.
70    op_decorator_storage: OpToDecoratorIds,
71
72    /// Efficient storage for node-level decorators (before_enter and after_exit).
73    node_decorator_storage: NodeToDecoratorIds,
74
75    /// Maps error codes to error messages.
76    error_codes: BTreeMap<u64, Arc<str>>,
77}
78
79impl DebugInfo {
80    // CONSTRUCTORS
81    // --------------------------------------------------------------------------------------------
82
83    /// Creates a new empty [DebugInfo].
84    pub fn new() -> Self {
85        Self {
86            decorators: IndexVec::new(),
87            op_decorator_storage: OpToDecoratorIds::new(),
88            node_decorator_storage: NodeToDecoratorIds::new(),
89            error_codes: BTreeMap::new(),
90        }
91    }
92
93    /// Creates an empty [DebugInfo] with specified capacities.
94    pub fn with_capacity(
95        decorators_capacity: usize,
96        nodes_capacity: usize,
97        operations_capacity: usize,
98        decorator_ids_capacity: usize,
99    ) -> Self {
100        Self {
101            decorators: IndexVec::with_capacity(decorators_capacity),
102            op_decorator_storage: OpToDecoratorIds::with_capacity(
103                nodes_capacity,
104                operations_capacity,
105                decorator_ids_capacity,
106            ),
107            node_decorator_storage: NodeToDecoratorIds::with_capacity(nodes_capacity, 0, 0),
108            error_codes: BTreeMap::new(),
109        }
110    }
111
112    /// Creates an empty [DebugInfo] with valid CSR structures for N nodes.
113    pub fn empty_for_nodes(num_nodes: usize) -> Self {
114        let mut node_indptr_for_op_idx = IndexVec::new();
115        for _ in 0..=num_nodes {
116            let _ = node_indptr_for_op_idx.push(0);
117        }
118
119        let op_decorator_storage =
120            OpToDecoratorIds::from_components(Vec::new(), Vec::new(), node_indptr_for_op_idx)
121                .expect("Empty CSR structure should be valid");
122
123        Self {
124            decorators: IndexVec::new(),
125            op_decorator_storage,
126            node_decorator_storage: NodeToDecoratorIds::new(),
127            error_codes: BTreeMap::new(),
128        }
129    }
130
131    // PUBLIC ACCESSORS
132    // --------------------------------------------------------------------------------------------
133
134    /// Returns true if this [DebugInfo] has no decorators or error codes.
135    pub fn is_empty(&self) -> bool {
136        self.decorators.is_empty() && self.error_codes.is_empty()
137    }
138
139    /// Strips all debug information, removing decorators and error codes.
140    ///
141    /// This is used for release builds where debug info is not needed.
142    pub fn clear(&mut self) {
143        self.clear_mappings();
144        self.decorators = IndexVec::new();
145        self.error_codes.clear();
146    }
147
148    // DECORATOR ACCESSORS
149    // --------------------------------------------------------------------------------------------
150
151    /// Returns the number of decorators.
152    pub fn num_decorators(&self) -> usize {
153        self.decorators.len()
154    }
155
156    /// Returns all decorators as a slice.
157    pub fn decorators(&self) -> &[Decorator] {
158        self.decorators.as_slice()
159    }
160
161    /// Returns the decorator with the given ID, if it exists.
162    pub fn decorator(&self, decorator_id: DecoratorId) -> Option<&Decorator> {
163        self.decorators.get(decorator_id)
164    }
165
166    /// Returns the before-enter decorators for the given node.
167    pub fn before_enter_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
168        self.node_decorator_storage.get_before_decorators(node_id)
169    }
170
171    /// Returns the after-exit decorators for the given node.
172    pub fn after_exit_decorators(&self, node_id: MastNodeId) -> &[DecoratorId] {
173        self.node_decorator_storage.get_after_decorators(node_id)
174    }
175
176    /// Returns decorators for a specific operation within a node.
177    pub fn decorators_for_operation(
178        &self,
179        node_id: MastNodeId,
180        local_op_idx: usize,
181    ) -> &[DecoratorId] {
182        self.op_decorator_storage
183            .decorator_ids_for_operation(node_id, local_op_idx)
184            .unwrap_or(&[])
185    }
186
187    /// Returns decorator links for a node, including operation indices.
188    pub(super) fn decorator_links_for_node(
189        &self,
190        node_id: MastNodeId,
191    ) -> Result<DecoratedLinks<'_>, DecoratorIndexError> {
192        self.op_decorator_storage.decorator_links_for_node(node_id)
193    }
194
195    // DECORATOR MUTATORS
196    // --------------------------------------------------------------------------------------------
197
198    /// Adds a decorator and returns its ID.
199    pub fn add_decorator(&mut self, decorator: Decorator) -> Result<DecoratorId, MastForestError> {
200        self.decorators.push(decorator).map_err(|_| MastForestError::TooManyDecorators)
201    }
202
203    /// Returns a mutable reference the decorator with the given ID, if it exists.
204    pub(super) fn decorator_mut(&mut self, decorator_id: DecoratorId) -> Option<&mut Decorator> {
205        if decorator_id.to_usize() < self.decorators.len() {
206            Some(&mut self.decorators[decorator_id])
207        } else {
208            None
209        }
210    }
211
212    /// Adds node-level decorators (before_enter and after_exit) for the given node.
213    ///
214    /// # Note
215    /// This method does not validate decorator IDs immediately. Validation occurs during
216    /// operations that need to access the actual decorator data (e.g., merging, serialization).
217    pub(super) fn register_node_decorators(
218        &mut self,
219        node_id: MastNodeId,
220        before_enter: &[DecoratorId],
221        after_exit: &[DecoratorId],
222    ) {
223        self.node_decorator_storage
224            .add_node_decorators(node_id, before_enter, after_exit);
225    }
226
227    /// Registers operation-indexed decorators for a node.
228    ///
229    /// This associates already-added decorators with specific operations within a node.
230    pub(crate) fn register_op_indexed_decorators(
231        &mut self,
232        node_id: MastNodeId,
233        decorators_info: Vec<(usize, DecoratorId)>,
234    ) -> Result<(), crate::mast::debuginfo::decorator_storage::DecoratorIndexError> {
235        self.op_decorator_storage.add_decorator_info_for_node(node_id, decorators_info)
236    }
237
238    /// Clears all decorator information while preserving error codes.
239    ///
240    /// This is used when rebuilding decorator information from nodes.
241    pub fn clear_mappings(&mut self) {
242        self.op_decorator_storage = OpToDecoratorIds::new();
243        self.node_decorator_storage.clear();
244    }
245
246    // ERROR CODE METHODS
247    // --------------------------------------------------------------------------------------------
248
249    /// Returns an error message by code.
250    pub fn error_message(&self, code: u64) -> Option<Arc<str>> {
251        self.error_codes.get(&code).cloned()
252    }
253
254    /// Returns an iterator over error codes.
255    pub fn error_codes(&self) -> impl Iterator<Item = (&u64, &Arc<str>)> {
256        self.error_codes.iter()
257    }
258
259    /// Inserts an error code with its message.
260    pub fn insert_error_code(&mut self, code: u64, msg: Arc<str>) {
261        self.error_codes.insert(code, msg);
262    }
263
264    /// Inserts multiple error codes at once.
265    ///
266    /// This is used when bulk error code insertion is needed.
267    pub fn extend_error_codes<I>(&mut self, error_codes: I)
268    where
269        I: IntoIterator<Item = (u64, Arc<str>)>,
270    {
271        self.error_codes.extend(error_codes);
272    }
273
274    /// Clears all error codes.
275    ///
276    /// This is used when error code information needs to be reset.
277    pub fn clear_error_codes(&mut self) {
278        self.error_codes.clear();
279    }
280
281    // TEST HELPERS
282    // --------------------------------------------------------------------------------------------
283
284    /// Returns the operation decorator storage.
285    #[cfg(test)]
286    pub(crate) fn op_decorator_storage(&self) -> &OpToDecoratorIds {
287        &self.op_decorator_storage
288    }
289}
290
291impl Default for DebugInfo {
292    fn default() -> Self {
293        Self::new()
294    }
295}