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