Skip to main content

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    /// A counter for each time the client received a DHCPACK that omitted the
69    /// IP Address Lease Time option.
70    pub(crate) recv_ack_no_addr_lease_time: Counter,
71}
72
73impl MessagingRelatedCounters {
74    fn record(&self, inspector: &mut impl Inspector) {
75        let Self {
76            send_message,
77            recv_message,
78            recv_message_fatal_socket_error,
79            recv_message_non_fatal_socket_error,
80            recv_time_out,
81            recv_wrong_xid,
82            recv_wrong_chaddr,
83            recv_failed_dhcp_parse,
84            recv_ack_no_addr_lease_time,
85        } = self;
86        inspector.record_inspectable_value("SendMessage", send_message);
87        inspector.record_inspectable_value("RecvMessage", recv_message);
88        inspector.record_inspectable_value(
89            "RecvMessageFatalSocketError",
90            recv_message_fatal_socket_error,
91        );
92        inspector.record_inspectable_value(
93            "RecvMessageNonFatalSocketError",
94            recv_message_non_fatal_socket_error,
95        );
96        inspector.record_inspectable_value("RecvTimeOut", recv_time_out);
97        inspector.record_inspectable_value("RecvWrongXid", recv_wrong_xid);
98        inspector.record_inspectable_value("RecvWrongChaddr", recv_wrong_chaddr);
99        inspector.record_inspectable_value("RecvFailedDhcpParse", recv_failed_dhcp_parse);
100        inspector.record_inspectable_value("NoLeaseTime", recv_ack_no_addr_lease_time);
101    }
102}
103
104/// Counters for the Init state.
105#[derive(Debug, Default)]
106pub(crate) struct InitCounters {
107    /// The number of times the state was entered.
108    pub(crate) entered: Counter,
109}
110
111impl InitCounters {
112    fn record(&self, inspector: &mut impl Inspector) {
113        let Self { entered } = self;
114        inspector.record_inspectable_value("Entered", entered);
115    }
116}
117
118/// Counters for the Selecting state.
119#[derive(Debug, Default)]
120pub(crate) struct SelectingCounters {
121    /// The number of times the state was entered.
122    pub(crate) entered: Counter,
123    /// Counters relating to sending and receiving messages.
124    pub(crate) messaging: MessagingRelatedCounters,
125    /// Counters for each error that could cause the client to reject a message
126    /// while receiving in the Selecting state.
127    pub(crate) recv_error: SelectingIncomingMessageErrorCounters,
128}
129
130impl SelectingCounters {
131    fn record(&self, inspector: &mut impl Inspector) {
132        let Self { entered, messaging, recv_error } = self;
133        inspector.record_inspectable_value("Entered", entered);
134        messaging.record(inspector);
135        recv_error.record(inspector);
136    }
137}
138
139/// Counters for the Requesting state.
140#[derive(Debug, Default)]
141pub(crate) struct RequestingCounters {
142    /// The number of times the state was entered.
143    pub(crate) entered: Counter,
144    /// Counters relating to sending and receiving messages.
145    pub(crate) messaging: MessagingRelatedCounters,
146    /// Counters for each error that could cause a client to reject a message
147    /// while receiving in the Requesting state.
148    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
149    /// Counter for each time the client received a NAK message while in the
150    /// Requesting state.
151    pub(crate) recv_nak: Counter,
152}
153
154impl RequestingCounters {
155    fn record(&self, inspector: &mut impl Inspector) {
156        let Self { entered, messaging, recv_error, recv_nak } = self;
157        inspector.record_inspectable_value("Entered", entered);
158        messaging.record(inspector);
159        recv_error.record(inspector);
160        inspector.record_inspectable_value("RecvNak", recv_nak);
161    }
162}
163
164/// Counters for the Bound state.
165#[derive(Debug, Default)]
166pub(crate) struct BoundCounters {
167    /// The number of times the state was entered.
168    pub(crate) entered: Counter,
169    /// The number of times the address transitions to `Assigned`.
170    pub(crate) assigned: Counter,
171}
172
173impl BoundCounters {
174    fn record(&self, inspector: &mut impl Inspector) {
175        let Self { entered, assigned } = self;
176        inspector.record_inspectable_value("Entered", entered);
177        inspector.record_inspectable_value("Assigned", assigned);
178    }
179}
180
181/// Counters for the Renewing state.
182#[derive(Debug, Default)]
183pub(crate) struct RenewingCounters {
184    /// The number of times the state was entered.
185    pub(crate) entered: Counter,
186    /// Counters relating to sending and receiving messages.
187    pub(crate) messaging: MessagingRelatedCounters,
188    /// Counters for each error that could cause a client to reject a message
189    /// while receiving in the Renewing state.
190    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
191    /// Counter for each time the client received a NAK message while in the
192    /// Renewing state.
193    pub(crate) recv_nak: Counter,
194}
195
196impl RenewingCounters {
197    fn record(&self, inspector: &mut impl Inspector) {
198        let Self { entered, messaging, recv_error, recv_nak } = self;
199        inspector.record_inspectable_value("Entered", entered);
200        messaging.record(inspector);
201        recv_error.record(inspector);
202        inspector.record_inspectable_value("RecvNak", recv_nak);
203    }
204}
205
206/// Counters for the Rebinding state.
207#[derive(Debug, Default)]
208pub(crate) struct RebindingCounters {
209    /// The number of times the state was entered.
210    pub(crate) entered: Counter,
211    /// Counters relating to sending and receiving messages.
212    pub(crate) messaging: MessagingRelatedCounters,
213    /// Counters for each error that could cause a client to reject a message
214    /// while receiving in the Rebinding state.
215    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
216    /// Counter for each time the client received a NAK message while in the
217    /// Rebinding state.
218    pub(crate) recv_nak: Counter,
219}
220
221impl RebindingCounters {
222    fn record(&self, inspector: &mut impl Inspector) {
223        let Self { entered, messaging, recv_error, recv_nak } = self;
224        inspector.record_inspectable_value("Entered", entered);
225        messaging.record(inspector);
226        recv_error.record(inspector);
227        inspector.record_inspectable_value("RecvNak", recv_nak);
228    }
229}
230
231/// Counters for the WaitingToRestart state.
232#[derive(Debug, Default)]
233pub(crate) struct WaitingToRestartCounters {
234    /// The number of times the state was entered.
235    pub(crate) entered: Counter,
236}
237
238impl WaitingToRestartCounters {
239    fn record(&self, inspector: &mut impl Inspector) {
240        let Self { entered } = self;
241        inspector.record_inspectable_value("Entered", entered);
242    }
243}
244
245/// Debugging counters for the core state machine.
246#[derive(Default, Debug)]
247pub struct Counters {
248    pub(crate) init: InitCounters,
249    pub(crate) selecting: SelectingCounters,
250    pub(crate) requesting: RequestingCounters,
251    pub(crate) bound: BoundCounters,
252    pub(crate) renewing: RenewingCounters,
253    pub(crate) rebinding: RebindingCounters,
254    pub(crate) waiting_to_restart: WaitingToRestartCounters,
255}
256
257impl Counters {
258    /// Records the counters in the given [`Inspector`].
259    pub fn record(&self, inspector: &mut impl Inspector) {
260        let Self { init, selecting, requesting, bound, renewing, rebinding, waiting_to_restart } =
261            self;
262        inspector.record_child("Init", |inspector| {
263            init.record(inspector);
264        });
265        inspector.record_child("Selecting", |inspector| {
266            selecting.record(inspector);
267        });
268        inspector.record_child("Requesting", |inspector| {
269            requesting.record(inspector);
270        });
271        inspector.record_child("Bound", |inspector| {
272            bound.record(inspector);
273        });
274        inspector.record_child("Renewing", |inspector| {
275            renewing.record(inspector);
276        });
277        inspector.record_child("Rebinding", |inspector| {
278            rebinding.record(inspector);
279        });
280        inspector.record_child("WaitingToRestart", |inspector| {
281            waiting_to_restart.record(inspector);
282        });
283    }
284}