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