use alloc::vec::Vec;
use std::{print, println};
use miden_air::RowIndex;
use vm_core::{DebugOptions, Felt};
use super::ProcessState;
use crate::{MemoryAddress, system::ContextId};
pub fn print_debug_info(process: ProcessState, options: &DebugOptions) {
let printer = Printer::new(process.clk(), process.ctx(), process.fmp());
match options {
DebugOptions::StackAll => {
printer.print_vm_stack(process, None);
},
DebugOptions::StackTop(n) => {
printer.print_vm_stack(process, Some(*n as usize));
},
DebugOptions::MemAll => {
printer.print_mem_all(process);
},
DebugOptions::MemInterval(n, m) => {
printer.print_mem_interval(process, *n, *m);
},
DebugOptions::LocalInterval(n, m, num_locals) => {
printer.print_local_interval(process, (*n as u32, *m as u32), *num_locals as u32);
},
}
}
struct Printer {
clk: RowIndex,
ctx: ContextId,
fmp: u32,
}
impl Printer {
fn new(clk: RowIndex, ctx: ContextId, fmp: u64) -> Self {
Self { clk, ctx, fmp: fmp as u32 }
}
fn print_vm_stack(&self, process: ProcessState, n: Option<usize>) {
let stack = process.get_stack_state();
let num_items = core::cmp::min(stack.len(), n.unwrap_or(stack.len()));
println!("Stack state before step {}:", self.clk);
for (i, element) in stack.iter().take(num_items - 1).enumerate() {
println!("├── {i:>2}: {element}");
}
let i = num_items - 1;
if num_items == stack.len() {
println!("└── {i:>2}: {}\n", stack[i]);
} else {
println!("├── {i:>2}: {}", stack[i]);
println!("└── ({} more items)\n", stack.len() - num_items);
}
}
fn print_mem_all(&self, process: ProcessState) {
let mem = process.get_mem_state(self.ctx);
let element_width = mem
.iter()
.map(|(_addr, value)| element_printed_width(Some(*value)))
.max()
.unwrap_or(0) as usize;
println!("Memory state before step {} for the context {}:", self.clk, self.ctx);
for (addr, value) in mem.iter().take(mem.len() - 1) {
print_mem_address(*addr, Some(*value), false, false, element_width);
}
if let Some((addr, value)) = mem.last() {
print_mem_address(*addr, Some(*value), true, false, element_width);
}
}
fn print_mem_interval(&self, process: ProcessState, n: u32, m: u32) {
let mut mem_interval = Vec::new();
for addr in n..m + 1 {
mem_interval.push((addr, process.get_mem_value(self.ctx, addr)));
}
if n == m {
println!(
"Memory state before step {} for the context {} at address {}:",
self.clk, self.ctx, n
)
} else {
println!(
"Memory state before step {} for the context {} in the interval [{}, {}]:",
self.clk, self.ctx, n, m
)
};
print_interval(mem_interval, false);
}
fn print_local_interval(&self, process: ProcessState, interval: (u32, u32), num_locals: u32) {
let mut local_mem_interval = Vec::new();
let local_memory_offset = self.fmp - num_locals + 1;
let (start, end) = if interval.0 == 0 && interval.1 == u16::MAX as u32 {
(0, num_locals - 1)
} else {
interval
};
for index in start..end + 1 {
local_mem_interval
.push((index, process.get_mem_value(self.ctx, index + local_memory_offset)))
}
if interval.0 == 0 && interval.1 == u16::MAX as u32 {
println!("State of procedure locals before step {}:", self.clk)
} else if interval.0 == interval.1 {
println!("State of procedure local at index {} before step {}:", interval.0, self.clk,)
} else {
println!(
"State of procedure locals [{}, {}] before step {}:",
interval.0, interval.1, self.clk,
)
};
print_interval(local_mem_interval, true);
}
}
fn print_interval(mem_interval: Vec<(u32, Option<Felt>)>, is_local: bool) {
let element_width = mem_interval
.iter()
.map(|(_addr, value)| element_printed_width(*value))
.max()
.unwrap_or(0) as usize;
for (addr, mem_value) in mem_interval.iter().take(mem_interval.len() - 1) {
print_mem_address((*addr).into(), *mem_value, false, is_local, element_width)
}
if let Some((addr, value)) = mem_interval.last() {
print_mem_address((*addr).into(), *value, true, is_local, element_width);
}
}
fn print_mem_address(
addr: MemoryAddress,
mem_value: Option<Felt>,
is_last: bool,
is_local: bool,
element_width: usize,
) {
if let Some(value) = mem_value {
if is_last {
if is_local {
print!("└── {addr:>5}: ");
} else {
print!("└── {addr:#010x}: ");
}
println!("{:>width$}\n", value.as_int(), width = element_width);
} else {
if is_local {
print!("├── {addr:>5}: ");
} else {
print!("├── {addr:#010x}: ");
}
println!("{:>width$}", value.as_int(), width = element_width);
}
} else if is_last {
if is_local {
println!("└── {addr:>5}: EMPTY\n");
} else {
println!("└── {addr:#010x}: EMPTY\n");
}
} else if is_local {
println!("├── {addr:>5}: EMPTY");
} else {
println!("├── {addr:#010x}: EMPTY");
}
}
fn element_printed_width(element: Option<Felt>) -> u32 {
if let Some(element) = element {
element.as_int().checked_ilog10().unwrap_or(1) + 1
} else {
0
}
}