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