1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5use core::{fmt, ops::RangeInclusive};
6
7use miden_core::{DebugOptions, FMP_ADDR};
8
9use crate::{DebugHandler, ExecutionError, Felt, ProcessState};
10
11#[derive(Default)]
16pub struct StdoutWriter;
17
18impl fmt::Write for StdoutWriter {
19 fn write_str(&mut self, s: &str) -> fmt::Result {
20 #[cfg(feature = "std")]
21 std::print!("{}", s);
22 Ok(())
23 }
24}
25
26pub struct DefaultDebugHandler<W: fmt::Write + Sync = StdoutWriter> {
32 writer: W,
33}
34
35impl Default for DefaultDebugHandler<StdoutWriter> {
36 fn default() -> Self {
37 Self { writer: StdoutWriter }
38 }
39}
40
41impl<W: fmt::Write + Sync> DefaultDebugHandler<W> {
42 pub fn new(writer: W) -> Self {
44 Self { writer }
45 }
46
47 pub fn writer(&self) -> &W {
49 &self.writer
50 }
51}
52
53impl<W: fmt::Write + Sync> DebugHandler for DefaultDebugHandler<W> {
54 fn on_debug(
55 &mut self,
56 process: &ProcessState,
57 options: &DebugOptions,
58 ) -> Result<(), ExecutionError> {
59 let _ = match *options {
60 DebugOptions::StackAll => {
61 let stack = process.get_stack_state();
62 self.print_stack(&stack, None, "Stack", process)
63 },
64 DebugOptions::StackTop(n) => {
65 let stack = process.get_stack_state();
66 let count = if n == 0 { None } else { Some(n as usize) };
67 self.print_stack(&stack, count, "Stack", process)
68 },
69 DebugOptions::MemAll => self.print_mem_all(process),
70 DebugOptions::MemInterval(n, m) => self.print_mem_interval(process, n..=m),
71 DebugOptions::LocalInterval(n, m, num_locals) => {
72 self.print_local_interval(process, n..=m, num_locals as u32)
73 },
74 DebugOptions::AdvStackTop(n) => {
75 let stack = process.advice_provider().stack();
77 let reversed_stack: Vec<_> = stack.iter().copied().rev().collect();
78
79 let count = if n == 0 { None } else { Some(n as usize) };
80 self.print_stack(&reversed_stack, count, "Advice stack", process)
81 },
82 };
83 Ok(())
84 }
85
86 fn on_trace(&mut self, process: &ProcessState, trace_id: u32) -> Result<(), ExecutionError> {
87 let _ = writeln!(
88 self.writer,
89 "Trace with id {} emitted at step {} in context {}",
90 trace_id,
91 process.clk(),
92 process.ctx()
93 );
94 Ok(())
95 }
96}
97
98impl<W: fmt::Write + Sync> DefaultDebugHandler<W> {
99 fn print_stack(
101 &mut self,
102 stack: &[Felt],
103 n: Option<usize>,
104 stack_type: &str,
105 process: &ProcessState,
106 ) -> fmt::Result {
107 if stack.is_empty() {
108 writeln!(self.writer, "{stack_type} empty before step {}.", process.clk())?;
109 return Ok(());
110 }
111
112 let num_items = n.unwrap_or(stack.len());
114
115 let is_partial = num_items < stack.len();
117 if is_partial {
118 writeln!(
119 self.writer,
120 "{stack_type} state in interval [0, {}] before step {}:",
121 num_items - 1,
122 process.clk()
123 )?
124 } else {
125 writeln!(self.writer, "{stack_type} state before step {}:", process.clk())?
126 }
127
128 let mut stack_items = Vec::new();
130 for (i, element) in stack.iter().enumerate().take(num_items) {
131 stack_items.push((i.to_string(), Some(element.to_string())));
132 }
133 for i in stack.len()..num_items {
135 stack_items.push((i.to_string(), None));
136 }
137
138 let remaining = if num_items < stack.len() {
140 Some(stack.len() - num_items)
141 } else {
142 None
143 };
144
145 self.print_interval(stack_items, remaining)
146 }
147
148 fn print_mem_all(&mut self, process: &ProcessState) -> fmt::Result {
150 let mem = process.get_mem_state(process.ctx());
151
152 writeln!(
153 self.writer,
154 "Memory state before step {} for the context {}:",
155 process.clk(),
156 process.ctx()
157 )?;
158
159 let mem_items: Vec<_> = mem
160 .into_iter()
161 .map(|(addr, value)| (format!("{addr:#010x}"), Some(value.to_string())))
162 .collect();
163
164 self.print_interval(mem_items, None)?;
165 Ok(())
166 }
167
168 fn print_mem_interval(
170 &mut self,
171 process: &ProcessState,
172 range: RangeInclusive<u32>,
173 ) -> fmt::Result {
174 let start = *range.start();
175 let end = *range.end();
176
177 if start == end {
178 let value = process.get_mem_value(process.ctx(), start);
179 let value_str = format_value(value);
180 writeln!(
181 self.writer,
182 "Memory state before step {} for the context {} at address {:#010x}: {value_str}",
183 process.clk(),
184 process.ctx(),
185 start
186 )
187 } else {
188 writeln!(
189 self.writer,
190 "Memory state before step {} for the context {} in the interval [{}, {}]:",
191 process.clk(),
192 process.ctx(),
193 start,
194 end
195 )?;
196 let mem_items: Vec<_> = range
197 .map(|addr| {
198 let value = process.get_mem_value(process.ctx(), addr);
199 let addr_str = format!("{addr:#010x}");
200 let value_str = value.map(|v| v.to_string());
201 (addr_str, value_str)
202 })
203 .collect();
204
205 self.print_interval(mem_items, None)
206 }
207 }
208
209 fn print_local_interval(
213 &mut self,
214 process: &ProcessState,
215 range: RangeInclusive<u16>,
216 num_locals: u32,
217 ) -> fmt::Result {
218 let local_memory_offset = {
219 let fmp = process
220 .get_mem_value(process.ctx(), FMP_ADDR.as_int() as u32)
221 .expect("FMP address is empty");
222
223 fmp.as_int() as u32 - num_locals
224 };
225
226 let start = *range.start() as u32;
227 let end = *range.end() as u32;
228
229 if start == end {
230 let addr = local_memory_offset + start;
231 let value = process.get_mem_value(process.ctx(), addr);
232 let value_str = format_value(value);
233
234 writeln!(
235 self.writer,
236 "State of procedure local {start} before step {}: {value_str}",
237 process.clk(),
238 )
239 } else {
240 writeln!(
241 self.writer,
242 "State of procedure locals [{start}, {end}] before step {}:",
243 process.clk()
244 )?;
245 let local_items: Vec<_> = range
246 .map(|local_idx| {
247 let addr = local_memory_offset + local_idx as u32;
248 let value = process.get_mem_value(process.ctx(), addr);
249 let addr_str = local_idx.to_string();
250 let value_str = value.map(|v| v.to_string());
251 (addr_str, value_str)
252 })
253 .collect();
254
255 self.print_interval(local_items, None)
256 }
257 }
258
259 fn print_interval(
266 &mut self,
267 items: Vec<(String, Option<String>)>,
268 remaining: Option<usize>,
269 ) -> fmt::Result {
270 let max_addr_width = items.iter().map(|(addr, _)| addr.len()).max().unwrap_or(0);
272
273 let mut formatted_items: Vec<String> = items
275 .into_iter()
276 .map(|(addr, value_opt)| {
277 let value_string = format_value(value_opt);
278 format!("{addr:>width$}: {value_string}", width = max_addr_width)
279 })
280 .collect();
281
282 if let Some(count) = remaining {
284 formatted_items.push(format!("({count} more items)"));
285 }
286
287 if let Some((last, front)) = formatted_items.split_last() {
290 for item in front {
292 writeln!(self.writer, "├── {item}")?;
293 }
294 writeln!(self.writer, "└── {last}")?;
296 }
297
298 Ok(())
299 }
300}
301
302fn format_value<T: ToString>(value: Option<T>) -> String {
307 value.map(|v| v.to_string()).unwrap_or_else(|| "EMPTY".to_string())
308}