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