miden_debug_engine/exec/
trace.rs1use alloc::vec::Vec;
2
3use miden_core::Word;
4use miden_processor::{
5 ContextId, FastProcessor, Felt, ProcessorState, StackInputs, StackOutputs, trace::RowIndex,
6};
7use smallvec::SmallVec;
8
9use crate::{debug::NativePtr, felt::FromMidenRepr};
10
11#[derive(Debug, thiserror::Error)]
13pub enum MemoryReadError {
14 #[error("attempted to read beyond end of linear memory")]
15 OutOfBounds,
16 #[error("unaligned reads are not supported yet")]
17 UnalignedRead,
18}
19
20pub struct ExecutionTrace {
26 pub(super) processor: FastProcessor,
27 pub(super) outputs: StackOutputs,
28}
29
30impl ExecutionTrace {
31 pub fn empty() -> Self {
35 Self {
36 processor: FastProcessor::new(StackInputs::default()),
37 outputs: StackOutputs::default(),
38 }
39 }
40
41 pub fn parse_result<T>(&self) -> Option<T>
43 where
44 T: FromMidenRepr,
45 {
46 let size = <T as FromMidenRepr>::size_in_felts();
47 let stack = self.outputs.get_num_elements(size);
48 if stack.len() < size {
49 return None;
50 }
51 let mut stack = stack.to_vec();
52 stack.reverse();
53 Some(<T as FromMidenRepr>::pop_from_stack(&mut stack))
54 }
55
56 #[inline]
58 pub fn into_outputs(self) -> StackOutputs {
59 self.outputs
60 }
61
62 #[inline]
64 pub fn outputs(&self) -> &StackOutputs {
65 &self.outputs
66 }
67}
68
69impl super::query::DebugQuery for ExecutionTrace {
70 fn state(&self) -> ProcessorState<'_> {
71 self.processor.state()
72 }
73
74 fn current_context(&self) -> ContextId {
75 self.processor.state().ctx()
76 }
77
78 fn current_clock(&self) -> RowIndex {
79 self.processor.state().clock()
80 }
81}
82
83impl ExecutionTrace {
84 pub fn read_memory_word_in_context(
86 &self,
87 addr: u32,
88 ctx: ContextId,
89 clk: RowIndex,
90 ) -> Option<Word> {
91 const ZERO: Word = Word::new([Felt::ZERO; 4]);
92
93 match self.processor.memory().read_word(
94 ctx,
95 Felt::new(addr as u64).expect("value exceeds field modulus"),
96 clk,
97 ) {
98 Ok(word) => Some(word),
99 Err(_) => Some(ZERO),
100 }
101 }
102
103 #[track_caller]
105 pub fn read_memory_element_in_context(
106 &self,
107 addr: u32,
108 ctx: ContextId,
109 _clk: RowIndex,
110 ) -> Option<Felt> {
111 self.processor
112 .memory()
113 .read_element(ctx, Felt::new(addr as u64).expect("value exceeds field modulus"))
114 .ok()
115 }
116
117 pub fn read_bytes_for_type_in_context(
120 &self,
121 addr: NativePtr,
122 ty: &miden_assembly_syntax::ast::types::Type,
123 ctx: ContextId,
124 clk: RowIndex,
125 ) -> Result<Vec<u8>, MemoryReadError> {
126 let size = ty.size_in_bytes();
127 super::query::read_memory_bytes(addr, size, |addr| {
128 Ok(self.read_memory_element_in_context(addr, ctx, clk).unwrap_or_default())
129 })
130 }
131
132 #[track_caller]
135 pub fn read_from_rust_memory_in_context<T>(
136 &self,
137 addr: u32,
138 ctx: ContextId,
139 clk: RowIndex,
140 ) -> Option<T>
141 where
142 T: core::any::Any + FromMidenRepr,
143 {
144 let ptr = NativePtr::from_ptr(addr);
145 assert_eq!(ptr.offset, 0, "support for unaligned reads is not yet implemented");
146 let size = <T as FromMidenRepr>::size_in_felts();
147 let mut felts = SmallVec::<[_; 4]>::with_capacity(size);
148 for index in 0..(size as u32) {
149 felts.push(self.read_memory_element_in_context(ptr.addr + index, ctx, clk)?);
150 }
151 Some(T::from_felts(&felts))
152 }
153}
154
155#[cfg(all(test, feature = "std"))]
156mod tests;