midenc_debug/exec/
host.rs

1use std::{collections::BTreeMap, sync::Arc};
2
3use miden_core::crypto::hash::RpoDigest;
4use miden_processor::{
5    AdviceExtractor, AdviceInjector, AdviceProvider, ExecutionError, Host, HostResponse,
6    MastForest, MastForestStore, MemAdviceProvider, MemMastForestStore, ProcessState, RowIndex,
7};
8
9use super::{TraceEvent, TraceHandler};
10
11/// This is an implementation of [Host] which is essentially [miden_processor::DefaultHost],
12/// but extended with additional functionality for debugging, in particular it manages trace
13/// events that record the entry or exit of a procedure call frame.
14#[derive(Default)]
15pub struct DebuggerHost {
16    adv_provider: MemAdviceProvider,
17    store: MemMastForestStore,
18    tracing_callbacks: BTreeMap<u32, Vec<Box<TraceHandler>>>,
19    on_assert_failed: Option<Box<TraceHandler>>,
20}
21impl DebuggerHost {
22    /// Construct a new instance of [DebuggerHost] with the given advice provider.
23    pub fn new(adv_provider: MemAdviceProvider) -> Self {
24        Self {
25            adv_provider,
26            store: Default::default(),
27            tracing_callbacks: Default::default(),
28            on_assert_failed: None,
29        }
30    }
31
32    /// Register a trace handler for `event`
33    pub fn register_trace_handler<F>(&mut self, event: TraceEvent, callback: F)
34    where
35        F: FnMut(RowIndex, TraceEvent) + 'static,
36    {
37        let key = match event {
38            TraceEvent::AssertionFailed(None) => u32::MAX,
39            ev => ev.into(),
40        };
41        self.tracing_callbacks.entry(key).or_default().push(Box::new(callback));
42    }
43
44    /// Register a handler to be called when an assertion in the VM fails
45    pub fn register_assert_failed_tracer<F>(&mut self, callback: F)
46    where
47        F: FnMut(RowIndex, TraceEvent) + 'static,
48    {
49        self.on_assert_failed = Some(Box::new(callback));
50    }
51
52    /// Load `forest` into the MAST store for this host
53    pub fn load_mast_forest(&mut self, forest: MastForest) {
54        self.store.insert(forest);
55    }
56}
57
58impl Host for DebuggerHost {
59    fn get_advice<P: ProcessState>(
60        &mut self,
61        process: &P,
62        extractor: AdviceExtractor,
63    ) -> Result<HostResponse, ExecutionError> {
64        self.adv_provider.get_advice(process, &extractor)
65    }
66
67    fn set_advice<P: ProcessState>(
68        &mut self,
69        process: &P,
70        injector: AdviceInjector,
71    ) -> Result<HostResponse, ExecutionError> {
72        self.adv_provider.set_advice(process, &injector)
73    }
74
75    fn get_mast_forest(&self, node_digest: &RpoDigest) -> Option<Arc<MastForest>> {
76        self.store.get(node_digest)
77    }
78
79    fn on_trace<S: ProcessState>(
80        &mut self,
81        process: &S,
82        trace_id: u32,
83    ) -> Result<HostResponse, ExecutionError> {
84        let event = TraceEvent::from(trace_id);
85        let clk = process.clk();
86        if let Some(handlers) = self.tracing_callbacks.get_mut(&trace_id) {
87            for handler in handlers.iter_mut() {
88                handler(clk, event);
89            }
90        }
91        Ok(HostResponse::None)
92    }
93
94    fn on_assert_failed<S: ProcessState>(&mut self, process: &S, err_code: u32) -> ExecutionError {
95        let clk = process.clk();
96        if let Some(handler) = self.on_assert_failed.as_mut() {
97            handler(clk, TraceEvent::AssertionFailed(core::num::NonZeroU32::new(err_code)));
98        }
99        let err_msg = match err_code {
100            midenc_hir::ASSERT_FAILED_ALIGNMENT => Some(
101                "failed alignment: use of memory address violates minimum alignment requirements \
102                 for that use"
103                    .to_string(),
104            ),
105            _ => None,
106        };
107        ExecutionError::FailedAssertion {
108            clk,
109            err_code,
110            err_msg,
111        }
112    }
113}