Skip to main content

miden_debug/exec/
host.rs

1use std::{collections::BTreeMap, num::NonZeroU32, sync::Arc};
2
3use miden_assembly::SourceManager;
4use miden_core::Word;
5use miden_debug_types::{Location, SourceFile, SourceSpan};
6use miden_processor::{
7    FutureMaybeSend, Host, MastForestStore, MemMastForestStore, ProcessorState, TraceError,
8    advice::AdviceMutation, event::EventError, mast::MastForest, trace::RowIndex,
9};
10
11use super::{TraceEvent, TraceHandler};
12
13/// This is an implementation of [Host] which is essentially [miden_processor::DefaultHost],
14/// but extended with additional functionality for debugging, in particular it manages trace
15/// events that record the entry or exit of a procedure call frame.
16pub struct DebuggerHost<S: SourceManager + ?Sized> {
17    store: MemMastForestStore,
18    tracing_callbacks: BTreeMap<u32, Vec<Box<TraceHandler>>>,
19    on_assert_failed: Option<Box<TraceHandler>>,
20    source_manager: Arc<S>,
21}
22impl<S> DebuggerHost<S>
23where
24    S: SourceManager + ?Sized,
25{
26    /// Construct a new instance of [DebuggerHost] with the given source manager.
27    pub fn new(source_manager: Arc<S>) -> Self {
28        Self {
29            store: Default::default(),
30            tracing_callbacks: Default::default(),
31            on_assert_failed: None,
32            source_manager,
33        }
34    }
35
36    /// Register a trace handler for `event`
37    pub fn register_trace_handler<F>(&mut self, event: TraceEvent, callback: F)
38    where
39        F: FnMut(RowIndex, TraceEvent) + 'static,
40    {
41        let key = match event {
42            TraceEvent::AssertionFailed(None) => u32::MAX,
43            ev => ev.into(),
44        };
45        self.tracing_callbacks.entry(key).or_default().push(Box::new(callback));
46    }
47
48    /// Register a handler to be called when an assertion in the VM fails
49    pub fn register_assert_failed_tracer<F>(&mut self, callback: F)
50    where
51        F: FnMut(RowIndex, TraceEvent) + 'static,
52    {
53        self.on_assert_failed = Some(Box::new(callback));
54    }
55
56    /// Invoke the assert-failed handler, if registered.
57    ///
58    /// This is called externally when `step()` returns an assertion error, since
59    /// `on_assert_failed` no longer exists on the Host trait in 0.21.
60    pub fn handle_assert_failed(&mut self, clk: RowIndex, err_code: Option<NonZeroU32>) {
61        if let Some(handler) = self.on_assert_failed.as_mut() {
62            handler(clk, TraceEvent::AssertionFailed(err_code));
63        }
64    }
65
66    /// Load `forest` into the MAST store for this host
67    pub fn load_mast_forest(&mut self, forest: Arc<MastForest>) {
68        self.store.insert(forest);
69    }
70}
71
72impl<S> Host for DebuggerHost<S>
73where
74    S: SourceManager + ?Sized,
75{
76    fn get_label_and_source_file(
77        &self,
78        location: &Location,
79    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
80        let maybe_file = self.source_manager.get_by_uri(location.uri());
81        let span = self.source_manager.location_to_span(location.clone()).unwrap_or_default();
82        (span, maybe_file)
83    }
84
85    fn get_mast_forest(&self, node_digest: &Word) -> impl FutureMaybeSend<Option<Arc<MastForest>>> {
86        std::future::ready(self.store.get(node_digest))
87    }
88
89    fn on_event(
90        &mut self,
91        _process: &ProcessorState<'_>,
92    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>> {
93        std::future::ready(Ok(Vec::new()))
94    }
95
96    fn on_trace(&mut self, process: &ProcessorState<'_>, trace_id: u32) -> Result<(), TraceError> {
97        let event = TraceEvent::from(trace_id);
98        let clk = process.clock();
99        if let Some(handlers) = self.tracing_callbacks.get_mut(&trace_id) {
100            for handler in handlers.iter_mut() {
101                handler(clk, event);
102            }
103        }
104        Ok(())
105    }
106}