Skip to main content

prns_runtime/runtime/
metrics.rs

1use alloc::vec::Vec;
2
3use crate::engine::{AnnounceOrigin, EngineMetricsSnapshot};
4use crate::interfaces::{InterfaceId, InterfaceKind};
5use crate::runtime::ReliabilityMetricsSnapshot;
6use crate::units::InstantMillis;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum AnnounceEgressOutcome {
11    Enqueued,
12    InterfaceUnavailable,
13    LaneFull,
14    LaneMissing,
15    IfacRejected,
16    PacerRejected,
17}
18
19impl AnnounceEgressOutcome {
20    pub const ALL: [Self; 6] = [
21        Self::Enqueued,
22        Self::InterfaceUnavailable,
23        Self::LaneFull,
24        Self::LaneMissing,
25        Self::IfacRejected,
26        Self::PacerRejected,
27    ];
28
29    const fn index(self) -> usize {
30        self as usize
31    }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct AnnounceEgressCounts {
36    counts: [[u64; AnnounceEgressOutcome::ALL.len()]; AnnounceOrigin::ALL.len()],
37}
38
39impl Default for AnnounceEgressCounts {
40    fn default() -> Self {
41        Self {
42            counts: [[0; AnnounceEgressOutcome::ALL.len()]; AnnounceOrigin::ALL.len()],
43        }
44    }
45}
46
47impl AnnounceEgressCounts {
48    pub const fn get(&self, origin: AnnounceOrigin, outcome: AnnounceEgressOutcome) -> u64 {
49        self.counts[origin.index()][outcome.index()]
50    }
51
52    pub fn iter(&self) -> impl Iterator<Item = (AnnounceOrigin, AnnounceEgressOutcome, u64)> + '_ {
53        AnnounceOrigin::ALL.into_iter().flat_map(move |origin| {
54            AnnounceEgressOutcome::ALL
55                .into_iter()
56                .map(move |outcome| (origin, outcome, self.get(origin, outcome)))
57        })
58    }
59
60    fn record(&mut self, origin: AnnounceOrigin, outcome: AnnounceEgressOutcome) {
61        let count = &mut self.counts[origin.index()][outcome.index()];
62        *count = count.saturating_add(1);
63    }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub struct AnnounceOriginCounts {
68    counts: [u64; AnnounceOrigin::ALL.len()],
69}
70
71impl Default for AnnounceOriginCounts {
72    fn default() -> Self {
73        Self {
74            counts: [0; AnnounceOrigin::ALL.len()],
75        }
76    }
77}
78
79impl AnnounceOriginCounts {
80    pub const fn get(&self, origin: AnnounceOrigin) -> u64 {
81        self.counts[origin.index()]
82    }
83
84    pub fn iter(&self) -> impl ExactSizeIterator<Item = (AnnounceOrigin, u64)> + '_ {
85        AnnounceOrigin::ALL
86            .into_iter()
87            .map(|origin| (origin, self.get(origin)))
88    }
89
90    fn add(&mut self, origin: AnnounceOrigin, value: u64) {
91        let count = &mut self.counts[origin.index()];
92        *count = count.saturating_add(value);
93    }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct EgressInterfaceKindCounts {
98    counts: [u64; InterfaceKind::ALL.len()],
99    unknown: u64,
100}
101
102impl Default for EgressInterfaceKindCounts {
103    fn default() -> Self {
104        Self {
105            counts: [0; InterfaceKind::ALL.len()],
106            unknown: 0,
107        }
108    }
109}
110
111impl EgressInterfaceKindCounts {
112    pub const fn get(&self, kind: InterfaceKind) -> u64 {
113        self.counts[kind as usize]
114    }
115
116    pub const fn unknown(&self) -> u64 {
117        self.unknown
118    }
119
120    pub fn iter(&self) -> impl ExactSizeIterator<Item = (InterfaceKind, u64)> + '_ {
121        InterfaceKind::ALL
122            .into_iter()
123            .map(|kind| (kind, self.get(kind)))
124    }
125
126    fn record(&mut self, kind: Option<InterfaceKind>) {
127        match kind {
128            Some(kind) => {
129                let count = &mut self.counts[kind as usize];
130                *count = count.saturating_add(1);
131            }
132            None => self.unknown = self.unknown.saturating_add(1),
133        }
134    }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct InterfaceAnnounceEgressMetricsSnapshot {
139    pub interface: InterfaceId,
140    pub outcomes: AnnounceEgressCounts,
141    pub enqueued_bytes_by_origin: AnnounceOriginCounts,
142    pub pacer_queue_depth: u32,
143}
144
145#[derive(Debug, Clone, Default, PartialEq, Eq)]
146pub struct AnnounceEgressMetricsSnapshot {
147    pub outcomes: AnnounceEgressCounts,
148    pub enqueued_by_interface_kind: EgressInterfaceKindCounts,
149    pub enqueued_bytes_by_origin: AnnounceOriginCounts,
150    pub pacer_queue_depth: u32,
151    pub interfaces: Vec<InterfaceAnnounceEgressMetricsSnapshot>,
152}
153
154impl AnnounceEgressMetricsSnapshot {
155    pub fn record(
156        &mut self,
157        origin: AnnounceOrigin,
158        interface: InterfaceId,
159        outcome: AnnounceEgressOutcome,
160        bytes: usize,
161    ) {
162        self.outcomes.record(origin, outcome);
163        if outcome == AnnounceEgressOutcome::Enqueued {
164            self.enqueued_by_interface_kind.record(interface.kind());
165            self.enqueued_bytes_by_origin
166                .add(origin, u64::try_from(bytes).unwrap_or(u64::MAX));
167        }
168        let interface_metrics = self.interface_mut(interface);
169        interface_metrics.outcomes.record(origin, outcome);
170        if outcome == AnnounceEgressOutcome::Enqueued {
171            interface_metrics
172                .enqueued_bytes_by_origin
173                .add(origin, u64::try_from(bytes).unwrap_or(u64::MAX));
174        }
175    }
176
177    pub fn register_interface(&mut self, interface: InterfaceId) {
178        let _ = self.interface_mut(interface);
179    }
180
181    pub fn reset_pacer_depths(&mut self) {
182        self.pacer_queue_depth = 0;
183        for metrics in &mut self.interfaces {
184            metrics.pacer_queue_depth = 0;
185        }
186    }
187
188    pub fn add_pacer_depth(&mut self, interface: InterfaceId, depth: usize) {
189        let depth = u32::try_from(depth).unwrap_or(u32::MAX);
190        self.pacer_queue_depth = self.pacer_queue_depth.saturating_add(depth);
191        let metrics = self.interface_mut(interface);
192        metrics.pacer_queue_depth = metrics.pacer_queue_depth.saturating_add(depth);
193    }
194
195    fn interface_mut(
196        &mut self,
197        interface: InterfaceId,
198    ) -> &mut InterfaceAnnounceEgressMetricsSnapshot {
199        if let Some(position) = self
200            .interfaces
201            .iter()
202            .position(|metrics| metrics.interface == interface)
203        {
204            return &mut self.interfaces[position];
205        }
206        self.interfaces
207            .push(InterfaceAnnounceEgressMetricsSnapshot {
208                interface,
209                outcomes: AnnounceEgressCounts::default(),
210                enqueued_bytes_by_origin: AnnounceOriginCounts::default(),
211                pacer_queue_depth: 0,
212            });
213        let position = self.interfaces.len() - 1;
214        &mut self.interfaces[position]
215    }
216}
217
218#[derive(Debug, Clone, Default, PartialEq, Eq)]
219pub struct EgressMetricsSnapshot {
220    pub enqueued_frames: u64,
221    pub unavailable_frame_skips: u64,
222    pub full_lane_drops: u64,
223    pub missing_lane_drops: u64,
224    pub announces: AnnounceEgressMetricsSnapshot,
225}
226
227#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
228pub struct CryptoMetricsSnapshot {
229    pub submitted_jobs: u64,
230    pub completed_jobs: u64,
231    pub queue_depth: u32,
232    pub maximum_queue_depth: u32,
233    pub backpressure_deferrals: u64,
234    pub packet_verdicts_owed: u32,
235}
236
237#[derive(Debug, Clone, PartialEq, Eq)]
238pub struct RuntimeMetricsSnapshot {
239    pub taken_at: InstantMillis,
240    pub engine: EngineMetricsSnapshot,
241    pub egress: EgressMetricsSnapshot,
242    pub crypto: Option<CryptoMetricsSnapshot>,
243    pub reliability: ReliabilityMetricsSnapshot,
244}