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}