summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/api/src/ffi
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/api/src/ffi')
-rw-r--r--toolkit/components/glean/api/src/ffi/boolean.rs28
-rw-r--r--toolkit/components/glean/api/src/ffi/counter.rs28
-rw-r--r--toolkit/components/glean/api/src/ffi/custom_distribution.rs82
-rw-r--r--toolkit/components/glean/api/src/ffi/datetime.rs66
-rw-r--r--toolkit/components/glean/api/src/ffi/denominator.rs28
-rw-r--r--toolkit/components/glean/api/src/ffi/event.rs168
-rw-r--r--toolkit/components/glean/api/src/ffi/labeled.rs84
-rw-r--r--toolkit/components/glean/api/src/ffi/macros.rs289
-rw-r--r--toolkit/components/glean/api/src/ffi/memory_distribution.rs64
-rw-r--r--toolkit/components/glean/api/src/ffi/mod.rs28
-rw-r--r--toolkit/components/glean/api/src/ffi/numerator.rs35
-rw-r--r--toolkit/components/glean/api/src/ffi/ping.rs18
-rw-r--r--toolkit/components/glean/api/src/ffi/quantity.rs28
-rw-r--r--toolkit/components/glean/api/src/ffi/rate.rs40
-rw-r--r--toolkit/components/glean/api/src/ffi/string.rs33
-rw-r--r--toolkit/components/glean/api/src/ffi/string_list.rs42
-rw-r--r--toolkit/components/glean/api/src/ffi/text.rs29
-rw-r--r--toolkit/components/glean/api/src/ffi/timespan.rs48
-rw-r--r--toolkit/components/glean/api/src/ffi/timing_distribution.rs90
-rw-r--r--toolkit/components/glean/api/src/ffi/url.rs29
-rw-r--r--toolkit/components/glean/api/src/ffi/uuid.rs37
21 files changed, 1294 insertions, 0 deletions
diff --git a/toolkit/components/glean/api/src/ffi/boolean.rs b/toolkit/components/glean/api/src/ffi/boolean.rs
new file mode 100644
index 0000000000..9184b23c00
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/boolean.rs
@@ -0,0 +1,28 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_boolean_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(BOOLEAN_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_boolean_test_get_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(BOOLEAN_MAP, id, metric, test_get!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_boolean_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(BOOLEAN_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn fog_boolean_set(id: u32, value: bool) {
+ with_metric!(BOOLEAN_MAP, id, metric, metric.set(value));
+}
diff --git a/toolkit/components/glean/api/src/ffi/counter.rs b/toolkit/components/glean/api/src/ffi/counter.rs
new file mode 100644
index 0000000000..5fba7c0dea
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/counter.rs
@@ -0,0 +1,28 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_counter_add(id: u32, amount: i32) {
+ with_metric!(COUNTER_MAP, id, metric, metric.add(amount));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_counter_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(COUNTER_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_counter_test_get_value(id: u32, ping_name: &nsACString) -> i32 {
+ with_metric!(COUNTER_MAP, id, metric, test_get!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_counter_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(COUNTER_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/custom_distribution.rs b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
new file mode 100644
index 0000000000..853a6e9845
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
@@ -0,0 +1,82 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+use thin_vec::ThinVec;
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_has!(metric, ping_name)
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ sum: &mut u64,
+ buckets: &mut ThinVec<u64>,
+ counts: &mut ThinVec<u64>,
+) {
+ let val = with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ // FIXME(bug 1771885): Glean should use `u64` where it can.
+ *sum = val.sum as _;
+ for (&bucket, &count) in val.values.iter() {
+ buckets.push(bucket as _);
+ counts.push(count as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_accumulate_samples(id: u32, samples: &ThinVec<u64>) {
+ // N.B.: Avoid reallocation here by making the underlying type take a slice.
+ let samples = samples.into_iter().map(|&i| i as i64).collect();
+ with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate_samples_signed(samples)
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_accumulate_samples_signed(
+ id: u32,
+ samples: &ThinVec<i64>,
+) {
+ // N.B.: Avoid reallocation here by making the underlying type take a slice.
+ let samples = samples.to_vec();
+ with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate_samples_signed(samples)
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_custom_distribution_test_get_error(
+ id: u32,
+
+ error_str: &mut nsACString,
+) -> bool {
+ let err = with_metric!(
+ CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/datetime.rs b/toolkit/components/glean/api/src/ffi/datetime.rs
new file mode 100644
index 0000000000..7529a524e6
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/datetime.rs
@@ -0,0 +1,66 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[repr(C)]
+pub struct FogDatetime {
+ year: i32,
+ month: u32,
+ day: u32,
+ hour: u32,
+ minute: u32,
+ second: u32,
+ nano: u32,
+ offset_seconds: i32,
+}
+
+#[no_mangle]
+pub extern "C" fn fog_datetime_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(DATETIME_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_datetime_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ value: &mut FogDatetime,
+) {
+ let val = with_metric!(DATETIME_MAP, id, metric, test_get!(metric, ping_name));
+ value.year = val.year;
+ value.month = val.month;
+ value.day = val.day;
+ value.hour = val.hour;
+ value.minute = val.minute;
+ value.second = val.second;
+ value.nano = val.nanosecond;
+ value.offset_seconds = val.offset_seconds;
+}
+
+#[no_mangle]
+pub extern "C" fn fog_datetime_set(id: u32, dt: &FogDatetime) {
+ with_metric!(
+ DATETIME_MAP,
+ id,
+ metric,
+ metric.set_with_details(
+ dt.year,
+ dt.month,
+ dt.day,
+ dt.hour,
+ dt.minute,
+ dt.second,
+ dt.nano,
+ dt.offset_seconds
+ )
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_datetime_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(DATETIME_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/denominator.rs b/toolkit/components/glean/api/src/ffi/denominator.rs
new file mode 100644
index 0000000000..ccb047f530
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/denominator.rs
@@ -0,0 +1,28 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_denominator_add(id: u32, amount: i32) {
+ with_metric!(DENOMINATOR_MAP, id, metric, metric.add(amount));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_denominator_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(DENOMINATOR_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_denominator_test_get_value(id: u32, ping_name: &nsACString) -> i32 {
+ with_metric!(DENOMINATOR_MAP, id, metric, test_get!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_denominator_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(DENOMINATOR_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/event.rs b/toolkit/components/glean/api/src/ffi/event.rs
new file mode 100644
index 0000000000..bd167021d6
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/event.rs
@@ -0,0 +1,168 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use std::collections::HashMap;
+
+use nsstring::{nsACString, nsCString};
+use thin_vec::ThinVec;
+
+use crate::metrics::__glean_metric_maps as metric_maps;
+use crate::private::EventRecordingError;
+
+#[no_mangle]
+pub extern "C" fn fog_event_record(
+ id: u32,
+ extra_keys: &ThinVec<nsCString>,
+ extra_values: &ThinVec<nsCString>,
+) {
+ // If no extra keys are passed, we can shortcut here.
+ if extra_keys.is_empty() {
+ if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::EVENT_MAP
+ .read()
+ .expect("Read lock for dynamic metric map was poisoned");
+ match map.get(&id.into()) {
+ Some(m) => m.record_raw(Default::default()),
+ None => panic!("No (dynamic) metric for event with id {}", id),
+ }
+ return;
+ }
+
+ if metric_maps::record_event_by_id(id, Default::default()).is_err() {
+ panic!("No event for id {}", id);
+ }
+
+ return;
+ }
+
+ assert_eq!(
+ extra_keys.len(),
+ extra_values.len(),
+ "Extra keys and values differ in length. ID: {}",
+ id
+ );
+
+ // Otherwise we need to decode them and pass them along.
+ let extra = extra_keys
+ .iter()
+ .zip(extra_values.iter())
+ .map(|(k, v)| (k.to_string(), v.to_string()))
+ .collect();
+ if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::EVENT_MAP
+ .read()
+ .expect("Read lock for dynamic metric map was poisoned");
+ match map.get(&id.into()) {
+ Some(m) => m.record_raw(extra),
+ None => panic!("No (dynamic) metric for event with id {}", id),
+ }
+ return;
+ } else {
+ match metric_maps::record_event_by_id(id, extra) {
+ Ok(()) => {}
+ Err(EventRecordingError::InvalidId) => panic!("No event for id {}", id),
+ Err(_) => panic!("Unpossible!"),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_event_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ let storage = if ping_name.is_empty() {
+ None
+ } else {
+ Some(ping_name.to_utf8().into_owned())
+ };
+ if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::EVENT_MAP
+ .read()
+ .expect("Read lock for dynamic metric map was poisoned");
+ match map.get(&id.into()) {
+ Some(m) => m.test_get_value(storage.as_deref()).is_some(),
+ None => panic!("No (dynamic) metric for event with id {}", id),
+ }
+ } else {
+ metric_maps::event_test_get_value_wrapper(id, storage).is_some()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_event_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::EVENT_MAP
+ .read()
+ .expect("Read lock for dynamic metric map was poisoned");
+ match map.get(&id.into()) {
+ Some(m) => test_get_errors!(m),
+ None => panic!("No (dynamic) metric for event with id {}", id),
+ }
+ } else {
+ metric_maps::event_test_get_error(id)
+ };
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+/// FFI-compatible representation of recorded event data.
+#[repr(C)]
+pub struct FfiRecordedEvent {
+ timestamp: u64,
+ category: nsCString,
+ name: nsCString,
+
+ /// Array of extra data, keys and values are interleaved.
+ extras: ThinVec<nsCString>,
+}
+
+#[no_mangle]
+pub extern "C" fn fog_event_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ out_events: &mut ThinVec<FfiRecordedEvent>,
+) {
+ let storage = if ping_name.is_empty() {
+ None
+ } else {
+ Some(ping_name.to_utf8().into_owned())
+ };
+
+ let events = if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::EVENT_MAP
+ .read()
+ .expect("Read lock for dynamic metric map was poisoned");
+ let events = match map.get(&id.into()) {
+ Some(m) => m.test_get_value(storage.as_deref()),
+ None => return,
+ };
+ match events {
+ Some(events) => events,
+ None => return,
+ }
+ } else {
+ match metric_maps::event_test_get_value_wrapper(id, storage) {
+ Some(events) => events,
+ None => return,
+ }
+ };
+
+ for event in events {
+ let extra = event.extra.unwrap_or_else(HashMap::new);
+ let extra_len = extra.len();
+ let mut extras = ThinVec::with_capacity(extra_len * 2);
+ for (k, v) in extra.into_iter() {
+ extras.push(nsCString::from(k));
+ extras.push(nsCString::from(v));
+ }
+
+ let event = FfiRecordedEvent {
+ timestamp: event.timestamp,
+ category: nsCString::from(event.category),
+ name: nsCString::from(event.name),
+ extras,
+ };
+
+ out_events.push(event);
+ }
+}
diff --git a/toolkit/components/glean/api/src/ffi/labeled.rs b/toolkit/components/glean/api/src/ffi/labeled.rs
new file mode 100644
index 0000000000..2cd61230f7
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/labeled.rs
@@ -0,0 +1,84 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use crate::metrics::__glean_metric_maps as metric_maps;
+use nsstring::nsACString;
+use std::sync::atomic::Ordering;
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_enum_to_str(id: u32, label: u16, value: &mut nsACString) {
+ let val = metric_maps::labeled_enum_to_str(id, label);
+ value.assign(&val);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_boolean_get(id: u32, label: &nsACString) -> u32 {
+ labeled_submetric_get!(
+ id,
+ label,
+ LABELED_BOOLEAN_MAP,
+ labeled_boolean_get,
+ BOOLEAN_MAP,
+ LabeledBooleanMetric
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_boolean_enum_get(id: u32, label: u16) -> u32 {
+ labeled_submetric_enum_get!(
+ id,
+ label,
+ labeled_boolean_enum_get,
+ BOOLEAN_MAP,
+ LabeledBooleanMetric
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_counter_get(id: u32, label: &nsACString) -> u32 {
+ labeled_submetric_get!(
+ id,
+ label,
+ LABELED_COUNTER_MAP,
+ labeled_counter_get,
+ COUNTER_MAP,
+ LabeledCounterMetric
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_counter_enum_get(id: u32, label: u16) -> u32 {
+ labeled_submetric_enum_get!(
+ id,
+ label,
+ labeled_counter_enum_get,
+ COUNTER_MAP,
+ LabeledCounterMetric
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_string_get(id: u32, label: &nsACString) -> u32 {
+ labeled_submetric_get!(
+ id,
+ label,
+ LABELED_STRING_MAP,
+ labeled_string_get,
+ STRING_MAP,
+ LabeledStringMetric
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_string_enum_get(id: u32, label: u16) -> u32 {
+ labeled_submetric_enum_get!(
+ id,
+ label,
+ labeled_string_enum_get,
+ STRING_MAP,
+ LabeledStringMetric
+ )
+}
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
+ }};
+}
diff --git a/toolkit/components/glean/api/src/ffi/memory_distribution.rs b/toolkit/components/glean/api/src/ffi/memory_distribution.rs
new file mode 100644
index 0000000000..cf09d3f8de
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/memory_distribution.rs
@@ -0,0 +1,64 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+use thin_vec::ThinVec;
+
+#[no_mangle]
+pub extern "C" fn fog_memory_distribution_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(
+ MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_has!(metric, ping_name)
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_memory_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ sum: &mut u64,
+ buckets: &mut ThinVec<u64>,
+ counts: &mut ThinVec<u64>,
+) {
+ let val = with_metric!(
+ MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ *sum = val.sum as _;
+ for (&bucket, &count) in val.values.iter() {
+ buckets.push(bucket as _);
+ counts.push(count as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_memory_distribution_accumulate(id: u32, sample: u64) {
+ with_metric!(
+ MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate(sample)
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_memory_distribution_test_get_error(
+ id: u32,
+
+ error_str: &mut nsACString,
+) -> bool {
+ let err = with_metric!(
+ MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/mod.rs b/toolkit/components/glean/api/src/ffi/mod.rs
new file mode 100644
index 0000000000..23235fc2f1
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/mod.rs
@@ -0,0 +1,28 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+#[macro_use]
+mod macros;
+
+mod boolean;
+mod counter;
+mod custom_distribution;
+mod datetime;
+mod denominator;
+mod event;
+mod labeled;
+mod memory_distribution;
+mod numerator;
+mod ping;
+mod quantity;
+mod rate;
+mod string;
+mod string_list;
+mod text;
+mod timespan;
+mod timing_distribution;
+mod url;
+mod uuid;
diff --git a/toolkit/components/glean/api/src/ffi/numerator.rs b/toolkit/components/glean/api/src/ffi/numerator.rs
new file mode 100644
index 0000000000..e679e426c4
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/numerator.rs
@@ -0,0 +1,35 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_numerator_add_to_numerator(id: u32, amount: i32) {
+ with_metric!(NUMERATOR_MAP, id, metric, metric.add_to_numerator(amount));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_numerator_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(NUMERATOR_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_numerator_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ num: &mut i32,
+ den: &mut i32,
+) {
+ let rate = with_metric!(NUMERATOR_MAP, id, metric, test_get!(metric, ping_name));
+ *num = rate.numerator;
+ *den = rate.denominator;
+}
+
+#[no_mangle]
+pub extern "C" fn fog_numerator_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(NUMERATOR_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/ping.rs b/toolkit/components/glean/api/src/ffi/ping.rs
new file mode 100644
index 0000000000..2834655a03
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/ping.rs
@@ -0,0 +1,18 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use crate::pings;
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_submit_ping_by_id(id: u32, reason: &nsACString) {
+ let reason = if reason.is_empty() {
+ None
+ } else {
+ Some(reason.to_utf8())
+ };
+ pings::submit_ping_by_id(id, reason.as_deref());
+}
diff --git a/toolkit/components/glean/api/src/ffi/quantity.rs b/toolkit/components/glean/api/src/ffi/quantity.rs
new file mode 100644
index 0000000000..7f94bcff27
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/quantity.rs
@@ -0,0 +1,28 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_quantity_set(id: u32, value: i64) {
+ with_metric!(QUANTITY_MAP, id, metric, metric.set(value));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_quantity_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(QUANTITY_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_quantity_test_get_value(id: u32, ping_name: &nsACString) -> i64 {
+ with_metric!(QUANTITY_MAP, id, metric, test_get!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_quantity_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(QUANTITY_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/rate.rs b/toolkit/components/glean/api/src/ffi/rate.rs
new file mode 100644
index 0000000000..c14b33f3ea
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/rate.rs
@@ -0,0 +1,40 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_rate_add_to_numerator(id: u32, amount: i32) {
+ with_metric!(RATE_MAP, id, metric, metric.add_to_numerator(amount));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_rate_add_to_denominator(id: u32, amount: i32) {
+ with_metric!(RATE_MAP, id, metric, metric.add_to_denominator(amount));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_rate_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(RATE_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_rate_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ num: &mut i32,
+ den: &mut i32,
+) {
+ let rate = with_metric!(RATE_MAP, id, metric, test_get!(metric, ping_name));
+ *num = rate.numerator;
+ *den = rate.denominator;
+}
+
+#[no_mangle]
+pub extern "C" fn fog_rate_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(RATE_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/string.rs b/toolkit/components/glean/api/src/ffi/string.rs
new file mode 100644
index 0000000000..fc28e03a38
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/string.rs
@@ -0,0 +1,33 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_string_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(STRING_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ value: &mut nsACString,
+) {
+ let val = with_metric!(STRING_MAP, id, metric, test_get!(metric, ping_name));
+ value.assign(&val);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_set(id: u32, value: &nsACString) {
+ with_metric!(STRING_MAP, id, metric, metric.set(value.to_utf8()));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(STRING_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/string_list.rs b/toolkit/components/glean/api/src/ffi/string_list.rs
new file mode 100644
index 0000000000..42ebd9e445
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/string_list.rs
@@ -0,0 +1,42 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::{nsACString, nsCString};
+use thin_vec::ThinVec;
+
+#[no_mangle]
+pub extern "C" fn fog_string_list_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(STRING_LIST_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_list_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ value: &mut ThinVec<nsCString>,
+) {
+ let val = with_metric!(STRING_LIST_MAP, id, metric, test_get!(metric, ping_name));
+ for v in val {
+ value.push(v.into());
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_list_add(id: u32, value: &nsACString) {
+ with_metric!(STRING_LIST_MAP, id, metric, metric.add(value.to_utf8()));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_list_set(id: u32, value: &ThinVec<nsCString>) {
+ let value = value.iter().map(|s| s.to_utf8().into()).collect();
+ with_metric!(STRING_LIST_MAP, id, metric, metric.set(value));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_string_list_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(STRING_LIST_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/text.rs b/toolkit/components/glean/api/src/ffi/text.rs
new file mode 100644
index 0000000000..da46fb849f
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/text.rs
@@ -0,0 +1,29 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_text_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(TEXT_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_text_test_get_value(id: u32, ping_name: &nsACString, value: &mut nsACString) {
+ let val = with_metric!(TEXT_MAP, id, metric, test_get!(metric, ping_name));
+ value.assign(&val);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_text_set(id: u32, value: &nsACString) {
+ with_metric!(TEXT_MAP, id, metric, metric.set(value.to_utf8()));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_text_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(TEXT_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/timespan.rs b/toolkit/components/glean/api/src/ffi/timespan.rs
new file mode 100644
index 0000000000..5de411ffab
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/timespan.rs
@@ -0,0 +1,48 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_start(id: u32) {
+ with_metric!(TIMESPAN_MAP, id, metric, metric.start());
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_stop(id: u32) {
+ with_metric!(TIMESPAN_MAP, id, metric, metric.stop());
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_cancel(id: u32) {
+ with_metric!(TIMESPAN_MAP, id, metric, metric.cancel());
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_set_raw(id: u32, duration: u32) {
+ with_metric!(
+ TIMESPAN_MAP,
+ id,
+ metric,
+ metric.set_raw_unitless(duration.into())
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(TIMESPAN_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_test_get_value(id: u32, ping_name: &nsACString) -> u64 {
+ with_metric!(TIMESPAN_MAP, id, metric, test_get!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timespan_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(TIMESPAN_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/timing_distribution.rs b/toolkit/components/glean/api/src/ffi/timing_distribution.rs
new file mode 100644
index 0000000000..4ac5d03986
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/timing_distribution.rs
@@ -0,0 +1,90 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+use std::time::Duration;
+use thin_vec::ThinVec;
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_start(id: u32) -> u64 {
+ with_metric!(TIMING_DISTRIBUTION_MAP, id, metric, metric.start().id)
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_stop_and_accumulate(id: u32, timing_id: u64) {
+ with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.stop_and_accumulate(timing_id.into())
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_accumulate_raw_nanos(id: u32, sample: u64) {
+ with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.accumulate_raw_duration(Duration::from_nanos(sample))
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_cancel(id: u32, timing_id: u64) {
+ with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ metric.cancel(timing_id.into())
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_has!(metric, ping_name)
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ sum: &mut u64,
+ buckets: &mut ThinVec<u64>,
+ counts: &mut ThinVec<u64>,
+) {
+ let val = with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ *sum = val.sum as _;
+ for (&bucket, &count) in val.values.iter() {
+ buckets.push(bucket as _);
+ counts.push(count as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_timing_distribution_test_get_error(
+ id: u32,
+
+ error_str: &mut nsACString,
+) -> bool {
+ let err = with_metric!(
+ TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/url.rs b/toolkit/components/glean/api/src/ffi/url.rs
new file mode 100644
index 0000000000..b94915f7cf
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/url.rs
@@ -0,0 +1,29 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+#[no_mangle]
+pub extern "C" fn fog_url_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(URL_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_url_test_get_value(id: u32, ping_name: &nsACString, value: &mut nsACString) {
+ let val = with_metric!(URL_MAP, id, metric, test_get!(metric, ping_name));
+ value.assign(&val);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_url_set(id: u32, value: &nsACString) {
+ with_metric!(URL_MAP, id, metric, metric.set(value.to_utf8()));
+}
+
+#[no_mangle]
+pub extern "C" fn fog_url_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(URL_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/uuid.rs b/toolkit/components/glean/api/src/ffi/uuid.rs
new file mode 100644
index 0000000000..e3101863ea
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/uuid.rs
@@ -0,0 +1,37 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+use uuid::Uuid;
+
+#[no_mangle]
+pub extern "C" fn fog_uuid_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ with_metric!(UUID_MAP, id, metric, test_has!(metric, ping_name))
+}
+
+#[no_mangle]
+pub extern "C" fn fog_uuid_test_get_value(id: u32, ping_name: &nsACString, value: &mut nsACString) {
+ let uuid = with_metric!(UUID_MAP, id, metric, test_get!(metric, ping_name)).to_string();
+ value.assign(&uuid);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_uuid_set(id: u32, value: &nsACString) {
+ if let Ok(uuid) = Uuid::parse_str(&value.to_utf8()) {
+ with_metric!(UUID_MAP, id, metric, metric.set(uuid));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_uuid_generate_and_set(id: u32) {
+ with_metric!(UUID_MAP, id, metric, metric.generate_and_set());
+}
+
+#[no_mangle]
+pub extern "C" fn fog_uuid_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = with_metric!(UUID_MAP, id, metric, test_get_errors!(metric));
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}