summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/api/src/private/timespan.rs
blob: af740d5a414d6880a22dcf03e64ffbc6a79c7b53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use inherent::inherent;

use super::{CommonMetricData, MetricId, TimeUnit};

use glean_core::traits::Timespan;

use crate::ipc::need_ipc;

/// A timespan metric.
///
/// Timespans are used to make a measurement of how much time is spent in a particular task.
pub enum TimespanMetric {
    Parent(glean::private::TimespanMetric),
    Child,
}

impl TimespanMetric {
    /// Create a new timespan metric.
    pub fn new(_id: MetricId, meta: CommonMetricData, time_unit: TimeUnit) -> Self {
        if need_ipc() {
            TimespanMetric::Child
        } else {
            TimespanMetric::Parent(glean::private::TimespanMetric::new(meta, time_unit))
        }
    }
}

#[inherent(pub)]
impl Timespan for TimespanMetric {
    fn start(&self) {
        match self {
            TimespanMetric::Parent(p) => Timespan::start(p),
            TimespanMetric::Child => {
                log::error!("Unable to start timespan metric in non-main process. Ignoring.");
                // TODO: Record an error.
            }
        }
    }

    fn stop(&self) {
        match self {
            TimespanMetric::Parent(p) => Timespan::stop(p),
            TimespanMetric::Child => {
                log::error!("Unable to stop timespan metric in non-main process. Ignoring.");
                // TODO: Record an error.
            }
        }
    }

    fn cancel(&self) {
        match self {
            TimespanMetric::Parent(p) => Timespan::cancel(p),
            TimespanMetric::Child => {
                log::error!("Unable to cancel timespan metric in non-main process. Ignoring.");
                // TODO: Record an error.
            }
        }
    }

    fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> {
        match self {
            TimespanMetric::Parent(p) => p.test_get_value(ping_name),
            TimespanMetric::Child => {
                panic!("Cannot get test value for in non-parent process!");
            }
        }
    }

    fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
        &self,
        error: glean::ErrorType,
        ping_name: S,
    ) -> i32 {
        match self {
            TimespanMetric::Parent(p) => p.test_get_num_recorded_errors(error, ping_name),
            TimespanMetric::Child => {
                panic!("Cannot get the number of recorded errors for timespan metric in non-parent process!");
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::{common_test::*, ipc, metrics};

    #[test]
    fn smoke_test_timespan() {
        let _lock = lock_test();

        let metric = TimespanMetric::new(
            0.into(),
            CommonMetricData {
                name: "timespan_metric".into(),
                category: "telemetry".into(),
                send_in_pings: vec!["store1".into()],
                disabled: false,
                ..Default::default()
            },
            TimeUnit::Nanosecond,
        );

        metric.start();
        // Stopping right away might not give us data, if the underlying clock source is not precise
        // enough.
        // So let's cancel and make sure nothing blows up.
        metric.cancel();

        assert_eq!(None, metric.test_get_value("store1"));
    }

    #[test]
    fn timespan_ipc() {
        let _lock = lock_test();
        let _raii = ipc::test_set_need_ipc(true);

        let child_metric = &metrics::test_only::can_we_time_it;

        // Instrumentation calls do not panic.
        child_metric.start();
        // Stopping right away might not give us data,
        // if the underlying clock source is not precise enough.
        // So let's cancel and make sure nothing blows up.
        child_metric.cancel();

        // (They also shouldn't do anything,
        // but that's not something we can inspect in this test)
    }
}