miden_debug_engine/exec/
snapshot.rs1use alloc::{
13 string::{String, ToString},
14 sync::Arc,
15 vec::Vec,
16};
17
18use miden_core::{
19 mast::MastForest,
20 program::StackInputs,
21 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
22};
23use miden_debug_types::Uri;
24use miden_mast_package::{Package, debug_info::PackageDebugInfo};
25use miden_processor::{
26 ExecutionOptions, LoadedMastForest,
27 advice::{AdviceInputs, AdviceMutation},
28};
29use miden_utils_sync::RwLock;
30
31use super::advice::{read_event_log, write_event_log};
32
33#[derive(Clone, Default)]
40pub struct MastForestRecorder {
41 forests: Arc<RwLock<Vec<LoadedMastForest>>>,
42}
43
44impl MastForestRecorder {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn snapshot(&self) -> Vec<LoadedMastForest> {
52 self.forests.read().clone()
53 }
54
55 pub(crate) fn record(&self, forest: LoadedMastForest) {
57 let mut guard = self.forests.write();
58 if !guard
59 .iter()
60 .any(|existing| Arc::ptr_eq(existing.mast_forest(), forest.mast_forest()))
61 {
62 guard.push(forest);
63 }
64 }
65
66 #[cfg(feature = "dap")]
68 pub(crate) fn clear(&self) {
69 self.forests.write().clear();
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct ReplaySnapshotWrite {
76 pub path: Uri,
77 pub event_count: usize,
78 pub forest_count: usize,
79}
80
81#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
83#[error("failed to write replay snapshot to {}: {}", path, message)]
84pub struct ReplaySnapshotWriteError {
85 pub path: Uri,
86 pub message: String,
87}
88
89#[derive(Clone, Debug, Default)]
91pub struct ReplaySnapshotRecorder {
92 status: Arc<RwLock<Option<Result<ReplaySnapshotWrite, ReplaySnapshotWriteError>>>>,
93}
94
95impl ReplaySnapshotRecorder {
96 pub fn new() -> Self {
97 Self::default()
98 }
99
100 pub fn take(&self) -> Option<Result<ReplaySnapshotWrite, ReplaySnapshotWriteError>> {
102 self.status.write().take()
103 }
104
105 #[cfg(feature = "dap")]
106 pub(crate) fn record_success(&self, write: ReplaySnapshotWrite) {
107 *self.status.write() = Some(Ok(write));
108 }
109
110 #[cfg(feature = "dap")]
111 pub(crate) fn record_error(&self, path: Uri, err: impl ToString) {
112 *self.status.write() = Some(Err(ReplaySnapshotWriteError {
113 path,
114 message: err.to_string(),
115 }));
116 }
117}
118
119const SNAPSHOT_MAGIC: [u8; 6] = *b"MDNSNP";
122const SNAPSHOT_VERSION: u8 = 3;
123
124pub struct ReplaySnapshot {
126 pub package: Arc<Package>,
128 pub stack_inputs: StackInputs,
130 pub advice_inputs: AdviceInputs,
132 pub options: ExecutionOptions,
134 pub mast_forests: Vec<LoadedMastForest>,
137 pub event_log: Vec<Vec<AdviceMutation>>,
140}
141
142impl ReplaySnapshot {
143 #[cfg(feature = "std")]
145 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
146 std::fs::write(path, self.to_bytes())
147 }
148
149 #[cfg(feature = "std")]
151 pub fn read_from_file(path: impl AsRef<std::path::Path>) -> Result<Self, ReplaySnapshotError> {
152 let bytes = std::fs::read(path).map_err(ReplaySnapshotError::Io)?;
153 Self::read_from_bytes(&bytes).map_err(ReplaySnapshotError::Deserialization)
154 }
155
156 pub fn to_bytes(&self) -> Vec<u8> {
158 let mut bytes = Vec::new();
159 self.write_into(&mut bytes);
160 bytes
161 }
162
163 pub fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
165 let mut reader = miden_core::serde::SliceReader::new(bytes);
166 Self::read_from(&mut reader)
167 }
168}
169
170impl Serializable for ReplaySnapshot {
171 fn write_into<W: ByteWriter>(&self, target: &mut W) {
172 target.write_bytes(&SNAPSHOT_MAGIC);
173 target.write_u8(SNAPSHOT_VERSION);
174 self.package.write_into(target);
175 self.stack_inputs.write_into(target);
176 self.advice_inputs.write_into(target);
177 write_execution_options(&self.options, target);
178 target.write_usize(self.mast_forests.len());
179 for forest in &self.mast_forests {
180 forest.mast_forest().as_ref().write_into(target);
181 match forest.package_debug_info().ok().flatten() {
182 Some(debug_info) => {
183 target.write_bool(true);
184 debug_info.as_ref().write_into(target);
185 }
186 None => {
187 target.write_bool(false);
188 }
189 }
190 }
191 write_event_log(&self.event_log, target);
192 }
193}
194
195impl Deserializable for ReplaySnapshot {
196 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
197 let magic: [u8; 6] = source.read_array()?;
198 if magic != SNAPSHOT_MAGIC {
199 return Err(DeserializationError::InvalidValue(
200 "not a Miden debugger replay snapshot (bad magic)".to_string(),
201 ));
202 }
203 let version = source.read_u8()?;
204 if version != SNAPSHOT_VERSION {
205 return Err(DeserializationError::InvalidValue(format!(
206 "unsupported replay snapshot version {version} (expected {SNAPSHOT_VERSION})"
207 )));
208 }
209 let package = Arc::new(Package::read_from_trusted(source)?);
210 let stack_inputs = StackInputs::read_from(source)?;
211 let advice_inputs = AdviceInputs::read_from(source)?;
212 let options = read_execution_options(source)?;
213 let forest_count = source.read_usize()?;
214 let mut mast_forests = Vec::with_capacity(forest_count);
215 for _ in 0..forest_count {
216 let mast_forest = Arc::new(MastForest::read_from(source)?);
217 mast_forests.push(if source.read_bool()? {
218 let debug_info = Some(PackageDebugInfo::read_from(source)?);
219 LoadedMastForest::with_package_debug_info(mast_forest, Ok(debug_info))
220 } else {
221 LoadedMastForest::new(mast_forest)
222 });
223 }
224 let event_log = read_event_log(source)?;
225 Ok(Self {
226 package,
227 stack_inputs,
228 advice_inputs,
229 options,
230 mast_forests,
231 event_log,
232 })
233 }
234}
235
236fn write_execution_options<W: ByteWriter>(options: &ExecutionOptions, target: &mut W) {
237 target.write_u32(options.max_cycles());
238 target.write_u32(options.expected_cycles());
239 target.write_usize(options.core_trace_fragment_size());
240 target.write_usize(options.max_advice_size_bytes());
241 target.write_usize(options.max_hash_len_bytes());
242 target.write_bool(options.overlapped_trace_build());
243 target.write_usize(options.max_num_continuations());
244 target.write_usize(options.max_stack_depth());
245 target.write_usize(options.max_memory_elements());
246}
247
248fn read_execution_options<R: ByteReader>(
249 source: &mut R,
250) -> Result<ExecutionOptions, DeserializationError> {
251 let max_cycles = source.read_u32()?;
252 let expected_cycles = source.read_u32()?;
253 let core_trace_fragment_size = source.read_usize()?;
254 let max_advice_size_bytes = source.read_usize()?;
255 let max_hash_len_bytes = source.read_usize()?;
256 let overlapped_trace_build = source.read_bool()?;
257 let max_num_continuations = source.read_usize()?;
258 let max_stack_depth = source.read_usize()?;
259 let max_memory_elements = source.read_usize()?;
260
261 ExecutionOptions::new(Some(max_cycles), expected_cycles, core_trace_fragment_size)
262 .map_err(|err| {
263 DeserializationError::InvalidValue(format!("invalid execution options: {err}"))
264 })
265 .and_then(|options| {
266 options
267 .with_max_advice_size_bytes(max_advice_size_bytes)
268 .with_max_hash_len_bytes(max_hash_len_bytes)
269 .with_overlapped_trace_build(overlapped_trace_build)
270 .with_max_num_continuations(max_num_continuations)
271 .with_max_memory_elements(max_memory_elements)
272 .with_max_stack_depth(max_stack_depth)
273 .map_err(|err| {
274 DeserializationError::InvalidValue(format!("invalid execution options: {err}"))
275 })
276 })
277}
278
279#[derive(Debug, thiserror::Error)]
281#[cfg(feature = "std")]
282pub enum ReplaySnapshotError {
283 #[error("failed to read replay snapshot file: {0}")]
284 Io(std::io::Error),
285 #[error("failed to deserialize replay snapshot: {0}")]
286 Deserialization(DeserializationError),
287}
288
289#[cfg(test)]
290mod tests;