summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/api/src/ffi/macros.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/glean/api/src/ffi/macros.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/glean/api/src/ffi/macros.rs')
-rw-r--r--toolkit/components/glean/api/src/ffi/macros.rs289
1 files changed, 289 insertions, 0 deletions
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
+ }};
+}