1use alloc::{
2 collections::{BTreeMap, VecDeque},
3 rc::Rc,
4 string::String,
5 sync::Arc,
6 vec::Vec,
7};
8use core::{
9 cell::{Cell, RefCell},
10 fmt,
11 ops::Deref,
12};
13
14use log::Level;
15use miden_assembly_syntax::{ast::DebugVarInfo, debuginfo::SourceFile, diagnostics::Report};
16use miden_core::program::StackInputs;
17use miden_debug_types::{ByteIndex, SourceManager};
18use miden_mast_package::Package;
19use miden_package_registry::PackageCache;
20use miden_processor::{
21 ContextId, ExecutionError, ExecutionOptions, FastProcessor, Felt, LoadedMastForest,
22 ProcessorState,
23 advice::{AdviceInputs, AdviceMutation},
24 event::{EventError, EventHandler, EventName},
25 trace::RowIndex,
26};
27use miden_utils_sync::RwLock;
28
29use super::{
30 DebugExecutor, DebuggerHost, Event, ExecutionConfig, ExecutionTrace,
31 event::{FRAME_END_EVENT, FRAME_START_EVENT, PRINTLN_EVENT},
32 query::read_memory_bytes,
33};
34use crate::{
35 HybridPackageRegistry,
36 debug::{CallStack, DebugVarTracker, NativePtr},
37 felt::FromMidenRepr,
38 profiling::{Profiler, ProfilerConfig},
39};
40
41const MAX_PRINTLN_BYTES: usize = 512 * 1024;
48
49pub struct Executor {
56 stack: StackInputs,
57 advice: AdviceInputs,
58 options: ExecutionOptions,
59 event_handlers: Vec<(EventName, Arc<dyn EventHandler>)>,
60 registry: HybridPackageRegistry,
61 record_event_mutations: bool,
62 profiler_config: ProfilerConfig,
63}
64
65impl Executor {
66 pub fn new(args: Vec<Felt>) -> Self {
68 let config = ExecutionConfig {
69 inputs: StackInputs::new(&args).expect("invalid stack inputs"),
70 ..Default::default()
71 };
72
73 Self::from_config(config)
74 }
75
76 pub fn from_config(config: ExecutionConfig) -> Self {
80 let ExecutionConfig {
81 inputs,
82 advice_inputs,
83 options,
84 } = config;
85
86 Self {
87 stack: inputs,
88 advice: advice_inputs,
89 options,
90 event_handlers: Default::default(),
91 registry: HybridPackageRegistry::empty(),
92 record_event_mutations: false,
93 profiler_config: Default::default(),
94 }
95 }
96
97 #[inline]
98 pub fn with_registry(mut self, registry: HybridPackageRegistry) -> Self {
99 self.registry = registry;
100 self
101 }
102
103 pub fn with_advice_inputs(&mut self, advice: AdviceInputs) -> &mut Self {
105 self.advice.extend(advice);
106 self
107 }
108
109 pub fn with_package(&mut self, package: Arc<Package>) -> Result<&mut Self, Report> {
111 self.registry.cache_package(package)?;
112 Ok(self)
113 }
114
115 pub fn with_event_advice_mutations_recording(&mut self) -> &mut Self {
126 self.record_event_mutations = true;
127 self
128 }
129
130 pub fn register_event_handler(
132 &mut self,
133 event: EventName,
134 handler: Arc<dyn EventHandler>,
135 ) -> Result<&mut Self, ExecutionError> {
136 self.event_handlers.push((event, handler));
137 Ok(self)
138 }
139
140 pub fn with_profiler_config(&mut self, profiler_config: ProfilerConfig) -> &mut Self {
142 self.profiler_config = profiler_config;
143 self
144 }
145
146 pub fn into_debug(
149 mut self,
150 package: Arc<Package>,
151 source_manager: Arc<dyn SourceManager>,
152 ) -> DebugExecutor {
153 assert!(package.is_program());
154
155 log::debug!("creating debug executor");
156
157 let mut host = DebuggerHost::new(source_manager.clone());
158 for lib in self.registry.all() {
159 host.load_package(lib);
160 }
161 for (event, handler) in core::mem::take(&mut self.event_handlers) {
162 host.register_event_handler(event, handler)
163 .expect("failed to register debug executor event handler");
164 }
165 if self.record_event_mutations {
166 host = host.with_event_advice_mutations_recording();
167 }
168
169 let events: Arc<RwLock<BTreeMap<RowIndex, Event>>> = Arc::new(Default::default());
170 register_builtin_event_handlers(&mut host, Arc::clone(&events));
171
172 let debug_var_events: Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>> =
174 Rc::new(Default::default());
175
176 let mut processor = FastProcessor::new_with_options(self.stack, self.advice, self.options)
177 .expect("advice inputs should fit advice map limits");
178
179 let root_context = ContextId::root();
180 let resume_ctx = processor
181 .get_initial_resume_context_for_package(package)
182 .expect("failed to get initial resume context");
183
184 let callstack = CallStack::new(events);
185 let debug_vars = DebugVarTracker::new(debug_var_events);
186 DebugExecutor {
187 processor,
188 host,
189 resume_ctx: Some(resume_ctx),
190 current_stack: vec![],
191 current_op: None,
192 current_asmop: None,
193 stack_outputs: Default::default(),
194 contexts: Default::default(),
195 root_context,
196 current_context: root_context,
197 callstack,
198 current_proc: None,
199 debug_vars,
200 last_debug_var_count: 0,
201 recent: VecDeque::with_capacity(5),
202 cycle: 0,
203 stopped: false,
204 profiler: Profiler::from_config(self.profiler_config),
205 }
206 }
207
208 pub fn into_debug_with_replay(
217 self,
218 package: Arc<Package>,
219 source_manager: Arc<dyn SourceManager>,
220 extra_mast_forests: Vec<LoadedMastForest>,
221 event_replay: VecDeque<Vec<AdviceMutation>>,
222 ) -> DebugExecutor {
223 assert!(package.is_program());
224
225 log::debug!("creating debug executor with event replay");
226
227 let mut host = DebuggerHost::new(source_manager.clone());
228 for lib in self.registry.all() {
229 host.load_package(lib);
230 }
231 for forest in extra_mast_forests {
232 host.load_mast_forest(forest);
233 }
234 host.set_event_replay(event_replay);
235
236 let debug_var_events: Rc<RefCell<BTreeMap<RowIndex, Vec<DebugVarInfo>>>> =
237 Rc::new(Default::default());
238
239 let events: Arc<RwLock<BTreeMap<RowIndex, Event>>> = Arc::new(Default::default());
240 register_builtin_event_handlers(&mut host, Arc::clone(&events));
241
242 let mut processor = FastProcessor::new_with_options(self.stack, self.advice, self.options)
243 .expect("advice inputs should fit advice map limits");
244
245 let root_context = ContextId::root();
246 let resume_ctx = processor
247 .get_initial_resume_context_for_package(package)
248 .expect("failed to get initial resume context");
249
250 let callstack = CallStack::new(events);
251 let debug_vars = DebugVarTracker::new(debug_var_events);
252 DebugExecutor {
253 processor,
254 host,
255 resume_ctx: Some(resume_ctx),
256 current_stack: vec![],
257 current_op: None,
258 current_asmop: None,
259 stack_outputs: Default::default(),
260 contexts: Default::default(),
261 root_context,
262 current_context: root_context,
263 callstack,
264 current_proc: None,
265 debug_vars,
266 last_debug_var_count: 0,
267 recent: VecDeque::with_capacity(5),
268 cycle: 0,
269 stopped: false,
270 profiler: Profiler::from_config(self.profiler_config),
271 }
272 }
273
274 pub fn capture_trace(
276 self,
277 package: Arc<Package>,
278 source_manager: Arc<dyn SourceManager>,
279 ) -> ExecutionTrace {
280 let mut executor = self.into_debug(package, source_manager);
281 loop {
282 if executor.stopped {
283 break;
284 }
285 match executor.step() {
286 Ok(_) => continue,
287 Err(err) => {
288 log::warn!(
289 target: "executor",
290 "capture_trace stopped early at cycle {}: {err}",
291 executor.cycle,
292 );
293 break;
294 }
295 }
296 }
297 executor.into_execution_trace()
298 }
299
300 #[track_caller]
302 pub fn execute(
303 self,
304 package: Arc<Package>,
305 source_manager: Arc<dyn SourceManager>,
306 ) -> ExecutionTrace {
307 let mut executor = self.into_debug(package, source_manager.clone());
308 loop {
309 if executor.stopped {
310 break;
311 }
312 match executor.step() {
313 Ok(_) => {
314 if log::log_enabled!(target: "executor", log::Level::Trace)
315 && let (Some(op), Some(asmop)) =
316 (executor.current_op, executor.current_asmop.as_ref())
317 {
318 log::trace!(target: "executor", "stack: {:?}", executor.current_stack);
319 let source_loc = asmop
320 .location()
321 .and_then(|loc| location_to_source_file(loc, &source_manager));
322 if let Some((source_file, line_start)) = source_loc {
323 let line_number = source_file.content().line_index(line_start).number();
324 log::trace!(target: "executor", "in {} (located at {}:{})", asmop.context_name(), source_file.deref().uri().as_str(), line_number);
325 } else {
326 log::trace!(target: "executor", "in {} (no source location available)", asmop.context_name());
327 }
328 log::trace!(target: "executor", " executed `{op:?}` of `{}` ({} cycles)", asmop.op(), asmop.num_cycles());
329 log::trace!(target: "executor", " stack state: {:#?}", executor.current_stack);
330 }
331 }
332 Err(err) => {
333 render_execution_error(err, &executor, &source_manager);
334 }
335 }
336 }
337
338 executor.into_execution_trace()
339 }
340
341 pub fn execute_into<T>(self, package: Arc<Package>, source_manager: Arc<dyn SourceManager>) -> T
343 where
344 T: FromMidenRepr + PartialEq,
345 {
346 let out = self.execute(package, source_manager);
347 out.parse_result().expect("invalid result")
348 }
349}
350
351#[cfg(feature = "std")]
352fn location_to_source_file(
353 loc: &miden_debug_types::Location,
354 source_manager: &dyn SourceManager,
355) -> Option<(Arc<SourceFile>, ByteIndex)> {
356 use miden_assembly_syntax::debuginfo::SourceManagerExt;
357 let path = loc.uri().to_path()?;
358 let file = source_manager.load_file(&path).ok()?;
359 Some((file, loc.start))
360}
361
362#[cfg(not(feature = "std"))]
363fn location_to_source_file(
364 loc: &miden_debug_types::Location,
365 source_manager: &dyn SourceManager,
366) -> Option<(Arc<SourceFile>, ByteIndex)> {
367 let file = source_manager.get_by_uri(loc.uri())?;
368 Some((file, loc.start))
369}
370
371#[derive(Debug, thiserror::Error)]
372enum PrintLnError {
373 #[error("address should fit in u32")]
374 InvalidAddress,
375 #[error("string length should fit in usize")]
376 InvalidLength,
377 #[error("string length {requested} exceeds maximum {max}")]
378 LengthExceeded { requested: usize, max: usize },
379 #[error("memory is not initialized")]
380 MemoryNotInitialized,
381 #[error("failed to read memory: {0}")]
382 MemoryRead(#[from] super::trace::MemoryReadError),
383 #[error("invalid UTF-8")]
384 InvalidUtf8,
385}
386
387fn register_builtin_event_handlers(
388 host: &mut DebuggerHost<dyn SourceManager>,
389 events: Arc<RwLock<BTreeMap<RowIndex, Event>>>,
390) {
391 let println_handler = |process: &ProcessorState| -> Result<Vec<AdviceMutation>, EventError> {
392 match decode_println(process) {
393 Ok(content) => {
394 log::log!(target: "stdout", Level::Info, "{content}");
395 }
396 Err(err) => {
397 log::warn!(
398 target: "executor",
399 "emit.{PRINTLN_EVENT} failed at cycle {}: {err}",
400 process.clock(),
401 );
402 }
403 }
404
405 Ok(vec![])
406 };
407
408 host.register_event_handler(PRINTLN_EVENT, Arc::new(println_handler))
411 .expect("failed to register println event handler");
412
413 let frame_start_events = Arc::clone(&events);
414 let frame_start_handler =
415 move |process: &ProcessorState| -> Result<Vec<AdviceMutation>, EventError> {
416 frame_start_events.write().insert(process.clock(), Event::FrameStart);
417 Ok(vec![])
418 };
419 host.register_event_handler(FRAME_START_EVENT, Arc::new(frame_start_handler))
420 .expect("failed to register frame start event handler");
421
422 let frame_end_events = Arc::clone(&events);
423 let frame_end_handler =
424 move |process: &ProcessorState| -> Result<Vec<AdviceMutation>, EventError> {
425 frame_end_events.write().insert(process.clock(), Event::FrameEnd);
426 Ok(vec![])
427 };
428 host.register_event_handler(FRAME_END_EVENT, Arc::from(frame_end_handler))
429 .expect("failed to register frame end event handler");
430
431 }
438
439fn decode_println(process: &ProcessorState<'_>) -> Result<String, PrintLnError> {
444 let addr = u32::try_from(process.get_stack_item(1).as_canonical_u64())
445 .map_err(|_| PrintLnError::InvalidAddress)?;
446 let len = usize::try_from(process.get_stack_item(2).as_canonical_u64())
447 .map_err(|_| PrintLnError::InvalidLength)?;
448 if len > MAX_PRINTLN_BYTES {
449 return Err(PrintLnError::LengthExceeded {
450 requested: len,
451 max: MAX_PRINTLN_BYTES,
452 });
453 }
454 let ptr = NativePtr::from_ptr(addr);
455 let ctx = process.ctx();
456
457 let bytes = read_memory_bytes(ptr, len, |addr| {
458 process.get_mem_value(ctx, addr).ok_or(PrintLnError::MemoryNotInitialized)
459 })?;
460
461 String::from_utf8(bytes).map_err(|_| PrintLnError::InvalidUtf8)
462}
463
464#[cfg(feature = "std")]
465#[track_caller]
466fn render_execution_error(
467 err: ExecutionError,
468 execution_state: &DebugExecutor,
469 source_manager: &dyn SourceManager,
470) -> ! {
471 use miden_assembly_syntax::diagnostics::{
472 LabeledSpan, miette::miette, reporting::PrintDiagnostic,
473 };
474
475 let stacktrace = execution_state.callstack.stacktrace(&execution_state.recent, source_manager);
476
477 eprintln!("{stacktrace}");
478
479 if !execution_state.current_stack.is_empty() {
480 let stack = execution_state.current_stack.iter().map(|elem| elem.as_canonical_u64());
481 let stack = DisplayValues::new(stack);
482 eprintln!(
483 "\nLast Known State (at most recent instruction which succeeded):
484 | Operand Stack: [{stack}]
485 "
486 );
487
488 let mut labels = vec![];
489 if let Some(span) = stacktrace
490 .current_frame()
491 .and_then(|frame| frame.location.as_ref())
492 .map(|loc| loc.span)
493 {
494 labels.push(LabeledSpan::new_with_span(
495 None,
496 span.start().to_usize()..span.end().to_usize(),
497 ));
498 }
499 let report = miette!(
500 labels = labels,
501 "program execution failed at step {step} (cycle {cycle}): {err}",
502 step = execution_state.cycle,
503 cycle = execution_state.cycle,
504 );
505 let report = match stacktrace
506 .current_frame()
507 .and_then(|frame| frame.location.as_ref())
508 .map(|loc| loc.source_file.clone())
509 {
510 Some(source) => report.with_source_code(source),
511 None => report,
512 };
513
514 panic!("{}", PrintDiagnostic::new(report));
515 } else {
516 panic!("program execution failed at step {step}: {err}", step = execution_state.cycle);
517 }
518}
519
520#[cfg(not(feature = "std"))]
521#[track_caller]
522fn render_execution_error(
523 err: ExecutionError,
524 execution_state: &DebugExecutor,
525 source_manager: &dyn SourceManager,
526) -> ! {
527 use core::fmt::Write;
528
529 use miden_assembly_syntax::diagnostics::{
530 LabeledSpan, miette::miette, reporting::PrintDiagnostic,
531 };
532
533 let stacktrace = execution_state.callstack.stacktrace(&execution_state.recent, source_manager);
534
535 let mut buf = String::with_capacity(1024);
536 writeln!(&mut buf, "{stacktrace}").unwrap();
537
538 if !execution_state.current_stack.is_empty() {
539 let stack = execution_state.current_stack.iter().map(|elem| elem.as_canonical_u64());
540 let stack = DisplayValues::new(stack);
541 writeln!(
542 &mut buf,
543 "\nLast Known State (at most recent instruction which succeeded):
544 | Operand Stack: [{stack}]
545 "
546 )
547 .unwrap();
548
549 let mut labels = vec![];
550 if let Some(span) = stacktrace
551 .current_frame()
552 .and_then(|frame| frame.location.as_ref())
553 .map(|loc| loc.span)
554 {
555 labels.push(LabeledSpan::new_with_span(
556 None,
557 span.start().to_usize()..span.end().to_usize(),
558 ));
559 }
560 let report = miette!(
561 labels = labels,
562 "program execution failed at step {step} (cycle {cycle}): {err}",
563 step = execution_state.cycle,
564 cycle = execution_state.cycle,
565 );
566 let report = match stacktrace
567 .current_frame()
568 .and_then(|frame| frame.location.as_ref())
569 .map(|loc| loc.source_file.clone())
570 {
571 Some(source) => report.with_source_code(source),
572 None => report,
573 };
574
575 panic!("{buf}\n\n{}", PrintDiagnostic::new(report));
576 } else {
577 panic!(
578 "{buf}\n\nprogram execution failed at step {step}: {err}",
579 step = execution_state.cycle
580 );
581 }
582}
583struct DisplayValues<T>(Cell<Option<T>>);
585
586impl<T> DisplayValues<T> {
587 pub fn new(inner: T) -> Self {
588 Self(Cell::new(Some(inner)))
589 }
590}
591
592impl<T, I> fmt::Display for DisplayValues<I>
593where
594 T: fmt::Display,
595 I: Iterator<Item = T>,
596{
597 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598 let iter = self.0.take().unwrap();
599 for (i, item) in iter.enumerate() {
600 if i == 0 {
601 write!(f, "{item}")?;
602 } else {
603 write!(f, ", {item}")?;
604 }
605 }
606 Ok(())
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use alloc::string::ToString;
613
614 use super::*;
615
616 #[test]
619 fn records_event_mutations_and_replays_them() {
620 use std::sync::atomic::{AtomicU64, Ordering};
621
622 use miden_assembly::DefaultSourceManager;
623 use miden_core::events::EventId;
624 use miden_processor::{ProcessorState, advice::AdviceMutation, event::EventError};
625
626 struct CountingHandler {
627 calls: AtomicU64,
628 }
629
630 impl EventHandler for CountingHandler {
631 fn on_event(
632 &self,
633 _process: &ProcessorState<'_>,
634 ) -> Result<Vec<AdviceMutation>, EventError> {
635 let call = self.calls.fetch_add(1, Ordering::SeqCst);
636 Ok(vec![AdviceMutation::extend_advice_stack(
637 [Felt::from(100u32 + call as u32)].into_iter().collect(),
638 )])
639 }
640 }
641
642 let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
643 let event_name = "miden-debug::test::record-replay";
644 let event_id = EventId::from_name(event_name).as_u64();
645 let source = format!(
648 "begin push.{event_id} emit drop adv_push push.{event_id} emit drop adv_push add swap \
649 drop end"
650 );
651 let program = miden_assembly::Assembler::new(source_manager.clone())
652 .assemble_program("program", source)
653 .map(Arc::from)
654 .expect("failed to assemble test program");
655
656 let mut executor = Executor::new(Vec::new());
657 executor
658 .register_event_handler(
659 EventName::from_string(event_name.to_string()),
660 Arc::new(CountingHandler {
661 calls: AtomicU64::new(0),
662 }),
663 )
664 .expect("failed to register event handler");
665 executor.with_event_advice_mutations_recording();
666
667 let mut debug_executor = executor.into_debug(Arc::clone(&program), source_manager.clone());
670 while !debug_executor.stopped {
671 debug_executor.step().expect("recording step failed");
672 }
673 let recorded = debug_executor.host.take_recorded_event_mutations();
674 let recorded_result: u32 =
675 debug_executor.into_execution_trace().parse_result().expect("invalid result");
676 assert_eq!(recorded_result, 201);
677
678 assert_eq!(recorded.len(), 2, "expected one recorded entry per emit");
679 for (index, batch) in recorded.iter().enumerate() {
680 match batch.as_slice() {
681 [AdviceMutation::ExtendStack { stack }] => {
682 assert_eq!(
683 stack.iter().copied().collect::<Vec<_>>(),
684 [Felt::from(100u32 + index as u32)],
685 );
686 }
687 _ => panic!("unexpected mutations recorded for event {index}"),
688 }
689 }
690
691 let replay_executor = Executor::new(Vec::new());
694 let mut debug_executor = replay_executor.into_debug_with_replay(
695 program,
696 source_manager,
697 Vec::new(),
698 recorded.into(),
699 );
700 while !debug_executor.stopped {
701 debug_executor.step().expect("replay step failed");
702 }
703 let replayed_result: u32 = debug_executor
704 .into_execution_trace()
705 .parse_result()
706 .expect("invalid replay result");
707 assert_eq!(replayed_result, recorded_result);
708 }
709
710 #[test]
712 fn replay_invokes_builtin_event_handlers() {
713 use miden_assembly::DefaultSourceManager;
714
715 let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
716 let program = miden_assembly::Assembler::new(source_manager.clone())
717 .assemble_program(
718 "program",
719 format!(
720 r#"
721begin
722 emit.event("{FRAME_START_EVENT}")
723 emit.event("{FRAME_START_EVENT}")
724end
725"#
726 ),
727 )
728 .map(Arc::<Package>::from)
729 .expect("failed to assemble test program");
730
731 let event_replay = VecDeque::from([Vec::new(), Vec::new()]);
732 let mut debug_executor = Executor::new(Vec::new()).into_debug_with_replay(
733 program,
734 source_manager,
735 Vec::new(),
736 event_replay,
737 );
738 while !debug_executor.stopped {
739 debug_executor.step().expect("replay step failed");
740 }
741
742 assert!(
743 debug_executor.callstack.frames().len() >= 2,
744 "expected replayed frame-start events to update the debugger call stack"
745 );
746 }
747
748 #[test]
752 fn replays_from_a_serialized_snapshot() {
753 use std::sync::atomic::{AtomicU64, Ordering};
754
755 use miden_assembly::DefaultSourceManager;
756 use miden_core::events::EventId;
757 use miden_processor::{ProcessorState, advice::AdviceMutation, event::EventError};
758
759 use crate::exec::ReplaySnapshot;
760
761 struct CountingHandler {
762 calls: AtomicU64,
763 }
764
765 impl EventHandler for CountingHandler {
766 fn on_event(
767 &self,
768 _process: &ProcessorState<'_>,
769 ) -> Result<Vec<AdviceMutation>, EventError> {
770 let call = self.calls.fetch_add(1, Ordering::SeqCst);
771 Ok(vec![AdviceMutation::extend_advice_stack(
772 [Felt::from(100u32 + call as u32)].into_iter().collect(),
773 )])
774 }
775 }
776
777 let source_manager: Arc<DefaultSourceManager> = Arc::new(DefaultSourceManager::default());
778 let event_name = "miden-debug::test::snapshot-replay";
779 let event_id = EventId::from_name(event_name).as_u64();
780 let source = format!(
781 "begin push.{event_id} emit drop adv_push push.{event_id} emit drop adv_push add add \
782 end"
783 );
784 let program = miden_assembly::Assembler::new(source_manager.clone())
785 .assemble_program("program", source)
786 .map(Arc::<Package>::from)
787 .expect("failed to assemble test program");
788 let stack_inputs = StackInputs::new(&[Felt::from(7u32)]).unwrap();
789 let advice_inputs = AdviceInputs::default();
790 let options = ExecutionOptions::default();
791
792 let mut executor = Executor::from_config(ExecutionConfig {
794 inputs: stack_inputs,
795 advice_inputs: advice_inputs.clone(),
796 options,
797 });
798 executor
799 .register_event_handler(
800 EventName::from_string(event_name.to_string()),
801 Arc::new(CountingHandler {
802 calls: AtomicU64::new(0),
803 }),
804 )
805 .expect("failed to register event handler");
806 executor.with_event_advice_mutations_recording();
807 let mut debug_executor = executor.into_debug(program.clone(), source_manager.clone());
808 while !debug_executor.stopped {
809 debug_executor.step().expect("recording step failed");
810 }
811 let event_log = debug_executor.host.take_recorded_event_mutations();
812 let recorded_result: u32 =
813 debug_executor.into_execution_trace().parse_result().expect("invalid result");
814
815 let snapshot = ReplaySnapshot {
817 package: program.clone(),
818 stack_inputs,
819 advice_inputs,
820 options,
821 mast_forests: vec![LoadedMastForest::with_package_debug_info(
822 program.mast_forest().clone(),
823 program.debug_info(),
824 )],
825 event_log,
826 };
827 let restored = ReplaySnapshot::read_from_bytes(&snapshot.to_bytes())
828 .expect("snapshot failed to deserialize");
829
830 let replay_executor = Executor::from_config(ExecutionConfig {
832 inputs: restored.stack_inputs,
833 advice_inputs: restored.advice_inputs,
834 options: restored.options,
835 });
836 let mut debug_executor = replay_executor.into_debug_with_replay(
837 restored.package.clone(),
838 source_manager,
839 restored.mast_forests.clone(),
840 restored.event_log.into(),
841 );
842 while !debug_executor.stopped {
843 debug_executor.step().expect("replay step failed");
844 }
845 let replayed_result: u32 = debug_executor
846 .into_execution_trace()
847 .parse_result()
848 .expect("invalid replay result");
849 assert_eq!(replayed_result, recorded_result);
850 assert_eq!(replayed_result, 208);
851 }
852}