miden_debug_engine/debug/
variables.rs1use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
2
3use miden_core::{
4 Felt,
5 operations::{DebugVarInfo, DebugVarLocation},
6};
7use miden_processor::trace::RowIndex;
8
9const FRAME_BASE_LOCAL_MARKER: u32 = 1 << 31;
10
11fn decode_frame_base_local_offset(encoded: u32) -> Option<i16> {
12 if encoded & FRAME_BASE_LOCAL_MARKER == 0 {
13 return None;
14 }
15
16 let low_bits = (encoded & 0xffff) as u16;
17 Some(i16::from_le_bytes(low_bits.to_le_bytes()))
18}
19
20#[derive(Debug, Clone)]
22pub struct DebugVarSnapshot {
23 pub clk: RowIndex,
25 pub info: DebugVarInfo,
27}
28
29pub struct DebugVarTracker {
31 events: Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>>,
33 current_vars: BTreeMap<String, DebugVarSnapshot>,
35 processed_up_to: RowIndex,
37}
38
39impl DebugVarTracker {
40 pub fn new(events: Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>>) -> Self {
42 Self {
43 events,
44 current_vars: BTreeMap::new(),
45 processed_up_to: RowIndex::from(0),
46 }
47 }
48
49 pub fn record_events(&self, clk: RowIndex, infos: Vec<DebugVarInfo>) {
51 if !infos.is_empty() {
52 self.events.borrow_mut().entry(clk).or_default().extend(infos);
53 }
54 }
55
56 pub fn update_to_cycle(&mut self, clk: RowIndex) {
58 let events = self.events.borrow();
59
60 for (event_clk, var_infos) in events.range(self.processed_up_to..=clk) {
62 for info in var_infos {
63 let snapshot = DebugVarSnapshot {
64 clk: *event_clk,
65 info: info.clone(),
66 };
67 self.current_vars.insert(info.name().to_string(), snapshot);
68 }
69 }
70
71 self.processed_up_to = clk;
72 }
73
74 pub fn reset(&mut self) {
76 self.current_vars.clear();
77 self.processed_up_to = RowIndex::from(0);
78 }
79
80 pub fn current_variables(&self) -> impl Iterator<Item = &DebugVarSnapshot> {
82 self.current_vars.values()
83 }
84
85 pub fn get_variable(&self, name: &str) -> Option<&DebugVarSnapshot> {
87 self.current_vars.get(name)
88 }
89
90 pub fn variable_count(&self) -> usize {
92 self.current_vars.len()
93 }
94
95 pub fn has_variables(&self) -> bool {
97 !self.current_vars.is_empty()
98 }
99}
100
101pub fn resolve_variable_value(
103 location: &DebugVarLocation,
104 stack: &[Felt],
105 get_memory: impl Fn(u32) -> Option<Felt>,
106 get_local: impl Fn(i16) -> Option<Felt>,
107) -> Option<Felt> {
108 match location {
109 DebugVarLocation::Stack(pos) => stack.get(*pos as usize).copied(),
110 DebugVarLocation::Memory(addr) => get_memory(*addr),
111 DebugVarLocation::Const(felt) => Some(*felt),
112 DebugVarLocation::Local(offset) => get_local(*offset),
113 DebugVarLocation::FrameBase {
114 global_index,
115 byte_offset,
116 } => {
117 if let Some(local_offset) = decode_frame_base_local_offset(*global_index) {
118 let base = get_local(local_offset)?;
119 let byte_addr = base.as_canonical_u64() as i64 + byte_offset;
120 let elem_addr = u32::try_from(byte_addr / 4).ok()?;
121 return get_memory(elem_addr);
122 }
123
124 let sp_elem_addr = *global_index / 4;
127 let base = get_memory(sp_elem_addr)?;
128 let byte_addr = base.as_canonical_u64() as i64 + byte_offset;
131 let elem_addr = (byte_addr / 4) as u32;
132 get_memory(elem_addr)
133 }
134 DebugVarLocation::Expression(_) => None,
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_tracker_basic() {
144 let events: Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>> =
145 Rc::new(Default::default());
146
147 {
149 let mut events_mut = events.borrow_mut();
150 events_mut.insert(
151 RowIndex::from(1),
152 vec![DebugVarInfo::new("x", DebugVarLocation::Stack(0))],
153 );
154 events_mut.insert(
155 RowIndex::from(5),
156 vec![DebugVarInfo::new("y", DebugVarLocation::Stack(1))],
157 );
158 }
159
160 let mut tracker = DebugVarTracker::new(events);
161
162 assert_eq!(tracker.variable_count(), 0);
164
165 tracker.update_to_cycle(RowIndex::from(3));
167 assert_eq!(tracker.variable_count(), 1);
168 assert!(tracker.get_variable("x").is_some());
169 assert!(tracker.get_variable("y").is_none());
170
171 tracker.update_to_cycle(RowIndex::from(10));
173 assert_eq!(tracker.variable_count(), 2);
174 assert!(tracker.get_variable("x").is_some());
175 assert!(tracker.get_variable("y").is_some());
176
177 let x_snapshot = tracker.get_variable("x").unwrap();
179 let value = resolve_variable_value(
180 x_snapshot.info.value_location(),
181 &[Felt::new(42)],
182 |_| None,
183 |_| None,
184 );
185 assert_eq!(value, Some(Felt::new(42)));
186 }
187}