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