Skip to main content

miden_debug_engine/exec/
host.rs

1use std::{collections::VecDeque, num::NonZeroU32, sync::Arc};
2
3use miden_assembly::SourceManager;
4use miden_core::{
5    Word,
6    events::{EventId, EventName},
7};
8use miden_debug_types::{Location, SourceFile, SourceSpan};
9use miden_mast_package::Package;
10use miden_processor::{
11    BaseHost, ExecutionError, FutureMaybeSend, Host, LoadedMastForest, MastForestStore,
12    MemMastForestStore, ProcessorState,
13    advice::AdviceMutation,
14    event::{EventError, EventHandler, EventHandlerRegistry},
15};
16
17use super::advice::clone_advice_mutations;
18
19/// This is an implementation of [Host] which is essentially [miden_processor::DefaultHost],
20/// but extended with additional functionality for debugging, in particular it manages trace
21/// events that record the entry or exit of a procedure call frame.
22pub struct DebuggerHost<S: SourceManager + ?Sized> {
23    store: MemMastForestStore,
24    event_handlers: EventHandlerRegistry,
25    #[allow(clippy::type_complexity)]
26    on_assert_failed: Option<Box<dyn FnMut(&ProcessorState<'_>, u32)>>,
27    source_manager: Arc<S>,
28    event_replay: VecDeque<Vec<AdviceMutation>>,
29    event_recording: Option<Vec<Vec<AdviceMutation>>>,
30}
31impl<S> DebuggerHost<S>
32where
33    S: SourceManager + ?Sized,
34{
35    /// Construct a new instance of [DebuggerHost] with the given source manager.
36    pub fn new(source_manager: Arc<S>) -> Self {
37        Self {
38            store: Default::default(),
39            event_handlers: EventHandlerRegistry::default(),
40            on_assert_failed: None,
41            source_manager,
42            event_replay: VecDeque::new(),
43            event_recording: None,
44        }
45    }
46
47    /// Set the event replay queue.
48    ///
49    /// When non-empty, `on_event()` will pop mutations from this queue instead of
50    /// returning empty results. This is used for transaction debugging where events
51    /// were recorded during a prior execution.
52    pub fn set_event_replay(&mut self, events: VecDeque<Vec<AdviceMutation>>) {
53        self.event_replay = events;
54    }
55
56    /// Record the advice mutations produced by each event handler invocation.
57    ///
58    /// One entry is recorded per `on_event` invocation, in execution order, **including empty
59    /// mutation sets**, so the recorded log can be fed directly back into
60    /// [DebuggerHost::set_event_replay] to replay this execution later. Take the log with
61    /// [DebuggerHost::take_recorded_event_mutations] once execution completes.
62    ///
63    /// Mutations are only recorded for live event handling; nothing is recorded while an event
64    /// replay queue is being consumed.
65    pub fn with_event_advice_mutations_recording(mut self) -> Self {
66        self.event_recording = Some(Vec::new());
67        self
68    }
69
70    /// Returns the advice mutations recorded so far, leaving the recording empty.
71    ///
72    /// Returns an empty log when recording was not enabled via
73    /// [DebuggerHost::with_event_advice_mutations_recording].
74    pub fn take_recorded_event_mutations(&mut self) -> Vec<Vec<AdviceMutation>> {
75        self.event_recording.as_mut().map(core::mem::take).unwrap_or_default()
76    }
77
78    /// Register a handler to be called when an assertion in the VM fails
79    pub fn register_assert_failed_tracer<F>(&mut self, callback: F)
80    where
81        F: FnMut(&ProcessorState<'_>, u32) + 'static,
82    {
83        self.on_assert_failed = Some(Box::new(callback));
84    }
85
86    /// Invoke the assert-failed handler, if registered.
87    ///
88    /// This is called externally when `step()` returns an assertion error, since
89    /// `on_assert_failed` no longer exists on the Host trait in 0.21.
90    pub fn handle_assert_failed(
91        &mut self,
92        process: &ProcessorState<'_>,
93        err_code: Option<NonZeroU32>,
94    ) {
95        if let Some(handler) = self.on_assert_failed.as_mut() {
96            handler(process, err_code.map(|nz| nz.get()).unwrap_or_default());
97        }
98    }
99
100    /// Load `package` into the MAST store for this host
101    pub fn load_package(&mut self, package: Arc<Package>) {
102        let mast = package.mast_forest().clone();
103        let debug_info = package.debug_info();
104        self.store
105            .insert_loaded(LoadedMastForest::with_package_debug_info(mast, debug_info));
106    }
107
108    /// Load `forest` into the MAST store for this host
109    pub fn load_mast_forest(&mut self, forest: LoadedMastForest) {
110        self.store.insert_loaded(forest);
111    }
112
113    /// Registers an event handler for use during program execution.
114    pub fn register_event_handler(
115        &mut self,
116        event: EventName,
117        handler: Arc<dyn EventHandler>,
118    ) -> Result<(), ExecutionError> {
119        self.event_handlers.register(event, handler)
120    }
121}
122
123impl<S> BaseHost for DebuggerHost<S>
124where
125    S: SourceManager + ?Sized,
126{
127    fn get_label_and_source_file(
128        &self,
129        location: &Location,
130    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
131        let maybe_file = self.source_manager.get_by_uri(location.uri());
132        let span = self.source_manager.location_to_span(location.clone()).unwrap_or_default();
133        (span, maybe_file)
134    }
135
136    fn resolve_event(&self, event_id: EventId) -> Option<&EventName> {
137        self.event_handlers.resolve_event(event_id)
138    }
139}
140
141impl<S> Host for DebuggerHost<S>
142where
143    S: SourceManager + ?Sized,
144{
145    fn get_mast_forest(
146        &self,
147        node_digest: &Word,
148    ) -> impl FutureMaybeSend<Option<LoadedMastForest>> {
149        std::future::ready(self.store.get(node_digest))
150    }
151
152    fn on_event(
153        &mut self,
154        process: &ProcessorState<'_>,
155    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>> {
156        if !self.event_replay.is_empty() {
157            let mutations = self.event_replay.pop_front().unwrap_or_default();
158            return std::future::ready(Ok(mutations));
159        }
160
161        let event_id = EventId::from_felt(process.get_stack_item(0));
162        let result = match self.event_handlers.handle_event(event_id, process) {
163            Ok(Some(mutations)) => Ok(mutations),
164            Ok(None) => {
165                #[derive(Debug, thiserror::Error)]
166                #[error("no event handler registered")]
167                struct UnhandledEvent;
168
169                Err(UnhandledEvent.into())
170            }
171            Err(err) => Err(err),
172        };
173        if let (Some(log), Ok(mutations)) = (self.event_recording.as_mut(), &result) {
174            log.push(clone_advice_mutations(mutations));
175        }
176        std::future::ready(result)
177    }
178}