dhcp_client_core/
inspect.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::time::Duration;
7
8use diagnostics_traits::{InspectableValue, Inspector};
9
10use crate::parse::{IncomingResponseToRequestErrorCounters, SelectingIncomingMessageErrorCounters};
11
12/// An incrementing counter.
13#[derive(Default, Debug)]
14pub(crate) struct Counter(AtomicUsize);
15
16impl Counter {
17    pub(crate) fn increment(&self) {
18        let _: usize = self.0.fetch_add(1, Ordering::Relaxed);
19    }
20
21    pub(crate) fn load(&self) -> usize {
22        self.0.load(Ordering::Relaxed)
23    }
24}
25
26impl InspectableValue for Counter {
27    fn record<I: diagnostics_traits::Inspector>(&self, name: &str, inspector: &mut I) {
28        inspector.record_uint(name, u64::try_from(self.load()).unwrap_or(u64::MAX));
29    }
30}
31
32pub(crate) fn record_optional_duration_secs(
33    inspector: &mut impl Inspector,
34    name: &str,
35    value: Option<Duration>,
36) {
37    match value {
38        Some(value) => inspector.record_uint(name, value.as_secs()),
39        None => inspector.record_display(name, "Unset"),
40    }
41}
42
43/// Counters relating to sending and receiving messages.
44#[derive(Debug, Default)]
45pub(crate) struct MessagingRelatedCounters {
46    /// A counter for each time the client sent a message.
47    pub(crate) send_message: Counter,
48    /// A counter for each time the client received a message.
49    pub(crate) recv_message: Counter,
50    /// A counter for each time the client observed a fatal socket error while
51    /// trying to receive a message.
52    pub(crate) recv_message_fatal_socket_error: Counter,
53    /// A counter for each time the client observed a non-fatal socket error
54    /// while trying to receive a message.
55    pub(crate) recv_message_non_fatal_socket_error: Counter,
56    /// A counter for each time the client has timed out waiting to receive
57    /// (causing it to retransmit or state-transition).
58    pub(crate) recv_time_out: Counter,
59    /// A counter for each time the client received a message with the wrong
60    /// transaction ID.
61    pub(crate) recv_wrong_xid: Counter,
62    /// A counter for each time the client received a message specifying an
63    /// incorrect chaddr (client hardware address).
64    pub(crate) recv_wrong_chaddr: Counter,
65    /// A counter for each time the client received a UDPv4 packet on the
66    /// dhcp-client port that did not parse as a DHCP message.
67    pub(crate) recv_failed_dhcp_parse: Counter,
68}
69
70impl MessagingRelatedCounters {
71    fn record(&self, inspector: &mut impl Inspector) {
72        let Self {
73            send_message,
74            recv_message,
75            recv_message_fatal_socket_error,
76            recv_message_non_fatal_socket_error,
77            recv_time_out,
78            recv_wrong_xid,
79            recv_wrong_chaddr,
80            recv_failed_dhcp_parse,
81        } = self;
82        inspector.record_inspectable_value("SendMessage", send_message);
83        inspector.record_inspectable_value("RecvMessage", recv_message);
84        inspector.record_inspectable_value(
85            "RecvMessageFatalSocketError",
86            recv_message_fatal_socket_error,
87        );
88        inspector.record_inspectable_value(
89            "RecvMessageNonFatalSocketError",
90            recv_message_non_fatal_socket_error,
91        );
92        inspector.record_inspectable_value("RecvTimeOut", recv_time_out);
93        inspector.record_inspectable_value("RecvWrongXid", recv_wrong_xid);
94        inspector.record_inspectable_value("RecvWrongChaddr", recv_wrong_chaddr);
95        inspector.record_inspectable_value("RecvFailedDhcpParse", recv_failed_dhcp_parse);
96    }
97}
98
99/// Counters for the Init state.
100#[derive(Debug, Default)]
101pub(crate) struct InitCounters {
102    /// The number of times the state was entered.
103    pub(crate) entered: Counter,
104}
105
106impl InitCounters {
107    fn record(&self, inspector: &mut impl Inspector) {
108        let Self { entered } = self;
109        inspector.record_inspectable_value("Entered", entered);
110    }
111}
112
113/// Counters for the Selecting state.
114#[derive(Debug, Default)]
115pub(crate) struct SelectingCounters {
116    /// The number of times the state was entered.
117    pub(crate) entered: Counter,
118    /// Counters relating to sending and receiving messages.
119    pub(crate) messaging: MessagingRelatedCounters,
120    /// Counters for each error that could cause the client to reject a message
121    /// while receiving in the Selecting state.
122    pub(crate) recv_error: SelectingIncomingMessageErrorCounters,
123}
124
125impl SelectingCounters {
126    fn record(&self, inspector: &mut impl Inspector) {
127        let Self { entered, messaging, recv_error } = self;
128        inspector.record_inspectable_value("Entered", entered);
129        messaging.record(inspector);
130        recv_error.record(inspector);
131    }
132}
133
134/// Counters for the Requesting state.
135#[derive(Debug, Default)]
136pub(crate) struct RequestingCounters {
137    /// The number of times the state was entered.
138    pub(crate) entered: Counter,
139    /// Counters relating to sending and receiving messages.
140    pub(crate) messaging: MessagingRelatedCounters,
141    /// Counters for each error that could cause a client to reject a message
142    /// while receiving in the Requesting state.
143    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
144    /// Counter for each time the client received a NAK message while in the
145    /// Requesting state.
146    pub(crate) recv_nak: Counter,
147}
148
149impl RequestingCounters {
150    fn record(&self, inspector: &mut impl Inspector) {
151        let Self { entered, messaging, recv_error, recv_nak } = self;
152        inspector.record_inspectable_value("Entered", entered);
153        messaging.record(inspector);
154        recv_error.record(inspector);
155        inspector.record_inspectable_value("RecvNak", recv_nak);
156    }
157}
158
159/// Counters for the Bound state.
160#[derive(Debug, Default)]
161pub(crate) struct BoundCounters {
162    /// The number of times the state was entered.
163    pub(crate) entered: Counter,
164    /// The number of times the address transitions to `Assigned`.
165    pub(crate) assigned: Counter,
166}
167
168impl BoundCounters {
169    fn record(&self, inspector: &mut impl Inspector) {
170        let Self { entered, assigned } = self;
171        inspector.record_inspectable_value("Entered", entered);
172        inspector.record_inspectable_value("Assigned", assigned);
173    }
174}
175
176/// Counters for the Renewing state.
177#[derive(Debug, Default)]
178pub(crate) struct RenewingCounters {
179    /// The number of times the state was entered.
180    pub(crate) entered: Counter,
181    /// Counters relating to sending and receiving messages.
182    pub(crate) messaging: MessagingRelatedCounters,
183    /// Counters for each error that could cause a client to reject a message
184    /// while receiving in the Requesting state.
185    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
186    /// Counter for each time the client received a NAK message while in the
187    /// Renewing state.
188    pub(crate) recv_nak: Counter,
189}
190
191impl RenewingCounters {
192    fn record(&self, inspector: &mut impl Inspector) {
193        let Self { entered, messaging, recv_error, recv_nak } = self;
194        inspector.record_inspectable_value("Entered", entered);
195        messaging.record(inspector);
196        recv_error.record(inspector);
197        inspector.record_inspectable_value("RecvNak", recv_nak);
198    }
199}
200
201/// Counters for the Rebinding state.
202#[derive(Debug, Default)]
203pub(crate) struct RebindingCounters {
204    /// The number of times the state was entered.
205    pub(crate) entered: Counter,
206    /// Counters relating to sending and receiving messages.
207    pub(crate) messaging: MessagingRelatedCounters,
208    /// Counters for each error that could cause a client to reject a message
209    /// while receiving in the Rebinding state.
210    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
211    /// Counter for each time the client received a NAK message while in the
212    /// Rebinding state.
213    pub(crate) recv_nak: Counter,
214}
215
216impl RebindingCounters {
217    fn record(&self, inspector: &mut impl Inspector) {
218        let Self { entered, messaging, recv_error, recv_nak } = self;
219        inspector.record_inspectable_value("Entered", entered);
220        messaging.record(inspector);
221        recv_error.record(inspector);
222        inspector.record_inspectable_value("RecvNak", recv_nak);
223    }
224}
225
226/// Counters for the WaitingToRestart state.
227#[derive(Debug, Default)]
228pub(crate) struct WaitingToRestartCounters {
229    /// The number of times the state was entered.
230    pub(crate) entered: Counter,
231}
232
233impl WaitingToRestartCounters {
234    fn record(&self, inspector: &mut impl Inspector) {
235        let Self { entered } = self;
236        inspector.record_inspectable_value("Entered", entered);
237    }
238}
239
240/// Debugging counters for the core state machine.
241#[derive(Default, Debug)]
242pub struct Counters {
243    pub(crate) init: InitCounters,
244    pub(crate) selecting: SelectingCounters,
245    pub(crate) requesting: RequestingCounters,
246    pub(crate) bound: BoundCounters,
247    pub(crate) renewing: RenewingCounters,
248    pub(crate) rebinding: RebindingCounters,
249    pub(crate) waiting_to_restart: WaitingToRestartCounters,
250}
251
252impl Counters {
253    /// Records the counters in the given [`Inspector`].
254    pub fn record(&self, inspector: &mut impl Inspector) {
255        let Self { init, selecting, requesting, bound, renewing, rebinding, waiting_to_restart } =
256            self;
257        inspector.record_child("Init", |inspector| {
258            init.record(inspector);
259        });
260        inspector.record_child("Selecting", |inspector| {
261            selecting.record(inspector);
262        });
263        inspector.record_child("Requesting", |inspector| {
264            requesting.record(inspector);
265        });
266        inspector.record_child("Bound", |inspector| {
267            bound.record(inspector);
268        });
269        inspector.record_child("Renewing", |inspector| {
270            renewing.record(inspector);
271        });
272        inspector.record_child("Rebinding", |inspector| {
273            rebinding.record(inspector);
274        });
275        inspector.record_child("WaitingToRestart", |inspector| {
276            waiting_to_restart.record(inspector);
277        });
278    }
279}