miden_debug/exec/
trace.rs1use miden_assembly_syntax::ast::types::Type;
2use miden_core::{FieldElement, Word};
3use miden_processor::{ContextId, Felt, RowIndex, StackOutputs, TraceLenSummary};
4use smallvec::SmallVec;
5
6use super::{MemoryChiplet, TraceEvent};
7use crate::{debug::NativePtr, felt::FromMidenRepr};
8
9pub type TraceHandler = dyn FnMut(RowIndex, TraceEvent);
11
12#[derive(Debug, thiserror::Error)]
14pub enum MemoryReadError {
15 #[error("attempted to read beyond end of linear memory")]
16 OutOfBounds,
17 #[error("unaligned reads are not supported yet")]
18 UnalignedRead,
19}
20
21pub struct ExecutionTrace {
27 pub(super) root_context: ContextId,
28 pub(super) last_cycle: RowIndex,
29 pub(super) memory: MemoryChiplet,
30 pub(super) outputs: StackOutputs,
31 pub(super) trace_len_summary: TraceLenSummary,
32}
33
34impl ExecutionTrace {
35 pub fn parse_result<T>(&self) -> Option<T>
37 where
38 T: FromMidenRepr,
39 {
40 let size = <T as FromMidenRepr>::size_in_felts();
41 let stack = self.outputs.stack_truncated(size);
42 if stack.len() < size {
43 return None;
44 }
45 let mut stack = stack.to_vec();
46 stack.reverse();
47 Some(<T as FromMidenRepr>::pop_from_stack(&mut stack))
48 }
49
50 #[inline]
52 pub fn into_outputs(self) -> StackOutputs {
53 self.outputs
54 }
55
56 #[inline]
58 pub fn outputs(&self) -> &StackOutputs {
59 &self.outputs
60 }
61
62 #[inline]
64 pub fn trace_len_summary(&self) -> &TraceLenSummary {
65 &self.trace_len_summary
66 }
67
68 pub fn read_memory_word(&self, addr: u32) -> Option<Word> {
70 self.read_memory_word_in_context(addr, self.root_context, self.last_cycle)
71 }
72
73 pub fn read_memory_word_in_context(
75 &self,
76 addr: u32,
77 ctx: ContextId,
78 _clk: RowIndex,
79 ) -> Option<Word> {
80 use miden_core::FieldElement;
81
82 const ZERO: Word = Word::new([Felt::ZERO; 4]);
83
84 Some(
85 self.memory
86 .get_word(ctx, addr)
87 .unwrap_or_else(|err| panic!("{err}"))
88 .unwrap_or(ZERO),
89 )
90 }
91
92 #[track_caller]
94 pub fn read_memory_element(&self, addr: u32) -> Option<Felt> {
95 self.memory.get_value(self.root_context, addr)
96 }
97
98 #[track_caller]
101 pub fn read_memory_element_in_context(
102 &self,
103 addr: u32,
104 ctx: ContextId,
105 _clk: RowIndex,
106 ) -> Option<Felt> {
107 self.memory.get_value(ctx, addr)
108 }
109
110 pub fn read_bytes_for_type(
113 &self,
114 addr: NativePtr,
115 ty: &Type,
116 ctx: ContextId,
117 clk: RowIndex,
118 ) -> Result<Vec<u8>, MemoryReadError> {
119 const U32_MASK: u64 = u32::MAX as u64;
120 let size = ty.size_in_bytes();
121 let mut buf = Vec::with_capacity(size);
122
123 let size_in_felts = ty.size_in_felts();
124 let mut elems = Vec::with_capacity(size_in_felts);
125
126 if addr.is_element_aligned() {
127 for i in 0..size_in_felts {
128 let addr = addr.addr.checked_add(i as u32).ok_or(MemoryReadError::OutOfBounds)?;
129 elems.push(self.read_memory_element_in_context(addr, ctx, clk).unwrap_or_default());
130 }
131 } else {
132 return Err(MemoryReadError::UnalignedRead);
133 }
134
135 let mut needed = size - buf.len();
136 for elem in elems {
137 let bytes = ((elem.as_int() & U32_MASK) as u32).to_be_bytes();
138 let take = core::cmp::min(needed, 4);
139 buf.extend(&bytes[0..take]);
140 needed -= take;
141 }
142
143 Ok(buf)
144 }
145
146 #[track_caller]
148 pub fn read_from_rust_memory<T>(&self, addr: u32) -> Option<T>
149 where
150 T: core::any::Any + FromMidenRepr,
151 {
152 self.read_from_rust_memory_in_context(addr, self.root_context, self.last_cycle)
153 }
154
155 #[track_caller]
158 pub fn read_from_rust_memory_in_context<T>(
159 &self,
160 addr: u32,
161 ctx: ContextId,
162 clk: RowIndex,
163 ) -> Option<T>
164 where
165 T: core::any::Any + FromMidenRepr,
166 {
167 use core::any::TypeId;
168
169 let ptr = NativePtr::from_ptr(addr);
170 if TypeId::of::<T>() == TypeId::of::<Felt>() {
171 assert_eq!(ptr.offset, 0, "cannot read values of type Felt from unaligned addresses");
172 }
173 assert_eq!(ptr.offset, 0, "support for unaligned reads is not yet implemented");
174 match <T as FromMidenRepr>::size_in_felts() {
175 1 => {
176 let felt = self.read_memory_element_in_context(ptr.addr, ctx, clk)?;
177 Some(T::from_felts(&[felt]))
178 }
179 2 => {
180 let lo = self.read_memory_element_in_context(ptr.addr, ctx, clk)?;
181 let hi = self.read_memory_element_in_context(ptr.addr + 1, ctx, clk)?;
182 Some(T::from_felts(&[lo, hi]))
183 }
184 3 => {
185 let lo_l = self.read_memory_element_in_context(ptr.addr, ctx, clk)?;
186 let lo_h = self.read_memory_element_in_context(ptr.addr + 1, ctx, clk)?;
187 let hi_l = self.read_memory_element_in_context(ptr.addr + 2, ctx, clk)?;
188 Some(T::from_felts(&[lo_l, lo_h, hi_l]))
189 }
190 n => {
191 assert_ne!(n, 0);
192 let num_words = n.next_multiple_of(4) / 4;
193 let mut words = SmallVec::<[_; 2]>::with_capacity(num_words);
194 for word_index in 0..(num_words as u32) {
195 let addr = ptr.addr + (word_index * 4);
196 let mut word = self.read_memory_word(addr)?;
197 word.reverse();
198 dbg!(word_index, word);
199 words.push(word);
200 }
201 words.resize(num_words, Word::new([Felt::ZERO; 4]));
202 Some(T::from_words(&words))
203 }
204 }
205 }
206}