use std::{print, println};
use super::ProcessState;
use crate::system::ContextId;
use alloc::vec::Vec;
use vm_core::{DebugOptions, Word};
pub fn print_debug_info<S: ProcessState>(process: &S, 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: u32,
ctx: ContextId,
fmp: u32,
}
impl Printer {
fn new(clk: u32, ctx: ContextId, fmp: u64) -> Self {
Self {
clk,
ctx,
fmp: fmp as u32,
}
}
fn print_vm_stack<S: ProcessState>(&self, process: &S, 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<S: ProcessState>(&self, process: &S) {
let mem = process.get_mem_state(self.ctx);
let padding =
mem.iter().fold(0, |max, value| word_elem_max_len(Some(value.1)).max(max)) 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 as u32, Some(*value), false, false, padding);
}
if let Some((addr, value)) = mem.last() {
print_mem_address(*addr as u32, Some(*value), true, false, padding);
}
}
fn print_mem_interval<S: ProcessState>(&self, process: &S, 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<S: ProcessState>(
&self,
process: &S,
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<Word>)>, is_local: bool) {
let padding =
mem_interval.iter().fold(0, |max, value| word_elem_max_len(value.1).max(max)) as usize;
for (addr, value) in mem_interval.iter().take(mem_interval.len() - 1) {
print_mem_address(*addr, *value, false, is_local, padding)
}
if let Some((addr, value)) = mem_interval.last() {
print_mem_address(*addr, *value, true, is_local, padding);
}
}
fn print_mem_address(
addr: u32,
value: Option<Word>,
is_last: bool,
is_local: bool,
padding: usize,
) {
if let Some(value) = value {
if is_last {
if is_local {
print!("└── {addr:>5}: ");
} else {
print!("└── {addr:#010x}: ");
}
print_word(value, padding);
println!();
} else {
if is_local {
print!("├── {addr:>5}: ");
} else {
print!("├── {addr:#010x}: ");
}
print_word(value, padding);
}
} 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 print_word(value: Word, padding: usize) {
println!(
"[{:>width$}, {:>width$}, {:>width$}, {:>width$}]",
value[0].as_int(),
value[1].as_int(),
value[2].as_int(),
value[3].as_int(),
width = padding
)
}
fn word_elem_max_len(word: Option<Word>) -> u32 {
if let Some(word) = word {
word.iter()
.fold(0, |max, value| (value.as_int().checked_ilog10().unwrap_or(1) + 1).max(max))
} else {
0
}
}