wlan_telemetry/processors/
toggle_events.rs1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use fuchsia_inspect::Node as InspectNode;
8use fuchsia_inspect_contrib::inspect_log;
9use fuchsia_inspect_contrib::nodes::BoundedListNode;
10use {fuchsia_async as fasync, wlan_legacy_metrics_registry as metrics, zx};
11
12pub const INSPECT_TOGGLE_EVENTS_LIMIT: usize = 20;
13const TIME_QUICK_TOGGLE_WIFI: zx::BootDuration = zx::BootDuration::from_seconds(5);
14
15#[derive(Debug, PartialEq)]
16pub enum ClientConnectionsToggleEvent {
17 Enabled,
18 Disabled,
19}
20
21pub struct ToggleLogger {
22 toggle_inspect_node: BoundedListNode,
23 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
24 current_state: Option<ClientConnectionsToggleEvent>,
27 time_started: Option<fasync::BootInstant>,
29 time_stopped: Option<fasync::BootInstant>,
32}
33
34impl ToggleLogger {
35 pub fn new(
36 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
37 inspect_node: &InspectNode,
38 ) -> Self {
39 let toggle_events = inspect_node.create_child("client_connections_toggle_events");
41 let toggle_inspect_node = BoundedListNode::new(toggle_events, INSPECT_TOGGLE_EVENTS_LIMIT);
42 let current_state = None;
43 let time_started = None;
44 let time_stopped = None;
45
46 Self { toggle_inspect_node, cobalt_proxy, current_state, time_started, time_stopped }
47 }
48
49 pub async fn log_toggle_event(&mut self, event_type: ClientConnectionsToggleEvent) {
50 inspect_log!(self.toggle_inspect_node, {
52 event_type: std::format!("{:?}", event_type)
53 });
54
55 let mut metric_events = vec![];
56 let now = fasync::BootInstant::now();
57 match &event_type {
58 ClientConnectionsToggleEvent::Enabled => {
59 if self.current_state != Some(ClientConnectionsToggleEvent::Enabled) {
61 self.time_started = Some(now);
62
63 metric_events.push(MetricEvent {
64 metric_id: metrics::CLIENT_CONNECTION_ENABLED_OCCURRENCE_METRIC_ID,
65 event_codes: vec![],
66 payload: MetricEventPayload::Count(1),
67 });
68 }
69
70 if self.current_state == Some(ClientConnectionsToggleEvent::Disabled) {
73 if let Some(time_stopped) = self.time_stopped {
74 if now - time_stopped < TIME_QUICK_TOGGLE_WIFI {
75 metric_events.push(MetricEvent {
76 metric_id: metrics::CLIENT_CONNECTIONS_STOP_AND_START_METRIC_ID,
77 event_codes: vec![],
78 payload: MetricEventPayload::Count(1),
79 });
80 }
81 }
82 }
83 }
84 ClientConnectionsToggleEvent::Disabled => {
85 if self.current_state == Some(ClientConnectionsToggleEvent::Enabled) {
87 self.time_stopped = Some(now);
88
89 if let Some(time_started) = self.time_started {
90 let duration = now - time_started;
91 metric_events.push(MetricEvent {
92 metric_id: metrics::CLIENT_CONNECTION_ENABLED_DURATION_METRIC_ID,
93 event_codes: vec![],
94 payload: MetricEventPayload::IntegerValue(duration.into_millis()),
95 });
96 }
97 }
98 }
99 }
100 self.current_state = Some(event_type);
101
102 if !metric_events.is_empty() {
103 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "log_toggle_events");
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::testing::{setup_test, TestHelper};
112 use diagnostics_assertions::{assert_data_tree, AnyNumericProperty};
113 use futures::task::Poll;
114 use std::pin::pin;
115 use wlan_common::assert_variant;
116
117 #[fuchsia::test]
118 fn test_toggle_is_recorded_to_inspect() {
119 let mut test_helper = setup_test();
120 let node = test_helper.create_inspect_node("wlan_mock_node");
121 let mut toggle_logger = ToggleLogger::new(test_helper.cobalt_proxy.clone(), &node);
122
123 let event = ClientConnectionsToggleEvent::Enabled;
124 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
125
126 let event = ClientConnectionsToggleEvent::Disabled;
127 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
128
129 let event = ClientConnectionsToggleEvent::Enabled;
130 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
131
132 assert_data_tree!(test_helper.inspector, root: contains {
133 wlan_mock_node: {
134 client_connections_toggle_events: {
135 "0": {
136 "event_type": "Enabled",
137 "@time": AnyNumericProperty
138 },
139 "1": {
140 "event_type": "Disabled",
141 "@time": AnyNumericProperty
142 },
143 "2": {
144 "event_type": "Enabled",
145 "@time": AnyNumericProperty
146 },
147 }
148 }
149 });
150 }
151
152 fn run_log_toggle_event(
155 test_helper: &mut TestHelper,
156 toggle_logger: &mut ToggleLogger,
157 event: ClientConnectionsToggleEvent,
158 ) {
159 let mut test_fut = pin!(toggle_logger.log_toggle_event(event));
160 assert_eq!(
161 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
162 Poll::Ready(())
163 );
164 }
165
166 #[fuchsia::test]
167 fn test_quick_toggle_metric_is_recorded() {
168 let mut test_helper = setup_test();
169 let inspect_node = test_helper.create_inspect_node("test_stats");
170 let mut toggle_logger = ToggleLogger::new(test_helper.cobalt_proxy.clone(), &inspect_node);
171
172 let mut test_time = fasync::MonotonicInstant::from_nanos(123);
174 let event = ClientConnectionsToggleEvent::Enabled;
175 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
176
177 test_time += fasync::MonotonicDuration::from_minutes(40);
179 test_helper.exec.set_fake_time(test_time);
180 let event = ClientConnectionsToggleEvent::Disabled;
181 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
182
183 test_time += fasync::MonotonicDuration::from_seconds(1);
184 test_helper.exec.set_fake_time(test_time);
185 let event = ClientConnectionsToggleEvent::Enabled;
186 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
187
188 let logged_metrics =
190 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTIONS_STOP_AND_START_METRIC_ID);
191 assert_variant!(&logged_metrics[..], [metric] => {
192 let expected_metric = fidl_fuchsia_metrics::MetricEvent {
193 metric_id: metrics::CLIENT_CONNECTIONS_STOP_AND_START_METRIC_ID,
194 event_codes: vec![],
195 payload: fidl_fuchsia_metrics::MetricEventPayload::Count(1),
196 };
197 assert_eq!(metric, &expected_metric);
198 });
199 }
200
201 #[fuchsia::test]
202 fn test_quick_toggle_no_metric_is_recorded_if_not_quick() {
203 let mut test_helper = setup_test();
204 let inspect_node = test_helper.create_inspect_node("test_stats");
205 let mut toggle_logger = ToggleLogger::new(test_helper.cobalt_proxy.clone(), &inspect_node);
206
207 let mut test_time = fasync::MonotonicInstant::from_nanos(123);
209 let event = ClientConnectionsToggleEvent::Enabled;
210 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
211
212 test_time += fasync::MonotonicDuration::from_minutes(20);
214 test_helper.exec.set_fake_time(test_time);
215 let event = ClientConnectionsToggleEvent::Disabled;
216 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
217
218 test_time += fasync::MonotonicDuration::from_minutes(30);
219 test_helper.exec.set_fake_time(test_time);
220 let event = ClientConnectionsToggleEvent::Enabled;
221 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
222
223 let logged_metrics =
226 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTIONS_STOP_AND_START_METRIC_ID);
227 assert!(logged_metrics.is_empty());
228 }
229
230 #[fuchsia::test]
231 fn test_quick_toggle_metric_second_disable_doesnt_update_time() {
232 let mut test_helper = setup_test();
235 let inspect_node = test_helper.create_inspect_node("test_stats");
236 let mut toggle_logger = ToggleLogger::new(test_helper.cobalt_proxy.clone(), &inspect_node);
237
238 let mut test_time = fasync::MonotonicInstant::from_nanos(123);
240 let event = ClientConnectionsToggleEvent::Enabled;
241 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
242
243 test_time += fasync::MonotonicDuration::from_minutes(40);
245 test_helper.exec.set_fake_time(test_time);
246 let event = ClientConnectionsToggleEvent::Disabled;
247 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
248
249 test_time += fasync::MonotonicDuration::from_minutes(30);
250 test_helper.exec.set_fake_time(test_time);
251 let event = ClientConnectionsToggleEvent::Disabled;
252 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
253
254 test_time += fasync::MonotonicDuration::from_seconds(1);
256 test_helper.exec.set_fake_time(test_time);
257 let event = ClientConnectionsToggleEvent::Enabled;
258 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
259
260 let logged_metrics =
263 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTIONS_STOP_AND_START_METRIC_ID);
264 assert!(logged_metrics.is_empty());
265 }
266
267 #[fuchsia::test]
268 fn test_log_client_connection_enabled() {
269 let mut test_helper = setup_test();
270 let inspect_node = test_helper.create_inspect_node("test_stats");
271 let mut toggle_logger = ToggleLogger::new(test_helper.cobalt_proxy.clone(), &inspect_node);
272
273 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(10_000_000));
275 let event = ClientConnectionsToggleEvent::Enabled;
276 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
277
278 let metrics =
279 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTION_ENABLED_OCCURRENCE_METRIC_ID);
280 assert_eq!(metrics.len(), 1);
281 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
282
283 test_helper.clear_cobalt_events();
286 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(50_000_000));
287 let event = ClientConnectionsToggleEvent::Enabled;
288 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
289
290 let metrics =
291 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTION_ENABLED_OCCURRENCE_METRIC_ID);
292 assert!(metrics.is_empty());
293
294 test_helper.clear_cobalt_events();
297 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(100_000_000));
298 let event = ClientConnectionsToggleEvent::Disabled;
299 run_log_toggle_event(&mut test_helper, &mut toggle_logger, event);
300
301 let metrics =
302 test_helper.get_logged_metrics(metrics::CLIENT_CONNECTION_ENABLED_DURATION_METRIC_ID);
303 assert_eq!(metrics.len(), 1);
304 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(90));
305 }
306}