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