Skip to main content

vyre_runtime/megakernel/
telemetry.rs

1//! Host-side telemetry decoders for the megakernel ring and control buffers.
2//!
3//! The runtime already exposes low-level helpers such as
4//! `read_done_count`, `read_epoch`, and `read_metrics`. This module adds a
5//! single structured snapshot surface useful for wrappers like VyreOffload.
6
7use super::protocol::{control, read_word, slot, ARG0_WORD, OPCODE_WORD, STATUS_WORD, TENANT_WORD};
8use super::scaling::{
9    PriorityRequeueAccounting, ResidentLaunchPolicy, ResidentLaunchRecommendation,
10    ResidentLaunchRequest,
11};
12use super::staging_reserve::{
13    reserve_hash_map_capacity, reserve_vec_capacity as reserve_target_capacity,
14};
15use crate::PipelineError;
16
17mod errors;
18mod evidence;
19mod ring_state;
20mod sketch;
21pub use evidence::{
22    ResidentRuntimeEvidence, RuntimeEvidenceMetricCoverage, RuntimeEvidenceMetricFamily,
23    TelemetryDecodeCapacityEvidence, RUNTIME_IO_EVIDENCE_SCHEMA_VERSION,
24    TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION,
25};
26use ring_state::WindowAccumulator;
27pub use ring_state::{
28    ControlSnapshot, ResidentRuntimeCounters, ResidentWatchdogSnapshot, RingOccupancy,
29    RingSlotSnapshot, RingStatus, RingTelemetry, TelemetryDecodeScratch, WindowTelemetry,
30};
31pub use sketch::{CountMinSketch, SketchTelemetry, SketchTelemetryScratch};
32
33const SLOT_WORDS_USIZE: usize = 16;
34
35fn try_read_slot_chunk_word(slot_bytes: &[u8], word_idx: u32) -> Result<u32, PipelineError> {
36    let word_idx = telemetry_u32_to_usize(word_idx, "slot word index")?;
37    let off = word_idx
38        .checked_mul(4)
39        .ok_or_else(|| errors::slot_word_offset_overflow())?;
40    let end = off
41        .checked_add(4)
42        .ok_or_else(|| errors::slot_word_end_overflow())?;
43    let bytes = slot_bytes
44        .get(off..end)
45        .ok_or_else(|| errors::missing_slot_word(word_idx, slot_bytes.len()))?;
46    Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
47}
48
49fn is_sorted_unique_u32(values: &[u32]) -> bool {
50    values.windows(2).all(|pair| pair[0] < pair[1])
51}
52
53impl ControlSnapshot {
54    /// Strictly decode a structured control-buffer view into owned storage.
55    ///
56    /// # Errors
57    ///
58    /// Returns [`PipelineError`] when any fixed control word is missing from
59    /// the control snapshot.
60    pub fn try_decode(control_bytes: &[u8]) -> Result<Self, PipelineError> {
61        let mut out = Self::default();
62        Self::try_decode_into(control_bytes, &mut out)?;
63        Ok(out)
64    }
65
66    /// Strictly decode a structured control-buffer view.
67    ///
68    /// # Errors
69    ///
70    /// Returns [`PipelineError`] when any fixed control word is missing from
71    /// the control snapshot.
72    pub fn try_decode_into(control_bytes: &[u8], out: &mut Self) -> Result<(), PipelineError> {
73        validate_control_snapshot(control_bytes)?;
74        out.shutdown =
75            read_required_control_word(control_bytes, control_word_index(control::SHUTDOWN)?)? != 0;
76        out.done_count =
77            read_required_control_word(control_bytes, control_word_index(control::DONE_COUNT)?)?;
78        out.epoch = read_required_control_word(control_bytes, control_word_index(control::EPOCH)?)?;
79        out.metrics.clear();
80        reserve_target_capacity(
81            &mut out.metrics,
82            telemetry_u32_to_usize(control::METRICS_SLOTS, "metrics slot count")?,
83            "metrics",
84        )?;
85        for i in 0..control::METRICS_SLOTS {
86            let count = read_required_control_word(
87                control_bytes,
88                control_offset_index(control::METRICS_BASE, i)?,
89            )?;
90            if count > 0 {
91                out.metrics.push((i, count));
92            }
93        }
94        out.tenant_fairness.clear();
95        reserve_target_capacity(
96            &mut out.tenant_fairness,
97            telemetry_u32_to_usize(control::TENANT_FAIRNESS_SLOTS, "tenant fairness slot count")?,
98            "tenant fairness",
99        )?;
100        for i in 0..control::TENANT_FAIRNESS_SLOTS {
101            out.tenant_fairness.push(read_required_control_word(
102                control_bytes,
103                control_offset_index(control::TENANT_FAIRNESS_BASE, i)?,
104            )?);
105        }
106        out.priority_fairness.clear();
107        reserve_target_capacity(
108            &mut out.priority_fairness,
109            telemetry_u32_to_usize(
110                control::PRIORITY_FAIRNESS_SLOTS,
111                "priority fairness slot count",
112            )?,
113            "priority fairness",
114        )?;
115        for i in 0..control::PRIORITY_FAIRNESS_SLOTS {
116            out.priority_fairness.push(read_required_control_word(
117                control_bytes,
118                control_offset_index(control::PRIORITY_FAIRNESS_BASE, i)?,
119            )?);
120        }
121        Ok(())
122    }
123}
124
125impl RingTelemetry {
126    /// Decode the ring and control buffers into one structured snapshot.
127    #[must_use]
128    #[cfg(test)]
129    pub fn decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Self {
130        Self::decode_with_window_opcodes(control_bytes, ring_bytes, &[])
131    }
132
133    /// Strictly decode ring and control bytes after validating ABI alignment.
134    ///
135    /// # Errors
136    ///
137    /// Returns [`PipelineError`] when buffers are truncated or not aligned to
138    /// the megakernel wire protocol.
139    pub fn try_decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Result<Self, PipelineError> {
140        Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, &[])
141    }
142
143    /// Decode the ring and control buffers, additionally grouping any slots
144    /// whose opcode is present in `window_opcodes` into ticketed route-window
145    /// telemetry records.
146    #[must_use]
147    #[cfg(test)]
148    pub fn decode_with_window_opcodes(
149        control_bytes: &[u8],
150        ring_bytes: &[u8],
151        window_opcodes: &[u32],
152    ) -> Self {
153        Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, window_opcodes)
154            .unwrap_or_default()
155    }
156
157    /// Decode the ring and control buffers into caller-owned telemetry and
158    /// scratch storage.
159    #[cfg(test)]
160    pub fn decode_with_window_opcodes_into(
161        control_bytes: &[u8],
162        ring_bytes: &[u8],
163        window_opcodes: &[u32],
164        out: &mut Self,
165        scratch: &mut TelemetryDecodeScratch,
166    ) {
167        Self::try_decode_with_window_opcodes_into(
168            control_bytes,
169            ring_bytes,
170            window_opcodes,
171            out,
172            scratch,
173        )
174        .unwrap_or_else(|_| {
175            *out = Self::default();
176            scratch.clear();
177        });
178    }
179
180    fn try_decode_with_window_opcodes_into_unchecked(
181        control_bytes: &[u8],
182        ring_bytes: &[u8],
183        window_opcodes: &[u32],
184        out: &mut Self,
185        scratch: &mut TelemetryDecodeScratch,
186    ) -> Result<(), PipelineError> {
187        enum WindowOpcodeMatcher<'a> {
188            None,
189            Single(u32),
190            DenseBitmap(u128),
191            SmallSlice(&'a [u32]),
192            LargeSlice(&'a [u32]),
193        }
194
195        ControlSnapshot::try_decode_into(control_bytes, &mut out.control)?;
196        let slot_count = ring_bytes.len() / slot_byte_len()?;
197        out.occupancy = RingOccupancy::default();
198        out.slots.clear();
199        reserve_target_capacity(&mut out.slots, slot_count, "ring slots")?;
200        out.windows.clear();
201        scratch.window_opcodes.clear();
202        scratch.windows.clear();
203        let window_opcode_lookup = if window_opcodes.is_empty() {
204            &[][..]
205        } else if is_sorted_unique_u32(window_opcodes) {
206            window_opcodes
207        } else {
208            reserve_target_capacity(
209                &mut scratch.window_opcodes,
210                window_opcodes.len(),
211                "window opcode scratch",
212            )?;
213            scratch.window_opcodes.extend_from_slice(window_opcodes);
214            scratch.window_opcodes.sort_unstable();
215            scratch.window_opcodes.dedup();
216            scratch.window_opcodes.as_slice()
217        };
218        let window_opcode_matcher = match window_opcode_lookup {
219            [] => WindowOpcodeMatcher::None,
220            [opcode] => WindowOpcodeMatcher::Single(*opcode),
221            opcodes if opcodes.len() > 1 && opcodes.iter().all(|opcode| *opcode < 128) => {
222                let bitmap = opcodes
223                    .iter()
224                    .fold(0_u128, |acc, &opcode| acc | (1_u128 << opcode));
225                WindowOpcodeMatcher::DenseBitmap(bitmap)
226            }
227            opcodes if opcodes.len() <= 8 => WindowOpcodeMatcher::SmallSlice(opcodes),
228            opcodes => WindowOpcodeMatcher::LargeSlice(opcodes),
229        };
230        if !matches!(window_opcode_matcher, WindowOpcodeMatcher::None) {
231            reserve_hash_map_capacity(
232                &mut scratch.windows,
233                slot_count,
234                "window accumulator scratch",
235            )?;
236        }
237        let decode_windows = !matches!(window_opcode_matcher, WindowOpcodeMatcher::None);
238
239        let slot_byte_len = slot_byte_len()?;
240        for (slot_idx, slot_bytes) in ring_bytes.chunks_exact(slot_byte_len).enumerate() {
241            let slot_idx = u32::try_from(slot_idx).map_err(|source| {
242                PipelineError::Backend(format!(
243                    "megakernel telemetry slot index cannot fit u32: {source}. Fix: shard ring snapshots before host decode."
244                ))
245            })?;
246            let status_raw = try_read_slot_chunk_word(slot_bytes, STATUS_WORD)?;
247            let status = RingStatus::from_raw(status_raw);
248            match status {
249                RingStatus::Empty => out.occupancy.empty += 1,
250                RingStatus::Published => out.occupancy.published += 1,
251                RingStatus::Claimed => out.occupancy.claimed += 1,
252                RingStatus::Done => out.occupancy.done += 1,
253                RingStatus::WaitIo => out.occupancy.wait_io += 1,
254                RingStatus::Yield => out.occupancy.yield_count += 1,
255                RingStatus::Requeue => out.occupancy.requeue += 1,
256                RingStatus::Fault => out.occupancy.fault += 1,
257                RingStatus::Unknown(_) => out.occupancy.unknown += 1,
258            }
259            let tenant_id = try_read_slot_chunk_word(slot_bytes, TENANT_WORD)?;
260            let opcode = try_read_slot_chunk_word(slot_bytes, OPCODE_WORD)?;
261            let args_prefix = [
262                try_read_slot_chunk_word(slot_bytes, ARG0_WORD)?,
263                try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 1)?,
264                try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 2)?,
265            ];
266            let is_window_opcode = match window_opcode_matcher {
267                WindowOpcodeMatcher::None => false,
268                WindowOpcodeMatcher::Single(expected) => opcode == expected,
269                WindowOpcodeMatcher::DenseBitmap(bitmap) => {
270                    opcode < 128 && ((bitmap >> opcode) & 1) == 1
271                }
272                WindowOpcodeMatcher::SmallSlice(window_opcodes) => window_opcodes.contains(&opcode),
273                WindowOpcodeMatcher::LargeSlice(window_opcodes) => {
274                    window_opcodes.binary_search(&opcode).is_ok()
275                }
276            };
277            if decode_windows && is_window_opcode {
278                let ticket = args_prefix[0];
279                let class_tag = args_prefix[1];
280                let entry =
281                    scratch
282                        .windows
283                        .entry((ticket, opcode))
284                        .or_insert_with(|| WindowAccumulator {
285                            tenant_id,
286                            opcode,
287                            ..WindowAccumulator::default()
288                        });
289                match class_tag {
290                    0 => entry.required_slots += 1,
291                    1 => entry.lookahead_slots += 1,
292                    _ => {}
293                }
294                match status {
295                    RingStatus::Published => entry.published += 1,
296                    RingStatus::Claimed => entry.claimed += 1,
297                    RingStatus::Done => entry.done += 1,
298                    RingStatus::WaitIo => entry.wait_io += 1,
299                    RingStatus::Yield => entry.yield_count += 1,
300                    RingStatus::Requeue => entry.requeue += 1,
301                    RingStatus::Fault => entry.fault += 1,
302                    RingStatus::Empty | RingStatus::Unknown(_) => {}
303                }
304            }
305            out.slots.push(RingSlotSnapshot {
306                slot_idx,
307                status,
308                tenant_id,
309                opcode,
310                args_prefix,
311            });
312        }
313
314        reserve_target_capacity(&mut out.windows, scratch.windows.len(), "window output")?;
315        for (&(ticket, _), acc) in &scratch.windows {
316            out.windows.push(WindowTelemetry {
317                ticket,
318                tenant_id: acc.tenant_id,
319                opcode: acc.opcode,
320                required_slots: acc.required_slots,
321                lookahead_slots: acc.lookahead_slots,
322                published: acc.published,
323                claimed: acc.claimed,
324                done: acc.done,
325                wait_io: acc.wait_io,
326                yield_count: acc.yield_count,
327                requeue: acc.requeue,
328                fault: acc.fault,
329            });
330        }
331        out.windows
332            .sort_unstable_by_key(|window| (window.ticket, window.opcode));
333        Ok(())
334    }
335
336    /// Strictly decode ring/control bytes and group selected window opcodes.
337    ///
338    /// # Errors
339    ///
340    /// Returns [`PipelineError`] when buffers are truncated or not aligned to
341    /// the megakernel wire protocol.
342    pub fn try_decode_with_window_opcodes(
343        control_bytes: &[u8],
344        ring_bytes: &[u8],
345        window_opcodes: &[u32],
346    ) -> Result<Self, PipelineError> {
347        validate_telemetry_buffers(control_bytes, ring_bytes)?;
348        let mut out = Self::default();
349        let mut scratch = TelemetryDecodeScratch::new();
350        Self::try_decode_with_window_opcodes_into_unchecked(
351            control_bytes,
352            ring_bytes,
353            window_opcodes,
354            &mut out,
355            &mut scratch,
356        )?;
357        Ok(out)
358    }
359
360    /// Strictly decode ring/control bytes into caller-owned telemetry and
361    /// scratch storage.
362    ///
363    /// # Errors
364    ///
365    /// Returns [`PipelineError`] when buffers are truncated or not aligned to
366    /// the megakernel wire protocol.
367    pub fn try_decode_with_window_opcodes_into(
368        control_bytes: &[u8],
369        ring_bytes: &[u8],
370        window_opcodes: &[u32],
371        out: &mut Self,
372        scratch: &mut TelemetryDecodeScratch,
373    ) -> Result<(), PipelineError> {
374        validate_telemetry_buffers(control_bytes, ring_bytes)?;
375        Self::try_decode_with_window_opcodes_into_unchecked(
376            control_bytes,
377            ring_bytes,
378            window_opcodes,
379            out,
380            scratch,
381        )?;
382        Ok(())
383    }
384
385    /// Active slots matching a given opcode.
386    #[must_use]
387    #[cfg(test)]
388    pub fn active_slots_for_opcode(&self, opcode: u32) -> Vec<&RingSlotSnapshot> {
389        self.try_active_slots_for_opcode(opcode).unwrap_or_default()
390    }
391
392    /// Active slots matching a given opcode with fallible output staging.
393    ///
394    /// # Errors
395    ///
396    /// Returns [`PipelineError`] when output storage cannot be reserved.
397    pub fn try_active_slots_for_opcode(
398        &self,
399        opcode: u32,
400    ) -> Result<Vec<&RingSlotSnapshot>, PipelineError> {
401        let mut out = Vec::default();
402        self.try_active_slots_for_opcode_into(opcode, &mut out)?;
403        Ok(out)
404    }
405
406    /// Active slots matching a given opcode as an iterator.
407    pub fn active_slots_for_opcode_iter(
408        &self,
409        opcode: u32,
410    ) -> impl Iterator<Item = &RingSlotSnapshot> {
411        self.slots
412            .iter()
413            .filter(move |slot| slot.opcode == opcode && slot.status.is_active())
414    }
415
416    /// Active slots matching a given opcode into caller-owned storage.
417    ///
418    /// # Errors
419    ///
420    /// Returns [`PipelineError`] when output storage cannot be reserved.
421    pub fn try_active_slots_for_opcode_into<'a>(
422        &'a self,
423        opcode: u32,
424        out: &mut Vec<&'a RingSlotSnapshot>,
425    ) -> Result<(), PipelineError> {
426        out.clear();
427        reserve_target_capacity(out, self.slots.len(), "active slot output")?;
428        self.slots
429            .iter()
430            .filter(|slot| slot.opcode == opcode && slot.status.is_active())
431            .for_each(|slot| out.push(slot));
432        Ok(())
433    }
434
435    /// Unfinished ticketed windows.
436    #[must_use]
437    #[cfg(test)]
438    pub fn active_windows(&self) -> Vec<&WindowTelemetry> {
439        self.try_active_windows().unwrap_or_default()
440    }
441
442    /// Unfinished ticketed windows with fallible output staging.
443    ///
444    /// # Errors
445    ///
446    /// Returns [`PipelineError`] when output storage cannot be reserved.
447    pub fn try_active_windows(&self) -> Result<Vec<&WindowTelemetry>, PipelineError> {
448        let mut out = Vec::default();
449        self.try_active_windows_into(&mut out)?;
450        Ok(out)
451    }
452
453    /// Unfinished ticketed windows into caller-owned storage.
454    ///
455    /// # Errors
456    ///
457    /// Returns [`PipelineError`] when output storage cannot be reserved.
458    pub fn try_active_windows_into<'a>(
459        &'a self,
460        out: &mut Vec<&'a WindowTelemetry>,
461    ) -> Result<(), PipelineError> {
462        out.clear();
463        reserve_target_capacity(out, self.windows.len(), "active window output")?;
464        self.windows
465            .iter()
466            .filter(|window| window.is_active())
467            .for_each(|window| out.push(window));
468        Ok(())
469    }
470
471    /// Summarize priority requeue/aging pressure visible in the ring snapshot.
472    #[must_use]
473    pub fn priority_accounting(&self) -> PriorityRequeueAccounting {
474        PriorityRequeueAccounting {
475            requeue_count: u64::from(self.occupancy.requeue),
476            aged_promotions: 0,
477            max_priority_age: 0,
478        }
479    }
480
481    /// Aggregate queue, idle, fairness, and drain counters into one cheap
482    /// runtime snapshot for SRE dashboards and launch-policy feedback.
483    #[must_use]
484    #[cfg(test)]
485    pub fn runtime_counters(&self) -> ResidentRuntimeCounters {
486        match self.try_runtime_counters() {
487            Ok(counters) => counters,
488            Err(_) => zero_runtime_counters(),
489        }
490    }
491
492    /// Fallibly aggregate queue, idle, fairness, and drain counters.
493    ///
494    /// # Errors
495    ///
496    /// Returns [`PipelineError`] when counter aggregation overflows or decoded
497    /// telemetry contains an impossible relationship.
498    pub fn try_runtime_counters(&self) -> Result<ResidentRuntimeCounters, PipelineError> {
499        let total_slots = self.occupancy.total_slots();
500        let queue_depth = self.occupancy.queue_depth();
501        let gpu_idle_slots = self.occupancy.empty;
502        let gpu_idle_ppm = if total_slots == 0 {
503            0
504        } else {
505            let raw_idle_ppm = (u64::from(gpu_idle_slots) * 1_000_000) / u64::from(total_slots);
506            raw_idle_ppm.min(1_000_000) as u32
507        };
508        let frontier_density_bps = try_density_bps(queue_depth, total_slots)?;
509        let active_slots = total_slots.saturating_sub(gpu_idle_slots);
510        let occupancy_proxy_bps = try_density_bps(active_slots, total_slots)?;
511        let tenant_fairness_total = try_sum_u32_as_u64(
512            &self.control.tenant_fairness,
513            "tenant fairness total",
514            "shard tenant counters before telemetry aggregation",
515        )?;
516        let priority_fairness_total = try_sum_u32_as_u64(
517            &self.control.priority_fairness,
518            "priority fairness total",
519            "shard priority counters before telemetry aggregation",
520        )?;
521        let tenant_fairness_skew = try_fairness_skew(&self.control.tenant_fairness)?;
522        Ok(ResidentRuntimeCounters {
523            total_slots,
524            queue_depth,
525            gpu_idle_slots,
526            gpu_idle_ppm,
527            frontier_density_bps,
528            occupancy_proxy_bps,
529            drained_slots: self.control.done_count,
530            unreclaimed_done_slots: self.occupancy.done,
531            tenant_fairness_total,
532            tenant_fairness_skew,
533            priority_fairness_total,
534            requeue_slots: self.occupancy.requeue,
535            fault_slots: self.occupancy.fault,
536        })
537    }
538
539    /// Derive persistent-kernel health from two snapshots without polling the
540    /// device or synchronizing with the GPU.
541    #[must_use]
542    #[cfg(test)]
543    pub fn health_since(&self, previous: &RingTelemetry) -> ResidentWatchdogSnapshot {
544        match self.try_health_since(previous) {
545            Ok(snapshot) => snapshot,
546            Err(_) => zero_watchdog_snapshot(),
547        }
548    }
549
550    /// Fallibly derive persistent-kernel health from two snapshots.
551    ///
552    /// # Errors
553    ///
554    /// Returns [`PipelineError`] when counters wrap, move backwards, or cannot
555    /// be aggregated without overflow.
556    pub fn try_health_since(
557        &self,
558        previous: &RingTelemetry,
559    ) -> Result<ResidentWatchdogSnapshot, PipelineError> {
560        let counters = self.try_runtime_counters()?;
561        let done_delta = self
562            .control
563            .done_count
564            .checked_sub(previous.control.done_count)
565            .ok_or_else(|| {
566                errors::done_counter_backwards(previous.control.done_count, self.control.done_count)
567            })?;
568        let suspected_stall =
569            counters.queue_depth > 0 && done_delta == 0 && counters.fault_slots == 0;
570        Ok(ResidentWatchdogSnapshot {
571            done_delta,
572            queue_depth: counters.queue_depth,
573            fault_slots: counters.fault_slots,
574            requeue_slots: counters.requeue_slots,
575            gpu_idle_ppm: counters.gpu_idle_ppm,
576            suspected_stall,
577        })
578    }
579
580    /// Feed telemetry into the shared launch policy.
581    ///
582    /// # Errors
583    ///
584    /// Returns a backend error when the supplied adapter limits are malformed.
585    pub fn recommend_launch(
586        &self,
587        mut request: ResidentLaunchRequest,
588    ) -> Result<ResidentLaunchRecommendation, vyre_driver::BackendError> {
589        let counters = self
590            .try_runtime_counters()
591            .map_err(errors::launch_telemetry_failed)?;
592        if request.graph_node_count == 0 {
593            request.graph_node_count = counters.total_slots;
594        }
595        if request.graph_edge_count == 0 {
596            request.graph_edge_count = counters.queue_depth;
597        }
598        if request.frontier_density_bps == 0 {
599            request.frontier_density_bps = counters.frontier_density_bps;
600        }
601        request.hot_opcode_count = self
602            .control
603            .metrics
604            .iter()
605            .filter(|(_, count)| *count > 0)
606            .count()
607            .try_into()
608            .map_err(errors::hot_opcode_count_overflow)?;
609        let mut hot_window_count = 0usize;
610        for window in &self.windows {
611            let demand = window
612                .required_slots
613                .checked_add(window.lookahead_slots)
614                .ok_or_else(|| errors::route_window_demand_overflow())?;
615            if demand >= 4 {
616                hot_window_count = hot_window_count
617                    .checked_add(1)
618                    .ok_or_else(|| errors::hot_window_count_overflow())?;
619            }
620        }
621        request.hot_window_count = hot_window_count
622            .try_into()
623            .map_err(errors::hot_window_count_too_wide)?;
624        request.requeue_count = request
625            .requeue_count
626            .checked_add(u64::from(self.occupancy.requeue))
627            .ok_or_else(errors::requeue_count_overflow)?;
628        ResidentLaunchPolicy::standard().recommend(request)
629    }
630}
631
632/// All-zero runtime counters, returned by the infallible `runtime_counters`
633/// accessor when the fallible decode path reports an error.
634#[cfg(test)]
635fn zero_runtime_counters() -> ResidentRuntimeCounters {
636    ResidentRuntimeCounters {
637        total_slots: 0,
638        queue_depth: 0,
639        gpu_idle_slots: 0,
640        gpu_idle_ppm: 0,
641        frontier_density_bps: 0,
642        occupancy_proxy_bps: 0,
643        drained_slots: 0,
644        unreclaimed_done_slots: 0,
645        tenant_fairness_total: 0,
646        tenant_fairness_skew: 0,
647        priority_fairness_total: 0,
648        requeue_slots: 0,
649        fault_slots: 0,
650    }
651}
652
653/// All-zero watchdog snapshot, returned by the infallible `health_since`
654/// accessor when the fallible derivation path reports an error.
655#[cfg(test)]
656fn zero_watchdog_snapshot() -> ResidentWatchdogSnapshot {
657    ResidentWatchdogSnapshot {
658        done_delta: 0,
659        queue_depth: 0,
660        fault_slots: 0,
661        requeue_slots: 0,
662        gpu_idle_ppm: 0,
663        suspected_stall: false,
664    }
665}
666
667fn read_required_control_word(control_bytes: &[u8], word_idx: usize) -> Result<u32, PipelineError> {
668    read_word(control_bytes, word_idx).ok_or_else(|| errors::missing_control_word(word_idx))
669}
670
671fn try_density_bps(numerator: u32, denominator: u32) -> Result<u16, PipelineError> {
672    if denominator == 0 {
673        return Ok(0);
674    }
675    let bps = (u64::from(numerator) * 10_000) / u64::from(denominator);
676    u16::try_from(bps.min(u64::from(u16::MAX))).map_err(errors::density_bps_overflow)
677}
678
679fn validate_telemetry_buffers(
680    control_bytes: &[u8],
681    ring_bytes: &[u8],
682) -> Result<(), PipelineError> {
683    validate_control_snapshot(control_bytes)?;
684    let slot_bytes = slot_byte_len()?;
685    if ring_bytes.len() % slot_bytes != 0 {
686        return Err(errors::ring_slot_alignment(ring_bytes.len(), slot_bytes));
687    }
688    let slot_count = ring_bytes.len() / slot_bytes;
689    if u32::try_from(slot_count).is_err() {
690        return Err(errors::ring_slot_count_too_wide(slot_count));
691    }
692    Ok(())
693}
694
695fn validate_control_snapshot(control_bytes: &[u8]) -> Result<(), PipelineError> {
696    let min_control =
697        super::protocol::control_byte_len(0).ok_or_else(|| errors::control_length_overflow())?;
698    if control_bytes.len() < min_control || control_bytes.len() % 4 != 0 {
699        return Err(errors::bad_control_snapshot(
700            control_bytes.len(),
701            min_control,
702        ));
703    }
704    Ok(())
705}
706
707fn slot_byte_len() -> Result<usize, PipelineError> {
708    SLOT_WORDS_USIZE
709        .checked_mul(4)
710        .ok_or_else(|| errors::slot_byte_width_overflow())
711}
712
713fn telemetry_u32_to_usize(value: u32, label: &'static str) -> Result<usize, PipelineError> {
714    usize::try_from(value).map_err(|source| errors::telemetry_u32_to_usize(value, label, source))
715}
716
717fn control_word_index(word: u32) -> Result<usize, PipelineError> {
718    usize::try_from(word).map_err(|source| errors::control_word_index(word, source))
719}
720
721fn control_offset_index(base: u32, offset: u32) -> Result<usize, PipelineError> {
722    let word = base
723        .checked_add(offset)
724        .ok_or_else(|| errors::control_word_offset_overflow())?;
725    control_word_index(word)
726}
727
728fn try_sum_u32_as_u64(
729    counters: &[u32],
730    label: &'static str,
731    fix: &'static str,
732) -> Result<u64, PipelineError> {
733    counters.iter().try_fold(0u64, |acc, &count| {
734        acc.checked_add(u64::from(count))
735            .ok_or_else(|| errors::counter_sum_overflow(label, fix))
736    })
737}
738
739fn try_fairness_skew(counters: &[u32]) -> Result<u32, PipelineError> {
740    let mut min_nonzero = u32::MAX;
741    let mut max = 0u32;
742    for &count in counters {
743        if count != 0 {
744            min_nonzero = min_nonzero.min(count);
745            max = max.max(count);
746        }
747    }
748    if min_nonzero == u32::MAX {
749        Ok(0)
750    } else {
751        max.checked_sub(min_nonzero)
752            .ok_or_else(|| errors::fairness_skew_invalid(max, min_nonzero))
753    }
754}
755
756#[cfg(test)]
757#[path = "telemetry_tests.rs"]
758mod tests;