diff options
Diffstat (limited to 'toolkit/components/glean/api/src/ffi')
-rw-r--r-- | toolkit/components/glean/api/src/ffi/event.rs | 90 | ||||
-rw-r--r-- | toolkit/components/glean/api/src/ffi/macros.rs | 61 | ||||
-rw-r--r-- | toolkit/components/glean/api/src/ffi/mod.rs | 268 |
3 files changed, 419 insertions, 0 deletions
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..6a433eda54 --- /dev/null +++ b/toolkit/components/glean/api/src/ffi/event.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, 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<i32>, + extra_values: &ThinVec<nsCString>, +) { + // If no extra keys are passed, we can shortcut here. + if extra_keys.is_empty() { + if metric_maps::event_record_wrapper(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, v.to_string())) + .collect(); + match metric_maps::event_record_wrapper(id, extra) { + Ok(()) => {} + Err(EventRecordingError::InvalidId) => panic!("No event for id {}", id), + Err(EventRecordingError::InvalidExtraKey) => { + panic!("Invalid extra keys in map for id {}", id) + } + } +} + +#[no_mangle] +pub extern "C" fn fog_event_record_str( + 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 metric_maps::event_record_wrapper_str(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(); + match metric_maps::event_record_wrapper_str(id, extra) { + Ok(()) => {} + Err(EventRecordingError::InvalidId) => panic!("No event for id {}", id), + Err(EventRecordingError::InvalidExtraKey) => { + panic!("Invalid extra keys in map for id {}", id) + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn fog_event_test_has_value(id: u32, storage_name: &nsACString) -> bool { + metric_maps::event_test_get_value_wrapper(id, &storage_name.to_utf8()).is_some() +} 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..c6776e96af --- /dev/null +++ b/toolkit/components/glean/api/src/ffi/macros.rs @@ -0,0 +1,61 @@ +// 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. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +macro_rules! metric_get { + ($map:ident, $id:ident) => { + match $crate::metrics::__glean_metric_maps::$map.get(&$id.into()) { + Some(metric) => metric, + None => panic!("No metric for id {}", $id), + } + }; +} + +/// Test whether a value is stored for the metric identified by its ID. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$storage` - the storage name to look into. +macro_rules! test_has { + ($map:ident, $id:ident, $storage:ident) => {{ + let metric = metric_get!($map, $id); + 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 metric identified by its ID. +/// +/// # Arguments +/// +/// * `$map` - The name of the hash map within `metrics::__glean_metric_maps` +/// as generated by glean_parser. +/// * `$id` - The ID of the metric to get. +/// * `$storage` - the storage name to look into. +macro_rules! test_get { + ($map:ident, $id:ident, $storage:ident) => {{ + let metric = metric_get!($map, $id); + let storage = if $storage.is_empty() { + None + } else { + Some($storage.to_utf8()) + }; + metric.test_get_value(storage.as_deref()).unwrap() + }}; +} 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..b5ba32b430 --- /dev/null +++ b/toolkit/components/glean/api/src/ffi/mod.rs @@ -0,0 +1,268 @@ +// 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, nsCString}; +use thin_vec::ThinVec; +use uuid::Uuid; + +#[macro_use] +mod macros; +mod event; + +#[no_mangle] +pub unsafe extern "C" fn fog_counter_add(id: u32, amount: i32) { + let metric = metric_get!(COUNTER_MAP, id); + metric.add(amount); +} + +#[no_mangle] +pub unsafe extern "C" fn fog_counter_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(COUNTER_MAP, id, storage_name) +} + +#[no_mangle] +pub unsafe extern "C" fn fog_counter_test_get_value(id: u32, storage_name: &nsACString) -> i32 { + test_get!(COUNTER_MAP, id, storage_name) +} + +#[no_mangle] +pub unsafe extern "C" fn fog_timespan_start(id: u32) { + let metric = metric_get!(TIMESPAN_MAP, id); + metric.start(); +} + +#[no_mangle] +pub unsafe extern "C" fn fog_timespan_stop(id: u32) { + let metric = metric_get!(TIMESPAN_MAP, id); + metric.stop(); +} + +#[no_mangle] +pub unsafe extern "C" fn fog_timespan_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(TIMESPAN_MAP, id, storage_name) +} + +#[no_mangle] +pub unsafe extern "C" fn fog_timespan_test_get_value(id: u32, storage_name: &nsACString) -> u64 { + test_get!(TIMESPAN_MAP, id, storage_name) +} + +#[no_mangle] +pub unsafe extern "C" fn fog_boolean_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(BOOLEAN_MAP, id, storage_name) +} + +#[no_mangle] +pub unsafe extern "C" fn fog_boolean_test_get_value(id: u32, storage_name: &nsACString) -> bool { + test_get!(BOOLEAN_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_boolean_set(id: u32, value: bool) { + let metric = metric_get!(BOOLEAN_MAP, id); + metric.set(value); +} + +// The String functions are custom because test_get needs to use an outparam. +// If we can make test_get optional, we can go back to using the macro to +// generate the rest of the functions, or something. + +#[no_mangle] +pub extern "C" fn fog_string_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(STRING_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_string_test_get_value( + id: u32, + storage_name: &nsACString, + value: &mut nsACString, +) { + let val = test_get!(STRING_MAP, id, storage_name); + value.assign(&val); +} + +#[no_mangle] +pub extern "C" fn fog_string_set(id: u32, value: &nsACString) { + let metric = metric_get!(STRING_MAP, id); + metric.set(value.to_utf8()); +} + +// String List Functions: + +#[no_mangle] +pub extern "C" fn fog_string_list_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(STRING_LIST_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_string_list_test_get_value( + id: u32, + storage_name: &nsACString, + value: &mut ThinVec<nsCString>, +) { + let val = test_get!(STRING_LIST_MAP, id, storage_name); + for v in val { + value.push(v.into()); + } +} + +#[no_mangle] +pub extern "C" fn fog_string_list_add(id: u32, value: &nsACString) { + let metric = metric_get!(STRING_LIST_MAP, id); + metric.add(value.to_utf8()); +} + +#[no_mangle] +pub extern "C" fn fog_string_list_set(id: u32, value: &ThinVec<nsCString>) { + let metric = metric_get!(STRING_LIST_MAP, id); + let value = value.iter().map(|s| s.to_utf8().into()).collect(); + metric.set(value); +} + +// The Uuid functions are custom because test_get needs to use an outparam. +// If we can make test_get optional, we can go back to using the macro to +// generate the rest of the functions, or something. + +#[no_mangle] +pub extern "C" fn fog_uuid_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(UUID_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_uuid_test_get_value( + id: u32, + storage_name: &nsACString, + value: &mut nsACString, +) { + let uuid = test_get!(UUID_MAP, id, storage_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()) { + let metric = metric_get!(UUID_MAP, id); + metric.set(uuid); + } +} + +#[no_mangle] +pub extern "C" fn fog_uuid_generate_and_set(id: u32) { + let metric = metric_get!(UUID_MAP, id); + metric.generate_and_set(); +} + +#[no_mangle] +pub extern "C" fn fog_datetime_test_has_value(id: u32, storage_name: &nsACString) -> bool { + test_has!(DATETIME_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_datetime_test_get_value( + id: u32, + storage_name: &nsACString, + value: &mut nsACString, +) { + let val = test_get!(DATETIME_MAP, id, storage_name); + value.assign(&val.to_rfc3339()); +} + +#[no_mangle] +pub extern "C" fn fog_datetime_set( + id: u32, + year: i32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + nano: u32, + offset_seconds: i32, +) { + let metric = metric_get!(DATETIME_MAP, id); + metric.set_with_details(year, month, day, hour, minute, second, nano, offset_seconds); +} + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_test_has_value( + id: u32, + storage_name: &nsACString, +) -> bool { + test_has!(MEMORY_DISTRIBUTION_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_test_get_value( + id: u32, + storage_name: &nsACString, + sum: &mut u64, + buckets: &mut ThinVec<u64>, + counts: &mut ThinVec<u64>, +) { + let val = test_get!(MEMORY_DISTRIBUTION_MAP, id, storage_name); + *sum = val.sum; + for (&bucket, &count) in val.values.iter() { + buckets.push(bucket); + counts.push(count); + } +} + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_accumulate(id: u32, sample: u64) { + let metric = metric_get!(MEMORY_DISTRIBUTION_MAP, id); + metric.accumulate(sample); +} + +#[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()); +} + +#[no_mangle] +pub extern "C" fn fog_timing_distribution_start(id: u32) -> u64 { + let metric = metric_get!(TIMING_DISTRIBUTION_MAP, id); + metric.start() +} + +#[no_mangle] +pub extern "C" fn fog_timing_distribution_stop_and_accumulate(id: u32, timing_id: u64) { + let metric = metric_get!(TIMING_DISTRIBUTION_MAP, id); + metric.stop_and_accumulate(timing_id); +} + +#[no_mangle] +pub extern "C" fn fog_timing_distribution_cancel(id: u32, timing_id: u64) { + let metric = metric_get!(TIMING_DISTRIBUTION_MAP, id); + metric.cancel(timing_id); +} + +#[no_mangle] +pub extern "C" fn fog_timing_distribution_test_has_value(id: u32, ping_name: &nsACString) -> bool { + test_has!(TIMING_DISTRIBUTION_MAP, id, 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 = test_get!(TIMING_DISTRIBUTION_MAP, id, ping_name); + *sum = val.sum; + for (&bucket, &count) in val.values.iter() { + buckets.push(bucket); + counts.push(count); + } +} |