Backed out 8 changesets (bug 1677455, bug 1678583, bug 1677962, bug 1675286) for xpcshell failures at toolkit/components/glean/xpcshell/test_Glean.js on a CLOSED TREE

Backed out changeset ab0d5057817b (bug 1677455)
Backed out changeset 1e26d2cecfe7 (bug 1678583)
Backed out changeset 555eb48642be (bug 1678583)
Backed out changeset 7346565f35b4 (bug 1678583)
Backed out changeset 0e40c4b34ec7 (bug 1678583)
Backed out changeset b3b6cfebc8e3 (bug 1677962)
Backed out changeset 6c6a59d7c472 (bug 1675286)
Backed out changeset 45c8c0f1d7bf (bug 1675286)
This commit is contained in:
Cristina Coroiu 2020-12-09 16:38:20 +02:00
Родитель 4eae934956
Коммит dfff858221
44 изменённых файлов: 635 добавлений и 1186 удалений

2
Cargo.lock сгенерированный
Просмотреть файл

@ -1523,6 +1523,7 @@ dependencies = [
"bincode",
"chrono",
"crossbeam-channel",
"ffi-support",
"glean",
"glean-core",
"inherent",
@ -1531,7 +1532,6 @@ dependencies = [
"once_cell",
"serde",
"tempfile",
"thin-vec",
"thiserror",
"uuid",
"xpcom",

Просмотреть файл

@ -8,6 +8,7 @@ publish = false
[dependencies]
bincode = "1.0"
chrono = "0.4.10"
ffi-support = "0.4"
glean = "33.7.0"
glean-core = { version = "33.7.0", features = ["rkv-safe-mode"] }
inherent = "0.1.4"
@ -19,7 +20,6 @@ uuid = { version = "0.8.1", features = ["v4"] }
xpcom = { path = "../../../../xpcom/rust/xpcom", optional = true }
thiserror = "1.0.4"
crossbeam-channel = "0.4.3"
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
[dev-dependencies]
tempfile = "3.1.0"

Просмотреть файл

@ -1,90 +0,0 @@
// 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()
}

Просмотреть файл

@ -2,54 +2,84 @@
// 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.
// TODO: Allow argument conversions, so that implementations don't need to do the boilerplate
// themselves.
/// Get a metric object by ID from the corresponding map.
/// Define the global handle map, constructor and destructor functions and any user-defined
/// functions for a new metric
///
/// This allows to define most common functionality and simple operations for a metric type.
/// More complex operations should be written as plain functions directly.
///
/// # 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),
/// * `$metric_map` - name of the map in `metrics::__glean_metric_maps` to fetch metrics from, should be all uppercase, e.g. `COUNTERS`.
/// * `$ffi_has_fn` - FFI name of the `test_has_value` function, something like `fog_counter_test_has_value`.
/// * `$ffi_get_fn` - FFI name of the `test_get_value` function, something like `fog_counter_test_get_value`.
/// * `get_ret` - Return value of the `test_get_value` function.
///
/// Additional simple functions can be defined as a mapping `$op -> $ffi_fn`:
///
/// * `$op` - function on the metric type to call.
/// * `$op_fn` - FFI function name for the operation, followed by its arguments and the return type.
/// Arguments are passed on as is.
/// If arguments need a conversion before being passed to the metric implementation
/// the FFI function needs to be implemented manually.
macro_rules! define_metric_ffi {
($metric_map:ident {
test_has -> $ffi_has_fn:ident,
test_get -> $ffi_get_fn:ident: $get_ret:ty,
$(
$op:ident -> $ffi_fn:ident($($op_argname:ident: $op_argtyp:ty),* $(,)*)
),* $(,)*
}) => {
$(
#[no_mangle]
pub extern "C" fn $ffi_fn(id: u32, $($op_argname: $op_argtyp),*) {
match $crate::metrics::__glean_metric_maps::$metric_map.get(&id.into()) {
Some(metric) => metric.$op($($op_argname),*),
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 = $storage.to_utf8();
let storage = Some(&storage[..]);
metric.test_get_value(storage).is_some()
}};
}
/// FFI wrapper to test whether a value is stored for the metric.
///
/// Returns `1` if a metric value exists, otherwise `0`.
///
/// Panics if no metric exists for the given ID.
/// This indicates a bug in the calling code, as this method is only called through generated code,
/// which will guarantee the use of valid IDs.
#[no_mangle]
pub unsafe extern "C" fn $ffi_has_fn(id: u32, storage_name: *const ::std::os::raw::c_char) -> u8 {
let storage_name = match ::std::ffi::CStr::from_ptr(storage_name).to_str() {
Ok(s) => s,
Err(e) => panic!("Invalid string for storage name, metric id {}, error: {:?}", id, e),
};
match $crate::metrics::__glean_metric_maps::$metric_map.get(&id.into()) {
Some(metric) => metric.test_get_value(storage_name).is_some() as u8,
None => panic!("No metric for id {}", id)
}
}
/// 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 = $storage.to_utf8();
let storage = Some(&storage[..]);
metric.test_get_value(storage).unwrap()
}};
/// FFI wrapper to get the currently stored value.
///
/// Returns the value of the stored metric.
///
/// Panics if no metric exists for the given ID.
/// This indicates a bug in the calling code, as this method is only called through generated code,
/// which will guarantee the use of valid IDs.
#[no_mangle]
pub unsafe extern "C" fn $ffi_get_fn(id: u32, storage_name: *const ::std::os::raw::c_char) -> $get_ret {
let storage_name = match ::std::ffi::CStr::from_ptr(storage_name).to_str() {
Ok(s) => s,
Err(e) => panic!("Invalid string for storage name, metric id {}, error: {:?}", id, e),
};
match $crate::metrics::__glean_metric_maps::$metric_map.get(&id.into()) {
Some(metric) => metric.test_get_value(storage_name).unwrap().into(),
None => panic!("No metric for id {}", id)
}
}
}
}

Просмотреть файл

@ -2,66 +2,37 @@
// 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, uuid::Uuid};
#[macro_use]
mod macros;
mod event;
use ffi_support::FfiStr;
#[cfg(feature = "with_gecko")]
use {nsstring::nsACString, uuid::Uuid};
define_metric_ffi!(COUNTER_MAP {
test_has -> fog_counter_test_has_value,
test_get -> fog_counter_test_get_value: i32,
add -> fog_counter_add(amount: i32),
});
define_metric_ffi!(TIMESPAN_MAP {
test_has -> fog_timespan_test_has_value,
test_get -> fog_timespan_test_get_value: u64,
start -> fog_timespan_start(),
stop -> fog_timespan_stop(),
});
define_metric_ffi!(BOOLEAN_MAP {
test_has -> fog_boolean_test_has_value,
test_get -> fog_boolean_test_get_value: u8,
});
#[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);
pub extern "C" fn fog_boolean_set(id: u32, value: u8) {
match crate::metrics::__glean_metric_maps::BOOLEAN_MAP.get(&id.into()) {
Some(metric) => metric.set(value != 0),
None => panic!("No metric for id {}", id),
}
}
// The String functions are custom because test_get needs to use an outparam.
@ -69,24 +40,31 @@ pub extern "C" fn fog_boolean_set(id: u32, value: bool) {
// 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)
pub extern "C" fn fog_string_test_has_value(id: u32, storage_name: FfiStr) -> u8 {
match crate::metrics::__glean_metric_maps::STRING_MAP.get(&id.into()) {
Some(metric) => metric.test_get_value(storage_name.as_str()).is_some() as u8,
None => panic!("No metric for id {}", id),
}
}
#[cfg(feature = "with_gecko")]
#[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);
pub extern "C" fn fog_string_test_get_value(id: u32, storage_name: FfiStr, value: &mut nsACString) {
match crate::metrics::__glean_metric_maps::STRING_MAP.get(&id.into()) {
Some(metric) => {
value.assign(&metric.test_get_value(storage_name.as_str()).unwrap());
}
None => panic!("No metric for id {}", id),
}
}
#[cfg(feature = "with_gecko")]
#[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());
match crate::metrics::__glean_metric_maps::STRING_MAP.get(&id.into()) {
Some(metric) => metric.set(value.to_utf8()),
None => panic!("No metric for id {}", id),
}
}
// The Uuid functions are custom because test_get needs to use an outparam.
@ -94,47 +72,71 @@ pub extern "C" fn fog_string_set(id: u32, value: &nsACString) {
// 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)
pub extern "C" fn fog_uuid_test_has_value(id: u32, storage_name: FfiStr) -> u8 {
match crate::metrics::__glean_metric_maps::UUID_MAP.get(&id.into()) {
Some(metric) => metric.test_get_value(storage_name.as_str()).is_some() as u8,
None => panic!("No metric for id {}", id),
}
}
#[cfg(feature = "with_gecko")]
#[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);
pub extern "C" fn fog_uuid_test_get_value(id: u32, storage_name: FfiStr, value: &mut nsACString) {
match crate::metrics::__glean_metric_maps::UUID_MAP.get(&id.into()) {
Some(uuid) => {
value.assign(
&uuid
.test_get_value(storage_name.as_str())
.unwrap()
.to_string(),
);
}
None => panic!("No metric for id {}", id),
}
}
#[cfg(feature = "with_gecko")]
#[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);
match crate::metrics::__glean_metric_maps::UUID_MAP.get(&id.into()) {
Some(metric) => metric.set(uuid),
None => panic!("No metric for id {}", id),
}
}
}
#[no_mangle]
pub extern "C" fn fog_uuid_generate_and_set(id: u32) {
let metric = metric_get!(UUID_MAP, id);
metric.generate_and_set();
match crate::metrics::__glean_metric_maps::UUID_MAP.get(&id.into()) {
Some(metric) => {
metric.generate_and_set();
}
None => panic!("No metric for id {}", id),
}
}
#[no_mangle]
pub extern "C" fn fog_datetime_test_has_value(id: u32, storage_name: &nsACString) -> bool {
test_has!(DATETIME_MAP, id, storage_name)
pub extern "C" fn fog_datetime_test_has_value(id: u32, storage_name: FfiStr) -> u8 {
match crate::metrics::__glean_metric_maps::DATETIME_MAP.get(&id.into()) {
Some(metric) => metric.test_get_value(storage_name.as_str()).is_some() as u8,
None => panic!("No metric for id {}", id),
}
}
#[cfg(feature = "with_gecko")]
#[no_mangle]
pub extern "C" fn fog_datetime_test_get_value(
id: u32,
storage_name: &nsACString,
storage_name: FfiStr,
value: &mut nsACString,
) {
let val = test_get!(DATETIME_MAP, id, storage_name);
value.assign(&val);
match crate::metrics::__glean_metric_maps::DATETIME_MAP.get(&id.into()) {
Some(metric) => {
value.assign(&metric.test_get_value(storage_name.as_str()).unwrap());
}
None => panic!("No metric for id {}", id),
}
}
#[no_mangle]
@ -149,6 +151,10 @@ pub extern "C" fn fog_datetime_set(
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);
match crate::metrics::__glean_metric_maps::DATETIME_MAP.get(&id.into()) {
Some(metric) => {
metric.set_with_details(year, month, day, hour, minute, second, nano, offset_seconds);
}
None => panic!("No metric for id {}", id),
}
}

Просмотреть файл

@ -44,27 +44,4 @@ pub(crate) mod __glean_metric_maps {
pub static STRING_LIST_MAP: Lazy<HashMap<MetricId, &Lazy<StringListMetric>>> =
Lazy::new(HashMap::new);
pub static UUID_MAP: Lazy<HashMap<MetricId, &Lazy<UuidMetric>>> = Lazy::new(HashMap::new);
pub(crate) fn event_record_wrapper(
_metric_id: u32,
_extra: Option<HashMap<i32, String>>,
) -> Result<(), EventRecordingError> {
Err(EventRecordingError::InvalidId)
}
pub(crate) fn event_record_wrapper_str(
_metric_id: u32,
_extra: Option<HashMap<String, String>>,
) -> Result<(), EventRecordingError> {
Err(EventRecordingError::InvalidId)
}
pub(crate) fn event_test_get_value_wrapper(
_metric_id: u32,
_storage_name: &str,
) -> Option<Vec<RecordedEvent>> {
None
}
}

Просмотреть файл

@ -78,8 +78,6 @@ impl DatetimeMetric {
/// * `nano` - the nanosecond fraction to the last whole second.
/// * `offset_seconds` - the timezone difference, in seconds, for the Eastern
/// Hemisphere. Negative seconds mean Western Hemisphere.
#[cfg_attr(not(feature = "with-gecko"), allow(dead_code))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn set_with_details(
&self,
year: i32,
@ -134,7 +132,7 @@ impl DatetimeMetric {
/// ## Return value
///
/// Returns the stored value or `None` if nothing stored.
pub fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, storage_name: S) -> Option<String> {
pub fn test_get_value(&self, storage_name: &str) -> Option<String> {
match self {
DatetimeMetric::Parent(p) => {
dispatcher::block_on_queue();
@ -157,7 +155,6 @@ impl DatetimeMetricImpl {
crate::with_glean(move |glean| self.0.set(glean, value))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn set_with_details(
&self,
year: i32,
@ -184,10 +181,8 @@ impl DatetimeMetricImpl {
})
}
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, storage_name: S) -> Option<String> {
// FIXME(bug 1677448): This hack goes away when the type is implemented in RLB.
let storage = storage_name.into().expect("storage name required.");
crate::with_glean(move |glean| self.0.test_get_value_as_string(glean, storage))
pub fn test_get_value(&self, storage_name: &str) -> Option<String> {
crate::with_glean(move |glean| self.0.test_get_value_as_string(glean, storage_name))
}
}

Просмотреть файл

@ -3,32 +3,78 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::collections::HashMap;
use inherent::inherent;
use std::hash::Hash;
use std::marker::PhantomData;
use std::sync::Arc;
use super::{CommonMetricData, Instant, MetricId, RecordedEvent};
use crate::dispatcher;
use crate::ipc::{need_ipc, with_ipc_payload};
use glean_core::traits::Event;
pub use glean_core::traits::{EventRecordingError, ExtraKeys, NoExtraKeys};
/// Extra keys for events.
///
/// Extra keys need to be pre-defined and map to a string representation.
///
/// For user-defined `EventMetric`s these will be defined as `enums`.
/// Each variant will correspond to an entry in the `ALLOWED_KEYS` list.
/// The Glean SDK requires the keys as strings for submission in pings,
/// whereas in code we want to provide users a type to work with
/// (e.g. to avoid typos or misuse of the API).
pub trait ExtraKeys: Hash + Eq + PartialEq + Copy {
/// List of allowed extra keys as strings.
const ALLOWED_KEYS: &'static [&'static str];
/// The index of the extra key.
///
/// It corresponds to its position in the associated `ALLOWED_KEYS` list.
///
/// *Note*: An index of `-1` indicates an invalid / non-existing extra key.
/// Invalid / non-existing extra keys will be recorded as an error.
/// This cannot happen for generated code.
fn index(self) -> i32;
}
/// Default of no extra keys for events.
///
/// An enum with no values for convenient use as the default set of extra keys
/// that an `EventMetric` can accept.
///
/// *Note*: There exist no values for this enum, it can never exist.
/// It its equivalent to the [`never / !` type](https://doc.rust-lang.org/std/primitive.never.html).
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum NoExtraKeys {}
impl ExtraKeys for NoExtraKeys {
const ALLOWED_KEYS: &'static [&'static str] = &[];
fn index(self) -> i32 {
// This index will never be used.
-1
}
}
/// An event metric.
///
/// Events allow recording of e.g. individual occurences of user actions, say
/// every time a view was open and from where. Each time you record an event, it
/// records a timestamp, the event's name and a set of custom values.
#[derive(Debug)]
pub enum EventMetric<K> {
Parent {
/// The metric's ID.
///
/// **TEST-ONLY** - Do not use unless gated with `#[cfg(test)]`.
id: MetricId,
inner: glean::private::EventMetric<K>,
inner: Arc<EventMetricImpl<K>>,
},
Child(EventMetricIpc),
}
#[derive(Clone, Debug)]
pub struct EventMetricImpl<K> {
inner: glean_core::metrics::EventMetric,
extra_keys: PhantomData<K>,
}
#[derive(Debug)]
pub struct EventMetricIpc(MetricId);
@ -38,7 +84,7 @@ impl<K: 'static + ExtraKeys + Send + Sync> EventMetric<K> {
if need_ipc() {
EventMetric::Child(EventMetricIpc(id))
} else {
let inner = glean::private::EventMetric::new(meta);
let inner = Arc::new(EventMetricImpl::new(meta));
EventMetric::Parent { id, inner }
}
}
@ -50,16 +96,22 @@ impl<K: 'static + ExtraKeys + Send + Sync> EventMetric<K> {
EventMetric::Child(_) => panic!("Can't get a child metric from a child metric"),
}
}
}
#[inherent(pub)]
impl<K: 'static + ExtraKeys + Send + Sync> Event for EventMetric<K> {
type Extra = K;
fn record<M: Into<Option<HashMap<K, String>>>>(&self, extra: M) {
/// Record an event.
///
/// Records an event under this metric's name and attaches a timestamp.
/// If given a map of extra values is added to the event log.
///
/// ## Arguments
///
/// * `extra` - An (optional) map of (key, value) pairs.
pub fn record<M: Into<Option<HashMap<K, String>>> + Send + Sync>(&self, extra: M) {
match self {
EventMetric::Parent { inner, .. } => {
inner.record(extra);
let metric = Arc::clone(&inner);
let extra = extra.into();
let now = Instant::now();
dispatcher::launch(move || metric.record(now, extra));
}
EventMetric::Child(c) => {
let extra = extra.into().map(|hash_map| {
@ -82,33 +134,61 @@ impl<K: 'static + ExtraKeys + Send + Sync> Event for EventMetric<K> {
}
}
fn test_get_value<'a, S: Into<Option<&'a str>>>(
&self,
ping_name: S,
) -> Option<Vec<RecordedEvent>> {
/// **Test-only API.**
///
/// Get the currently stored events for this event metric as a JSON-encoded string.
/// This doesn't clear the stored value.
///
/// ## Note
///
/// This currently returns the value as a JSON encoded string.
/// `glean_core` doesn't expose the underlying recorded event type.
/// This will eventually change to a proper `RecordedEventData` type.
/// See [Bug 1635074](https://bugzilla.mozilla.org/show_bug.cgi?id=1635074).
///
/// ## Arguments
///
/// * `storage_name` - the storage name to look into.
///
/// ## Return value
///
/// Returns the stored value or `None` if nothing stored.
pub fn test_get_value(&self, storage_name: &str) -> Option<Vec<RecordedEvent>> {
match self {
EventMetric::Parent { inner, .. } => inner.test_get_value(ping_name),
EventMetric::Parent { inner, .. } => {
dispatcher::block_on_queue();
inner.test_get_value(storage_name)
}
EventMetric::Child(_) => {
panic!("Cannot get test value for event metric in non-parent process!",)
}
}
}
}
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
&self,
error: glean::ErrorType,
ping_name: S,
) -> i32 {
match self {
EventMetric::Parent { inner, .. } => {
inner.test_get_num_recorded_errors(error, ping_name)
}
EventMetric::Child(c) => panic!(
"Cannot get the number of recorded errors for {:?} in non-parent process!",
c.0
),
impl<K: ExtraKeys> EventMetricImpl<K> {
pub fn new(meta: CommonMetricData) -> Self {
let allowed_extra_keys = K::ALLOWED_KEYS.iter().map(|s| s.to_string()).collect();
let inner = glean_core::metrics::EventMetric::new(meta, allowed_extra_keys);
Self {
inner,
extra_keys: PhantomData,
}
}
pub fn record<M: Into<Option<HashMap<K, String>>>>(&self, then: Instant, extra: M) {
// Translate from [ExtraKey -> String] to a [Int -> String] map
let extra = extra
.into()
.map(|h| h.into_iter().map(|(k, v)| (k.index(), v)).collect());
crate::with_glean(|glean| self.inner.record(glean, then.as_millis(), extra))
}
pub fn test_get_value(&self, storage_name: &str) -> Option<Vec<RecordedEvent>> {
crate::with_glean(|glean| self.inner.test_get_value(glean, storage_name))
}
}
#[cfg(test)]

Просмотреть файл

@ -33,7 +33,7 @@ mod uuid;
pub use self::boolean::BooleanMetric;
pub use self::counter::CounterMetric;
pub use self::datetime::DatetimeMetric;
pub use self::event::{EventMetric, EventRecordingError, ExtraKeys, NoExtraKeys};
pub use self::event::{EventMetric, ExtraKeys, NoExtraKeys};
pub use self::labeled::LabeledMetric;
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::ping::Ping;
@ -75,6 +75,12 @@ impl Instant {
fn as_nanos(&self) -> u64 {
self.0
}
/// Get this instant as a timestamp in milliseconds.
fn as_millis(&self) -> u64 {
const NANOS_PER_MILLI: u64 = 1_000_000;
self.0 / NANOS_PER_MILLI
}
}
/// Uniquely identifies a single metric within its metric type.

Просмотреть файл

@ -2,85 +2,155 @@
// 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/.
use inherent::inherent;
use std::sync::{Arc, RwLock};
use super::{CommonMetricData, MetricId, TimeUnit};
use glean_core::traits::Timespan;
use super::{CommonMetricData, Instant, MetricId, TimeUnit};
use crate::dispatcher;
use crate::ipc::need_ipc;
/// A timespan metric.
///
/// Timespans are used to make a measurement of how much time is spent in a particular task.
#[derive(Debug)]
pub enum TimespanMetric {
Parent(glean::private::TimespanMetric),
Child,
Parent(Arc<TimespanMetricImpl>),
Child(TimespanMetricIpc),
}
#[derive(Debug)]
pub struct TimespanMetricImpl {
inner: RwLock<glean_core::metrics::TimespanMetric>,
}
#[derive(Debug)]
pub struct TimespanMetricIpc;
impl TimespanMetric {
/// Create a new timespan metric.
pub fn new(_id: MetricId, meta: CommonMetricData, time_unit: TimeUnit) -> Self {
if need_ipc() {
TimespanMetric::Child
TimespanMetric::Child(TimespanMetricIpc)
} else {
TimespanMetric::Parent(glean::private::TimespanMetric::new(meta, time_unit))
TimespanMetric::Parent(Arc::new(TimespanMetricImpl::new(meta, time_unit)))
}
}
pub fn start(&self) {
match self {
TimespanMetric::Parent(p) => {
let now = Instant::now();
let metric = Arc::clone(&p);
dispatcher::launch(move || metric.start(now));
}
TimespanMetric::Child(_) => {
log::error!(
"Unable to start timespan metric {:?} in non-main process. Ignoring.",
self
);
// TODO: Record an error.
}
}
}
pub fn stop(&self) {
match self {
TimespanMetric::Parent(p) => {
let now = Instant::now();
let metric = Arc::clone(&p);
dispatcher::launch(move || metric.stop(now));
}
TimespanMetric::Child(_) => {
log::error!(
"Unable to stop timespan metric {:?} in non-main process. Ignoring.",
self
);
// TODO: Record an error.
}
}
}
pub fn cancel(&self) {
match self {
TimespanMetric::Parent(p) => {
let metric = Arc::clone(&p);
dispatcher::launch(move || metric.cancel());
}
TimespanMetric::Child(_) => {
log::error!(
"Unable to cancel timespan metric {:?} in non-main process. Ignoring.",
self
);
// TODO: Record an error.
}
}
}
/// **Test-only API (exported for FFI purposes).**
///
/// Gets the currently stored value as an integer.
/// This doesn't clear the stored value.
///
/// * `storage_name` - the storage name to look into.
///
/// ## Return value
///
/// Returns the stored value or `None` if nothing stored.
pub fn test_get_value(&self, storage_name: &str) -> Option<u64> {
match self {
TimespanMetric::Parent(p) => {
dispatcher::block_on_queue();
p.test_get_value(storage_name)
}
TimespanMetric::Child(_c) => panic!(
"Cannot get test value for {:?} in non-parent process!",
self
),
}
}
}
#[inherent(pub)]
impl Timespan for TimespanMetric {
fn start(&self) {
match self {
TimespanMetric::Parent(p) => Timespan::start(p),
TimespanMetric::Child => {
log::error!("Unable to start timespan metric in non-main process. Ignoring.");
// TODO: Record an error.
}
}
impl TimespanMetricImpl {
fn new(meta: CommonMetricData, time_unit: TimeUnit) -> Self {
let inner = RwLock::new(glean_core::metrics::TimespanMetric::new(meta, time_unit));
Self { inner }
}
fn stop(&self) {
match self {
TimespanMetric::Parent(p) => Timespan::stop(p),
TimespanMetric::Child => {
log::error!("Unable to stop timespan metric in non-main process. Ignoring.");
// TODO: Record an error.
}
}
fn start(&self, now: Instant) {
crate::with_glean(move |glean| {
let mut inner = self
.inner
.write()
.expect("lock of wrapped metric was poisoned");
inner.set_start(glean, now.as_nanos())
})
}
fn stop(&self, now: Instant) {
crate::with_glean(move |glean| {
let mut inner = self
.inner
.write()
.expect("lock of wrapped metric was poisoned");
inner.set_stop(glean, now.as_nanos())
})
}
fn cancel(&self) {
match self {
TimespanMetric::Parent(p) => Timespan::cancel(p),
TimespanMetric::Child => {
log::error!("Unable to cancel timespan metric in non-main process. Ignoring.");
// TODO: Record an error.
}
}
let mut inner = self
.inner
.write()
.expect("lock of wrapped metric was poisoned");
inner.cancel()
}
fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, ping_name: S) -> Option<u64> {
match self {
TimespanMetric::Parent(p) => p.test_get_value(ping_name),
TimespanMetric::Child => {
panic!("Cannot get test value for in non-parent process!");
}
}
}
fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
&self,
error: glean::ErrorType,
ping_name: S,
) -> i32 {
match self {
TimespanMetric::Parent(p) => p.test_get_num_recorded_errors(error, ping_name),
TimespanMetric::Child => {
panic!("Cannot get the number of recorded errors for timespan metric in non-parent process!");
}
}
fn test_get_value(&self, storage_name: &str) -> Option<u64> {
crate::with_glean(move |glean| {
let inner = self
.inner
.read()
.expect("lock of wrapped metric was poisoned");
inner.test_get_value(glean, storage_name)
})
}
}
@ -90,6 +160,7 @@ mod test {
use crate::{common_test::*, ipc, metrics};
#[test]
#[ignore] // TODO: Enable them back when bug 1677455 lands.
fn smoke_test_timespan() {
let _lock = lock_test();
@ -115,6 +186,7 @@ mod test {
}
#[test]
#[ignore] // TODO: Enable them back when bug 1677455 lands.
fn timespan_ipc() {
let _lock = lock_test();
let _raii = ipc::test_set_need_ipc(true);

Просмотреть файл

@ -8,7 +8,6 @@
#include "mozilla/glean/Boolean.h"
#include "mozilla/glean/Counter.h"
#include "mozilla/glean/Datetime.h"
#include "mozilla/glean/Event.h"
#include "mozilla/glean/Timespan.h"
#include "mozilla/glean/String.h"
#include "mozilla/glean/Uuid.h"

Просмотреть файл

@ -17,20 +17,22 @@ NS_IMPL_CLASSINFO(GleanBoolean, nullptr, 0, {0})
NS_IMPL_ISUPPORTS_CI(GleanBoolean, nsIGleanBoolean)
NS_IMETHODIMP
GleanBoolean::Set(bool aValue) {
mBoolean.Set(aValue);
GleanBoolean::Set(bool value, JSContext* cx) {
this->mBoolean.Set(value);
return NS_OK;
}
NS_IMETHODIMP
GleanBoolean::TestGetValue(const nsACString& aStorageName,
JS::MutableHandleValue aResult) {
auto result = mBoolean.TestGetValue(aStorageName);
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {
aResult.set(JS::BooleanValue(result.value()));
}
GleanBoolean::TestHasValue(const nsACString& aStorageName, JSContext* cx,
bool* result) {
*result = this->mBoolean.TestHasValue(PromiseFlatCString(aStorageName).get());
return NS_OK;
}
NS_IMETHODIMP
GleanBoolean::TestGetValue(const nsACString& aStorageName, JSContext* cx,
bool* result) {
*result = this->mBoolean.TestGetValue(PromiseFlatCString(aStorageName).get());
return NS_OK;
}

Просмотреть файл

@ -8,12 +8,16 @@
#define mozilla_glean_GleanBoolean_h
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla {
namespace glean {
namespace impl {
extern "C" {
void fog_boolean_set(uint32_t id, bool value);
uint8_t fog_boolean_test_has_value(uint32_t id, const char* storageName);
uint8_t fog_boolean_test_get_value(uint32_t id, const char* storageName);
}
class BooleanMetric {
public:
@ -26,6 +30,24 @@ class BooleanMetric {
*/
void Set(bool value) const { fog_boolean_set(mId, int(value)); }
/**
* **Test-only API**
*
* Tests whether a value is stored for the metric.
*
* This function will attempt to await the last parent-process task (if any)
* writing to the the metric's storage engine before returning a value.
* This function will not wait for data from child processes.
*
* Parent process only. Panics in child processes.
*
* @param aStorageName the name of the ping to retrieve the metric for.
* @return true if metric value exists, otherwise false
*/
bool TestHasValue(const char* aStorageName) const {
return fog_boolean_test_has_value(mId, aStorageName) != 0;
}
/**
* **Test-only API**
*
@ -40,11 +62,8 @@ class BooleanMetric {
*
* @return value of the stored metric.
*/
Maybe<bool> TestGetValue(const nsACString& aStorageName) const {
if (!fog_boolean_test_has_value(mId, &aStorageName)) {
return Nothing();
}
return Some(fog_boolean_test_get_value(mId, &aStorageName));
bool TestGetValue(const char* aStorageName) const {
return fog_boolean_test_get_value(mId, aStorageName) != 0;
}
private:

Просмотреть файл

@ -1,32 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 http://mozilla.org/MPL/2.0/. */
#include "Common.h"
#include "nsComponentManagerUtils.h"
#include "nsIConsoleService.h"
#include "nsServiceManagerUtils.h"
namespace mozilla::glean {
// This is copied from TelemetryCommons.cpp (and modified because consoleservice
// handles threading), but that one is not exported.
// There's _at least_ a third instance of `LogToBrowserConsole`,
// but that one is slightly different.
void LogToBrowserConsole(uint32_t aLogLevel, const nsAString& aMsg) {
nsCOMPtr<nsIConsoleService> console(
do_GetService("@mozilla.org/consoleservice;1"));
if (!console) {
NS_WARNING("Failed to log message to console.");
return;
}
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(aMsg, u""_ns, u""_ns, 0, 0, aLogLevel, "chrome javascript",
false /* from private window */, true /* from chrome context */);
console->LogMessage(error);
}
} // namespace mozilla::glean

Просмотреть файл

@ -1,25 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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 http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_glean_Common_h
#define mozilla_glean_Common_h
#include "jsapi.h"
#include "nsIScriptError.h"
namespace mozilla::glean {
/**
* Dumps a log message to the Browser Console using the provided level.
*
* @param aLogLevel The level to use when displaying the message in the browser
* console (e.g. nsIScriptError::warningFlag, ...).
* @param aMsg The text message to print to the console.
*/
void LogToBrowserConsole(uint32_t aLogLevel, const nsAString& aMsg);
} // namespace mozilla::glean
#endif /* mozilla_glean_Common_h */

Просмотреть файл

@ -17,14 +17,15 @@ NS_IMPL_ISUPPORTS_CI(GleanCounter, nsIGleanCounter)
NS_IMETHODIMP
GleanCounter::Add(uint32_t aAmount) {
mCounter.Add(aAmount);
this->mCounter.Add(aAmount);
return NS_OK;
}
NS_IMETHODIMP
GleanCounter::TestGetValue(const nsACString& aStorageName,
JS::MutableHandleValue aResult) {
auto result = mCounter.TestGetValue(aStorageName);
auto result =
this->mCounter.TestGetValue(PromiseFlatCString(aStorageName).get());
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {

Просмотреть файл

@ -9,11 +9,15 @@
#include "mozilla/Maybe.h"
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla::glean {
namespace impl {
extern "C" {
void fog_counter_add(uint32_t aId, int32_t aAmount);
uint32_t fog_counter_test_has_value(uint32_t aId, const char* aStorageName);
int32_t fog_counter_test_get_value(uint32_t aId, const char* aStorageName);
}
class CounterMetric {
public:
@ -40,11 +44,11 @@ class CounterMetric {
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<int32_t> TestGetValue(const nsACString& aStorageName) const {
if (!fog_counter_test_has_value(mId, &aStorageName)) {
Maybe<int32_t> TestGetValue(const char* aStorageName) const {
if (!fog_counter_test_has_value(mId, aStorageName)) {
return Nothing();
}
return Some(fog_counter_test_get_value(mId, &aStorageName));
return Some(fog_counter_test_get_value(mId, aStorageName));
}
private:

Просмотреть файл

@ -32,7 +32,7 @@ GleanDatetime::Set(PRTime aValue, uint8_t aOptionalArgc) {
NS_IMETHODIMP
GleanDatetime::TestGetValue(const nsACString& aStorageName, JSContext* aCx,
JS::MutableHandleValue aResult) {
auto result = mDatetime.TestGetValue(aStorageName);
auto result = mDatetime.TestGetValue(PromiseFlatCString(aStorageName).get());
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {

Просмотреть файл

@ -9,13 +9,20 @@
#include "mozilla/Maybe.h"
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "nsString.h"
#include "prtime.h"
namespace mozilla::glean {
namespace impl {
extern "C" {
void fog_datetime_set(uint32_t aId, int32_t aYear, uint32_t aMonth,
uint32_t aDay, uint32_t aHour, uint32_t aMinute,
uint32_t aSecond, uint32_t aNano, int32_t aOffsetSeconds);
uint32_t fog_datetime_test_has_value(uint32_t aId, const char* aStorageName);
void fog_datetime_test_get_value(uint32_t aId, const char* aStorageName,
nsACString& aValue);
}
class DatetimeMetric {
public:
@ -55,12 +62,12 @@ class DatetimeMetric {
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<nsCString> TestGetValue(const nsACString& aStorageName) const {
if (!fog_datetime_test_has_value(mId, &aStorageName)) {
Maybe<nsCString> TestGetValue(const char* aStorageName) const {
if (!fog_datetime_test_has_value(mId, aStorageName)) {
return Nothing();
}
nsCString ret;
fog_datetime_test_get_value(mId, &aStorageName, &ret);
fog_datetime_test_get_value(mId, aStorageName, ret);
return Some(ret);
}

Просмотреть файл

@ -1,91 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 http://mozilla.org/MPL/2.0/. */
#include "mozilla/glean/Event.h"
#include "Common.h"
#include "nsString.h"
#include "mozilla/Components.h"
#include "nsIClassInfoImpl.h"
#include "jsapi.h"
#include "nsIScriptError.h"
namespace mozilla::glean {
NS_IMPL_CLASSINFO(GleanEvent, nullptr, 0, {0})
NS_IMPL_ISUPPORTS_CI(GleanEvent, nsIGleanEvent)
NS_IMETHODIMP
GleanEvent::Record(JS::HandleValue aExtra, JSContext* aCx) {
if (aExtra.isNullOrUndefined()) {
mEvent.Record();
return NS_OK;
}
if (!aExtra.isObject()) {
LogToBrowserConsole(nsIScriptError::warningFlag,
u"Extras need to be an object"_ns);
return NS_OK;
}
nsTArray<nsCString> extraKeys;
nsTArray<nsCString> extraValues;
JS::RootedObject obj(aCx, &aExtra.toObject());
JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
if (!JS_Enumerate(aCx, obj, &ids)) {
LogToBrowserConsole(nsIScriptError::warningFlag,
u"Failed to enumerate object."_ns);
return NS_OK;
}
for (size_t i = 0, n = ids.length(); i < n; i++) {
nsAutoJSCString jsKey;
if (!jsKey.init(aCx, ids[i])) {
LogToBrowserConsole(
nsIScriptError::warningFlag,
u"Extra dictionary should only contain string keys."_ns);
return NS_OK;
}
JS::Rooted<JS::Value> value(aCx);
if (!JS_GetPropertyById(aCx, obj, ids[i], &value)) {
LogToBrowserConsole(nsIScriptError::warningFlag,
u"Failed to get extra property."_ns);
return NS_OK;
}
nsAutoJSCString jsValue;
if (!value.isString() || !jsValue.init(aCx, value)) {
LogToBrowserConsole(nsIScriptError::warningFlag,
u"Extra properties should have string values."_ns);
return NS_OK;
}
extraKeys.AppendElement(jsKey);
extraValues.AppendElement(jsValue);
}
// Calling the implementation directly, because we have a `string->string`
// map, not a `T->string` map the C++ API expects.
impl::fog_event_record_str(mEvent.mId, &extraKeys, &extraValues);
return NS_OK;
}
NS_IMETHODIMP
GleanEvent::TestGetValue(const nsACString& aStorageName, JSContext* aCx,
JS::MutableHandleValue aResult) {
auto result = mEvent.TestGetValue(aStorageName);
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
return NS_OK;
}
// TODO(bug 1678567): Implement this.
return NS_ERROR_FAILURE;
}
} // namespace mozilla::glean

Просмотреть файл

@ -1,110 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_glean_GleanEvent_h
#define mozilla_glean_GleanEvent_h
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "mozilla/Tuple.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla::glean {
// forward declaration
class GleanEvent;
namespace impl {
/**
* Represents the recorded data for a single event
*/
struct RecordedEvent {
public:
uint64_t mTimestamp;
nsCString mCategory;
nsCString mName;
nsTArray<Tuple<nsCString, nsCString>> mExtra;
};
template <class T>
class EventMetric {
friend class mozilla::glean::GleanEvent;
public:
constexpr explicit EventMetric(uint32_t id) : mId(id) {}
/**
* Record an event.
*
* @param aExtras The list of (extra key, value) pairs. Allowed extra keys are
* defined in the metric definition.
* If the wrong keys are used or values are too large
* an error is report and no event is recorded.
*/
void Record(const Span<const Tuple<T, nsCString>>& aExtras = {}) const {
static_assert(sizeof(T) <= sizeof(int32_t),
"Extra keys need to fit into 32 bits");
nsTArray<int32_t> extraKeys;
nsTArray<nsCString> extraValues;
for (auto& entry : aExtras) {
extraKeys.AppendElement(static_cast<int32_t>(mozilla::Get<0>(entry)));
extraValues.AppendElement(mozilla::Get<1>(entry));
}
fog_event_record(mId, &extraKeys, &extraValues);
}
/**
* **Test-only API**
*
* Get a list of currently stored events for this event metric.
*
* This function will attempt to await the last parent-process task (if any)
* writing to the the metric's storage engine before returning a value.
* This function will not wait for data from child processes.
*
* This doesn't clear the stored value.
* Parent process only. Panics in child processes.
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<nsTArray<RecordedEvent>> TestGetValue(
const nsACString& aStorageName) const {
if (!fog_event_test_has_value(mId, &aStorageName)) {
return Nothing();
}
// TODO(bug 1678567): Implement this.
nsTArray<RecordedEvent> empty;
return Some(std::move(empty));
}
private:
const uint32_t mId;
};
} // namespace impl
class GleanEvent final : public nsIGleanEvent {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGLEANEVENT
explicit GleanEvent(uint32_t id) : mEvent(id){};
private:
virtual ~GleanEvent() = default;
const impl::EventMetric<uint32_t> mEvent;
};
} // namespace mozilla::glean
#endif /* mozilla_glean_GleanEvent.h */

Просмотреть файл

@ -17,14 +17,15 @@ NS_IMPL_ISUPPORTS_CI(GleanString, nsIGleanString)
NS_IMETHODIMP
GleanString::Set(const nsACString& aValue) {
mString.Set(aValue);
this->mString.Set(aValue);
return NS_OK;
}
NS_IMETHODIMP
GleanString::TestGetValue(const nsACString& aStorageName, JSContext* aCx,
JS::MutableHandleValue aResult) {
auto result = mString.TestGetValue(aStorageName);
auto result =
this->mString.TestGetValue(PromiseFlatCString(aStorageName).get());
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {

Просмотреть файл

@ -9,12 +9,17 @@
#include "mozilla/Maybe.h"
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "nsString.h"
namespace mozilla::glean {
namespace impl {
extern "C" {
void fog_string_set(uint32_t aId, const nsACString& aValue);
uint32_t fog_string_test_has_value(uint32_t aId, const char* aStorageName);
void fog_string_test_get_value(uint32_t aId, const char* aStorageName,
nsACString& aValue);
}
class StringMetric {
public:
@ -28,7 +33,7 @@ class StringMetric {
*
* @param aValue The string to set the metric to.
*/
void Set(const nsACString& aValue) const { fog_string_set(mId, &aValue); }
void Set(const nsACString& aValue) const { fog_string_set(mId, aValue); }
/**
* **Test-only API**
@ -44,12 +49,12 @@ class StringMetric {
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<nsCString> TestGetValue(const nsACString& aStorageName) const {
if (!fog_string_test_has_value(mId, &aStorageName)) {
Maybe<nsCString> TestGetValue(const char* aStorageName) const {
if (!fog_string_test_has_value(mId, aStorageName)) {
return Nothing();
}
nsCString ret;
fog_string_test_get_value(mId, &aStorageName, &ret);
fog_string_test_get_value(mId, aStorageName, ret);
return Some(ret);
}

Просмотреть файл

@ -17,20 +17,21 @@ NS_IMPL_ISUPPORTS_CI(GleanTimespan, nsIGleanTimespan)
NS_IMETHODIMP
GleanTimespan::Start() {
mTimespan.Start();
this->mTimespan.Start();
return NS_OK;
}
NS_IMETHODIMP
GleanTimespan::Stop() {
mTimespan.Stop();
this->mTimespan.Stop();
return NS_OK;
}
NS_IMETHODIMP
GleanTimespan::TestGetValue(const nsACString& aStorageName,
JS::MutableHandleValue aResult) {
auto result = mTimespan.TestGetValue(aStorageName);
auto result =
this->mTimespan.TestGetValue(PromiseFlatCString(aStorageName).get());
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {

Просмотреть файл

@ -9,11 +9,16 @@
#include "mozilla/Maybe.h"
#include "nsIGleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla::glean {
namespace impl {
extern "C" {
void fog_timespan_start(uint32_t aId);
void fog_timespan_stop(uint32_t aId);
uint32_t fog_timespan_test_has_value(uint32_t aId, const char* aStorageName);
int64_t fog_timespan_test_get_value(uint32_t aId, const char* aStorageName);
}
class TimespanMetric {
public:
@ -52,11 +57,11 @@ class TimespanMetric {
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<int64_t> TestGetValue(const nsACString& aStorageName) const {
if (!fog_timespan_test_has_value(mId, &aStorageName)) {
Maybe<int64_t> TestGetValue(const char* aStorageName) const {
if (!fog_timespan_test_has_value(mId, aStorageName)) {
return Nothing();
}
return Some(fog_timespan_test_get_value(mId, &aStorageName));
return Some(fog_timespan_test_get_value(mId, aStorageName));
}
private:

Просмотреть файл

@ -17,20 +17,21 @@ NS_IMPL_ISUPPORTS_CI(GleanUuid, nsIGleanUuid)
NS_IMETHODIMP
GleanUuid::Set(const nsACString& aValue) {
mUuid.Set(aValue);
this->mUuid.Set(aValue);
return NS_OK;
}
NS_IMETHODIMP
GleanUuid::GenerateAndSet() {
mUuid.GenerateAndSet();
this->mUuid.GenerateAndSet();
return NS_OK;
}
NS_IMETHODIMP
GleanUuid::TestGetValue(const nsACString& aStorageName, JSContext* aCx,
JS::MutableHandleValue aResult) {
auto result = mUuid.TestGetValue(aStorageName);
auto result =
this->mUuid.TestGetValue(PromiseFlatCString(aStorageName).get());
if (result.isNothing()) {
aResult.set(JS::UndefinedValue());
} else {

Просмотреть файл

@ -10,11 +10,17 @@
#include "mozilla/Maybe.h"
#include "nsIGleanMetrics.h"
#include "nsString.h"
#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla::glean {
namespace impl {
extern "C" {
void fog_uuid_set(uint32_t aId, const nsACString& aUuid);
void fog_uuid_generate_and_set(uint32_t aId);
uint32_t fog_uuid_test_has_value(uint32_t aId, const char* aStorageName);
void fog_uuid_test_get_value(uint32_t aId, const char* aStorageName,
nsACString& aValue);
}
class UuidMetric {
public:
@ -25,7 +31,7 @@ class UuidMetric {
*
* @param aValue The UUID to set the metric to.
*/
void Set(const nsACString& aValue) const { fog_uuid_set(mId, &aValue); }
void Set(const nsACString& aValue) const { fog_uuid_set(mId, aValue); }
/*
* Generate a new random UUID and set the metric to it.
@ -47,12 +53,12 @@ class UuidMetric {
*
* @return value of the stored metric, or Nothing() if there is no value.
*/
Maybe<nsCString> TestGetValue(const nsACString& aStorageName) const {
if (!fog_uuid_test_has_value(mId, &aStorageName)) {
Maybe<nsCString> TestGetValue(const char* aStorageName) const {
if (!fog_uuid_test_has_value(mId, aStorageName)) {
return Nothing();
}
nsCString ret;
fog_uuid_test_get_value(mId, &aStorageName, &ret);
fog_uuid_test_get_value(mId, aStorageName, ret);
return Some(ret);
}

Просмотреть файл

@ -19,15 +19,6 @@ def type_name(obj):
Returns the C++ type to use for a given metric object.
"""
generate_enums = getattr(obj, "_generate_enums", []) # Extra Keys? Reasons?
if len(generate_enums):
for name, suffix in generate_enums:
if not len(getattr(obj, name)) and suffix == "Keys":
return util.Camelize(obj.type) + "Metric<uint32_t>"
else:
return "{}Metric<{}>".format(
util.Camelize(obj.type), util.Camelize(obj.name) + suffix
)
return util.Camelize(obj.type) + "Metric"
@ -51,8 +42,6 @@ def output_cpp(objs, output_fd, options={}):
trim_blocks=True,
lstrip_blocks=True,
)
env.filters["camelize"] = util.camelize
env.filters["Camelize"] = util.Camelize
for filter_name, filter_func in filters:
env.filters[filter_name] = filter_func
return env.get_template(template_name)

Просмотреть файл

@ -155,14 +155,6 @@ def output_rust(objs, output_fd, options={}):
# ("COUNTERS", "CounterMetric") -> [(1, "test_only::clicks"), ...]
objs_by_type = {}
# Map from a metric ID to the fully qualified path of the event object in Rust.
# Required for the special handling of event lookups.
#
# Example:
#
# 17 -> "test_only::an_event"
events_by_id = {}
if len(objs) == 1 and "pings" in objs:
template_filename = "rust_pings.jinja2"
else:
@ -170,22 +162,20 @@ def output_rust(objs, output_fd, options={}):
for category_name, metrics in objs.items():
for metric in metrics.values():
# FIXME: Support events correctly
if metric.type == "event":
continue
# The constant is all uppercase and suffixed by `_MAP`
const_name = util.snake_case(metric.type).upper() + "_MAP"
typ = type_name(metric)
key = (const_name, typ)
if key not in objs_by_type:
objs_by_type[key] = []
metric_name = util.snake_case(metric.name)
category_name = util.snake_case(category_name)
full_path = f"{category_name}::{metric_name}"
if metric.type == "event":
events_by_id[get_metric_id(metric)] = full_path
continue
if key not in objs_by_type:
objs_by_type[key] = []
objs_by_type[key].append((get_metric_id(metric), full_path))
# Now for the modules for each category.
@ -219,7 +209,6 @@ def output_rust(objs, output_fd, options={}):
common_metric_data_args=common_metric_data_args,
metric_by_type=objs_by_type,
extra_args=util.extra_args,
events_by_id=events_by_id,
)
)
output_fd.write("\n")

Просмотреть файл

@ -13,22 +13,8 @@ Jinja2 template is not. Please file bugs! #}
#include "mozilla/glean/MetricTypes.h"
namespace mozilla::glean {
enum class NoExtraKeys {};
{% macro generate_extra_keys(obj) %}
{% for name, suffix in obj["_generate_enums"] %}
{% if obj|attr(name)|length %}
enum class {{ obj.name|Camelize }}{{ suffix }} : int32_t {
{% for key in obj|attr(name) %}
{{ key | Camelize }},
{% endfor %}
};
{% endif %}
{% endfor %}
{% endmacro %}
namespace mozilla {
namespace glean {
{% for category_name, objs in all_objs.items() %}
namespace {{ category_name|snake_case }} {
@ -36,11 +22,7 @@ namespace {{ category_name|snake_case }} {
{% if obj.type|is_implemented_type %}
/**
* generated from {{ category_name }}.{{ obj.name }}
*/
{% if obj|attr("_generate_enums") %}
{{ generate_extra_keys(obj) }}
{%- endif %}
/**
*
* {{ obj.description|wordwrap() | replace('\n', '\n * ') }}
*/
constexpr impl::{{ obj|type_name }} {{obj.name|snake_case }}({{obj|metric_id}});
@ -50,6 +32,6 @@ namespace {{ category_name|snake_case }} {
}
{% endfor %}
} // namespace mozilla::glean
} // namespace glean
} // namespace mozilla
#endif // mozilla_Metrics_h

Просмотреть файл

@ -43,7 +43,9 @@ static already_AddRefed<nsISupports> NewMetricFromId(uint32_t id) {
{% if type|is_implemented_type %}
case {{ type_id }}: /* {{ type|Camelize }} */
{
return MakeAndAddRef<{{type | type_name}}>(metricId);
RefPtr<nsISupports> metric = new {{type | type_name}}(metricId);
return metric.forget();
break;
}
{% endif %}
{% endfor %}

Просмотреть файл

@ -26,34 +26,6 @@ Jinja2 template is not. Please file bugs! #}
}
}
/// Convert from an extra key's index to its variant.
impl std::convert::TryFrom<i32> for {{ obj.name|Camelize }}{{ suffix }} {
type Error = EventRecordingError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
{% for key in obj|attr(name) %}
{{loop.index-1}} => Ok(Self::{{key | Camelize}}),
{% endfor %}
_ => Err(EventRecordingError::InvalidExtraKey),
}
}
}
/// Convert from an extra key's string representation to its variant.
impl std::convert::TryFrom<&str> for {{ obj.name|Camelize }}{{ suffix }} {
type Error = EventRecordingError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
{% for key in obj|attr(name) %}
"{{key}}" => Ok(Self::{{key | Camelize}}),
{% endfor %}
_ => Err(EventRecordingError::InvalidExtraKey),
}
}
}
{% endif %}
{% endfor %}
{% endmacro %}
@ -100,7 +72,6 @@ pub mod {{ category_name|snake_case }} {
#[allow(dead_code)]
pub(crate) mod __glean_metric_maps {
use std::collections::HashMap;
use std::convert::TryInto;
use crate::private::*;
use once_cell::sync::Lazy;
@ -115,103 +86,5 @@ pub(crate) mod __glean_metric_maps {
});
{% endfor %}
/// Helper to get the number of allowed extra keys for a given event metric.
fn extra_keys_len<K: ExtraKeys>(_event: &EventMetric<K>) -> usize {
K::ALLOWED_KEYS.len()
}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An (optional) map of (extra key id, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` type.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized.
pub(crate) fn event_record_wrapper(metric_id: u32, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => {
assert!(
extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra: HashMap<_, _> = extra
.into_iter()
.map(|(k, v)| k.try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::{{event}}.record(Some(extra));
Ok(())
}
{% endfor %}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An (optional) map of (string, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` types.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized.
pub(crate) fn event_record_wrapper_str(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => {
assert!(
extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra = extra
.into_iter()
.map(|(k, v)| (&*k).try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::{{event}}.record(Some(extra));
Ok(())
}
{% endfor %}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to get the currently stored events for event metric.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `storage_name` - the storage name to look into.
///
/// # Returns
///
/// Returns the recorded events or `None` if nothing stored.
///
/// # Panics
///
/// Panics if no event by the given metric ID could be found.
pub(crate) fn event_test_get_value_wrapper(metric_id: u32, storage_name: &str) -> Option<Vec<RecordedEvent>> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => super::{{event}}.test_get_value(storage_name),
{% endfor %}
_ => panic!("No event for metric id {}", metric_id),
}
}
}
{% endif %}

Просмотреть файл

@ -29,15 +29,7 @@ def generate_metric_ids(objs):
return lambda metric: metric_id_mapping[(metric.category, metric.name)]
IMPLEMENTED_CPP_TYPES = [
"boolean",
"counter",
"datetime",
"event",
"string",
"timespan",
"uuid",
]
IMPLEMENTED_CPP_TYPES = ["boolean", "counter", "datetime", "string", "timespan", "uuid"]
def is_implemented_metric_type(typ):

Просмотреть файл

@ -1,19 +0,0 @@
header = """/* 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 http://mozilla.org/MPL/2.0/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. */"""
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
namespaces = ["mozilla::glean::impl"]
includes = ["nsTArray.h", "nsString.h"]
[export.rename]
"ThinVec" = "nsTArray"
[parse]
parse_deps = true
include = ["fog"]
extra_bindings = ["fog"]

Просмотреть файл

@ -4,10 +4,6 @@
#include "gtest/gtest.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "mozilla/Maybe.h"
#include "mozilla/Tuple.h"
#include "nsTArray.h"
#include "mozilla/Preferences.h"
#include "nsString.h"
@ -15,7 +11,6 @@
using mozilla::Preferences;
using namespace mozilla::glean;
using namespace mozilla::glean::impl;
#define DATA_PREF "datareporting.healthreport.uploadEnabled"
@ -25,6 +20,9 @@ extern "C" {
void GTest_FOG_ExpectFailure(const char* aMessage) {
EXPECT_STREQ(aMessage, "");
}
nsresult fog_init();
nsresult fog_submit_ping(const nsACString* aPingName);
}
// Initialize FOG exactly once.
@ -60,7 +58,7 @@ TEST(FOG, TestCppCounterWorks)
ASSERT_EQ(
42,
mozilla::glean::test_only::bad_code.TestGetValue("test-ping"_ns).value());
mozilla::glean::test_only::bad_code.TestGetValue("test-ping").value());
}
TEST(FOG, TestCppStringWorks)
@ -69,45 +67,45 @@ TEST(FOG, TestCppStringWorks)
mozilla::glean::test_only::cheesy_string.Set(kValue);
ASSERT_STREQ(kValue.get(), mozilla::glean::test_only::cheesy_string
.TestGetValue("test-ping"_ns)
.TestGetValue("test-ping")
.value()
.get());
}
TEST(FOG, TestCppTimespanWorks)
{
mozilla::glean::test_only::can_we_time_it.Start();
PR_Sleep(PR_MillisecondsToInterval(10));
mozilla::glean::test_only::can_we_time_it.Stop();
ASSERT_TRUE(
mozilla::glean::test_only::can_we_time_it.TestGetValue("test-ping"_ns)
.value() > 0);
}
// TODO: to be enabled after changes from bug 1677455 are vendored.
// TEST(FOG, TestCppTimespanWorks)
// {
// mozilla::glean::test_only::can_we_time_it.Start();
// PR_Sleep(PR_MillisecondsToInterval(10));
// mozilla::glean::test_only::can_we_time_it.Stop();
//
// ASSERT_TRUE(
// mozilla::glean::test_only::can_we_time_it.TestGetValue("test-ping")
// .value() > 0);
// }
TEST(FOG, TestCppUuidWorks)
{
nsCString kTestUuid("decafdec-afde-cafd-ecaf-decafdecafde");
test_only::what_id_it.Set(kTestUuid);
ASSERT_STREQ(
kTestUuid.get(),
test_only::what_id_it.TestGetValue("test-ping"_ns).value().get());
ASSERT_STREQ(kTestUuid.get(),
test_only::what_id_it.TestGetValue("test-ping").value().get());
test_only::what_id_it.GenerateAndSet();
// Since we generate v4 UUIDs, and the first character of the third group
// isn't 4, this won't ever collide with kTestUuid.
ASSERT_STRNE(
kTestUuid.get(),
test_only::what_id_it.TestGetValue("test-ping"_ns).value().get());
ASSERT_STRNE(kTestUuid.get(),
test_only::what_id_it.TestGetValue("test-ping").value().get());
}
TEST(FOG, TestCppBooleanWorks)
{
mozilla::glean::test_only::can_we_flag_it.Set(false);
ASSERT_EQ(false, mozilla::glean::test_only::can_we_flag_it
.TestGetValue("test-ping"_ns)
.value());
ASSERT_TRUE(
mozilla::glean::test_only::can_we_flag_it.TestHasValue("test-ping"));
ASSERT_EQ(false, mozilla::glean::test_only::can_we_flag_it.TestGetValue(
"test-ping"));
}
// TODO: to be enabled after changes from bug 1677448 are vendored.
@ -119,21 +117,3 @@ TEST(FOG, TestCppBooleanWorks)
// auto received = test_only::what_a_date.TestGetValue("test-ping");
// ASSERT_STREQ(received.value().get(), "2020-11-06T12:10:35+05:00");
// }
using mozilla::MakeTuple;
using mozilla::Tuple;
using mozilla::glean::test_only_ipc::AnEventKeys;
TEST(FOG, TestCppEventWorks)
{
test_only_ipc::no_extra_event.Record();
ASSERT_TRUE(test_only_ipc::no_extra_event.TestGetValue("store1"_ns).isSome());
// Ugh, this API...
nsTArray<Tuple<test_only_ipc::AnEventKeys, nsCString>> extra;
nsCString val = "can set extras"_ns;
extra.AppendElement(MakeTuple(AnEventKeys::Extra1, val));
test_only_ipc::an_event.Record(std::move(extra));
ASSERT_TRUE(test_only_ipc::an_event.TestGetValue("store1"_ns).isSome());
}

Просмотреть файл

@ -5,7 +5,6 @@
#include "FOGIPC.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/MozPromise.h"
@ -19,6 +18,12 @@ using FlushFOGDataPromise = mozilla::dom::ContentParent::FlushFOGDataPromise;
namespace mozilla {
namespace glean {
extern "C" {
uint32_t fog_serialize_ipc_buf();
uint32_t fog_give_ipc_buf(uint8_t* buf, uint32_t buf_len);
void fog_use_ipc_buf(uint8_t* buf, uint32_t buf_len);
}
/**
* The parent process is asking you to flush your data ASAP.
*
@ -27,12 +32,12 @@ namespace glean {
*/
void FlushFOGData(std::function<void(ipc::ByteBuf&&)>&& aResolver) {
ByteBuf buf;
uint32_t ipcBufferSize = impl::fog_serialize_ipc_buf();
uint32_t ipcBufferSize = fog_serialize_ipc_buf();
bool ok = buf.Allocate(ipcBufferSize);
if (!ok) {
return;
}
uint32_t writtenLen = impl::fog_give_ipc_buf(buf.mData, buf.mLen);
uint32_t writtenLen = fog_give_ipc_buf(buf.mData, buf.mLen);
if (writtenLen != ipcBufferSize) {
return;
}
@ -79,7 +84,7 @@ void FlushAllChildData(
* A child process has sent you this buf as a treat.
* @param buf - a bincoded serialized payload that the Rust impl understands.
*/
void FOGData(ipc::ByteBuf&& buf) { impl::fog_use_ipc_buf(buf.mData, buf.mLen); }
void FOGData(ipc::ByteBuf&& buf) { fog_use_ipc_buf(buf.mData, buf.mLen); }
/**
* Called by FOG on a child process when it wants to send a buf to the parent.

Просмотреть файл

@ -31,27 +31,17 @@ if CONFIG["MOZ_GLEAN"]:
"bindings/private/Boolean.h",
"bindings/private/Counter.h",
"bindings/private/Datetime.h",
"bindings/private/Event.h",
"bindings/private/String.h",
"bindings/private/Timespan.h",
"bindings/private/Uuid.h",
]
if CONFIG["COMPILE_ENVIRONMENT"]:
EXPORTS.mozilla.glean += [
"!fog_ffi_generated.h",
]
CbindgenHeader("fog_ffi_generated.h", inputs=["/toolkit/components/glean"])
UNIFIED_SOURCES += [
"bindings/Category.cpp",
"bindings/Glean.cpp",
"bindings/private/Boolean.cpp",
"bindings/private/Common.cpp",
"bindings/private/Counter.cpp",
"bindings/private/Datetime.cpp",
"bindings/private/Event.cpp",
"bindings/private/String.cpp",
"bindings/private/Timespan.cpp",
"bindings/private/Uuid.cpp",

Просмотреть файл

@ -288,32 +288,6 @@ pub mod test_nested {
}
}
/// Convert from an extra key's index to its variant.
impl std::convert::TryFrom<i32> for EventMetricWithExtraKeys {
type Error = EventRecordingError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::AnExtraKey),
1 => Ok(Self::AnotherExtraKey),
_ => Err(EventRecordingError::InvalidExtraKey),
}
}
}
/// Convert from an extra key's string representation to its variant.
impl std::convert::TryFrom<&str> for EventMetricWithExtraKeys {
type Error = EventRecordingError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"an_extra_key" => Ok(Self::AnExtraKey),
"another_extra_key" => Ok(Self::AnotherExtraKey),
_ => Err(EventRecordingError::InvalidExtraKey),
}
}
}
#[allow(non_upper_case_globals)]
/// generated from test.nested.event_metric_with_extra
///
@ -335,7 +309,6 @@ pub mod test_nested {
#[allow(dead_code)]
pub(crate) mod __glean_metric_maps {
use std::collections::HashMap;
use std::convert::TryInto;
use crate::private::*;
use once_cell::sync::Lazy;
@ -415,128 +388,5 @@ pub(crate) mod __glean_metric_maps {
map
});
/// Helper to get the number of allowed extra keys for a given event metric.
fn extra_keys_len<K: ExtraKeys>(_event: &EventMetric<K>) -> usize {
K::ALLOWED_KEYS.len()
}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An (optional) map of (extra key id, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` type.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized.
pub(crate) fn event_record_wrapper(metric_id: u32, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
match metric_id {
16 => {
assert!(
extra_keys_len(&super::test_nested::event_metric) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra: HashMap<_, _> = extra
.into_iter()
.map(|(k, v)| k.try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::test_nested::event_metric.record(Some(extra));
Ok(())
}
17 => {
assert!(
extra_keys_len(&super::test_nested::event_metric_with_extra) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra: HashMap<_, _> = extra
.into_iter()
.map(|(k, v)| k.try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::test_nested::event_metric_with_extra.record(Some(extra));
Ok(())
}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An (optional) map of (string, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` types.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized.
pub(crate) fn event_record_wrapper_str(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
match metric_id {
16 => {
assert!(
extra_keys_len(&super::test_nested::event_metric) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra = extra
.into_iter()
.map(|(k, v)| (&*k).try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::test_nested::event_metric.record(Some(extra));
Ok(())
}
17 => {
assert!(
extra_keys_len(&super::test_nested::event_metric_with_extra) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
// In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains.
#[allow(unused_variables)]
let extra = extra
.into_iter()
.map(|(k, v)| (&*k).try_into().map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::test_nested::event_metric_with_extra.record(Some(extra));
Ok(())
}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to get the currently stored events for event metric.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `storage_name` - the storage name to look into.
///
/// # Returns
///
/// Returns the recorded events or `None` if nothing stored.
///
/// # Panics
///
/// Panics if no event by the given metric ID could be found.
pub(crate) fn event_test_get_value_wrapper(metric_id: u32, storage_name: &str) -> Option<Vec<RecordedEvent>> {
match metric_id {
16 => super::test_nested::event_metric.test_get_value(storage_name),
17 => super::test_nested::event_metric_with_extra.test_get_value(storage_name),
_ => panic!("No event for metric id {}", metric_id),
}
}
}

Просмотреть файл

@ -11,16 +11,13 @@
#include "mozilla/glean/MetricTypes.h"
namespace mozilla::glean {
enum class NoExtraKeys {};
namespace mozilla {
namespace glean {
namespace test {
/**
* generated from test.boolean_metric
*/
/**
*
* A multi-line
* description
*/
@ -28,8 +25,7 @@ namespace test {
/**
* generated from test.counter_metric
*/
/**
*
* A multi-line
* description
*/
@ -37,8 +33,7 @@ namespace test {
/**
* generated from test.string_metric
*/
/**
*
* A multi-line
* description
*/
@ -46,8 +41,7 @@ namespace test {
/**
* generated from test.timespan_metric
*/
/**
*
* A multi-line
* description
*/
@ -57,8 +51,7 @@ namespace test {
namespace test_nested {
/**
* generated from test.nested.uuid_metric
*/
/**
*
* A multi-line
* description
*/
@ -66,38 +59,14 @@ namespace test_nested {
/**
* generated from test.nested.datetime_metric
*/
/**
*
* A multi-line
* description
*/
constexpr impl::DatetimeMetric datetime_metric(15);
/**
* generated from test.nested.event_metric
*/
/**
* A multi-line
* description
*/
constexpr impl::EventMetric<uint32_t> event_metric(16);
/**
* generated from test.nested.event_metric_with_extra
*/
enum class EventMetricWithExtraKeys : int32_t {
AnExtraKey,
AnotherExtraKey,
};
/**
* A multi-line
* description
*/
constexpr impl::EventMetric<EventMetricWithExtraKeys> event_metric_with_extra(17);
}
} // namespace mozilla::glean
} // namespace glean
} // namespace mozilla
#endif // mozilla_Metrics_h

Просмотреть файл

@ -39,31 +39,39 @@ static already_AddRefed<nsISupports> NewMetricFromId(uint32_t id) {
switch (typeId) {
case 1: /* Boolean */
{
return MakeAndAddRef<GleanBoolean>(metricId);
RefPtr<nsISupports> metric = new GleanBoolean(metricId);
return metric.forget();
break;
}
case 3: /* Counter */
{
return MakeAndAddRef<GleanCounter>(metricId);
RefPtr<nsISupports> metric = new GleanCounter(metricId);
return metric.forget();
break;
}
case 5: /* String */
{
return MakeAndAddRef<GleanString>(metricId);
RefPtr<nsISupports> metric = new GleanString(metricId);
return metric.forget();
break;
}
case 8: /* Timespan */
{
return MakeAndAddRef<GleanTimespan>(metricId);
RefPtr<nsISupports> metric = new GleanTimespan(metricId);
return metric.forget();
break;
}
case 11: /* Uuid */
{
return MakeAndAddRef<GleanUuid>(metricId);
RefPtr<nsISupports> metric = new GleanUuid(metricId);
return metric.forget();
break;
}
case 12: /* Datetime */
{
return MakeAndAddRef<GleanDatetime>(metricId);
}
case 13: /* Event */
{
return MakeAndAddRef<GleanEvent>(metricId);
RefPtr<nsISupports> metric = new GleanDatetime(metricId);
return metric.forget();
break;
}
default:
MOZ_ASSERT_UNREACHABLE("Invalid type ID reached when trying to instantiate a new metric");

Просмотреть файл

@ -245,8 +245,6 @@ test_only.ipc:
extra_keys:
extra1:
description: "Some extra data"
extra2:
description: "Some extra data again"
description: |
This is a test-only metric.
Just recording some events.
@ -263,22 +261,6 @@ test_only.ipc:
- store1
no_lint:
- COMMON_PREFIX
no_extra_event:
type: event
description: |
This is a test-only metric.
Just recording some events without the extra fuss.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1646165
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1646165#c1
data_sensitivity:
- technical
notification_emails:
- glean-team@mozilla.com
expires: never
send_in_pings:
- store1
a_uuid:
type: uuid
description: |

Просмотреть файл

@ -6,7 +6,6 @@
#include "nsIFOG.h"
#include "mozilla/FOG.h"
#include "mozilla/glean/fog_ffi_generated.h"
#include "mozilla/ClearOnShutdown.h"
@ -28,24 +27,32 @@ already_AddRefed<FOG> FOG::GetSingleton() {
return do_AddRef(gFOG);
}
void FOG::Shutdown() { glean::impl::fog_shutdown(); }
extern "C" {
nsresult fog_init();
void fog_shutdown();
nsresult fog_set_log_pings(bool aEnableLogPings);
nsresult fog_set_debug_view_tag(const nsACString* aDebugTag);
nsresult fog_submit_ping(const nsACString* aPingName);
}
void FOG::Shutdown() { fog_shutdown(); }
NS_IMETHODIMP
FOG::InitializeFOG() { return glean::impl::fog_init(); }
FOG::InitializeFOG() { return fog_init(); }
NS_IMETHODIMP
FOG::SetLogPings(bool aEnableLogPings) {
return glean::impl::fog_set_log_pings(aEnableLogPings);
return fog_set_log_pings(aEnableLogPings);
}
NS_IMETHODIMP
FOG::SetTagPings(const nsACString& aDebugTag) {
return glean::impl::fog_set_debug_view_tag(&aDebugTag);
return fog_set_debug_view_tag(&aDebugTag);
}
NS_IMETHODIMP
FOG::SendPing(const nsACString& aPingName) {
return glean::impl::fog_submit_ping(&aPingName);
return fog_submit_ping(&aPingName);
}
NS_IMPL_ISUPPORTS(FOG, nsIFOG)

Просмотреть файл

@ -13,8 +13,26 @@ interface nsIGleanBoolean : nsISupports
*
* @param value the value to set.
*/
[implicit_jscontext]
void set(in bool value);
/**
* **Test-only API**
*
* Tests whether a value is stored for the metric.
*
* This function will attempt to await the last parent-process task (if any)
* writing to the the metric's storage engine before returning a value.
* This function will not wait for data from child processes.
*
* Parent process only. Panics in child processes.
*
* @param aStorageName the name of the ping to retrieve the metric for.
* @return true if metric value exists, otherwise false
*/
[implicit_jscontext]
bool testHasValue(in ACString aStorageName);
/**
* **Test-only API**
*
@ -27,9 +45,10 @@ interface nsIGleanBoolean : nsISupports
* This doesn't clear the stored value.
* Parent process only. Panics in child processes.
*
* @return value of the stored metric, or undefined if there is no value.
* @return value of the stored metric.
*/
jsval testGetValue(in AUTF8String aStorageName);
[implicit_jscontext]
bool testGetValue(in ACString aStorageName);
};
[scriptable, uuid(aa15fd20-1e8a-11eb-9bec-0800200c9a66)]
@ -58,7 +77,7 @@ interface nsIGleanDatetime : nsISupports
* @return value of the stored metric, or undefined if there is no value.
*/
[implicit_jscontext]
jsval testGetValue(in AUTF8String aStorageName);
jsval testGetValue(in ACString aStorageName);
};
[scriptable, uuid(05b89d2a-d57c-11ea-82da-3f63399a6f5a)]
@ -85,7 +104,7 @@ interface nsIGleanCounter : nsISupports
*
* @return value of the stored metric, or undefined if there is no value.
*/
jsval testGetValue(in AUTF8String aStorageName);
jsval testGetValue(in ACString aStorageName);
};
[scriptable, uuid(d84a3555-46f1-48c1-9122-e8e88b069d2b)]
@ -96,7 +115,7 @@ interface nsIGleanString : nsISupports
*
* @param value The string to set the metric to.
*/
void set(in AUTF8String value);
void set(in ACString value);
/**
* **Test-only API**
@ -113,7 +132,7 @@ interface nsIGleanString : nsISupports
* @return value of the stored metric, or undefined if there is no value.
*/
[implicit_jscontext]
jsval testGetValue(in AUTF8String aStorageName);
jsval testGetValue(in ACString aStorageName);
};
[scriptable, uuid(2586530c-030f-11eb-93cb-cbf30d25225a)]
@ -152,7 +171,7 @@ interface nsIGleanTimespan : nsISupports
*
* @return value of the stored metric, or undefined if there is no value.
*/
jsval testGetValue(in AUTF8String aStorageName);
jsval testGetValue(in ACString aStorageName);
};
[scriptable, uuid(395700e7-06f6-46be-adcc-ea58977fda6d)]
@ -163,7 +182,7 @@ interface nsIGleanUuid : nsISupports
*
* @param aValue The UUID to set the metric to.
*/
void set(in AUTF8String aValue);
void set(in ACString aValue);
/**
* Generate a new random UUID and set the metric to it.
@ -185,34 +204,5 @@ interface nsIGleanUuid : nsISupports
* @return value of the stored metric, or undefined if there is no value.
*/
[implicit_jscontext]
jsval testGetValue(in AUTF8String aStorageName);
};
[scriptable, uuid(1b01424a-1f55-11eb-92a5-0754f6c3f240)]
interface nsIGleanEvent : nsISupports
{
/*
* Record an event.
*
* @param aExtra An (optional) map of extra values.
*/
[implicit_jscontext]
void record([optional] in jsval aExtra);
/**
* **Test-only API**
*
* Get a list of currently stored events for this event metric.
*
* This function will attempt to await the last parent-process task (if any)
* writing to the the metric's storage engine before returning a value.
* This function will not wait for data from child processes.
*
* This doesn't clear the stored value.
* Parent process only. Panics in child processes.
*
* @return value of the stored metric.
*/
[implicit_jscontext]
jsval testGetValue(in AUTF8String aStorageName);
jsval testGetValue(in ACString aStorageName);
};

Просмотреть файл

@ -98,7 +98,8 @@ add_task(async function test_fog_string_works() {
Assert.equal(value, Glean.test_only.cheesy_string.testGetValue("test-ping"));
});
add_task(async function test_fog_timespan_works() {
// Enable test after bug 1677455 is fixed.
add_task({ skip_if: () => true }, async function test_fog_timespan_works() {
// We start, briefly sleep and then stop.
// That guarantees some time to measure.
Glean.test_only.can_we_time_it.start();
@ -134,16 +135,6 @@ add_task({ skip_if: () => true }, function test_fog_datetime_works() {
add_task(function test_fog_boolean_works() {
Glean.test_only.can_we_flag_it.set(false);
Assert.ok(Glean.test_only.can_we_flag_it.testHasValue("test-ping"));
Assert.equal(false, Glean.test_only.can_we_flag_it.testGetValue("test-ping"));
});
add_task(async function test_fog_event_works() {
Glean.test_only_ipc.no_extra_event.record();
// FIXME(bug 1678567): Check that the value was recorded when we can.
// Assert.ok(Glean.test_only_ipc.no_extra_event.testGetValue("store1"));
let extra = { extra1: "can set extras", extra2: "passing more data" };
Glean.test_only_ipc.an_event.record(extra);
// FIXME(bug 1678567): Check that the value was recorded when we can.
// Assert.ok(Glean.test_only_ipc.an_event.testGetValue("store1"));
});