1use 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 {
157 use std::sync::Arc;
158
159 use miden_assembly::DefaultSourceManager;
160 use miden_assembly_syntax::ast::types::Type;
161 use miden_processor::{ContextId, trace::RowIndex};
162
163 use super::ExecutionTrace;
164 use crate::{Executor, debug::NativePtr, exec::DebugQuery, felt::ToMidenRepr};
165
166 fn empty_trace() -> ExecutionTrace {
167 ExecutionTrace {
168 processor: miden_processor::FastProcessor::new(miden_processor::StackInputs::default()),
169 outputs: miden_processor::StackOutputs::default(),
170 }
171 }
172
173 fn execute_trace(source: &str) -> ExecutionTrace {
174 let source_manager = Arc::new(DefaultSourceManager::default());
175 let program = miden_assembly::Assembler::new(source_manager.clone())
176 .assemble_program("program", source)
177 .map(Arc::from)
178 .unwrap();
179
180 Executor::new(vec![]).capture_trace(program, source_manager)
181 }
182
183 #[test]
184 fn parse_result_reads_multi_felt_outputs_in_stack_order() {
185 let outputs = 0x0807_0605_0403_0201_u64.to_felts();
186 let trace = ExecutionTrace {
187 outputs: miden_processor::StackOutputs::new(&outputs).unwrap(),
188 ..empty_trace()
189 };
190
191 let result = trace.parse_result::<u64>().unwrap();
192
193 assert_eq!(result, 0x0807_0605_0403_0201_u64);
194 }
195
196 #[test]
197 fn read_bytes_for_type_preserves_little_endian_bytes() {
198 let trace = execute_trace(
199 r#"
200begin
201 push.4660
202 push.8
203 mem_store
204
205 push.67305985
206 push.12
207 mem_store
208
209 push.134678021
210 push.13
211 mem_store
212end
213"#,
214 );
215 let ctx = ContextId::root();
216
217 let u16_bytes = trace
218 .read_bytes_for_type_in_context(
219 NativePtr::new(8, 0),
220 &Type::U16,
221 ctx,
222 RowIndex::from(0_u32),
223 )
224 .unwrap();
225 let u64_bytes = trace
226 .read_bytes_for_type_in_context(
227 NativePtr::new(12, 0),
228 &Type::U64,
229 ctx,
230 RowIndex::from(0_u32),
231 )
232 .unwrap();
233 let unaligned_u32_bytes = trace
234 .read_bytes_for_type_in_context(
235 NativePtr::new(12, 1),
236 &Type::U32,
237 ctx,
238 RowIndex::from(0_u32),
239 )
240 .unwrap();
241
242 assert_eq!(u16_bytes, vec![0x34, 0x12]);
243 assert_eq!(u64_bytes, vec![1, 2, 3, 4, 5, 6, 7, 8]);
244 assert_eq!(unaligned_u32_bytes, vec![2, 3, 4, 5]);
245 }
246
247 #[test]
248 fn debug_query_reads_rust_memory_from_byte_address() {
249 let trace = execute_trace(
250 r#"
251begin
252 push.67305985
253 push.3
254 mem_store
255
256 push.134678021
257 push.4
258 mem_store
259end
260"#,
261 );
262
263 assert_eq!(trace.read_from_rust_memory::<u64>(12), Some(0x0807_0605_0403_0201));
264 }
265
266 #[test]
267 fn debug_query_reads_uninitialized_rust_memory_as_zero() {
268 let trace = execute_trace(
269 r#"
270begin
271 push.67305985
272 push.3
273 mem_store
274end
275"#,
276 );
277
278 assert_eq!(trace.read_from_rust_memory::<u64>(12), Some(0x0000_0000_0403_0201));
279 }
280}