Skip to main content

hara_native/vm/machine/instrumentation/
ring.rs

1use super::{InstructionEvent, TerminalEvent, TransitionEvent, VmProbe, BYTECODE_EVENTS_SCHEMA};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum VmEvent {
5    Instruction(InstructionEvent),
6    Transition(TransitionEvent),
7    Terminal(TerminalEvent),
8}
9
10pub struct EventRing {
11    slots: Box<[Option<VmEvent>]>,
12    next: usize,
13    len: usize,
14    dropped: u64,
15}
16
17impl EventRing {
18    pub fn with_capacity(capacity: usize) -> Self {
19        Self {
20            slots: vec![None; capacity].into_boxed_slice(),
21            next: 0,
22            len: 0,
23            dropped: 0,
24        }
25    }
26
27    pub fn schema(&self) -> &'static str {
28        BYTECODE_EVENTS_SCHEMA
29    }
30
31    pub fn capacity(&self) -> usize {
32        self.slots.len()
33    }
34
35    pub fn len(&self) -> usize {
36        self.len
37    }
38
39    pub fn is_empty(&self) -> bool {
40        self.len == 0
41    }
42
43    pub fn dropped(&self) -> u64 {
44        self.dropped
45    }
46
47    pub fn iter(&self) -> impl Iterator<Item = &VmEvent> {
48        let capacity = self.slots.len();
49        let start = if self.len == capacity { self.next } else { 0 };
50        (0..self.len).filter_map(move |offset| {
51            let index = if capacity == 0 {
52                0
53            } else {
54                (start + offset) % capacity
55            };
56            self.slots.get(index).and_then(Option::as_ref)
57        })
58    }
59
60    fn push(&mut self, event: VmEvent) {
61        if self.slots.is_empty() {
62            self.dropped = self.dropped.saturating_add(1);
63            return;
64        }
65        if self.len == self.slots.len() {
66            self.dropped = self.dropped.saturating_add(1);
67        } else {
68            self.len += 1;
69        }
70        self.slots[self.next] = Some(event);
71        self.next = (self.next + 1) % self.slots.len();
72    }
73}
74
75impl VmProbe for EventRing {
76    #[inline(always)]
77    fn on_instruction(&mut self, event: InstructionEvent) {
78        self.push(VmEvent::Instruction(event));
79    }
80
81    #[inline(always)]
82    fn on_transition(&mut self, event: TransitionEvent) {
83        self.push(VmEvent::Transition(event));
84    }
85
86    #[inline(always)]
87    fn on_terminal(&mut self, event: TerminalEvent) {
88        self.push(VmEvent::Terminal(event));
89    }
90}
91
92pub struct SampledProbe<P> {
93    inner: P,
94    every: u64,
95    seen: u64,
96}
97
98impl<P> SampledProbe<P> {
99    pub fn new(inner: P, every: u64) -> Self {
100        Self {
101            inner,
102            every: every.max(1),
103            seen: 0,
104        }
105    }
106
107    pub fn inner(&self) -> &P {
108        &self.inner
109    }
110
111    pub fn into_inner(self) -> P {
112        self.inner
113    }
114}
115
116impl<P: VmProbe> VmProbe for SampledProbe<P> {
117    #[inline(always)]
118    fn on_instruction(&mut self, event: InstructionEvent) {
119        let emit = self.seen % self.every == 0;
120        self.seen = self.seen.saturating_add(1);
121        if emit {
122            self.inner.on_instruction(event);
123        }
124    }
125
126    #[inline(always)]
127    fn on_transition(&mut self, event: TransitionEvent) {
128        self.inner.on_transition(event);
129    }
130
131    #[inline(always)]
132    fn on_terminal(&mut self, event: TerminalEvent) {
133        self.inner.on_terminal(event);
134    }
135}