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}