1 // This Source Code Form is subject to the terms of the Mozilla Public 2 // License, v. 2.0. If a copy of the MPL was not distributed with this 3 // file, You can obtain one at https://mozilla.org/MPL/2.0/. 4 5 use inherent::inherent; 6 use std::sync::{Arc, RwLock}; 7 8 use glean_core::metrics::{DistributionData, MetricType, TimeUnit, TimerId}; 9 use glean_core::ErrorType; 10 11 use crate::dispatcher; 12 13 // We need to wrap the glean-core type: otherwise if we try to implement 14 // the trait for the metric in `glean_core::metrics` we hit error[E0117]: 15 // only traits defined in the current crate can be implemented for arbitrary 16 // types. 17 18 /// Developer-facing API for recording timing distribution metrics. 19 /// 20 /// Instances of this class type are automatically generated by the parsers 21 /// at build time, allowing developers to record values that were previously 22 /// registered in the metrics.yaml file. 23 #[derive(Clone)] 24 pub struct TimingDistributionMetric( 25 pub(crate) Arc<RwLock<glean_core::metrics::TimingDistributionMetric>>, 26 ); 27 28 impl TimingDistributionMetric { 29 /// The public constructor used by automatically generated metrics. new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self30 pub fn new(meta: glean_core::CommonMetricData, time_unit: TimeUnit) -> Self { 31 Self(Arc::new(RwLock::new( 32 glean_core::metrics::TimingDistributionMetric::new(meta, time_unit), 33 ))) 34 } 35 36 /// Accumulates the provided samples in the metric. 37 /// 38 /// # Arguments 39 /// 40 /// * `samples` - A list of samples recorded by the metric. 41 /// Samples must be in nanoseconds. 42 /// 43 /// ## Notes 44 /// 45 /// Reports an [`ErrorType::InvalidOverflow`] error for samples that 46 /// are longer than `MAX_SAMPLE_TIME`. accumulate_raw_samples_nanos(&self, samples: Vec<u64>)47 pub fn accumulate_raw_samples_nanos(&self, samples: Vec<u64>) { 48 let metric = Arc::clone(&self.0); 49 crate::launch_with_glean(move |glean| { 50 metric 51 .write() 52 .unwrap() 53 .accumulate_raw_samples_nanos(glean, &samples); 54 }); 55 } 56 } 57 58 #[inherent(pub)] 59 impl glean_core::traits::TimingDistribution for TimingDistributionMetric { start(&self) -> TimerId60 fn start(&self) -> TimerId { 61 let start_time = time::precise_time_ns(); 62 self.0.write().unwrap().set_start(start_time) 63 } 64 stop_and_accumulate(&self, id: TimerId)65 fn stop_and_accumulate(&self, id: TimerId) { 66 let stop_time = time::precise_time_ns(); 67 let metric = Arc::clone(&self.0); 68 crate::launch_with_glean(move |glean| { 69 metric 70 .write() 71 .unwrap() 72 .set_stop_and_accumulate(glean, id, stop_time) 73 }); 74 } 75 cancel(&self, id: TimerId)76 fn cancel(&self, id: TimerId) { 77 let metric = Arc::clone(&self.0); 78 dispatcher::launch(move || metric.write().unwrap().cancel(id)); 79 } 80 test_get_value<'a, S: Into<Option<&'a str>>>( &self, ping_name: S, ) -> Option<DistributionData>81 fn test_get_value<'a, S: Into<Option<&'a str>>>( 82 &self, 83 ping_name: S, 84 ) -> Option<DistributionData> { 85 crate::block_on_dispatcher(); 86 87 crate::with_glean(|glean| { 88 // The order of taking these locks matter. Glean must be first. 89 let inner = self 90 .0 91 .read() 92 .expect("Lock poisoned for timing distribution metric on test_get_value."); 93 let queried_ping_name = ping_name 94 .into() 95 .unwrap_or_else(|| &inner.meta().send_in_pings[0]); 96 97 inner.test_get_value(glean, queried_ping_name) 98 }) 99 } 100 test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( &self, error: ErrorType, ping_name: S, ) -> i32101 fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( 102 &self, 103 error: ErrorType, 104 ping_name: S, 105 ) -> i32 { 106 crate::block_on_dispatcher(); 107 108 crate::with_glean_mut(|glean| { 109 glean_core::test_get_num_recorded_errors( 110 &glean, 111 self.0.read().unwrap().meta(), 112 error, 113 ping_name.into(), 114 ) 115 .unwrap_or(0) 116 }) 117 } 118 } 119