1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5use core::{fmt, ops::RangeInclusive};
6
7use miden_core::{FMP_ADDR, Felt, operations::DebugOptions};
8
9use crate::{DebugError, ProcessorState, 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")]
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: &ProcessorState,
57 options: &DebugOptions,
58 ) -> Result<(), DebugError> {
59 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 count = if n == 0 { None } else { Some(n as usize) };
78 self.print_stack(&stack, count, "Advice stack", process)
79 },
80 }
81 .map_err(DebugError::from)
82 }
83
84 fn on_trace(&mut self, process: &ProcessorState, trace_id: u32) -> Result<(), TraceError> {
85 writeln!(
86 self.writer,
87 "Trace with id {} emitted at step {} in context {}",
88 trace_id,
89 process.clock(),
90 process.ctx()
91 )
92 .map_err(TraceError::from)
93 }
94}
95
96impl<W: fmt::Write + Sync> DefaultDebugHandler<W> {
97 fn print_stack(
99 &mut self,
100 stack: &[Felt],
101 n: Option<usize>,
102 stack_type: &str,
103 process: &ProcessorState,
104 ) -> fmt::Result {
105 if stack.is_empty() {
106 writeln!(self.writer, "{stack_type} empty before step {}.", process.clock())?;
107 return Ok(());
108 }
109
110 let num_items = n.unwrap_or(stack.len());
112
113 let is_partial = num_items < stack.len();
115 if is_partial {
116 writeln!(
117 self.writer,
118 "{stack_type} state in interval [0, {}] before step {}:",
119 num_items - 1,
120 process.clock()
121 )?
122 } else {
123 writeln!(self.writer, "{stack_type} state before step {}:", process.clock())?
124 }
125
126 let mut stack_items = Vec::new();
128 for (i, element) in stack.iter().enumerate().take(num_items) {
129 stack_items.push((i.to_string(), Some(element.to_string())));
130 }
131 for i in stack.len()..num_items {
133 stack_items.push((i.to_string(), None));
134 }
135
136 let remaining = if num_items < stack.len() {
138 Some(stack.len() - num_items)
139 } else {
140 None
141 };
142
143 self.print_interval(stack_items, remaining)
144 }
145
146 fn print_mem_all(&mut self, process: &ProcessorState) -> fmt::Result {
148 let mem = process.get_mem_state(process.ctx());
149
150 writeln!(
151 self.writer,
152 "Memory state before step {} for the context {}:",
153 process.clock(),
154 process.ctx()
155 )?;
156
157 let mem_items: Vec<_> = mem
158 .into_iter()
159 .map(|(addr, value)| (format!("{addr:#010x}"), Some(value.to_string())))
160 .collect();
161
162 self.print_interval(mem_items, None)?;
163 Ok(())
164 }
165
166 fn print_mem_interval(
168 &mut self,
169 process: &ProcessorState,
170 range: RangeInclusive<u32>,
171 ) -> fmt::Result {
172 let start = *range.start();
173 let end = *range.end();
174
175 if start == end {
176 let value = process.get_mem_value(process.ctx(), start);
177 let value_str = format_value(value);
178 writeln!(
179 self.writer,
180 "Memory state before step {} for the context {} at address {:#010x}: {value_str}",
181 process.clock(),
182 process.ctx(),
183 start
184 )
185 } else {
186 writeln!(
187 self.writer,
188 "Memory state before step {} for the context {} in the interval [{}, {}]:",
189 process.clock(),
190 process.ctx(),
191 start,
192 end
193 )?;
194 let mem_items: Vec<_> = range
195 .map(|addr| {
196 let value = process.get_mem_value(process.ctx(), addr);
197 let addr_str = format!("{addr:#010x}");
198 let value_str = value.map(|v| v.to_string());
199 (addr_str, value_str)
200 })
201 .collect();
202
203 self.print_interval(mem_items, None)
204 }
205 }
206
207 fn print_local_interval(
211 &mut self,
212 process: &ProcessorState,
213 range: RangeInclusive<u16>,
214 num_locals: u32,
215 ) -> fmt::Result {
216 let local_memory_offset = {
217 let fmp = process
218 .get_mem_value(process.ctx(), FMP_ADDR.as_canonical_u64() as u32)
219 .expect("FMP address is empty");
220
221 fmp.as_canonical_u64() as u32 - num_locals
222 };
223
224 let start = *range.start() as u32;
225 let end = *range.end() as u32;
226
227 if start == end {
228 let addr = local_memory_offset + start;
229 let value = process.get_mem_value(process.ctx(), addr);
230 let value_str = format_value(value);
231
232 writeln!(
233 self.writer,
234 "State of procedure local {start} before step {}: {value_str}",
235 process.clock(),
236 )
237 } else {
238 writeln!(
239 self.writer,
240 "State of procedure locals [{start}, {end}] before step {}:",
241 process.clock()
242 )?;
243 let local_items: Vec<_> = range
244 .map(|local_idx| {
245 let addr = local_memory_offset + local_idx as u32;
246 let value = process.get_mem_value(process.ctx(), addr);
247 let addr_str = local_idx.to_string();
248 let value_str = value.map(|v| v.to_string());
249 (addr_str, value_str)
250 })
251 .collect();
252
253 self.print_interval(local_items, None)
254 }
255 }
256
257 fn print_interval(
264 &mut self,
265 items: Vec<(String, Option<String>)>,
266 remaining: Option<usize>,
267 ) -> fmt::Result {
268 let max_addr_width = items.iter().map(|(addr, _)| addr.len()).max().unwrap_or(0);
270
271 let mut formatted_items: Vec<String> = items
273 .into_iter()
274 .map(|(addr, value_opt)| {
275 let value_string = format_value(value_opt);
276 format!("{addr:>width$}: {value_string}", width = max_addr_width)
277 })
278 .collect();
279
280 if let Some(count) = remaining {
282 formatted_items.push(format!("({count} more items)"));
283 }
284
285 if let Some((last, front)) = formatted_items.split_last() {
288 for item in front {
290 writeln!(self.writer, "├── {item}")?;
291 }
292 writeln!(self.writer, "└── {last}")?;
294 }
295
296 Ok(())
297 }
298}
299
300fn format_value<T: ToString>(value: Option<T>) -> String {
305 value.map(|v| v.to_string()).unwrap_or_else(|| "EMPTY".to_string())
306}