glean_ffi/
labeled.rs

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
5use std::convert::TryFrom;
6
7use glean_core::{metrics::*, CommonMetricData, Lifetime};
8
9use crate::boolean::BOOLEAN_METRICS;
10use crate::counter::COUNTER_METRICS;
11use crate::string::STRING_METRICS;
12use crate::*;
13
14/// Generate FFI functions for labeled metrics.
15///
16/// This can be used to reduce the amount of duplicated boilerplate around calling
17/// `LabeledMetric::new` and LabeledMetric.get`.
18/// The constructor function takes the general common meta data.
19///
20/// If any additional data needs to be passed in, this macro cannot be used.
21///
22/// Arguments:
23///
24/// * `metric` - The metric type, e.g. `CounterMetric`.
25/// * `global` - The name of the newly constructed global to hold instances of the labeled metric.
26/// * `metric_global` - The name of the map to hold instances of the underlying metric type.
27/// * `new_name` - Function name to create a new labeled metric of this type.
28/// * `destroy_name` - Function name to destroy the labeled metric.
29/// * `get_name` - Function name to get a new instance of the underlying metric.
30macro_rules! impl_labeled_metric {
31    ($metric:ty, $global:ident, $metric_global:ident, $new_name:ident, $destroy_name:ident, $get_name:ident, $test_get_num_recorded_errors:ident) => {
32        static $global: once_cell::sync::Lazy<ConcurrentHandleMap<LabeledMetric<$metric>>> =
33            once_cell::sync::Lazy::new(ConcurrentHandleMap::new);
34        $crate::define_infallible_handle_map_deleter!($global, $destroy_name);
35
36        /// Create a new labeled metric.
37        #[no_mangle]
38        pub extern "C" fn $new_name(
39            category: FfiStr,
40            name: FfiStr,
41            send_in_pings: RawStringArray,
42            send_in_pings_len: i32,
43            lifetime: i32,
44            disabled: u8,
45            labels: RawStringArray,
46            label_count: i32,
47        ) -> u64 {
48            $global.insert_with_log(|| {
49                let name = name.to_string_fallible()?;
50                let category = category.to_string_fallible()?;
51                let send_in_pings = from_raw_string_array(send_in_pings, send_in_pings_len)?;
52                let labels = from_raw_string_array(labels, label_count)?;
53                let labels = if labels.is_empty() {
54                    None
55                } else {
56                    Some(labels)
57                };
58                let lifetime = Lifetime::try_from(lifetime)?;
59
60                Ok(LabeledMetric::new(
61                    <$metric>::new(CommonMetricData {
62                        name,
63                        category,
64                        send_in_pings,
65                        lifetime,
66                        disabled: disabled != 0,
67                        ..Default::default()
68                    }),
69                    labels,
70                ))
71            })
72        }
73
74        /// Create a new instance of the sub-metric of this labeled metric.
75        #[no_mangle]
76        pub extern "C" fn $get_name(handle: u64, label: FfiStr) -> u64 {
77            // A map from a unique ID for the labeled submetric to a handle of an instantiated
78            // metric type.
79            static LABEL_MAP: once_cell::sync::Lazy<
80                std::sync::Mutex<std::collections::HashMap<String, u64>>,
81            > = once_cell::sync::Lazy::new(|| {
82                std::sync::Mutex::new(std::collections::HashMap::new())
83            });
84
85            // The handle is a unique number per metric.
86            // The label identifies the submetric.
87            let id = format!("{}/{}", handle, label.as_str());
88
89            let mut map = LABEL_MAP.lock().unwrap();
90
91            use std::collections::hash_map::Entry;
92            match map.entry(id) {
93                Entry::Occupied(entry) => {
94                    // This label handle _could_ get stale if language SDKs call the corresponding
95                    // `glean_destroy_*` functions on the sub-metric.
96                    // We don't guard against this for now, but ensure our language SDKs don't call
97                    // it for metric types that can be labeled (counter, string, boolean).
98                    *entry.get()
99                }
100                Entry::Vacant(entry) => {
101                    let label_handle = $global.call_infallible_mut(handle, |labeled| {
102                        let metric = labeled.get(label.as_str());
103                        $metric_global.insert_with_log(|| Ok(metric))
104                    });
105                    entry.insert(label_handle);
106                    label_handle
107                }
108            }
109        }
110
111        #[no_mangle]
112        pub extern "C" fn $test_get_num_recorded_errors(
113            metric_id: u64,
114            error_type: i32,
115            storage_name: FfiStr,
116        ) -> i32 {
117            crate::with_glean_value(|glean| {
118                crate::HandleMapExtension::call_infallible(&*$global, metric_id, |metric| {
119                    let error_type = std::convert::TryFrom::try_from(error_type).unwrap();
120                    let storage_name =
121                        crate::FallibleToString::to_string_fallible(&storage_name).unwrap();
122                    glean_core::test_get_num_recorded_errors(
123                        glean,
124                        &metric.get_submetric().meta(),
125                        error_type,
126                        Some(&storage_name),
127                    )
128                    .unwrap_or(0)
129                })
130            })
131        }
132    };
133}
134
135// Create the required FFI functions for LabeledMetric<CounterMetric>
136impl_labeled_metric!(
137    CounterMetric,
138    LABELED_COUNTER,
139    COUNTER_METRICS,
140    glean_new_labeled_counter_metric,
141    glean_destroy_labeled_counter_metric,
142    glean_labeled_counter_metric_get,
143    glean_labeled_counter_test_get_num_recorded_errors
144);
145
146// Create the required FFI functions for LabeledMetric<BooleanMetric>
147impl_labeled_metric!(
148    BooleanMetric,
149    LABELED_BOOLEAN,
150    BOOLEAN_METRICS,
151    glean_new_labeled_boolean_metric,
152    glean_destroy_labeled_boolean_metric,
153    glean_labeled_boolean_metric_get,
154    glean_labeled_boolean_test_get_num_recorded_errors
155);
156
157// Create the required FFI functions for LabeledMetric<StringMetric>
158impl_labeled_metric!(
159    StringMetric,
160    LABELED_STRING,
161    STRING_METRICS,
162    glean_new_labeled_string_metric,
163    glean_destroy_labeled_string_metric,
164    glean_labeled_string_metric_get,
165    glean_labeled_string_test_get_num_recorded_errors
166);