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