midenc_debug/exec/
state.rs1use std::{
2 collections::{BTreeSet, VecDeque},
3 rc::Rc,
4};
5
6use miden_core::Word;
7use miden_processor::{
8 ContextId, ExecutionError, MemoryAddress, MemoryError, Operation, RowIndex, StackOutputs,
9 VmState, VmStateIterator,
10};
11
12use super::ExecutionTrace;
13use crate::{CallFrame, CallStack, TestFelt};
14
15pub struct DebugExecutor {
22 pub iter: VmStateIterator,
24 pub stack_outputs: StackOutputs,
26 pub contexts: BTreeSet<ContextId>,
28 pub root_context: ContextId,
30 pub current_context: ContextId,
32 pub callstack: CallStack,
34 pub recent: VecDeque<Operation>,
36 pub last: Option<VmState>,
38 pub cycle: usize,
40 pub stopped: bool,
42}
43
44impl DebugExecutor {
45 pub fn step(&mut self) -> Result<Option<CallFrame>, ExecutionError> {
52 if self.stopped {
53 return Ok(None);
54 }
55 match self.iter.next() {
56 Some(Ok(state)) => {
57 self.cycle += 1;
58 if self.current_context != state.ctx {
59 self.contexts.insert(state.ctx);
60 self.current_context = state.ctx;
61 }
62
63 if let Some(op) = state.op {
64 if self.recent.len() == 5 {
65 self.recent.pop_front();
66 }
67 self.recent.push_back(op);
68 }
69
70 let exited = self.callstack.next(&state);
71
72 self.last = Some(state);
73
74 Ok(exited)
75 }
76 Some(Err(err)) => {
77 self.stopped = true;
78 Err(err)
79 }
80 None => {
81 self.stopped = true;
82 Ok(None)
83 }
84 }
85 }
86
87 pub fn into_execution_trace(self) -> ExecutionTrace {
89 let last_cycle = self.cycle;
90 let trace_len_summary = *self.iter.trace_len_summary();
91 let (_, _, _, chiplets, _) = self.iter.into_parts();
92 let chiplets = Rc::new(chiplets);
93
94 let chiplets0 = chiplets.clone();
95 let get_state_at = move |context, clk| chiplets0.memory.get_state_at(context, clk);
96 let chiplets1 = chiplets.clone();
97 let get_word = move |context, addr| chiplets1.memory.get_word(context, addr);
98 let get_value = move |context, addr| chiplets.memory.get_value(context, addr);
99
100 let memory = MemoryChiplet {
101 get_value: Box::new(get_value),
102 get_word: Box::new(get_word),
103 get_state_at: Box::new(get_state_at),
104 };
105
106 ExecutionTrace {
107 root_context: self.root_context,
108 last_cycle: RowIndex::from(last_cycle),
109 memory,
110 outputs: self.stack_outputs,
111 trace_len_summary,
112 }
113 }
114}
115impl core::iter::FusedIterator for DebugExecutor {}
116impl Iterator for DebugExecutor {
117 type Item = Result<VmState, ExecutionError>;
118
119 #[inline]
120 fn next(&mut self) -> Option<Self::Item> {
121 if self.stopped {
122 return None;
123 }
124 match self.step() {
125 Ok(_) => self.last.clone().map(Ok),
126 Err(err) => Some(Err(err)),
127 }
128 }
129}
130
131pub struct MemoryChiplet {
133 get_value: Box<dyn Fn(ContextId, u32) -> Option<miden_core::Felt>>,
134 get_word: Box<dyn Fn(ContextId, u32) -> Result<Option<miden_core::Word>, MemoryError>>,
135 #[allow(clippy::type_complexity)]
136 get_state_at: Box<dyn Fn(ContextId, RowIndex) -> Vec<(MemoryAddress, miden_core::Felt)>>,
137}
138
139impl MemoryChiplet {
140 #[inline]
141 pub fn get_value(&self, context: ContextId, addr: u32) -> Option<miden_core::Felt> {
142 (self.get_value)(context, addr)
143 }
144
145 #[inline]
146 pub fn get_word(&self, context: ContextId, addr: u32) -> Result<Option<Word>, MemoryError> {
147 (self.get_word)(context, addr)
148 }
149
150 #[inline]
151 pub fn get_mem_state_at(
152 &self,
153 context: ContextId,
154 clk: RowIndex,
155 ) -> Vec<(MemoryAddress, miden_core::Felt)> {
156 (self.get_state_at)(context, clk)
157 }
158}