1use alloc::{
16 format,
17 string::{String, ToString},
18 sync::Arc,
19 vec,
20 vec::Vec,
21};
22use core::fmt;
23
24use miden_core::{Felt, Word};
25use miden_processor::{
26 MemoryError, ProcessorState, StdoutWriter,
27 advice::AdviceMutation,
28 event::{EventError, EventHandler, EventId, EventName},
29 write_interval, write_stack,
30};
31use miden_utils_sync::RwLock;
32
33pub const PRINT_STACK_EVENT_NAME: EventName = EventName::new("miden::core::debug::print_stack");
38pub const PRINT_MEM_EVENT_NAME: EventName = EventName::new("miden::core::debug::print_mem");
40pub const PRINT_MEM_ALL_EVENT_NAME: EventName = EventName::new("miden::core::debug::print_mem_all");
42pub const PRINT_ADV_STACK_EVENT_NAME: EventName =
44 EventName::new("miden::core::debug::print_adv_stack");
45pub const PRINT_ADV_MAP_EVENT_NAME: EventName = EventName::new("miden::core::debug::print_adv_map");
47pub const PRINT_ADV_MAP_ITEM_EVENT_NAME: EventName =
49 EventName::new("miden::core::debug::print_adv_map_item");
50
51const MAX_PRINT_MEM_RANGE: u64 = 1024;
56
57pub fn default_debug_handlers() -> Vec<(EventName, Arc<dyn EventHandler>)> {
62 let printer: Arc<dyn EventHandler> = Arc::new(DebugPrinter::default());
63 vec![
64 (PRINT_STACK_EVENT_NAME, printer.clone()),
65 (PRINT_MEM_EVENT_NAME, printer.clone()),
66 (PRINT_MEM_ALL_EVENT_NAME, printer),
67 ]
68}
69
70pub fn noop_debug_handlers() -> Vec<(EventName, Arc<dyn EventHandler>)> {
75 let handler: Arc<dyn EventHandler> = Arc::new(NoopDebugHandler);
76 vec![
77 (PRINT_STACK_EVENT_NAME, handler.clone()),
78 (PRINT_MEM_EVENT_NAME, handler.clone()),
79 (PRINT_MEM_ALL_EVENT_NAME, handler.clone()),
80 (PRINT_ADV_STACK_EVENT_NAME, handler.clone()),
81 (PRINT_ADV_MAP_EVENT_NAME, handler.clone()),
82 (PRINT_ADV_MAP_ITEM_EVENT_NAME, handler),
83 ]
84}
85
86pub fn debug_handlers() -> Vec<(EventName, Arc<dyn EventHandler>)> {
90 let printer: Arc<dyn EventHandler> = Arc::new(DebugPrinter::default());
91 vec![
92 (PRINT_STACK_EVENT_NAME, printer.clone()),
93 (PRINT_MEM_EVENT_NAME, printer.clone()),
94 (PRINT_MEM_ALL_EVENT_NAME, printer.clone()),
95 (PRINT_ADV_STACK_EVENT_NAME, printer.clone()),
96 (PRINT_ADV_MAP_EVENT_NAME, printer.clone()),
97 (PRINT_ADV_MAP_ITEM_EVENT_NAME, printer),
98 ]
99}
100
101pub fn advice_debug_handlers() -> Vec<(EventName, Arc<dyn EventHandler>)> {
109 let printer: Arc<dyn EventHandler> = Arc::new(DebugPrinter::default());
110 vec![
111 (PRINT_ADV_STACK_EVENT_NAME, printer.clone()),
112 (PRINT_ADV_MAP_EVENT_NAME, printer.clone()),
113 (PRINT_ADV_MAP_ITEM_EVENT_NAME, printer),
114 ]
115}
116
117pub struct DebugPrinter<W: fmt::Write + Send + Sync = StdoutWriter> {
126 writer: RwLock<W>,
127}
128
129impl Default for DebugPrinter<StdoutWriter> {
130 fn default() -> Self {
131 Self { writer: RwLock::new(StdoutWriter) }
132 }
133}
134
135impl<W: fmt::Write + Send + Sync> DebugPrinter<W> {
136 pub fn new(writer: W) -> Self {
138 Self { writer: RwLock::new(writer) }
139 }
140}
141
142impl<W: fmt::Write + Send + Sync + 'static> EventHandler for DebugPrinter<W> {
143 fn on_event(&self, process: &ProcessorState) -> Result<Vec<AdviceMutation>, EventError> {
144 let id = EventId::from_felt(process.get_stack_item(0));
147 let mut writer = self.writer.write();
148 let w: &mut W = &mut writer;
149
150 if id == PRINT_STACK_EVENT_NAME.to_event_id() {
151 let stack = process.get_stack_state();
154 let operand_stack = stack.get(1..).unwrap_or(&[]);
155 write_stack(w, operand_stack, None, "Stack", process.clock())?;
156 } else if id == PRINT_MEM_EVENT_NAME.to_event_id() {
157 let bounds = read_mem_print_range(process, 1, 2)?;
158 if let Some((first, last)) = bounds {
160 let len = u64::from(last - first) + 1;
161 if len > MAX_PRINT_MEM_RANGE {
162 return Err(format!(
163 "print_mem range length {len} exceeds maximum of {MAX_PRINT_MEM_RANGE}"
164 )
165 .into());
166 }
167 }
168 write_mem_range(w, process, bounds)?;
169 } else if id == PRINT_MEM_ALL_EVENT_NAME.to_event_id() {
170 write_mem_all(w, process)?;
171 } else if id == PRINT_ADV_STACK_EVENT_NAME.to_event_id() {
172 let start = stack_item_as_usize(process, 1);
173 let end = stack_item_as_usize(process, 2);
174 let adv_stack = process.advice_provider().stack();
175 let slice = slice_range(&adv_stack, start, end);
176 write_stack(w, slice, None, "Advice stack", process.clock())?;
177 } else if id == PRINT_ADV_MAP_EVENT_NAME.to_event_id() {
178 write_adv_map(w, process)?;
179 } else if id == PRINT_ADV_MAP_ITEM_EVENT_NAME.to_event_id() {
180 write_adv_map_entry(w, process)?;
181 }
182 Ok(Vec::new())
185 }
186}
187
188struct NoopDebugHandler;
189
190impl EventHandler for NoopDebugHandler {
191 fn on_event(&self, _process: &ProcessorState) -> Result<Vec<AdviceMutation>, EventError> {
192 Ok(Vec::new())
193 }
194}
195
196fn stack_item_as_usize(process: &ProcessorState, pos: usize) -> usize {
201 usize::try_from(process.get_stack_item(pos).as_canonical_u64()).unwrap_or(usize::MAX)
202}
203
204fn slice_range(slice: &[Felt], start: usize, end: usize) -> &[Felt] {
206 let len = slice.len();
207 let start = start.min(len);
208 let end = end.clamp(start, len);
209 &slice[start..end]
210}
211
212fn read_mem_print_range(
219 process: &ProcessorState,
220 start_idx: usize,
221 end_idx: usize,
222) -> Result<Option<(u32, u32)>, MemoryError> {
223 let start_addr = process.get_stack_item(start_idx).as_canonical_u64();
224 let end_addr = process.get_stack_item(end_idx).as_canonical_u64();
225
226 if start_addr > u32::MAX as u64 {
227 return Err(MemoryError::AddressOutOfBounds { addr: start_addr });
228 }
229 if end_addr > u32::MAX as u64 + 1 {
231 return Err(MemoryError::AddressOutOfBounds { addr: end_addr });
232 }
233 if start_addr > end_addr {
234 return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr });
235 }
236
237 if start_addr == end_addr {
238 Ok(None)
239 } else {
240 Ok(Some((start_addr as u32, (end_addr - 1) as u32)))
243 }
244}
245
246fn write_mem_range<W: fmt::Write>(
252 w: &mut W,
253 process: &ProcessorState,
254 bounds: Option<(u32, u32)>,
255) -> fmt::Result {
256 let (ctx, clk) = (process.ctx(), process.clock());
257 let Some((start, end)) = bounds else {
258 return writeln!(w, "Memory state before step {clk} for context {ctx}: range is empty.");
259 };
260 writeln!(
261 w,
262 "Memory state before step {clk} for context {ctx} in the range [{start}, {end}]:",
263 )?;
264 let items: Vec<_> = (start..=end)
265 .map(|addr| {
266 let value = process.get_mem_value(ctx, addr).map(|v| v.to_string());
267 (format!("{addr:#010x}"), value)
268 })
269 .collect();
270 write_interval(w, items, None)
271}
272
273fn write_mem_all<W: fmt::Write>(w: &mut W, process: &ProcessorState) -> fmt::Result {
275 let (ctx, clk) = (process.ctx(), process.clock());
276 writeln!(w, "Memory state before step {clk} for context {ctx}:")?;
277 let items: Vec<_> = process
278 .get_mem_state(ctx)
279 .into_iter()
280 .map(|(addr, value)| (format!("{addr:#010x}"), Some(value.to_string())))
281 .collect();
282 write_interval(w, items, None)
283}
284
285fn write_adv_map<W: fmt::Write>(w: &mut W, process: &ProcessorState) -> fmt::Result {
287 let clk = process.clock();
288 let map = process.advice_provider().map();
289 if map.is_empty() {
290 return writeln!(w, "Advice map before step {clk}: empty.");
291 }
292
293 writeln!(w, "Advice map before step {clk}:")?;
294 let items: Vec<_> = map
295 .iter()
296 .map(|(key, values)| (format_word(key), Some(format_felt_slice(values))))
297 .collect();
298 write_interval(w, items, None)
299}
300
301fn write_adv_map_entry<W: fmt::Write>(w: &mut W, process: &ProcessorState) -> fmt::Result {
303 let key = process.get_stack_word(1);
304 let key_str = format_word(&key);
305 let clk = process.clock();
306 match process.advice_provider().get_mapped_values(&key) {
307 Some(values) => {
308 writeln!(w, "Advice map entry for key {key_str} before step {clk}:")?;
309 let items: Vec<_> = values
310 .iter()
311 .enumerate()
312 .map(|(i, v)| (i.to_string(), Some(v.to_string())))
313 .collect();
314 write_interval(w, items, None)
315 },
316 None => writeln!(w, "No advice map entry for key {key_str} before step {clk}."),
317 }
318}
319
320fn format_word(word: &Word) -> String {
321 format!("[{}, {}, {}, {}]", word[0], word[1], word[2], word[3])
322}
323
324fn format_felt_slice(values: &[Felt]) -> String {
325 let mut out = String::from("[");
326 for (idx, value) in values.iter().enumerate() {
327 if idx > 0 {
328 out.push_str(", ");
329 }
330 out.push_str(&value.to_string());
331 }
332 out.push(']');
333 out
334}