miden_debug/exec/
state.rs1use std::collections::{BTreeSet, VecDeque};
2
3use miden_core::{
4 mast::{MastNode, MastNodeId},
5 operations::AssemblyOp,
6};
7use miden_processor::{
8 ContextId, Continuation, ExecutionError, FastProcessor, Felt, ResumeContext, StackOutputs,
9 operation::Operation, trace::RowIndex,
10};
11
12use super::{DebuggerHost, ExecutionTrace};
13use crate::debug::{CallFrame, CallStack, StepInfo};
14
15fn poll_immediately<T>(fut: impl std::future::Future<Output = T>) -> T {
22 let waker = std::task::Waker::noop();
23 let mut cx = std::task::Context::from_waker(waker);
24 let mut fut = std::pin::pin!(fut);
25 match fut.as_mut().poll(&mut cx) {
26 std::task::Poll::Ready(val) => val,
27 std::task::Poll::Pending => panic!("future was expected to complete immediately"),
28 }
29}
30
31pub struct DebugExecutor {
38 pub processor: FastProcessor,
40 pub host: DebuggerHost<dyn miden_assembly::SourceManager>,
42 pub resume_ctx: Option<ResumeContext>,
44
45 pub current_stack: Vec<Felt>,
48 pub current_op: Option<Operation>,
50 pub current_asmop: Option<AssemblyOp>,
52
53 pub stack_outputs: StackOutputs,
55 pub contexts: BTreeSet<ContextId>,
57 pub root_context: ContextId,
59 pub current_context: ContextId,
61 pub callstack: CallStack,
63 pub recent: VecDeque<Operation>,
65 pub cycle: usize,
67 pub stopped: bool,
69}
70
71fn extract_current_op(
74 ctx: &ResumeContext,
75) -> (Option<Operation>, Option<MastNodeId>, Option<usize>) {
76 let forest = ctx.current_forest();
77 for cont in ctx.continuation_stack().iter_continuations_for_next_clock() {
78 match cont {
79 Continuation::ResumeBasicBlock {
80 node_id,
81 batch_index,
82 op_idx_in_batch,
83 } => {
84 let node = &forest[*node_id];
85 if let MastNode::Block(block) = node {
86 let mut global_idx = 0;
88 for batch in &block.op_batches()[..*batch_index] {
89 global_idx += batch.ops().len();
90 }
91 global_idx += op_idx_in_batch;
92 let op = block.op_batches()[*batch_index].ops().get(*op_idx_in_batch).copied();
93 return (op, Some(*node_id), Some(global_idx));
94 }
95 }
96 Continuation::StartNode(node_id) => {
97 return (None, Some(*node_id), None);
98 }
99 Continuation::FinishBasicBlock(_) => {
100 return (Some(Operation::End), None, None);
101 }
102 other if other.increments_clk() => {
103 return (None, None, None);
104 }
105 _ => continue,
106 }
107 }
108 (None, None, None)
109}
110
111impl DebugExecutor {
112 pub fn step(&mut self) -> Result<Option<CallFrame>, ExecutionError> {
119 if self.stopped {
120 return Ok(None);
121 }
122
123 let resume_ctx = match self.resume_ctx.take() {
124 Some(ctx) => ctx,
125 None => {
126 self.stopped = true;
127 return Ok(None);
128 }
129 };
130
131 let (op, node_id, op_idx) = extract_current_op(&resume_ctx);
133 let asmop = node_id
134 .and_then(|nid| resume_ctx.current_forest().get_assembly_op(nid, op_idx).cloned());
135
136 match poll_immediately(self.processor.step(&mut self.host, resume_ctx)) {
138 Ok(Some(new_ctx)) => {
139 self.resume_ctx = Some(new_ctx);
140 self.cycle += 1;
141
142 let state = self.processor.state();
144 let ctx = state.ctx();
145 self.current_stack = state.get_stack_state();
146
147 if self.current_context != ctx {
148 self.contexts.insert(ctx);
149 self.current_context = ctx;
150 }
151
152 self.current_op = op;
154 self.current_asmop = asmop.clone();
155
156 if let Some(op) = op {
157 if self.recent.len() == 5 {
158 self.recent.pop_front();
159 }
160 self.recent.push_back(op);
161 }
162
163 let step_info = StepInfo {
165 op,
166 asmop: self.current_asmop.as_ref(),
167 clk: RowIndex::from(self.cycle as u32),
168 ctx: self.current_context,
169 };
170 let exited = self.callstack.next(&step_info);
171
172 Ok(exited)
173 }
174 Ok(None) => {
175 self.stopped = true;
177 let state = self.processor.state();
178 self.current_stack = state.get_stack_state();
179
180 let len = self.current_stack.len().min(16);
182 self.stack_outputs =
183 StackOutputs::new(&self.current_stack[..len]).expect("invalid stack outputs");
184 Ok(None)
185 }
186 Err(err) => {
187 self.stopped = true;
188 Err(err)
189 }
190 }
191 }
192
193 pub fn into_execution_trace(self) -> ExecutionTrace {
195 ExecutionTrace {
196 root_context: self.root_context,
197 last_cycle: RowIndex::from(self.cycle as u32),
198 processor: self.processor,
199 outputs: self.stack_outputs,
200 }
201 }
202}