Skip to main content

miden_debug_engine/profiling/instrument/
mod.rs

1//! Every distinct kind of profiling data collection is an [`Instrument`] implementation that
2//! lives in a submodule.
3//!
4//! Each instrument is uniquely identified by its [`Instrument::name`].
5
6use miden_core::operations::Operation;
7
8mod op_histogram_global;
9mod op_histogram_proc;
10
11pub use op_histogram_global::OpHistogramGlobal;
12pub use op_histogram_proc::OpHistogramProc;
13
14/// The functionality required for an instrument to be plugged in to `Profiler`.
15pub trait Instrument {
16    /// The human readable name of this instrument used in CLI arguments and user output
17    ///
18    /// This should be the same name as the corresponding `InstrumentRegistration::NAME` constant
19    fn name(&self) -> &'static str;
20    /// To be called each vm cycle an `Operation` is executed.
21    ///
22    /// `proc` is the name of the most recent live procedure, or `None` when the operation cannot
23    /// be attributed to a procedure. For example, it is `None` while executing a program without
24    /// assembly operation metadata.
25    fn on_operation_execution_cycle(&mut self, op: Operation, proc: Option<&str>);
26    /// Write this instrumentation's collected output as a report to `writer`
27    fn write_report_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()>;
28}
29
30/// Represents the information needed to construct an [Instrument] dynamically
31pub trait InstrumentRegistration: Sized + Instrument + 'static {
32    /// The human readable name of this instrument used in CLI arguments and user output
33    const NAME: &'static str;
34
35    /// Create an instance of this instrument with the provided configuration
36    fn build(config: &super::ProfilerConfig) -> Result<Self, InstrumentError>;
37}
38
39#[derive(Debug, thiserror::Error)]
40pub enum InstrumentError {
41    /// The given instrument name is not registered to any known instrument
42    #[error("unknown profiling instrument '{0}'")]
43    Undefined(String),
44    /// We failed to construct the named instrument
45    #[error("failed to construct instrument '{name}': {reason}")]
46    Build { name: String, reason: String },
47}
48
49/// Get an instance of instrument `name`, if one has been registered by that name.
50///
51/// Returns `Err` if no such instrument is registered, or the instrument constructor returned an
52/// error
53pub fn instrument_from_name(
54    name: &str,
55    config: &super::ProfilerConfig,
56) -> Result<Box<dyn Instrument>, InstrumentError> {
57    for instrument in inventory::iter::<InstrumentRegistrationInfo>() {
58        if instrument.name == name {
59            return (instrument.builder)(config);
60        }
61    }
62    Err(InstrumentError::Undefined(name.to_string()))
63}
64
65#[doc(hidden)]
66pub struct InstrumentRegistrationInfo {
67    name: &'static str,
68    builder: fn(&super::ProfilerConfig) -> Result<Box<dyn Instrument>, InstrumentError>,
69}
70
71impl InstrumentRegistrationInfo {
72    pub const fn new<T: InstrumentRegistration>() -> Self {
73        let name = <T as InstrumentRegistration>::NAME;
74        Self {
75            name,
76            builder: build_instrument::<T>,
77        }
78    }
79}
80
81#[macro_export]
82macro_rules! register_instrument {
83    ($t:ty) => {
84        inventory::submit!($crate::profiling::instrument::InstrumentRegistrationInfo::new::<$t>());
85    };
86}
87
88inventory::collect!(InstrumentRegistrationInfo);
89
90#[inline]
91fn build_instrument<T: InstrumentRegistration>(
92    config: &super::ProfilerConfig,
93) -> Result<Box<dyn Instrument>, InstrumentError> {
94    <T as InstrumentRegistration>::build(config).map(|inst| Box::new(inst) as Box<dyn Instrument>)
95}