1use crate::util::cobalt_logger::log_cobalt_batch;
6use fidl_fuchsia_metrics::{MetricEvent, MetricEventPayload};
7use fuchsia_async::{self as fasync, TimeoutExt};
8use fuchsia_inspect_contrib::nodes::LruCacheNode;
9use fuchsia_inspect_derive::{InspectNode, Unit};
10use futures::TryFutureExt;
11use futures::lock::Mutex;
12use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_stats as fidl_stats};
13
14use log::{error, warn};
15use std::collections::HashMap;
16use std::sync::Arc;
17use std::sync::atomic::{AtomicI64, Ordering};
18use windowed_stats::experimental::inspect::{InspectSender, InspectedTimeMatrix};
19use windowed_stats::experimental::series::interpolation::{ConstantSample, LastSample};
20use windowed_stats::experimental::series::metadata::BitSetNode;
21use windowed_stats::experimental::series::statistic::{
22 ArithmeticMean, Last, LatchMax, Max, Min, PostAggregation, Sum, Union,
23};
24use windowed_stats::experimental::series::{SamplingProfile, TimeMatrix};
25use wlan_legacy_metrics_registry as metrics;
26
27const GET_IFACE_STATS_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(5);
29const GET_SIGNAL_REPORT_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(5);
30
31#[derive(Debug)]
32enum IfaceState {
33 NotAvailable,
34 Created { iface_id: u16, telemetry_proxy: Option<fidl_fuchsia_wlan_sme::TelemetryProxy> },
35}
36
37#[derive(Debug)]
38enum GetIfaceStatsError {
39 FutureTimeout,
40 FidlError,
41 ResponseError,
42}
43
44type CountersTimeSeriesMap = HashMap<u16, InspectedTimeMatrix<u64>>;
45type GaugesTimeSeriesMap = HashMap<u16, Vec<InspectedTimeMatrix<i64>>>;
46
47pub struct ClientIfaceCountersLogger<S> {
48 iface_state: Arc<Mutex<IfaceState>>,
49 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
50 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
51 inspect_metadata_node: Mutex<InspectMetadataNode>,
52 time_series_stats: IfaceCountersTimeSeries,
53 signal_time_series: SignalTimeSeries,
54 driver_counters_time_matrix_client: S,
55 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
56 driver_gauges_time_matrix_client: S,
57 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
58 prev_connection_stats: Arc<Mutex<Option<fidl_stats::ConnectionStats>>>,
59 boot_mono_drift: AtomicI64,
60}
61
62impl<S: InspectSender> ClientIfaceCountersLogger<S> {
63 pub fn new(
64 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
65 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
66 inspect_metadata_node: &InspectNode,
67 inspect_metadata_path: &str,
68 time_matrix_client: &S,
69 driver_counters_time_matrix_client: S,
70 driver_gauges_time_matrix_client: S,
71 ) -> Self {
72 Self {
73 iface_state: Arc::new(Mutex::new(IfaceState::NotAvailable)),
74 cobalt_proxy,
75 monitor_svc_proxy,
76 inspect_metadata_node: Mutex::new(InspectMetadataNode::new(inspect_metadata_node)),
77 time_series_stats: IfaceCountersTimeSeries::new(time_matrix_client),
78 signal_time_series: SignalTimeSeries::new(time_matrix_client, inspect_metadata_path),
79 driver_counters_time_matrix_client,
80 driver_counters_time_series: Arc::new(Mutex::new(HashMap::new())),
81 driver_gauges_time_matrix_client,
82 driver_gauges_time_series: Arc::new(Mutex::new(HashMap::new())),
83 prev_connection_stats: Arc::new(Mutex::new(None)),
84 boot_mono_drift: AtomicI64::new(0),
85 }
86 }
87
88 pub async fn handle_iface_created(&self, iface_id: u16) {
89 let (proxy, server) = fidl::endpoints::create_proxy();
90 let telemetry_proxy = match self.monitor_svc_proxy.get_sme_telemetry(iface_id, server).await
91 {
92 Ok(Ok(())) => {
93 let (inspect_counter_configs, inspect_gauge_configs) = match proxy
94 .query_telemetry_support()
95 .await
96 {
97 Ok(Ok(support)) => {
98 (support.inspect_counter_configs, support.inspect_gauge_configs)
99 }
100 Ok(Err(code)) => {
101 warn!(
102 "Failed to query telemetry support with status code {}. No driver-specific stats will be captured",
103 code
104 );
105 (None, None)
106 }
107 Err(e) => {
108 error!(
109 "Failed to query telemetry support with error {}. No driver-specific stats will be captured",
110 e
111 );
112 (None, None)
113 }
114 };
115 if let Some(inspect_counter_configs) = &inspect_counter_configs {
116 let mut driver_counters_time_series =
117 self.driver_counters_time_series.lock().await;
118 for inspect_counter_config in inspect_counter_configs {
119 if let fidl_stats::InspectCounterConfig {
120 counter_id: Some(counter_id),
121 counter_name: Some(counter_name),
122 ..
123 } = inspect_counter_config
124 {
125 let _time_matrix_ref = driver_counters_time_series
126 .entry(*counter_id)
127 .or_insert_with(|| {
128 self.driver_counters_time_matrix_client.inspect_time_matrix(
129 counter_name,
130 TimeMatrix::<LatchMax<u64>, LastSample>::new(
131 SamplingProfile::balanced(),
132 LastSample::or(0),
133 ),
134 )
135 });
136 }
137 }
138 }
139 if let Some(inspect_gauge_configs) = &inspect_gauge_configs {
140 let mut driver_gauges_time_series = self.driver_gauges_time_series.lock().await;
141 for inspect_gauge_config in inspect_gauge_configs {
142 if let fidl_stats::InspectGaugeConfig {
143 gauge_id: Some(gauge_id),
144 gauge_name: Some(gauge_name),
145 statistics: Some(statistics),
146 ..
147 } = inspect_gauge_config
148 {
149 for statistic in statistics {
150 if let Some(time_matrix) = create_time_series_for_gauge(
151 &self.driver_gauges_time_matrix_client,
152 gauge_name,
153 statistic,
154 ) {
155 let time_matrices =
156 driver_gauges_time_series.entry(*gauge_id).or_default();
157 time_matrices.push(time_matrix);
158 }
159 }
160 }
161 }
162 }
163 Some(proxy)
164 }
165 Ok(Err(e)) => {
166 error!(
167 "Request for SME telemetry for iface {} completed with error {}. No telemetry will be captured.",
168 iface_id, e
169 );
170 None
171 }
172 Err(e) => {
173 error!(
174 "Failed to request SME telemetry for iface {} with error {}. No telemetry will be captured.",
175 iface_id, e
176 );
177 None
178 }
179 };
180 *self.iface_state.lock().await = IfaceState::Created { iface_id, telemetry_proxy }
181 }
182
183 pub async fn handle_iface_destroyed(&self, iface_id: u16) {
184 let destroyed = matches!(*self.iface_state.lock().await, IfaceState::Created { iface_id: existing_iface_id, .. } if iface_id == existing_iface_id);
185 if destroyed {
186 *self.iface_state.lock().await = IfaceState::NotAvailable;
187 }
188 }
189
190 pub async fn handle_periodic_telemetry(&self) {
191 let boot_now = fasync::BootInstant::now();
192 let mono_now = fasync::MonotonicInstant::now();
193 let boot_mono_drift = boot_now.into_nanos() - mono_now.into_nanos();
194 let prev_boot_mono_drift = self.boot_mono_drift.swap(boot_mono_drift, Ordering::SeqCst);
195 let suspended_during_last_period = boot_mono_drift > prev_boot_mono_drift;
198 match &*self.iface_state.lock().await {
199 IfaceState::NotAvailable => (),
200 IfaceState::Created { telemetry_proxy, .. } => {
201 if let Some(telemetry_proxy) = &telemetry_proxy {
202 let result = telemetry_proxy
203 .get_iface_stats()
204 .map_err(|_e| GetIfaceStatsError::FidlError)
205 .map_ok(|response| response.map_err(|_e| GetIfaceStatsError::ResponseError))
206 .on_timeout(GET_IFACE_STATS_TIMEOUT, || {
207 Err(GetIfaceStatsError::FutureTimeout)
208 })
209 .await;
210
211 match result {
212 Ok(Ok(stats)) => {
213 self.log_iface_stats_inspect(&stats).await;
214 self.log_iface_stats_cobalt(stats, suspended_during_last_period).await;
215 }
216 Ok(Err(e)) | Err(e) => {
217 warn!("Failed to get interface stats: {:?}", e);
218 self.log_get_iface_stats_failure_cobalt(&e).await;
219 }
220 }
221
222 match telemetry_proxy
223 .get_signal_report()
224 .on_timeout(GET_SIGNAL_REPORT_TIMEOUT, || {
225 Ok(Err(zx::Status::TIMED_OUT.into_raw()))
226 })
227 .await
228 {
229 Ok(Ok(report)) => {
230 self.log_signal_report_inspect(&report).await;
231 }
232 error => {
233 warn!("Failed to get signal report: {:?}", error);
234 }
235 }
236 }
237 }
238 }
239 }
240
241 async fn log_iface_stats_inspect(&self, stats: &fidl_stats::IfaceStats) {
242 if let Some(counters) = &stats.driver_specific_counters {
244 let time_series = Arc::clone(&self.driver_counters_time_series);
245 log_driver_specific_counters(&counters[..], time_series).await;
246 }
247 if let Some(gauges) = &stats.driver_specific_gauges {
249 let time_series = Arc::clone(&self.driver_gauges_time_series);
250 log_driver_specific_gauges(&gauges[..], time_series).await;
251 }
252 log_connection_stats_inspect(
253 stats,
254 &self.time_series_stats,
255 Arc::clone(&self.driver_counters_time_series),
256 Arc::clone(&self.driver_gauges_time_series),
257 )
258 .await;
259 }
260
261 async fn log_iface_stats_cobalt(
262 &self,
263 stats: fidl_stats::IfaceStats,
264 suspended_during_last_period: bool,
265 ) {
266 let mut prev_connection_stats = self.prev_connection_stats.lock().await;
267 if !suspended_during_last_period
269 && let (Some(prev_connection_stats), Some(current_connection_stats)) =
270 (prev_connection_stats.as_ref(), stats.connection_stats.as_ref())
271 {
272 match (prev_connection_stats.connection_id, current_connection_stats.connection_id) {
273 (Some(prev_id), Some(current_id)) if prev_id == current_id => {
274 diff_and_log_connection_stats_cobalt(
275 &self.cobalt_proxy,
276 prev_connection_stats,
277 current_connection_stats,
278 )
279 .await;
280 }
281 _ => (),
282 }
283 }
284 *prev_connection_stats = stats.connection_stats;
285 }
286
287 async fn log_get_iface_stats_failure_cobalt(&self, error: &GetIfaceStatsError) {
288 let mut metric_events = vec![MetricEvent {
289 metric_id: metrics::GET_IFACE_STATS_FAILURE_METRIC_ID,
290 event_codes: vec![],
291 payload: MetricEventPayload::Count(1),
292 }];
293 match error {
294 GetIfaceStatsError::FutureTimeout => {
295 metric_events.push(MetricEvent {
296 metric_id: metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID,
297 event_codes: vec![],
298 payload: MetricEventPayload::Count(1),
299 });
300 }
301 GetIfaceStatsError::FidlError | GetIfaceStatsError::ResponseError => {
302 metric_events.push(MetricEvent {
303 metric_id: metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID,
304 event_codes: vec![],
305 payload: MetricEventPayload::Count(1),
306 });
307 }
308 }
309 log_cobalt_batch!(self.cobalt_proxy, &metric_events, "log_get_iface_stats_failure_cobalt");
310 }
311
312 async fn log_signal_report_inspect(&self, report: &fidl_stats::SignalReport) {
313 if let Some(report) = &report.connection_signal_report {
314 if let Some(channel) = report.channel {
315 let channel = InspectWlanChannel::from(channel);
316 let channel_id =
317 self.inspect_metadata_node.lock().await.wlan_channels.insert(channel) as u64;
318 self.signal_time_series.wlan_channels.fold_or_log_error(1 << channel_id);
319 }
320 if let Some(tx_rate_500kbps) = report.tx_rate_500kbps {
321 self.signal_time_series.tx_rate_500kbps.fold_or_log_error(tx_rate_500kbps as u64);
322 }
323 if let Some(rssi) = report.rssi_dbm {
324 self.signal_time_series.rssi.fold_or_log_error(rssi as i64);
325 }
326 if let Some(snr) = report.snr_db {
327 self.signal_time_series.snr.fold_or_log_error(snr as i64);
328 }
329 }
330 }
331}
332
333fn create_time_series_for_gauge<S: InspectSender>(
334 time_matrix_client: &S,
335 gauge_name: &str,
336 statistic: &fidl_stats::GaugeStatistic,
337) -> Option<InspectedTimeMatrix<i64>> {
338 match statistic {
339 fidl_stats::GaugeStatistic::Min => Some(time_matrix_client.inspect_time_matrix(
340 format!("{gauge_name}.min"),
341 TimeMatrix::<Min<i64>, ConstantSample>::new(
342 SamplingProfile::balanced(),
343 ConstantSample::default(),
344 ),
345 )),
346 fidl_stats::GaugeStatistic::Max => Some(time_matrix_client.inspect_time_matrix(
347 format!("{gauge_name}.max"),
348 TimeMatrix::<Max<i64>, ConstantSample>::new(
349 SamplingProfile::balanced(),
350 ConstantSample::default(),
351 ),
352 )),
353 fidl_stats::GaugeStatistic::Sum => Some(time_matrix_client.inspect_time_matrix(
354 format!("{gauge_name}.sum"),
355 TimeMatrix::<Sum<i64>, ConstantSample>::new(
356 SamplingProfile::balanced(),
357 ConstantSample::default(),
358 ),
359 )),
360 fidl_stats::GaugeStatistic::Last => Some(time_matrix_client.inspect_time_matrix(
361 format!("{gauge_name}.last"),
362 TimeMatrix::<Last<i64>, ConstantSample>::new(
363 SamplingProfile::balanced(),
364 ConstantSample::default(),
365 ),
366 )),
367 fidl_stats::GaugeStatistic::Mean => Some(time_matrix_client.inspect_time_matrix(
368 format!("{gauge_name}.mean"),
369 TimeMatrix::<ArithmeticMean<i64>, ConstantSample>::new(
370 SamplingProfile::balanced(),
371 ConstantSample::default(),
372 ),
373 )),
374 _ => None,
375 }
376}
377
378async fn log_connection_stats_inspect(
379 stats: &fidl_stats::IfaceStats,
380 time_series_stats: &IfaceCountersTimeSeries,
381 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
382 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
383) {
384 let connection_stats = match &stats.connection_stats {
385 Some(counters) => counters,
386 None => return,
387 };
388
389 match &connection_stats.connection_id {
391 Some(_connection_id) => (),
392 _ => {
393 warn!("connection_id is not present, no connection counters will be logged");
394 return;
395 }
396 }
397
398 if let fidl_stats::ConnectionStats {
399 rx_unicast_total: Some(rx_unicast_total),
400 rx_unicast_drop: Some(rx_unicast_drop),
401 ..
402 } = connection_stats
403 {
404 time_series_stats.log_rx_unicast_total(*rx_unicast_total);
405 time_series_stats.log_rx_unicast_drop(*rx_unicast_drop);
406 }
407
408 if let fidl_stats::ConnectionStats {
409 tx_total: Some(tx_total), tx_drop: Some(tx_drop), ..
410 } = connection_stats
411 {
412 time_series_stats.log_tx_total(*tx_total);
413 time_series_stats.log_tx_drop(*tx_drop);
414 }
415
416 if let Some(counters) = &connection_stats.driver_specific_counters {
418 log_driver_specific_counters(&counters[..], driver_counters_time_series).await;
419 }
420 if let Some(gauges) = &connection_stats.driver_specific_gauges {
422 log_driver_specific_gauges(&gauges[..], driver_gauges_time_series).await;
423 }
424}
425
426async fn log_driver_specific_counters(
427 driver_specific_counters: &[fidl_stats::UnnamedCounter],
428 driver_counters_time_series: Arc<Mutex<CountersTimeSeriesMap>>,
429) {
430 let time_series_map = driver_counters_time_series.lock().await;
431 for counter in driver_specific_counters {
432 if let Some(ts) = time_series_map.get(&counter.id) {
433 ts.fold_or_log_error(counter.count);
434 }
435 }
436}
437
438async fn log_driver_specific_gauges(
439 driver_specific_gauges: &[fidl_stats::UnnamedGauge],
440 driver_gauges_time_series: Arc<Mutex<GaugesTimeSeriesMap>>,
441) {
442 let time_series_map = driver_gauges_time_series.lock().await;
443 for gauge in driver_specific_gauges {
444 if let Some(time_matrices) = time_series_map.get(&gauge.id) {
445 for ts in time_matrices {
446 ts.fold_or_log_error(gauge.value);
447 }
448 }
449 }
450}
451
452async fn diff_and_log_connection_stats_cobalt(
453 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
454 prev: &fidl_stats::ConnectionStats,
455 current: &fidl_stats::ConnectionStats,
456) {
457 match (current.rx_unicast_total, prev.rx_unicast_total) {
461 (Some(current), Some(prev)) if current < prev => return,
462 _ => (),
463 }
464 match (current.rx_unicast_drop, prev.rx_unicast_drop) {
465 (Some(current), Some(prev)) if current < prev => return,
466 _ => (),
467 }
468 match (current.tx_total, prev.tx_total) {
469 (Some(current), Some(prev)) if current < prev => return,
470 _ => (),
471 }
472 match (current.tx_drop, prev.tx_drop) {
473 (Some(current), Some(prev)) if current < prev => return,
474 _ => (),
475 }
476
477 diff_and_log_rx_cobalt(cobalt_proxy, prev, current).await;
478 diff_and_log_tx_cobalt(cobalt_proxy, prev, current).await;
479}
480
481async fn diff_and_log_rx_cobalt(
482 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
483 prev: &fidl_stats::ConnectionStats,
484 current: &fidl_stats::ConnectionStats,
485) {
486 let mut metric_events = vec![];
487
488 let (current_rx_unicast_total, prev_rx_unicast_total) =
489 match (current.rx_unicast_total, prev.rx_unicast_total) {
490 (Some(current), Some(prev)) => (current, prev),
491 _ => return,
492 };
493 let (current_rx_unicast_drop, prev_rx_unicast_drop) =
494 match (current.rx_unicast_drop, prev.rx_unicast_drop) {
495 (Some(current), Some(prev)) => (current, prev),
496 _ => return,
497 };
498
499 let rx_total = match current_rx_unicast_total.checked_sub(prev_rx_unicast_total) {
500 Some(diff) => diff,
501 _ => return,
502 };
503 let rx_drop = match current_rx_unicast_drop.checked_sub(prev_rx_unicast_drop) {
504 Some(diff) => diff,
505 _ => return,
506 };
507 let rx_drop_rate = if rx_total > 0 { rx_drop as f64 / rx_total as f64 } else { 0f64 };
508
509 metric_events.push(MetricEvent {
510 metric_id: metrics::BAD_RX_RATE_METRIC_ID,
511 event_codes: vec![],
512 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(rx_drop_rate)),
513 });
514 metric_events.push(MetricEvent {
515 metric_id: metrics::RX_UNICAST_PACKETS_METRIC_ID,
516 event_codes: vec![],
517 payload: MetricEventPayload::IntegerValue(rx_total as i64),
518 });
519
520 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_rx_cobalt",);
521}
522
523async fn diff_and_log_tx_cobalt(
524 cobalt_proxy: &fidl_fuchsia_metrics::MetricEventLoggerProxy,
525 prev: &fidl_stats::ConnectionStats,
526 current: &fidl_stats::ConnectionStats,
527) {
528 let mut metric_events = vec![];
529
530 let (current_tx_total, prev_tx_total) = match (current.tx_total, prev.tx_total) {
531 (Some(current), Some(prev)) => (current, prev),
532 _ => return,
533 };
534 let (current_tx_drop, prev_tx_drop) = match (current.tx_drop, prev.tx_drop) {
535 (Some(current), Some(prev)) => (current, prev),
536 _ => return,
537 };
538
539 let tx_total = match current_tx_total.checked_sub(prev_tx_total) {
540 Some(diff) => diff,
541 _ => return,
542 };
543 let tx_drop = match current_tx_drop.checked_sub(prev_tx_drop) {
544 Some(diff) => diff,
545 _ => return,
546 };
547 let tx_drop_rate = if tx_total > 0 { tx_drop as f64 / tx_total as f64 } else { 0f64 };
548
549 metric_events.push(MetricEvent {
550 metric_id: metrics::BAD_TX_RATE_METRIC_ID,
551 event_codes: vec![],
552 payload: MetricEventPayload::IntegerValue(float_to_ten_thousandth(tx_drop_rate)),
553 });
554
555 log_cobalt_batch!(cobalt_proxy, &metric_events, "diff_and_log_tx_cobalt",);
556}
557
558fn float_to_ten_thousandth(value: f64) -> i64 {
561 (value * 10000f64) as i64
562}
563
564#[derive(Debug, Clone)]
565struct IfaceCountersTimeSeries {
566 rx_unicast_total: InspectedTimeMatrix<u64>,
567 rx_unicast_drop: InspectedTimeMatrix<u64>,
568 tx_total: InspectedTimeMatrix<u64>,
569 tx_drop: InspectedTimeMatrix<u64>,
570}
571
572impl IfaceCountersTimeSeries {
573 pub fn new<S: InspectSender>(client: &S) -> Self {
574 let rx_unicast_total = client.inspect_time_matrix(
575 "rx_unicast_total",
576 TimeMatrix::<LatchMax<u64>, LastSample>::new(
577 SamplingProfile::balanced(),
578 LastSample::or(0),
579 ),
580 );
581 let rx_unicast_drop = client.inspect_time_matrix(
582 "rx_unicast_drop",
583 TimeMatrix::<LatchMax<u64>, LastSample>::new(
584 SamplingProfile::balanced(),
585 LastSample::or(0),
586 ),
587 );
588 let tx_total = client.inspect_time_matrix(
589 "tx_total",
590 TimeMatrix::<LatchMax<u64>, LastSample>::new(
591 SamplingProfile::balanced(),
592 LastSample::or(0),
593 ),
594 );
595 let tx_drop = client.inspect_time_matrix(
596 "tx_drop",
597 TimeMatrix::<LatchMax<u64>, LastSample>::new(
598 SamplingProfile::balanced(),
599 LastSample::or(0),
600 ),
601 );
602 Self { rx_unicast_total, rx_unicast_drop, tx_total, tx_drop }
603 }
604
605 fn log_rx_unicast_total(&self, data: u64) {
606 self.rx_unicast_total.fold_or_log_error(data);
607 }
608
609 fn log_rx_unicast_drop(&self, data: u64) {
610 self.rx_unicast_drop.fold_or_log_error(data);
611 }
612
613 fn log_tx_total(&self, data: u64) {
614 self.tx_total.fold_or_log_error(data);
615 }
616
617 fn log_tx_drop(&self, data: u64) {
618 self.tx_drop.fold_or_log_error(data);
619 }
620}
621
622#[derive(Debug, Clone)]
623struct SignalTimeSeries {
624 wlan_channels: InspectedTimeMatrix<u64>,
625 tx_rate_500kbps: InspectedTimeMatrix<u64>,
626 rssi: InspectedTimeMatrix<i64>,
627 snr: InspectedTimeMatrix<i64>,
628}
629
630impl SignalTimeSeries {
631 pub fn new<S: InspectSender>(client: &S, inspect_metadata_path: &str) -> Self {
632 let wlan_channels = client.inspect_time_matrix_with_metadata(
633 "wlan_channels",
634 TimeMatrix::<Union<u64>, LastSample>::new(
635 SamplingProfile::highly_granular(),
636 LastSample::or(0),
637 ),
638 BitSetNode::from_path(format!(
639 "{}/{}",
640 inspect_metadata_path,
641 InspectMetadataNode::WLAN_CHANNELS,
642 )),
643 );
644 let tx_rate_500kbps = client.inspect_time_matrix(
645 "tx_rate_500kbps",
646 TimeMatrix::<_, ConstantSample>::with_statistic(
647 SamplingProfile::default(),
648 ConstantSample::default(),
649 PostAggregation::<ArithmeticMean<u64>, _>::from_transform(|aggregation: f32| {
650 aggregation.ceil() as u64
651 }),
652 ),
653 );
654 let rssi = client.inspect_time_matrix(
655 "rssi",
656 TimeMatrix::<_, ConstantSample>::with_statistic(
657 SamplingProfile::default(),
658 ConstantSample::default(),
659 PostAggregation::<ArithmeticMean<i64>, _>::from_transform(|aggregation: f32| {
660 aggregation.ceil() as i64
661 }),
662 ),
663 );
664 let snr = client.inspect_time_matrix(
665 "snr",
666 TimeMatrix::<_, ConstantSample>::with_statistic(
667 SamplingProfile::default(),
668 ConstantSample::default(),
669 PostAggregation::<ArithmeticMean<i64>, _>::from_transform(|aggregation: f32| {
670 aggregation.ceil() as i64
671 }),
672 ),
673 );
674 Self { wlan_channels, tx_rate_500kbps, rssi, snr }
675 }
676}
677
678#[derive(PartialEq, Eq, Unit, Hash)]
679struct InspectWlanChannel {
680 primary: u8,
681 cbw: String,
682 secondary80: u8,
683}
684
685impl From<fidl_ieee80211::WlanChannel> for InspectWlanChannel {
686 fn from(channel: fidl_ieee80211::WlanChannel) -> Self {
687 Self {
688 primary: channel.primary,
689 cbw: format!("{:?}", channel.cbw),
690 secondary80: channel.secondary80,
691 }
692 }
693}
694
695struct InspectMetadataNode {
696 wlan_channels: LruCacheNode<InspectWlanChannel>,
697}
698
699impl InspectMetadataNode {
700 const WLAN_CHANNELS: &'static str = "wlan_channels";
701 const WLAN_CHANNELS_ID_LIMIT: usize = 32;
702
703 fn new(inspect_node: &InspectNode) -> Self {
704 let wlan_channels = inspect_node.create_child(Self::WLAN_CHANNELS);
705 Self { wlan_channels: LruCacheNode::new(wlan_channels, Self::WLAN_CHANNELS_ID_LIMIT) }
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 use super::*;
712 use crate::testing::*;
713 use assert_matches::assert_matches;
714 use diagnostics_assertions::{AnyNumericProperty, assert_data_tree};
715 use futures::TryStreamExt;
716 use std::pin::pin;
717 use std::task::Poll;
718 use test_case::test_case;
719 use windowed_stats::experimental::clock::Timed;
720 use windowed_stats::experimental::testing::{MockTimeMatrixClient, TimeMatrixCall};
721
722 const IFACE_ID: u16 = 66;
723
724 #[fuchsia::test]
725 fn test_handle_iface_created() {
726 let mut test_helper = setup_test();
727 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
728 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
729 let logger = ClientIfaceCountersLogger::new(
730 test_helper.cobalt_proxy.clone(),
731 test_helper.monitor_svc_proxy.clone(),
732 &test_helper.inspect_metadata_node,
733 &test_helper.inspect_metadata_path,
734 &test_helper.mock_time_matrix_client,
735 driver_counters_mock_matrix_client.clone(),
736 driver_gauges_mock_matrix_client.clone(),
737 );
738
739 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
740 assert_eq!(
741 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
742 Poll::Pending
743 );
744
745 let mocked_inspect_counter_configs = vec![fidl_stats::InspectCounterConfig {
746 counter_id: Some(1),
747 counter_name: Some("foo_counter".to_string()),
748 ..Default::default()
749 }];
750 let telemetry_support = fidl_stats::TelemetrySupport {
751 inspect_counter_configs: Some(mocked_inspect_counter_configs),
752 ..Default::default()
753 };
754 assert_eq!(
755 test_helper.run_and_respond_query_telemetry_support(
756 &mut handle_iface_created_fut,
757 Ok(&telemetry_support)
758 ),
759 Poll::Ready(())
760 );
761
762 assert_matches!(logger.iface_state.try_lock().as_deref(), Some(IfaceState::Created { .. }));
763 let driver_counters_time_series = logger.driver_counters_time_series.try_lock().unwrap();
764 assert_eq!(driver_counters_time_series.keys().copied().collect::<Vec<u16>>(), vec![1u16],);
765 }
766
767 #[fuchsia::test]
768 fn test_handle_periodic_telemetry_connection_stats() {
769 let mut test_helper = setup_test();
770 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
771 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
772 let logger = ClientIfaceCountersLogger::new(
773 test_helper.cobalt_proxy.clone(),
774 test_helper.monitor_svc_proxy.clone(),
775 &test_helper.inspect_metadata_node,
776 &test_helper.inspect_metadata_path,
777 &test_helper.mock_time_matrix_client,
778 driver_counters_mock_matrix_client.clone(),
779 driver_gauges_mock_matrix_client.clone(),
780 );
781
782 handle_iface_created(&mut test_helper, &logger);
784
785 let mut test_fut = pin!(logger.handle_periodic_telemetry());
786 let iface_stats = fidl_stats::IfaceStats {
787 connection_stats: Some(fidl_stats::ConnectionStats {
788 connection_id: Some(1),
789 rx_unicast_total: Some(100),
790 rx_unicast_drop: Some(5),
791 rx_multicast: Some(30),
792 tx_total: Some(50),
793 tx_drop: Some(2),
794 ..Default::default()
795 }),
796 ..Default::default()
797 };
798 assert_eq!(
799 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
800 Poll::Pending,
801 );
802 let signal_report = Default::default();
803 assert_eq!(
804 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
805 Poll::Ready(())
806 );
807
808 let mut time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
809 assert_eq!(
810 &time_matrix_calls.drain::<u64>("rx_unicast_total")[..],
811 &[TimeMatrixCall::Fold(Timed::now(100u64))]
812 );
813 assert_eq!(
814 &time_matrix_calls.drain::<u64>("rx_unicast_drop")[..],
815 &[TimeMatrixCall::Fold(Timed::now(5u64))]
816 );
817 assert_eq!(
818 &time_matrix_calls.drain::<u64>("tx_total")[..],
819 &[TimeMatrixCall::Fold(Timed::now(50u64))]
820 );
821 assert_eq!(
822 &time_matrix_calls.drain::<u64>("tx_drop")[..],
823 &[TimeMatrixCall::Fold(Timed::now(2u64))]
824 );
825 }
826
827 #[fuchsia::test]
828 fn test_handle_periodic_telemetry_driver_specific_counters() {
829 let mut test_helper = setup_test();
830 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
831 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
832 let logger = ClientIfaceCountersLogger::new(
833 test_helper.cobalt_proxy.clone(),
834 test_helper.monitor_svc_proxy.clone(),
835 &test_helper.inspect_metadata_node,
836 &test_helper.inspect_metadata_path,
837 &test_helper.mock_time_matrix_client,
838 driver_counters_mock_matrix_client.clone(),
839 driver_gauges_mock_matrix_client.clone(),
840 );
841
842 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
843 assert_eq!(
844 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
845 Poll::Pending
846 );
847
848 let mocked_inspect_configs = vec![
849 fidl_stats::InspectCounterConfig {
850 counter_id: Some(1),
851 counter_name: Some("foo_counter".to_string()),
852 ..Default::default()
853 },
854 fidl_stats::InspectCounterConfig {
855 counter_id: Some(2),
856 counter_name: Some("bar_counter".to_string()),
857 ..Default::default()
858 },
859 fidl_stats::InspectCounterConfig {
860 counter_id: Some(3),
861 counter_name: Some("baz_counter".to_string()),
862 ..Default::default()
863 },
864 ];
865 let telemetry_support = fidl_stats::TelemetrySupport {
866 inspect_counter_configs: Some(mocked_inspect_configs),
867 ..Default::default()
868 };
869 assert_eq!(
870 test_helper.run_and_respond_query_telemetry_support(
871 &mut handle_iface_created_fut,
872 Ok(&telemetry_support)
873 ),
874 Poll::Ready(())
875 );
876
877 let mut test_fut = pin!(logger.handle_periodic_telemetry());
878 let iface_stats = fidl_stats::IfaceStats {
879 driver_specific_counters: Some(vec![fidl_stats::UnnamedCounter { id: 1, count: 50 }]),
880 connection_stats: Some(fidl_stats::ConnectionStats {
881 connection_id: Some(1),
882 driver_specific_counters: Some(vec![
883 fidl_stats::UnnamedCounter { id: 2, count: 100 },
884 fidl_stats::UnnamedCounter { id: 3, count: 150 },
885 fidl_stats::UnnamedCounter { id: 4, count: 200 },
887 ]),
888 ..Default::default()
889 }),
890 ..Default::default()
891 };
892 assert_eq!(
893 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
894 Poll::Pending,
895 );
896 let signal_report = Default::default();
897 assert_eq!(
898 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
899 Poll::Ready(())
900 );
901
902 let time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
903 assert!(time_matrix_calls.is_empty());
904
905 let mut driver_counters_matrix_calls = driver_counters_mock_matrix_client.drain_calls();
906 assert_eq!(
907 &driver_counters_matrix_calls.drain::<u64>("foo_counter")[..],
908 &[TimeMatrixCall::Fold(Timed::now(50))]
909 );
910 assert_eq!(
911 &driver_counters_matrix_calls.drain::<u64>("bar_counter")[..],
912 &[TimeMatrixCall::Fold(Timed::now(100))]
913 );
914 assert_eq!(
915 &driver_counters_matrix_calls.drain::<u64>("baz_counter")[..],
916 &[TimeMatrixCall::Fold(Timed::now(150))]
917 );
918
919 let driver_gauges_matrix_calls = driver_gauges_mock_matrix_client.drain_calls();
920 assert!(driver_gauges_matrix_calls.is_empty());
921 }
922
923 #[fuchsia::test]
924 fn test_handle_periodic_telemetry_driver_specific_gauges() {
925 let mut test_helper = setup_test();
926 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
927 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
928 let logger = ClientIfaceCountersLogger::new(
929 test_helper.cobalt_proxy.clone(),
930 test_helper.monitor_svc_proxy.clone(),
931 &test_helper.inspect_metadata_node,
932 &test_helper.inspect_metadata_path,
933 &test_helper.mock_time_matrix_client,
934 driver_counters_mock_matrix_client.clone(),
935 driver_gauges_mock_matrix_client.clone(),
936 );
937
938 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
939 assert_eq!(
940 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
941 Poll::Pending
942 );
943
944 let mocked_inspect_configs = vec![
945 fidl_stats::InspectGaugeConfig {
946 gauge_id: Some(1),
947 gauge_name: Some("foo_gauge".to_string()),
948 statistics: Some(vec![
949 fidl_stats::GaugeStatistic::Mean,
950 fidl_stats::GaugeStatistic::Last,
951 ]),
952 ..Default::default()
953 },
954 fidl_stats::InspectGaugeConfig {
955 gauge_id: Some(2),
956 gauge_name: Some("bar_gauge".to_string()),
957 statistics: Some(vec![
958 fidl_stats::GaugeStatistic::Min,
959 fidl_stats::GaugeStatistic::Sum,
960 ]),
961 ..Default::default()
962 },
963 fidl_stats::InspectGaugeConfig {
964 gauge_id: Some(3),
965 gauge_name: Some("baz_gauge".to_string()),
966 statistics: Some(vec![fidl_stats::GaugeStatistic::Max]),
967 ..Default::default()
968 },
969 ];
970 let telemetry_support = fidl_stats::TelemetrySupport {
971 inspect_gauge_configs: Some(mocked_inspect_configs),
972 ..Default::default()
973 };
974 assert_eq!(
975 test_helper.run_and_respond_query_telemetry_support(
976 &mut handle_iface_created_fut,
977 Ok(&telemetry_support)
978 ),
979 Poll::Ready(())
980 );
981
982 let mut test_fut = pin!(logger.handle_periodic_telemetry());
983 let iface_stats = fidl_stats::IfaceStats {
984 driver_specific_gauges: Some(vec![fidl_stats::UnnamedGauge { id: 1, value: 50 }]),
985 connection_stats: Some(fidl_stats::ConnectionStats {
986 connection_id: Some(1),
987 driver_specific_gauges: Some(vec![
988 fidl_stats::UnnamedGauge { id: 2, value: 100 },
989 fidl_stats::UnnamedGauge { id: 3, value: 150 },
990 fidl_stats::UnnamedGauge { id: 4, value: 200 },
992 ]),
993 ..Default::default()
994 }),
995 ..Default::default()
996 };
997 assert_eq!(
998 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
999 Poll::Pending
1000 );
1001 let signal_report = Default::default();
1002 assert_eq!(
1003 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1004 Poll::Ready(())
1005 );
1006
1007 let time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
1008 assert!(time_matrix_calls.is_empty());
1009
1010 let driver_counters_matrix_calls = driver_counters_mock_matrix_client.drain_calls();
1011 assert!(driver_counters_matrix_calls.is_empty());
1012
1013 let mut driver_gauges_matrix_calls = driver_gauges_mock_matrix_client.drain_calls();
1014 assert_eq!(
1015 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.mean")[..],
1016 &[TimeMatrixCall::Fold(Timed::now(50))]
1017 );
1018 assert_eq!(
1019 &driver_gauges_matrix_calls.drain::<i64>("foo_gauge.last")[..],
1020 &[TimeMatrixCall::Fold(Timed::now(50))]
1021 );
1022 assert_eq!(
1023 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.min")[..],
1024 &[TimeMatrixCall::Fold(Timed::now(100))]
1025 );
1026 assert_eq!(
1027 &driver_gauges_matrix_calls.drain::<i64>("bar_gauge.sum")[..],
1028 &[TimeMatrixCall::Fold(Timed::now(100))]
1029 );
1030 assert_eq!(
1031 &driver_gauges_matrix_calls.drain::<i64>("baz_gauge.max")[..],
1032 &[TimeMatrixCall::Fold(Timed::now(150))]
1033 );
1034 }
1035
1036 #[fuchsia::test]
1037 fn test_handle_periodic_telemetry_signal_report() {
1038 let mut test_helper = setup_test();
1039 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1040 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1041 let logger = ClientIfaceCountersLogger::new(
1042 test_helper.cobalt_proxy.clone(),
1043 test_helper.monitor_svc_proxy.clone(),
1044 &test_helper.inspect_metadata_node,
1045 &test_helper.inspect_metadata_path,
1046 &test_helper.mock_time_matrix_client,
1047 driver_counters_mock_matrix_client.clone(),
1048 driver_gauges_mock_matrix_client.clone(),
1049 );
1050
1051 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
1052 assert_eq!(
1053 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
1054 Poll::Pending
1055 );
1056
1057 let telemetry_support = Default::default();
1058 assert_eq!(
1059 test_helper.run_and_respond_query_telemetry_support(
1060 &mut handle_iface_created_fut,
1061 Ok(&telemetry_support)
1062 ),
1063 Poll::Ready(())
1064 );
1065
1066 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1067 let iface_stats = Default::default();
1068 assert_eq!(
1069 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1070 Poll::Pending
1071 );
1072 let signal_report = fidl_stats::SignalReport {
1073 connection_signal_report: Some(fidl_stats::ConnectionSignalReport {
1074 channel: Some(fidl_ieee80211::WlanChannel {
1075 primary: 6,
1076 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
1077 secondary80: 0,
1078 }),
1079 tx_rate_500kbps: Some(11),
1080 rssi_dbm: Some(-40),
1081 snr_db: Some(25),
1082 ..Default::default()
1083 }),
1084 ..Default::default()
1085 };
1086 assert_eq!(
1087 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1088 Poll::Ready(())
1089 );
1090
1091 let mut time_matrix_calls = test_helper.mock_time_matrix_client.drain_calls();
1092 assert_eq!(
1093 &time_matrix_calls.drain::<u64>("wlan_channels")[..],
1094 &[TimeMatrixCall::Fold(Timed::now(1 << 0))] );
1096 assert_eq!(
1097 &time_matrix_calls.drain::<u64>("tx_rate_500kbps")[..],
1098 &[TimeMatrixCall::Fold(Timed::now(11))]
1099 );
1100 assert_eq!(
1101 &time_matrix_calls.drain::<i64>("rssi")[..],
1102 &[TimeMatrixCall::Fold(Timed::now(-40))]
1103 );
1104 assert_eq!(
1105 &time_matrix_calls.drain::<i64>("snr")[..],
1106 &[TimeMatrixCall::Fold(Timed::now(25))]
1107 );
1108
1109 let tree = test_helper.get_inspect_data_tree();
1110 assert_data_tree!(@executor test_helper.exec, tree, root: contains {
1111 test_stats: contains {
1112 metadata: contains {
1113 wlan_channels: contains {
1114 "0": {
1115 "@time": AnyNumericProperty,
1116 "data": contains {
1117 primary: 6u64,
1118 cbw: "Cbw20",
1119 secondary80: 0u64,
1120 }
1121 }
1122 }
1123 }
1124 }
1125 });
1126 }
1127
1128 #[fuchsia::test]
1129 fn test_handle_periodic_telemetry_cobalt() {
1130 let mut test_helper = setup_test();
1131 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1132 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1133 let logger = ClientIfaceCountersLogger::new(
1134 test_helper.cobalt_proxy.clone(),
1135 test_helper.monitor_svc_proxy.clone(),
1136 &test_helper.inspect_metadata_node,
1137 &test_helper.inspect_metadata_path,
1138 &test_helper.mock_time_matrix_client,
1139 driver_counters_mock_matrix_client.clone(),
1140 driver_gauges_mock_matrix_client.clone(),
1141 );
1142
1143 handle_iface_created(&mut test_helper, &logger);
1145
1146 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1147 let iface_stats = fidl_stats::IfaceStats {
1148 connection_stats: Some(fidl_stats::ConnectionStats {
1149 connection_id: Some(1),
1150 rx_unicast_total: Some(100),
1151 rx_unicast_drop: Some(5),
1152 rx_multicast: Some(30),
1153 tx_total: Some(50),
1154 tx_drop: Some(2),
1155 ..Default::default()
1156 }),
1157 ..Default::default()
1158 };
1159 assert_eq!(
1160 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1161 Poll::Pending
1162 );
1163 let signal_report = Default::default();
1164 assert_eq!(
1165 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1166 Poll::Ready(())
1167 );
1168
1169 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1170 assert!(metrics.is_empty());
1171 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1172 assert!(metrics.is_empty());
1173 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1174 assert!(metrics.is_empty());
1175
1176 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1177 let iface_stats = fidl_stats::IfaceStats {
1178 connection_stats: Some(fidl_stats::ConnectionStats {
1179 connection_id: Some(1),
1180 rx_unicast_total: Some(200),
1181 rx_unicast_drop: Some(15),
1182 rx_multicast: Some(30),
1183 tx_total: Some(150),
1184 tx_drop: Some(3),
1185 ..Default::default()
1186 }),
1187 ..Default::default()
1188 };
1189 assert_eq!(
1190 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1191 Poll::Pending
1192 );
1193 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1194 let signal_report: fidl_fuchsia_wlan_stats::SignalReport = Default::default();
1195 assert_eq!(
1196 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1197 Poll::Ready(())
1198 );
1199
1200 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1201 assert_eq!(metrics.len(), 1);
1202 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(1000)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1204 assert_eq!(metrics.len(), 1);
1205 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1206 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1207 assert_eq!(metrics.len(), 1);
1208 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
1210
1211 #[fuchsia::test]
1212 fn test_handle_periodic_telemetry_cobalt_changed_connection_id() {
1213 let mut test_helper = setup_test();
1214 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1215 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1216 let logger = ClientIfaceCountersLogger::new(
1217 test_helper.cobalt_proxy.clone(),
1218 test_helper.monitor_svc_proxy.clone(),
1219 &test_helper.inspect_metadata_node,
1220 &test_helper.inspect_metadata_path,
1221 &test_helper.mock_time_matrix_client,
1222 driver_counters_mock_matrix_client.clone(),
1223 driver_gauges_mock_matrix_client.clone(),
1224 );
1225
1226 handle_iface_created(&mut test_helper, &logger);
1228
1229 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1230 let iface_stats = fidl_stats::IfaceStats {
1231 connection_stats: Some(fidl_stats::ConnectionStats {
1232 connection_id: Some(1),
1233 rx_unicast_total: Some(100),
1234 rx_unicast_drop: Some(5),
1235 rx_multicast: Some(30),
1236 tx_total: Some(50),
1237 tx_drop: Some(2),
1238 ..Default::default()
1239 }),
1240 ..Default::default()
1241 };
1242 assert_eq!(
1243 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1244 Poll::Pending
1245 );
1246 let signal_report = Default::default();
1247 assert_eq!(
1248 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1249 Poll::Ready(())
1250 );
1251
1252 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1253 assert!(metrics.is_empty());
1254 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1255 assert!(metrics.is_empty());
1256 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1257 assert!(metrics.is_empty());
1258
1259 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1260 let iface_stats = fidl_stats::IfaceStats {
1261 connection_stats: Some(fidl_stats::ConnectionStats {
1262 connection_id: Some(2),
1263 rx_unicast_total: Some(200),
1264 rx_unicast_drop: Some(15),
1265 rx_multicast: Some(30),
1266 tx_total: Some(150),
1267 tx_drop: Some(3),
1268 ..Default::default()
1269 }),
1270 ..Default::default()
1271 };
1272 assert_eq!(
1273 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1274 Poll::Pending
1275 );
1276 let signal_report = Default::default();
1277 assert_eq!(
1278 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1279 Poll::Ready(())
1280 );
1281
1282 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1285 assert!(metrics.is_empty());
1286 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1287 assert!(metrics.is_empty());
1288 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1289 assert!(metrics.is_empty());
1290
1291 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1292 let iface_stats = fidl_stats::IfaceStats {
1293 connection_stats: Some(fidl_stats::ConnectionStats {
1294 connection_id: Some(2),
1295 rx_unicast_total: Some(300),
1296 rx_unicast_drop: Some(18),
1297 rx_multicast: Some(30),
1298 tx_total: Some(250),
1299 tx_drop: Some(5),
1300 ..Default::default()
1301 }),
1302 ..Default::default()
1303 };
1304 assert_eq!(
1305 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1306 Poll::Pending
1307 );
1308 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1309 let signal_report = Default::default();
1310 assert_eq!(
1311 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1312 Poll::Ready(())
1313 );
1314
1315 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1316 assert_eq!(metrics.len(), 1);
1317 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1319 assert_eq!(metrics.len(), 1);
1320 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1321 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1322 assert_eq!(metrics.len(), 1);
1323 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
1325
1326 #[fuchsia::test]
1327 fn test_handle_periodic_telemetry_cobalt_suspension_in_between() {
1328 let mut test_helper = setup_test();
1329 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1330 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1331 let logger = ClientIfaceCountersLogger::new(
1332 test_helper.cobalt_proxy.clone(),
1333 test_helper.monitor_svc_proxy.clone(),
1334 &test_helper.inspect_metadata_node,
1335 &test_helper.inspect_metadata_path,
1336 &test_helper.mock_time_matrix_client,
1337 driver_counters_mock_matrix_client.clone(),
1338 driver_gauges_mock_matrix_client.clone(),
1339 );
1340
1341 handle_iface_created(&mut test_helper, &logger);
1343
1344 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1345 let iface_stats = fidl_stats::IfaceStats {
1346 connection_stats: Some(fidl_stats::ConnectionStats {
1347 connection_id: Some(1),
1348 rx_unicast_total: Some(100),
1349 rx_unicast_drop: Some(5),
1350 rx_multicast: Some(30),
1351 tx_total: Some(50),
1352 tx_drop: Some(2),
1353 ..Default::default()
1354 }),
1355 ..Default::default()
1356 };
1357 assert_eq!(
1358 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1359 Poll::Pending
1360 );
1361 let signal_report = Default::default();
1362 assert_eq!(
1363 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1364 Poll::Ready(())
1365 );
1366
1367 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1368 assert!(metrics.is_empty());
1369 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1370 assert!(metrics.is_empty());
1371 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1372 assert!(metrics.is_empty());
1373
1374 test_helper.exec.set_fake_boot_to_mono_offset(zx::BootDuration::from_millis(1));
1375 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1376 let iface_stats = fidl_stats::IfaceStats {
1377 connection_stats: Some(fidl_stats::ConnectionStats {
1378 connection_id: Some(1),
1379 rx_unicast_total: Some(200),
1380 rx_unicast_drop: Some(15),
1381 rx_multicast: Some(30),
1382 tx_total: Some(150),
1383 tx_drop: Some(3),
1384 ..Default::default()
1385 }),
1386 ..Default::default()
1387 };
1388 assert_eq!(
1389 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1390 Poll::Pending
1391 );
1392 let signal_report = Default::default();
1393 assert_eq!(
1394 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1395 Poll::Ready(())
1396 );
1397
1398 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1401 assert!(metrics.is_empty());
1402 let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1403 assert!(metrics.is_empty());
1404 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1405 assert!(metrics.is_empty());
1406
1407 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1408 let iface_stats = fidl_stats::IfaceStats {
1409 connection_stats: Some(fidl_stats::ConnectionStats {
1410 connection_id: Some(1),
1411 rx_unicast_total: Some(300),
1412 rx_unicast_drop: Some(18),
1413 rx_multicast: Some(30),
1414 tx_total: Some(250),
1415 tx_drop: Some(5),
1416 ..Default::default()
1417 }),
1418 ..Default::default()
1419 };
1420 assert_eq!(
1421 test_helper.run_and_respond_iface_stats_req(&mut test_fut, Ok(&iface_stats)),
1422 Poll::Pending
1423 );
1424 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1425 let signal_report = Default::default();
1426 assert_eq!(
1427 test_helper.run_and_respond_get_signal_report(&mut test_fut, Ok(&signal_report)),
1428 Poll::Ready(())
1429 );
1430
1431 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1432 assert_eq!(metrics.len(), 1);
1433 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(300)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1435 assert_eq!(metrics.len(), 1);
1436 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100));
1437 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1438 assert_eq!(metrics.len(), 1);
1439 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200)); }
1441
1442 #[fuchsia::test]
1443 fn test_handle_periodic_telemetry_get_iface_stats_failure_non_timeout() {
1444 let mut test_helper = setup_test();
1445 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1446 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1447 let logger = ClientIfaceCountersLogger::new(
1448 test_helper.cobalt_proxy.clone(),
1449 test_helper.monitor_svc_proxy.clone(),
1450 &test_helper.inspect_metadata_node,
1451 &test_helper.inspect_metadata_path,
1452 &test_helper.mock_time_matrix_client,
1453 driver_counters_mock_matrix_client.clone(),
1454 driver_gauges_mock_matrix_client.clone(),
1455 );
1456
1457 handle_iface_created(&mut test_helper, &logger);
1459
1460 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1461 assert_eq!(
1462 test_helper
1463 .run_and_respond_iface_stats_req(&mut test_fut, Err(zx::sys::ZX_ERR_TIMED_OUT)),
1464 Poll::Pending
1465 );
1466 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1467
1468 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_FAILURE_METRIC_ID);
1469 assert_eq!(metrics.len(), 1);
1470 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1471
1472 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID);
1476 assert!(metrics.is_empty());
1477
1478 let metrics =
1479 test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID);
1480 assert_eq!(metrics.len(), 1);
1481 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1482 }
1483
1484 #[fuchsia::test]
1485 fn test_handle_periodic_telemetry_get_iface_stats_timeout() {
1486 let mut test_helper = setup_test();
1487 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1488 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1489 let logger = ClientIfaceCountersLogger::new(
1490 test_helper.cobalt_proxy.clone(),
1491 test_helper.monitor_svc_proxy.clone(),
1492 &test_helper.inspect_metadata_node,
1493 &test_helper.inspect_metadata_path,
1494 &test_helper.mock_time_matrix_client,
1495 driver_counters_mock_matrix_client.clone(),
1496 driver_gauges_mock_matrix_client.clone(),
1497 );
1498
1499 handle_iface_created(&mut test_helper, &logger);
1501
1502 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1503 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1504 test_helper.exec.set_fake_time(fasync::MonotonicInstant::from_nanos(5_000_000_000));
1505 assert_eq!(test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut), Poll::Pending);
1506
1507 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_FAILURE_METRIC_ID);
1508 assert_eq!(metrics.len(), 1);
1509 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1510
1511 let metrics = test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_TIMEOUT_METRIC_ID);
1512 assert_eq!(metrics.len(), 1);
1513 assert_eq!(metrics[0].payload, MetricEventPayload::Count(1));
1514
1515 let metrics =
1516 test_helper.get_logged_metrics(metrics::GET_IFACE_STATS_ERROR_IN_RESPONSE_METRIC_ID);
1517 assert!(metrics.is_empty());
1518 }
1519
1520 #[fuchsia::test]
1521 fn test_diff_and_log_rx_cobalt() {
1522 let mut test_helper = setup_test();
1523 let prev_stats = fidl_stats::ConnectionStats {
1524 rx_unicast_total: Some(100),
1525 rx_unicast_drop: Some(5),
1526 ..Default::default()
1527 };
1528 let current_stats = fidl_stats::ConnectionStats {
1529 rx_unicast_total: Some(300),
1530 rx_unicast_drop: Some(7),
1531 ..Default::default()
1532 };
1533 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1534 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1535 assert_eq!(
1536 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1537 Poll::Ready(())
1538 );
1539 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1540 assert_eq!(metrics.len(), 1);
1541 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); let metrics = test_helper.get_logged_metrics(metrics::RX_UNICAST_PACKETS_METRIC_ID);
1543 assert_eq!(metrics.len(), 1);
1544 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(200));
1545 }
1546
1547 #[test_case(
1548 fidl_stats::ConnectionStats { ..Default::default() },
1549 fidl_stats::ConnectionStats { ..Default::default() };
1550 "both empty"
1551 )]
1552 #[test_case(
1553 fidl_stats::ConnectionStats {
1554 rx_unicast_total: Some(100),
1555 rx_unicast_drop: Some(5),
1556 ..Default::default()
1557 },
1558 fidl_stats::ConnectionStats { ..Default::default() };
1559 "current empty"
1560 )]
1561 #[test_case(
1562 fidl_stats::ConnectionStats { ..Default::default() },
1563 fidl_stats::ConnectionStats {
1564 rx_unicast_total: Some(300),
1565 rx_unicast_drop: Some(7),
1566 ..Default::default()
1567 };
1568 "prev empty"
1569 )]
1570 #[fuchsia::test(add_test_attr = false)]
1571 fn test_diff_and_log_rx_cobalt_empty(
1572 prev_stats: fidl_stats::ConnectionStats,
1573 current_stats: fidl_stats::ConnectionStats,
1574 ) {
1575 let mut test_helper = setup_test();
1576 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1577 let mut test_fut = pin!(diff_and_log_rx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1578 assert_eq!(
1579 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1580 Poll::Ready(())
1581 );
1582 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1583 assert!(metrics.is_empty())
1584 }
1585
1586 #[fuchsia::test]
1587 fn test_diff_and_log_tx_cobalt() {
1588 let mut test_helper = setup_test();
1589 let prev_stats = fidl_stats::ConnectionStats {
1590 tx_total: Some(100),
1591 tx_drop: Some(5),
1592 ..Default::default()
1593 };
1594 let current_stats = fidl_stats::ConnectionStats {
1595 tx_total: Some(300),
1596 tx_drop: Some(7),
1597 ..Default::default()
1598 };
1599 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1600 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1601 assert_eq!(
1602 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1603 Poll::Ready(())
1604 );
1605 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1606 assert_eq!(metrics.len(), 1);
1607 assert_eq!(metrics[0].payload, MetricEventPayload::IntegerValue(100)); }
1609
1610 #[test_case(
1611 fidl_stats::ConnectionStats { ..Default::default() },
1612 fidl_stats::ConnectionStats { ..Default::default() };
1613 "both empty"
1614 )]
1615 #[test_case(
1616 fidl_stats::ConnectionStats {
1617 tx_total: Some(100),
1618 tx_drop: Some(5),
1619 ..Default::default()
1620 },
1621 fidl_stats::ConnectionStats { ..Default::default() };
1622 "current empty"
1623 )]
1624 #[test_case(
1625 fidl_stats::ConnectionStats { ..Default::default() },
1626 fidl_stats::ConnectionStats {
1627 tx_total: Some(300),
1628 tx_drop: Some(7),
1629 ..Default::default()
1630 };
1631 "prev empty"
1632 )]
1633 #[fuchsia::test(add_test_attr = false)]
1634 fn test_diff_and_log_tx_cobalt_empty(
1635 prev_stats: fidl_stats::ConnectionStats,
1636 current_stats: fidl_stats::ConnectionStats,
1637 ) {
1638 let mut test_helper = setup_test();
1639 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1640 let mut test_fut = pin!(diff_and_log_tx_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1641 assert_eq!(
1642 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1643 Poll::Ready(())
1644 );
1645 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1646 assert!(metrics.is_empty())
1647 }
1648
1649 #[test_case(
1650 fidl_stats::ConnectionStats {
1651 rx_unicast_total: Some(100),
1652 rx_unicast_drop: Some(5),
1653 tx_total: Some(100),
1654 tx_drop: Some(5),
1655 ..Default::default()
1656 },
1657 fidl_stats::ConnectionStats {
1658 rx_unicast_total: Some(10),
1659 rx_unicast_drop: Some(1),
1660 tx_total: Some(100),
1661 tx_drop: Some(5),
1662 ..Default::default()
1663 };
1664 "rx regressed"
1665 )]
1666 #[test_case(
1667 fidl_stats::ConnectionStats {
1668 tx_total: Some(100),
1669 tx_drop: Some(5),
1670 rx_unicast_total: Some(100),
1671 rx_unicast_drop: Some(5),
1672 ..Default::default()
1673 },
1674 fidl_stats::ConnectionStats {
1675 tx_total: Some(10),
1676 tx_drop: Some(1),
1677 rx_unicast_total: Some(100),
1678 rx_unicast_drop: Some(5),
1679 ..Default::default()
1680 };
1681 "tx regressed"
1682 )]
1683 #[fuchsia::test(add_test_attr = false)]
1684 fn test_diff_and_log_connection_stats_cobalt_counters_reset(
1685 prev_stats: fidl_stats::ConnectionStats,
1686 current_stats: fidl_stats::ConnectionStats,
1687 ) {
1688 let mut test_helper = setup_test();
1689 let cobalt_proxy = test_helper.cobalt_proxy.clone();
1690 let mut test_fut =
1691 pin!(diff_and_log_connection_stats_cobalt(&cobalt_proxy, &prev_stats, ¤t_stats));
1692 assert_eq!(
1694 test_helper.run_until_stalled_drain_cobalt_events(&mut test_fut),
1695 Poll::Ready(())
1696 );
1697 let metrics = test_helper.get_logged_metrics(metrics::BAD_RX_RATE_METRIC_ID);
1699 assert!(metrics.is_empty());
1700 let metrics = test_helper.get_logged_metrics(metrics::BAD_TX_RATE_METRIC_ID);
1701 assert!(metrics.is_empty());
1702 }
1703
1704 #[fuchsia::test]
1705 fn test_handle_iface_destroyed() {
1706 let mut test_helper = setup_test();
1707 let driver_counters_mock_matrix_client = MockTimeMatrixClient::new();
1708 let driver_gauges_mock_matrix_client = MockTimeMatrixClient::new();
1709 let logger = ClientIfaceCountersLogger::new(
1710 test_helper.cobalt_proxy.clone(),
1711 test_helper.monitor_svc_proxy.clone(),
1712 &test_helper.inspect_metadata_node,
1713 &test_helper.inspect_metadata_path,
1714 &test_helper.mock_time_matrix_client,
1715 driver_counters_mock_matrix_client.clone(),
1716 driver_gauges_mock_matrix_client.clone(),
1717 );
1718
1719 handle_iface_created(&mut test_helper, &logger);
1721
1722 let mut handle_iface_destroyed_fut = pin!(logger.handle_iface_destroyed(IFACE_ID));
1723 assert_eq!(
1724 test_helper.exec.run_until_stalled(&mut handle_iface_destroyed_fut),
1725 Poll::Ready(())
1726 );
1727
1728 let mut test_fut = pin!(logger.handle_periodic_telemetry());
1729 assert_eq!(test_helper.exec.run_until_stalled(&mut test_fut), Poll::Ready(()));
1730 let telemetry_svc_stream = test_helper.telemetry_svc_stream.as_mut().unwrap();
1731 let mut telemetry_svc_req_fut = pin!(telemetry_svc_stream.try_next());
1732 match test_helper.exec.run_until_stalled(&mut telemetry_svc_req_fut) {
1734 Poll::Ready(Ok(None)) => (),
1735 other => panic!("unexpected variant: {other:?}"),
1736 }
1737 }
1738
1739 fn handle_iface_created<S: InspectSender>(
1740 test_helper: &mut TestHelper,
1741 logger: &ClientIfaceCountersLogger<S>,
1742 ) {
1743 let mut handle_iface_created_fut = pin!(logger.handle_iface_created(IFACE_ID));
1744 assert_eq!(
1745 test_helper.run_and_handle_get_sme_telemetry(&mut handle_iface_created_fut),
1746 Poll::Pending
1747 );
1748 let telemetry_support = fidl_stats::TelemetrySupport::default();
1749 assert_eq!(
1750 test_helper.run_and_respond_query_telemetry_support(
1751 &mut handle_iface_created_fut,
1752 Ok(&telemetry_support)
1753 ),
1754 Poll::Ready(())
1755 );
1756 }
1757}