vyre_runtime/megakernel/telemetry/
evidence.rs1use super::ring_state::RingOccupancy;
6
7pub const RUNTIME_IO_EVIDENCE_SCHEMA_VERSION: u32 = 1;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum RuntimeEvidenceMetricFamily {
13 Ring,
15 Control,
17 Copy,
19 Residency,
21}
22
23impl RuntimeEvidenceMetricFamily {
24 #[must_use]
26 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::Ring => "ring",
29 Self::Control => "control",
30 Self::Copy => "copy",
31 Self::Residency => "residency",
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub struct RuntimeEvidenceMetricCoverage {
39 pub ring: bool,
41 pub control: bool,
43 pub copy: bool,
45 pub residency: bool,
47}
48
49impl RuntimeEvidenceMetricCoverage {
50 #[must_use]
52 pub const fn complete() -> Self {
53 Self {
54 ring: true,
55 control: true,
56 copy: true,
57 residency: true,
58 }
59 }
60
61 #[must_use]
63 pub fn missing_families(self) -> Vec<RuntimeEvidenceMetricFamily> {
64 let mut missing = Vec::new();
65 if !self.ring {
66 missing.push(RuntimeEvidenceMetricFamily::Ring);
67 }
68 if !self.control {
69 missing.push(RuntimeEvidenceMetricFamily::Control);
70 }
71 if !self.copy {
72 missing.push(RuntimeEvidenceMetricFamily::Copy);
73 }
74 if !self.residency {
75 missing.push(RuntimeEvidenceMetricFamily::Residency);
76 }
77 missing
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct ResidentRuntimeEvidence {
84 pub schema_version: u32,
86 pub resident_device_bytes: u64,
88 pub host_copy_bytes: u64,
90 pub host_copy_avoided_bytes: u64,
92 pub ring_occupancy: RingOccupancy,
94 pub control_decode_ns: u64,
96 pub ring_decode_ns: u64,
98 pub coverage: RuntimeEvidenceMetricCoverage,
100}
101
102impl ResidentRuntimeEvidence {
103 #[must_use]
105 pub const fn complete(
106 resident_device_bytes: u64,
107 host_copy_bytes: u64,
108 host_copy_avoided_bytes: u64,
109 ring_occupancy: RingOccupancy,
110 control_decode_ns: u64,
111 ring_decode_ns: u64,
112 ) -> Self {
113 Self {
114 schema_version: RUNTIME_IO_EVIDENCE_SCHEMA_VERSION,
115 resident_device_bytes,
116 host_copy_bytes,
117 host_copy_avoided_bytes,
118 ring_occupancy,
119 control_decode_ns,
120 ring_decode_ns,
121 coverage: RuntimeEvidenceMetricCoverage::complete(),
122 }
123 }
124
125 #[must_use]
127 pub fn missing_metric_families(&self) -> Vec<RuntimeEvidenceMetricFamily> {
128 self.coverage.missing_families()
129 }
130
131 #[must_use]
133 pub fn is_complete(&self) -> bool {
134 self.schema_version == RUNTIME_IO_EVIDENCE_SCHEMA_VERSION
135 && self.missing_metric_families().is_empty()
136 }
137
138 #[must_use]
140 pub fn host_copy_avoidance_bps(&self) -> u16 {
141 let total = u128::from(self.host_copy_bytes)
142 .saturating_add(u128::from(self.host_copy_avoided_bytes));
143 if total == 0 {
144 return 0;
145 }
146 let bps = u128::from(self.host_copy_avoided_bytes).saturating_mul(10_000) / total;
147 bps.min(10_000) as u16
148 }
149}
150
151#[cfg(test)]
152mod evidence_tests {
153 use super::*;
154
155 #[test]
156 fn runtime_evidence_reports_missing_metric_families() {
157 let evidence = ResidentRuntimeEvidence {
158 schema_version: RUNTIME_IO_EVIDENCE_SCHEMA_VERSION,
159 resident_device_bytes: 0,
160 host_copy_bytes: 0,
161 host_copy_avoided_bytes: 0,
162 ring_occupancy: RingOccupancy::default(),
163 control_decode_ns: 0,
164 ring_decode_ns: 0,
165 coverage: RuntimeEvidenceMetricCoverage {
166 ring: true,
167 control: false,
168 copy: true,
169 residency: false,
170 },
171 };
172
173 let missing = evidence
174 .missing_metric_families()
175 .into_iter()
176 .map(RuntimeEvidenceMetricFamily::as_str)
177 .collect::<Vec<_>>();
178
179 assert_eq!(missing, vec!["control", "residency"]);
180 assert!(!evidence.is_complete());
181 }
182
183 #[test]
184 fn runtime_evidence_records_copy_avoidance_and_occupancy() {
185 let evidence = ResidentRuntimeEvidence::complete(
186 4096,
187 1024,
188 3072,
189 RingOccupancy {
190 empty: 1,
191 published: 2,
192 claimed: 3,
193 done: 4,
194 wait_io: 5,
195 yield_count: 6,
196 requeue: 7,
197 fault: 8,
198 unknown: 9,
199 },
200 11,
201 13,
202 );
203
204 assert!(evidence.is_complete());
205 assert_eq!(evidence.resident_device_bytes, 4096);
206 assert_eq!(evidence.ring_occupancy.total_slots(), 45);
207 assert_eq!(evidence.ring_occupancy.queue_depth(), 40);
208 assert_eq!(evidence.host_copy_avoidance_bps(), 7500);
209 }
210}
211
212pub const TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION: u32 = 1;
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub struct TelemetryDecodeCapacityEvidence {
218 pub schema_version: u32,
220 pub decoded_slot_count: usize,
222 pub slot_output_capacity: usize,
224 pub decoded_window_count: usize,
226 pub window_output_capacity: usize,
228 pub window_opcode_scratch_capacity: usize,
230 pub window_accumulator_scratch_capacity: usize,
232 pub uses_caller_owned_scratch: bool,
234}
235
236impl TelemetryDecodeCapacityEvidence {
237 #[must_use]
239 pub fn is_complete(self) -> bool {
240 self.schema_version == TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION
241 && self.uses_caller_owned_scratch
242 && self.slot_output_capacity >= self.decoded_slot_count
243 && self.window_output_capacity >= self.decoded_window_count
244 && self.window_accumulator_scratch_capacity >= self.decoded_window_count
245 }
246}