hara_native/vm/machine/instrumentation/
ring.rs1use 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}