From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- toolkit/components/glean/api/src/ffi/macros.rs | 289 +++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 toolkit/components/glean/api/src/ffi/macros.rs (limited to 'toolkit/components/glean/api/src/ffi/macros.rs') diff --git a/toolkit/components/glean/api/src/ffi/macros.rs b/toolkit/components/glean/api/src/ffi/macros.rs new file mode 100644 index 0000000000..3571ebd88b --- /dev/null +++ b/toolkit/components/glean/api/src/ffi/macros.rs @@ -0,0 +1,289 @@ +// 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/. + +//! Helper macros for implementing the FFI API for metric types. + +/// Get a metric object by ID from the corresponding map, then +/// execute the provided closure with it. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// (or `factory::__jog_metric_maps`) +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$m` - The identifier to use for the retrieved metric. +/// The expression `$f` can use this identifier. +/// * `$f` - The expression to execute with the retrieved metric `$m`. +macro_rules! with_metric { + (BOOLEAN_MAP, $id:ident, $m:ident, $f:expr) => { + maybe_labeled_with_metric!(BOOLEAN_MAP, $id, $m, $f) + }; + (COUNTER_MAP, $id:ident, $m:ident, $f:expr) => { + maybe_labeled_with_metric!(COUNTER_MAP, $id, $m, $f) + }; + (STRING_MAP, $id:ident, $m:ident, $f:expr) => { + maybe_labeled_with_metric!(STRING_MAP, $id, $m, $f) + }; + ($map:ident, $id:ident, $m:ident, $f:expr) => { + just_with_metric!($map, $id, $m, $f) + }; +} + +/// Get a dynamically-registered metric object by id from the corresponding map, +/// then execute the provided closure with it. +/// +/// Assumes `$id` is for a dynamic non-submetric metric. +/// Will panic if it isn't. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `factory::__jog_metric_maps` +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$m` - The identifier to use for the retrieved metric. +/// The expression `$f` can use this identifier. +/// * `$f` - The expression to execute with the retrieved metric `$m`. +macro_rules! just_with_jog_metric { + ($map:ident, $id:ident, $m:ident, $f:expr) => {{ + let map = $crate::factory::__jog_metric_maps::$map + .read() + .expect("Read lock for dynamic metric map was poisoned"); + match map.get(&$id.into()) { + Some($m) => $f, + None => panic!("No (dynamic) metric for id {}", $id), + } + }}; +} + +/// Get a metric object by id from the corresponding map, then +/// execute the provided closure with it. +/// +/// Ignores the possibility that the $id might be for a labeled submetric. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// (or `factory::__jog_metric_maps`) +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$m` - The identifier to use for the retrieved metric. +/// The expression `$f` can use this identifier. +/// * `$f` - The expression to execute with the retrieved metric `$m`. +macro_rules! just_with_metric { + ($map:ident, $id:ident, $m:ident, $f:expr) => { + if $id & (1 << $crate::factory::DYNAMIC_METRIC_BIT) > 0 { + just_with_jog_metric!($map, $id, $m, $f) + } else { + match $crate::metrics::__glean_metric_maps::$map.get(&$id.into()) { + Some($m) => $f, + None => panic!("No metric for id {}", $id), + } + } + }; +} + +/// Get a metric object by id from the corresponding map, then +/// execute the provided closure with it. +/// +/// Requires that the provided $map be of a type that can be labeled, since it +/// assumes the presence of a same-named map in +/// `metrics::_glean_metrics_map::submetric_maps`. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// and `metrics::__glean_metric_maps::submetric_maps` as generated +/// by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$m` - The identifier to use for the retrieved metric. +/// The expression `$f` can use this identifier. +/// * `$f` - The expression to execute with the retrieved metric `$m`. +macro_rules! maybe_labeled_with_metric { + ($map:ident, $id:ident, $m:ident, $f:expr) => { + if $id & (1 << $crate::metrics::__glean_metric_maps::submetric_maps::SUBMETRIC_BIT) > 0 { + let map = $crate::metrics::__glean_metric_maps::submetric_maps::$map + .read() + .expect("Read lock for labeled metric map was poisoned"); + match map.get(&$id.into()) { + Some($m) => $f, + None => panic!("No submetric for id {}", $id), + } + } else { + just_with_metric!($map, $id, $m, $f) + } + }; +} + +/// Test whether a value is stored for the given metric. +/// +/// # Arguments +/// +/// * `$metric` - The metric to test. +/// * `$storage` - the storage name to look into. +macro_rules! test_has { + ($metric:ident, $storage:ident) => {{ + let storage = if $storage.is_empty() { + None + } else { + Some($storage.to_utf8()) + }; + $metric.test_get_value(storage.as_deref()).is_some() + }}; +} + +/// Get the currently stored value for the given metric. +/// +/// # Arguments +/// +/// * `$metric` - The metric to test. +/// * `$storage` - the storage name to look into. +macro_rules! test_get { + ($metric:ident, $storage:ident) => {{ + let storage = if $storage.is_empty() { + None + } else { + Some($storage.to_utf8()) + }; + $metric.test_get_value(storage.as_deref()).unwrap() + }}; +} + +/// Check the provided metric in the provided storage for errors. +/// On finding one, return an error string. +/// +/// # Arguments +/// +/// * `$metric` - The metric to test. +macro_rules! test_get_errors { + ($metric:path) => {{ + let error_types = [ + glean::ErrorType::InvalidValue, + glean::ErrorType::InvalidLabel, + glean::ErrorType::InvalidState, + glean::ErrorType::InvalidOverflow, + ]; + let mut error_str = None; + for &error_type in error_types.iter() { + let num_errors = $metric.test_get_num_recorded_errors(error_type); + if num_errors > 0 { + error_str = Some(format!( + "Metric had {} error(s) of type {}!", + num_errors, + error_type.as_str() + )); + break; + } + } + error_str + }}; +} + +/// Get the submetric id for a given labeled metric and label. +/// +/// # Arguments +/// +/// * `$id` - The id of the labeled metric. +/// * `$label` - The (string) label of the submetric. +/// * `$labeled_map` - The name of the labeled metric's map for retrieval (JOG only). +/// * `$labeled_get` - The name of the labeled metric's get fn for retrieval. +/// * `$submetric_map`- The name of the submetrics' map for storage. +/// * `$metric_type` - The submetric's type (needed for an internal closure). +macro_rules! labeled_submetric_get { + ($id:ident, $label:ident, $labeled_map:ident, $labeled_get:ident, $submetric_map:ident, $metric_type:ty) => {{ + let tuple = ($id, $label.to_utf8().into()); + { + let map = $crate::metrics::__glean_metric_maps::submetric_maps::LABELED_METRICS_TO_IDS + .read() + .expect("read lock of submetric ids was poisoned"); + if let Some(submetric_id) = map.get(&tuple) { + return *submetric_id; + } + } + + // Gotta actually create a new submetric with a new id. + let submetric_id = + $crate::metrics::__glean_metric_maps::submetric_maps::NEXT_LABELED_SUBMETRIC_ID + .fetch_add(1, Ordering::SeqCst); + { + if $id & (1 << $crate::factory::DYNAMIC_METRIC_BIT) > 0 { + just_with_jog_metric!($labeled_map, $id, metric, { + let submetric = metric.get(&tuple.1); + let mut map = + $crate::metrics::__glean_metric_maps::submetric_maps::$submetric_map + .write() + .expect("write lock of submetric map was poisoned"); + map.insert(submetric_id.into(), submetric); + }); + } else { + let mut map = $crate::metrics::__glean_metric_maps::submetric_maps::$submetric_map + .write() + .expect("write lock of submetric map was poisoned"); + map.insert( + submetric_id.into(), + $crate::metrics::__glean_metric_maps::$labeled_get($id, &tuple.1), + ); + } + } + + let mut map = $crate::metrics::__glean_metric_maps::submetric_maps::LABELED_METRICS_TO_IDS + .write() + .expect("write lock of submetric ids was poisoned"); + map.insert(tuple, submetric_id); + submetric_id + }}; +} + +/// Get the submetric id for a given labeled metric and label enum. +/// +/// # Arguments +/// +/// * `$id` - The id of the labeled metric. +/// * `$label` - The (enum) label of the submetric. +/// * `$labeled_get` - The name of the labeled metric's get fn for retrieval. +/// * `$submetric_map`- The name of the submetrics' map for storage. +/// * `$metric_type` - The submetric's type (needed for an internal closure). +macro_rules! labeled_submetric_enum_get { + ($id:ident, $label_enum:ident, $labeled_get:ident, $submetric_map:ident, $metric_type:ty) => {{ + let tuple = ($id, $label_enum.into()); + // First: Have we seen this enum before? If so, give out the same submetric id. + { + let map = $crate::metrics::__glean_metric_maps::submetric_maps::LABELED_ENUMS_TO_IDS + .read() + .expect("read lock of enum submetric ids was poisoned"); + if let Some(submetric_id) = map.get(&tuple) { + return *submetric_id; + } + } + + // Alas, this is the first time we've needed to handle this metric with this enum. + // Gotta actually create a new submetric with a new id. + let submetric_id = + $crate::metrics::__glean_metric_maps::submetric_maps::NEXT_LABELED_SUBMETRIC_ID + .fetch_add(1, Ordering::SeqCst); + { + // What if the dynamic bit is set? + // JOG only supports JS, and enum_get isn't (yet) supported in JS. + assert_eq!( + 0, + $id & (1 << $crate::factory::DYNAMIC_METRIC_BIT), + "No enum_get support for JOG" + ); + let mut map = $crate::metrics::__glean_metric_maps::submetric_maps::$submetric_map + .write() + .expect("write lock of submetric map was poisoned"); + map.insert( + submetric_id.into(), + $crate::metrics::__glean_metric_maps::$labeled_get($id, tuple.1), + ); + } + + // And now ensure we store the submetric so we need not create it on subsequent calls. + let mut map = $crate::metrics::__glean_metric_maps::submetric_maps::LABELED_ENUMS_TO_IDS + .write() + .expect("write lock of submetric ids was poisoned"); + map.insert(tuple, submetric_id); + submetric_id + }}; +} -- cgit v1.2.3