1use rustc_hash::FxHashMap;
4
5use crate::Duration;
6use crate::FrameType;
7
8use super::PathId;
9
10#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, derive_more::Add, derive_more::AddAssign)]
15#[non_exhaustive]
16pub struct UdpStats {
17 pub datagrams: u64,
19 pub bytes: u64,
21 pub ios: u64,
25}
26
27impl UdpStats {
28 pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
29 self.datagrams += datagrams;
30 self.bytes += bytes as u64;
31 self.ios += 1;
32 }
33}
34
35#[derive(Default, Copy, Clone, PartialEq, Eq, derive_more::Add, derive_more::AddAssign)]
37#[non_exhaustive]
38#[allow(missing_docs)]
39pub struct FrameStats {
40 pub acks: u64,
41 pub path_acks: u64,
42 pub ack_frequency: u64,
43 pub crypto: u64,
44 pub connection_close: u64,
45 pub data_blocked: u64,
46 pub datagram: u64,
47 pub handshake_done: u8,
48 pub immediate_ack: u64,
49 pub max_data: u64,
50 pub max_stream_data: u64,
51 pub max_streams_bidi: u64,
52 pub max_streams_uni: u64,
53 pub new_connection_id: u64,
54 pub path_new_connection_id: u64,
55 pub new_token: u64,
56 pub path_challenge: u64,
57 pub path_response: u64,
58 pub ping: u64,
59 pub reset_stream: u64,
60 pub retire_connection_id: u64,
61 pub path_retire_connection_id: u64,
62 pub stream_data_blocked: u64,
63 pub streams_blocked_bidi: u64,
64 pub streams_blocked_uni: u64,
65 pub stop_sending: u64,
66 pub stream: u64,
67 pub observed_addr: u64,
68 pub path_abandon: u64,
69 pub path_status_available: u64,
70 pub path_status_backup: u64,
71 pub max_path_id: u64,
72 pub paths_blocked: u64,
73 pub path_cids_blocked: u64,
74 pub add_address: u64,
75 pub reach_out: u64,
76 pub remove_address: u64,
77}
78
79impl FrameStats {
80 pub(crate) fn record(&mut self, frame_type: FrameType) {
81 use FrameType::*;
82 macro_rules! inc {
84 ($field_name: ident) => {{ self.$field_name = self.$field_name.saturating_add(1) }};
85 }
86 match frame_type {
87 Padding => {}
88 Ping => inc!(ping),
89 Ack | AckEcn => inc!(acks),
90 PathAck | PathAckEcn => inc!(path_acks),
91 ResetStream => inc!(reset_stream),
92 StopSending => inc!(stop_sending),
93 Crypto => inc!(crypto),
94 Datagram(_) => inc!(datagram),
95 NewToken => inc!(new_token),
96 MaxData => inc!(max_data),
97 MaxStreamData => inc!(max_stream_data),
98 MaxStreamsBidi => inc!(max_streams_bidi),
99 MaxStreamsUni => inc!(max_streams_uni),
100 DataBlocked => inc!(data_blocked),
101 Stream(_) => inc!(stream),
102 StreamDataBlocked => inc!(stream_data_blocked),
103 StreamsBlockedUni => inc!(streams_blocked_uni),
104 StreamsBlockedBidi => inc!(streams_blocked_bidi),
105 NewConnectionId => inc!(new_connection_id),
106 PathNewConnectionId => inc!(path_new_connection_id),
107 RetireConnectionId => inc!(retire_connection_id),
108 PathRetireConnectionId => inc!(path_retire_connection_id),
109 PathChallenge => inc!(path_challenge),
110 PathResponse => inc!(path_response),
111 ConnectionClose | ApplicationClose => inc!(connection_close),
112 AckFrequency => inc!(ack_frequency),
113 ImmediateAck => inc!(immediate_ack),
114 HandshakeDone => inc!(handshake_done),
115 ObservedIpv4Addr | ObservedIpv6Addr => inc!(observed_addr),
116 PathAbandon => inc!(path_abandon),
117 PathStatusAvailable => inc!(path_status_available),
118 PathStatusBackup => inc!(path_status_backup),
119 MaxPathId => inc!(max_path_id),
120 PathsBlocked => inc!(paths_blocked),
121 PathCidsBlocked => inc!(path_cids_blocked),
122 AddIpv4Address | AddIpv6Address => inc!(add_address),
123 ReachOutAtIpv4 | ReachOutAtIpv6 => inc!(reach_out),
124 RemoveAddress => inc!(remove_address),
125 };
126 }
127}
128
129impl std::fmt::Debug for FrameStats {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 let Self {
132 acks,
133 path_acks,
134 ack_frequency,
135 crypto,
136 connection_close,
137 data_blocked,
138 datagram,
139 handshake_done,
140 immediate_ack,
141 max_data,
142 max_stream_data,
143 max_streams_bidi,
144 max_streams_uni,
145 new_connection_id,
146 path_new_connection_id,
147 new_token,
148 path_challenge,
149 path_response,
150 ping,
151 reset_stream,
152 retire_connection_id,
153 path_retire_connection_id,
154 stream_data_blocked,
155 streams_blocked_bidi,
156 streams_blocked_uni,
157 stop_sending,
158 stream,
159 observed_addr,
160 path_abandon,
161 path_status_available,
162 path_status_backup,
163 max_path_id,
164 paths_blocked,
165 path_cids_blocked,
166 add_address,
167 reach_out,
168 remove_address,
169 } = self;
170 f.debug_struct("FrameStats")
171 .field("ACK", acks)
172 .field("ACK_FREQUENCY", ack_frequency)
173 .field("CONNECTION_CLOSE", connection_close)
174 .field("CRYPTO", crypto)
175 .field("DATA_BLOCKED", data_blocked)
176 .field("DATAGRAM", datagram)
177 .field("HANDSHAKE_DONE", handshake_done)
178 .field("IMMEDIATE_ACK", immediate_ack)
179 .field("MAX_DATA", max_data)
180 .field("MAX_PATH_ID", max_path_id)
181 .field("MAX_STREAM_DATA", max_stream_data)
182 .field("MAX_STREAMS_BIDI", max_streams_bidi)
183 .field("MAX_STREAMS_UNI", max_streams_uni)
184 .field("NEW_CONNECTION_ID", new_connection_id)
185 .field("NEW_TOKEN", new_token)
186 .field("PATHS_BLOCKED", paths_blocked)
187 .field("PATH_ABANDON", path_abandon)
188 .field("PATH_ACK", path_acks)
189 .field("PATH_STATUS_AVAILABLE", path_status_available)
190 .field("PATH_STATUS_BACKUP", path_status_backup)
191 .field("PATH_CHALLENGE", path_challenge)
192 .field("PATH_CIDS_BLOCKED", path_cids_blocked)
193 .field("PATH_NEW_CONNECTION_ID", path_new_connection_id)
194 .field("PATH_RESPONSE", path_response)
195 .field("PATH_RETIRE_CONNECTION_ID", path_retire_connection_id)
196 .field("PING", ping)
197 .field("RESET_STREAM", reset_stream)
198 .field("RETIRE_CONNECTION_ID", retire_connection_id)
199 .field("STREAM_DATA_BLOCKED", stream_data_blocked)
200 .field("STREAMS_BLOCKED_BIDI", streams_blocked_bidi)
201 .field("STREAMS_BLOCKED_UNI", streams_blocked_uni)
202 .field("STOP_SENDING", stop_sending)
203 .field("STREAM", stream)
204 .field("OBSERVED_ADDRESS", observed_addr)
205 .field("ADD_ADDRESS", add_address)
206 .field("REACH_OUT", reach_out)
207 .field("REMOVE_ADDRESS", remove_address)
208 .finish()
209 }
210}
211
212#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
214#[non_exhaustive]
215pub struct PathStats {
216 pub rtt: Duration,
218 pub udp_tx: UdpStats,
220 pub udp_rx: UdpStats,
222 pub frame_tx: FrameStats,
224 pub frame_rx: FrameStats,
226 pub cwnd: u64,
228 pub congestion_events: u64,
230 pub lost_packets: u64,
232 pub lost_bytes: u64,
234 pub sent_plpmtud_probes: u64,
238 pub lost_plpmtud_probes: u64,
242 pub black_holes_detected: u64,
244 pub current_mtu: u16,
246}
247
248#[derive(Debug, Default, Clone)]
253#[non_exhaustive]
254pub struct ConnectionStats {
255 pub udp_tx: UdpStats,
257 pub udp_rx: UdpStats,
259 pub frame_tx: FrameStats,
261 pub frame_rx: FrameStats,
263 pub lost_packets: u64,
265 pub lost_bytes: u64,
267}
268
269impl std::ops::Add<PathStats> for ConnectionStats {
270 type Output = Self;
271
272 fn add(self, rhs: PathStats) -> Self::Output {
273 let PathStats {
276 rtt: _,
277 udp_tx,
278 udp_rx,
279 frame_tx,
280 frame_rx,
281 cwnd: _,
282 congestion_events: _,
283 lost_packets,
284 lost_bytes,
285 sent_plpmtud_probes: _,
286 lost_plpmtud_probes: _,
287 black_holes_detected: _,
288 current_mtu: _,
289 } = rhs;
290 Self {
291 udp_tx: self.udp_tx + udp_tx,
292 udp_rx: self.udp_rx + udp_rx,
293 frame_tx: self.frame_tx + frame_tx,
294 frame_rx: self.frame_rx + frame_rx,
295 lost_packets: self.lost_packets + lost_packets,
296 lost_bytes: self.lost_bytes + lost_bytes,
297 }
298 }
299}
300
301impl std::ops::AddAssign<PathStats> for ConnectionStats {
302 fn add_assign(&mut self, rhs: PathStats) {
303 let PathStats {
306 rtt: _,
307 udp_tx: path_udp_tx,
308 udp_rx: path_udp_rx,
309 frame_tx: path_frame_tx,
310 frame_rx: path_frame_rx,
311 cwnd: _,
312 congestion_events: _,
313 lost_packets: path_lost_packets,
314 lost_bytes: path_lost_bytes,
315 sent_plpmtud_probes: _,
316 lost_plpmtud_probes: _,
317 black_holes_detected: _,
318 current_mtu: _,
319 } = rhs;
320 let Self {
321 udp_tx,
322 udp_rx,
323 frame_tx,
324 frame_rx,
325 lost_packets,
326 lost_bytes,
327 } = self;
328 *udp_tx += path_udp_tx;
329 *udp_rx += path_udp_rx;
330 *frame_tx += path_frame_tx;
331 *frame_rx += path_frame_rx;
332 *lost_packets += path_lost_packets;
333 *lost_bytes += path_lost_bytes;
334 }
335}
336
337#[derive(Debug, Default)]
344pub(super) struct PathStatsMap(FxHashMap<PathId, PathStats>);
345
346impl PathStatsMap {
347 pub(super) fn for_path(&mut self, path_id: PathId) -> &mut PathStats {
349 self.0.entry(path_id).or_default()
350 }
351
352 pub(super) fn iter_stats(&self) -> impl Iterator<Item = &PathStats> {
354 self.0.values()
355 }
356
357 pub(super) fn discard(&mut self, path_id: &PathId) -> PathStats {
361 self.0.remove(path_id).unwrap_or_default()
362 }
363}