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    AdviceProvider, BaseHost, EventHandlerRegistry, ExecutionError, MastForest, MastForestStore,
8    MemMastForestStore, ProcessState, RowIndex, SyncHost,
9};
10
11use super::{TraceEvent, TraceHandler};
12
13/// This is an implementation of [BaseHost] 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.
16#[derive(Default)]
17pub struct DebuggerHost<S: SourceManager> {
18    _adv_provider: AdviceProvider,
19    store: MemMastForestStore,
20    tracing_callbacks: BTreeMap<u32, Vec<Box<TraceHandler>>>,
21    _event_handlers: EventHandlerRegistry,
22    on_assert_failed: Option<Box<TraceHandler>>,
23    source_manager: Arc<S>,
24}
25impl<S> DebuggerHost<S>
26where
27    S: SourceManager,
28{
29    /// Construct a new instance of [DebuggerHost] with the given advice provider.
30    pub fn new(_adv_provider: AdviceProvider, source_manager: S) -> Self {
31        Self {
32            _adv_provider,
33            store: Default::default(),
34            tracing_callbacks: Default::default(),
35            _event_handlers: EventHandlerRegistry::default(),
36            on_assert_failed: None,
37            source_manager: Arc::new(source_manager),
38        }
39    }
40
41    /// Register a trace handler for `event`
42    pub fn register_trace_handler<F>(&mut self, event: TraceEvent, callback: F)
43    where
44        F: FnMut(RowIndex, TraceEvent) + 'static,
45    {
46        let key = match event {
47            TraceEvent::AssertionFailed(None) => u32::MAX,
48            ev => ev.into(),
49        };
50        self.tracing_callbacks.entry(key).or_default().push(Box::new(callback));
51    }
52
53    /// Register a handler to be called when an assertion in the VM fails
54    pub fn register_assert_failed_tracer<F>(&mut self, callback: F)
55    where
56        F: FnMut(RowIndex, TraceEvent) + 'static,
57    {
58        self.on_assert_failed = Some(Box::new(callback));
59    }
60
61    /// Load `forest` into the MAST store for this host
62    pub fn load_mast_forest(&mut self, forest: Arc<MastForest>) {
63        self.store.insert(forest);
64    }
65}
66
67impl<S> BaseHost for DebuggerHost<S>
68where
69    S: SourceManager,
70{
71    fn get_label_and_source_file(
72        &self,
73        location: &Location,
74    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
75        let maybe_file = self.source_manager.get_by_uri(location.uri());
76        let span = self.source_manager.location_to_span(location.clone()).unwrap_or_default();
77        (span, maybe_file)
78    }
79
80    fn on_trace(
81        &mut self,
82        process: &mut ProcessState,
83        trace_id: u32,
84    ) -> Result<(), ExecutionError> {
85        let event = TraceEvent::from(trace_id);
86        let clk = process.clk();
87        if let Some(handlers) = self.tracing_callbacks.get_mut(&trace_id) {
88            for handler in handlers.iter_mut() {
89                handler(clk, event);
90            }
91        }
92        Ok(())
93    }
94
95    fn on_assert_failed(&mut self, process: &ProcessState, err_code: miden_core::Felt) {
96        let clk = process.clk();
97        if let Some(handler) = self.on_assert_failed.as_mut() {
98            // TODO: We're truncating the error code here, but we may need to handle the full range
99            handler(clk, TraceEvent::AssertionFailed(NonZeroU32::new(err_code.as_int() as u32)));
100        }
101    }
102}
103
104impl<S> SyncHost for DebuggerHost<S>
105where
106    S: SourceManager,
107{
108    fn get_mast_forest(&self, node_digest: &Word) -> Option<Arc<MastForest>> {
109        self.store.get(node_digest)
110    }
111
112    fn on_event(
113        &mut self,
114        _process: &ProcessState,
115    ) -> Result<Vec<miden_processor::AdviceMutation>, miden_processor::EventError> {
116        Ok(Vec::new())
117    }
118}