commit bdcd65935eabfb2308f83ba7c7b7a9442b395513
parent cf03ca40e8851d529c1c5a07e05cd3fa40375901
Author: Charlie Humphreys <chumphreys@mozilla.com>
Date: Wed, 5 Nov 2025 19:25:45 +0000
Bug 1997174: add TestGetValue for C++ Labeled metrics r=chutten,toolkit-telemetry-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D270583
Diffstat:
9 files changed, 830 insertions(+), 11 deletions(-)
diff --git a/toolkit/components/glean/api/src/ffi/labeled.rs b/toolkit/components/glean/api/src/ffi/labeled.rs
@@ -5,7 +5,8 @@
#![cfg(feature = "with_gecko")]
use crate::metrics::__glean_metric_maps as metric_maps;
-use nsstring::nsACString;
+use nsstring::{nsACString, nsCString};
+use thin_vec::ThinVec;
fn is_jog_id(id: u32) -> bool {
id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0
@@ -39,6 +40,48 @@ pub extern "C" fn fog_labeled_boolean_enum_get(id: u32, label: u16) -> u32 {
}
#[no_mangle]
+pub extern "C" fn fog_labeled_boolean_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<bool>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_BOOLEAN_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_boolean_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(value as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_boolean_test_get_error(id: u32, error_str: &mut nsCString) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(LABELED_BOOLEAN_MAP, id, metric, test_get_errors!(metric));
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_boolean_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_labeled_counter_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
if is_jog_id(id) {
@@ -60,6 +103,48 @@ pub extern "C" fn fog_labeled_counter_enum_get(id: u32, label: u16) -> u32 {
}
#[no_mangle]
+pub extern "C" fn fog_labeled_counter_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<i32>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_COUNTER_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_counter_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(value as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_counter_test_get_error(id: u32, error_str: &mut nsCString) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(LABELED_COUNTER_MAP, id, metric, test_get_errors!(metric));
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_counter_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_labeled_custom_distribution_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
if is_jog_id(id) {
@@ -80,6 +165,84 @@ pub extern "C" fn fog_labeled_custom_distribution_enum_get(id: u32, label: u16)
metric_maps::labeled_submetric_id_get(id, metric_maps::labeled_enum_to_str(id, label))
}
+/// FFI-compatible representation of recorded distribution data.
+#[repr(C)]
+pub struct FfiDistributionData {
+ keys: ThinVec<i64>,
+ values: ThinVec<i64>,
+ sum: i64,
+ count: i64,
+}
+
+impl From<glean::DistributionData> for FfiDistributionData {
+ fn from(data: glean::DistributionData) -> Self {
+ let mut keys = ThinVec::new();
+ let mut values = ThinVec::new();
+
+ for (key, value) in data.values {
+ keys.push(key);
+ values.push(value);
+ }
+
+ FfiDistributionData {
+ keys,
+ values,
+ sum: data.sum,
+ count: data.count,
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_custom_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<FfiDistributionData>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_custom_distribution_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(FfiDistributionData::from(value));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_custom_distribution_test_get_error(
+ id: u32,
+ error_str: &mut nsCString,
+) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(
+ LABELED_CUSTOM_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_custom_distribution_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
#[no_mangle]
pub extern "C" fn fog_labeled_memory_distribution_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
@@ -102,6 +265,56 @@ pub extern "C" fn fog_labeled_memory_distribution_enum_get(id: u32, label: u16)
}
#[no_mangle]
+pub extern "C" fn fog_labeled_memory_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<FfiDistributionData>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_memory_distribution_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(FfiDistributionData::from(value));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_memory_distribution_test_get_error(
+ id: u32,
+ error_str: &mut nsCString,
+) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(
+ LABELED_MEMORY_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_memory_distribution_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_labeled_string_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
if is_jog_id(id) {
@@ -123,6 +336,43 @@ pub extern "C" fn fog_labeled_string_enum_get(id: u32, label: u16) -> u32 {
}
#[no_mangle]
+pub extern "C" fn fog_labeled_string_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<nsCString>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(LABELED_STRING_MAP, id, metric, test_get!(metric, ping_name));
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_string_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(nsCString::from(value));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_string_test_get_error(id: u32, error_str: &mut nsCString) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(LABELED_STRING_MAP, id, metric, test_get_errors!(metric));
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_string_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_labeled_timing_distribution_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
if is_jog_id(id) {
@@ -144,6 +394,56 @@ pub extern "C" fn fog_labeled_timing_distribution_enum_get(id: u32, label: u16)
}
#[no_mangle]
+pub extern "C" fn fog_labeled_timing_distribution_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<FfiDistributionData>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_timing_distribution_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(FfiDistributionData::from(value));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_timing_distribution_test_get_error(
+ id: u32,
+ error_str: &mut nsCString,
+) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(
+ LABELED_TIMING_DISTRIBUTION_MAP,
+ id,
+ metric,
+ test_get_errors!(metric)
+ );
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_timing_distribution_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_labeled_quantity_get(id: u32, label: &nsACString) -> u32 {
let label = &label.to_utf8();
if is_jog_id(id) {
@@ -165,6 +465,48 @@ pub extern "C" fn fog_labeled_quantity_enum_get(id: u32, label: u16) -> u32 {
}
#[no_mangle]
+pub extern "C" fn fog_labeled_quantity_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ count: &mut u64,
+ keys: &mut ThinVec<nsCString>,
+ values: &mut ThinVec<i64>,
+) {
+ let val;
+ if is_jog_id(id) {
+ val = just_with_jog_metric!(
+ LABELED_QUANTITY_MAP,
+ id,
+ metric,
+ test_get!(metric, ping_name)
+ );
+ } else {
+ let ping_name = &ping_name.to_utf8();
+ val = metric_maps::labeled_quantity_test_get(id, ping_name).unwrap();
+ }
+ *count = val.len() as _;
+ for (key, value) in val {
+ keys.push(nsCString::from(key));
+ values.push(value as _);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_labeled_quantity_test_get_error(id: u32, error_str: &mut nsCString) -> bool {
+ let err;
+ if is_jog_id(id) {
+ err = just_with_jog_metric!(LABELED_QUANTITY_MAP, id, metric, test_get_errors!(metric));
+ } else {
+ err = test_get_errors_with_method!(
+ metric_maps,
+ labeled_quantity_test_get_num_recorded_errors,
+ id
+ );
+ }
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
+
+#[no_mangle]
pub extern "C" fn fog_dual_labeled_counter_get(
id: u32,
key: &nsACString,
diff --git a/toolkit/components/glean/api/src/ffi/macros.rs b/toolkit/components/glean/api/src/ffi/macros.rs
@@ -219,3 +219,35 @@ macro_rules! test_get_errors {
error_str
}};
}
+
+/// Check the provided method on the metric map in the provided storage for errors.
+/// On finding one, return an error string.
+///
+/// # Arguments
+///
+/// * `$metric_map` - The metric map to use.
+/// * `$method` - The `labeled_<type>_test_get_num_recorded_errors` method to use.
+/// * `$metric_id` - The metric id to find in the metric map.
+macro_rules! test_get_errors_with_method {
+ ($metric_map:ident, $method:ident, $metric_id: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_map::$method($metric_id, 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
+ }};
+}
diff --git a/toolkit/components/glean/api/src/private/labeled.rs b/toolkit/components/glean/api/src/private/labeled.rs
@@ -2,6 +2,7 @@
// 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 glean::TestGetValue;
use inherent::inherent;
use super::{
@@ -12,6 +13,7 @@ use super::{
use crate::ipc::need_ipc;
use crate::metrics::__glean_metric_maps::submetric_maps;
use std::borrow::Cow;
+use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;
@@ -36,7 +38,11 @@ mod private {
/// This allows us to define which FOG metrics can be used
/// as labeled types.
pub trait Sealed {
- type GleanMetric: glean::private::AllowLabeled + Clone;
+ type Output;
+ type GleanMetric: glean::private::AllowLabeled
+ + Clone
+ + glean::TestGetValue<Output = Self::Output>;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -64,7 +70,9 @@ mod private {
//
// See [Labeled Booleans](https://mozilla.github.io/glean/book/user/metrics/labeled_booleans.html).
impl Sealed for LabeledBooleanMetric {
+ type Output = bool;
type GleanMetric = glean::private::BooleanMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -102,7 +110,9 @@ mod private {
//
// See [Labeled Strings](https://mozilla.github.io/glean/book/user/metrics/labeled_strings.html).
impl Sealed for LabeledStringMetric {
+ type Output = String;
type GleanMetric = glean::private::StringMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -133,7 +143,9 @@ mod private {
//
// See [Labeled Counters](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html).
impl Sealed for LabeledCounterMetric {
+ type Output = i32;
type GleanMetric = glean::private::CounterMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -166,7 +178,9 @@ mod private {
//
// See [Labeled Custom Distributions](https://mozilla.github.io/glean/book/user/metrics/labeled_custom_distributions.html).
impl Sealed for LabeledCustomDistributionMetric {
+ type Output = glean::DistributionData;
type GleanMetric = glean::private::CustomDistributionMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -199,7 +213,9 @@ mod private {
//
// See [Labeled Memory Distributions](https://mozilla.github.io/glean/book/user/metrics/labeled_memory_distributions.html).
impl Sealed for LabeledMemoryDistributionMetric {
+ type Output = glean::DistributionData;
type GleanMetric = glean::private::MemoryDistributionMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -232,7 +248,9 @@ mod private {
//
// See [Labeled Timing Distributions](https://mozilla.github.io/glean/book/user/metrics/labeled_timing_distributions.html).
impl Sealed for LabeledTimingDistributionMetric {
+ type Output = glean::DistributionData;
type GleanMetric = glean::private::TimingDistributionMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -281,7 +299,9 @@ mod private {
//
// See [Labeled Quantities](https://mozilla.github.io/glean/book/user/metrics/labeled_quantities.html).
impl Sealed for LabeledQuantityMetric {
+ type Output = i64;
type GleanMetric = glean::private::QuantityMetric;
+
fn from_glean_metric(
id: BaseMetricId,
metric: &glean::private::LabeledMetric<Self::GleanMetric>,
@@ -456,6 +476,22 @@ where
}
}
+#[inherent]
+impl<U, E> glean::TestGetValue for LabeledMetric<U, E>
+where
+ U: AllowLabeled + Clone,
+ <U as private::Sealed>::Output: 'static,
+{
+ type Output = HashMap<String, <U as private::Sealed>::Output>;
+
+ pub fn test_get_value(
+ &self,
+ ping_name: Option<String>,
+ ) -> Option<<LabeledMetric<U, E> as TestGetValue>::Output> {
+ self.core.test_get_value(ping_name)
+ }
+}
+
#[cfg(test)]
mod test {
use once_cell::sync::Lazy;
diff --git a/toolkit/components/glean/bindings/private/DistributionData.h b/toolkit/components/glean/bindings/private/DistributionData.h
@@ -7,6 +7,7 @@
#define mozilla_glean_DistributionData_h
#include "nsTHashMap.h"
+#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla::glean {
@@ -28,6 +29,16 @@ struct DistributionData final {
}
}
+ /**
+ * Create distribution data from an FfiDistributionData instance.
+ */
+ explicit DistributionData(const impl::FfiDistributionData& aData)
+ : sum(aData.sum), count(aData.count) {
+ for (size_t i = 0; i < aData.keys.Length(); ++i) {
+ this->values.InsertOrUpdate(aData.keys[i], aData.values[i]);
+ }
+ }
+
friend std::ostream& operator<<(std::ostream& aStream,
const DistributionData& aDist) {
aStream << "DistributionData(";
@@ -49,6 +60,14 @@ struct DistributionData final {
aStream << ")";
return aStream;
}
+
+ static void fromFFIArray(
+ const nsTArray<impl::FfiDistributionData>& aDataArray,
+ nsTArray<DistributionData>& aResultArray) {
+ for (const auto& d : aDataArray) {
+ aResultArray.AppendElement(DistributionData(d));
+ }
+ }
};
} // namespace mozilla::glean
diff --git a/toolkit/components/glean/bindings/private/Labeled.h b/toolkit/components/glean/bindings/private/Labeled.h
@@ -9,10 +9,12 @@
#include "nsISupports.h"
#include "nsWrapperCache.h"
+#include "mozilla/ResultVariant.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/glean/bindings/Boolean.h"
#include "mozilla/glean/bindings/Counter.h"
#include "mozilla/glean/bindings/CustomDistribution.h"
+#include "mozilla/glean/bindings/DistributionData.h"
#include "mozilla/glean/bindings/GleanMetric.h"
#include "mozilla/glean/bindings/HistogramGIFFTMap.h"
#include "mozilla/glean/bindings/MemoryDistribution.h"
@@ -52,19 +54,73 @@ static inline void UpdateLabeledDistributionMirror(
}
template <typename T>
+struct MetricOutputTypeTraits;
+
+template <>
+struct MetricOutputTypeTraits<BooleanMetric> {
+ using Output = bool;
+};
+
+template <>
+struct MetricOutputTypeTraits<QuantityMetric> {
+ using Output = int64_t;
+};
+
+template <>
+struct MetricOutputTypeTraits<CounterMetric<CounterType::eBaseOrLabeled>> {
+ using Output = int32_t;
+};
+
+template <>
+struct MetricOutputTypeTraits<StringMetric> {
+ using Output = nsCString;
+};
+
+template <>
+struct MetricOutputTypeTraits<CustomDistributionMetric> {
+ using Output = DistributionData;
+};
+
+template <>
+struct MetricOutputTypeTraits<MemoryDistributionMetric> {
+ using Output = DistributionData;
+};
+
+template <>
+struct MetricOutputTypeTraits<TimingDistributionMetric> {
+ using Output = DistributionData;
+};
+
+template <typename T>
inline uint32_t fog_labeled_get(uint32_t aId, const nsACString* aLabel);
+
template <typename T>
inline uint32_t fog_labeled_enum_get(uint32_t aId, uint16_t aLabel);
-#define FOG_LABEL_TYPE_MAP(type, name) \
- template <> \
- inline uint32_t fog_labeled_get<type>(uint32_t aId, \
- const nsACString* aLabel) { \
- return fog_labeled_##name##_get(aId, aLabel); \
- } \
- template <> \
- inline uint32_t fog_labeled_enum_get<type>(uint32_t aId, uint16_t aLabel) { \
- return fog_labeled_##name##_enum_get(aId, aLabel); \
+template <typename T, typename O>
+inline Maybe<nsTHashMap<nsCString, O>> fog_labeled_test_get_value(
+ uint32_t aId, const nsACString* aPingName);
+
+template <typename T>
+inline Maybe<nsCString> fog_labeled_test_get_error(uint32_t aId);
+
+#define FOG_LABEL_TYPE_MAP(_type, _name) \
+ template <> \
+ inline uint32_t fog_labeled_get<_type>(uint32_t aId, \
+ const nsACString* aLabel) { \
+ return fog_labeled_##_name##_get(aId, aLabel); \
+ } \
+ template <> \
+ inline uint32_t fog_labeled_enum_get<_type>(uint32_t aId, uint16_t aLabel) { \
+ return fog_labeled_##_name##_enum_get(aId, aLabel); \
+ } \
+ template <> \
+ inline Maybe<nsCString> fog_labeled_test_get_error<_type>(uint32_t aId) { \
+ nsCString error; \
+ if (fog_labeled_##_name##_test_get_error(aId, &error)) { \
+ return Some(error); \
+ } \
+ return Nothing(); \
}
FOG_LABEL_TYPE_MAP(BooleanMetric, boolean)
@@ -75,6 +131,8 @@ FOG_LABEL_TYPE_MAP(CustomDistributionMetric, custom_distribution)
FOG_LABEL_TYPE_MAP(MemoryDistributionMetric, memory_distribution)
FOG_LABEL_TYPE_MAP(TimingDistributionMetric, timing_distribution)
+#undef FOG_LABEL_TYPE_MAP
+
template <typename T, typename E>
class Labeled {
public:
@@ -152,6 +210,62 @@ class Labeled {
return T(submetricId);
}
+ template <typename M = T,
+ typename V = typename MetricOutputTypeTraits<T>::Output>
+ Result<Maybe<nsTHashMap<nsCString, V>>, nsCString> TestGetValue(
+ const nsCString aPingName = nsCString()) const {
+ Maybe<nsCString> err;
+ err = fog_labeled_test_get_error<T>(mId);
+ if (err.isSome()) {
+ return Err(err.value());
+ }
+ uint64_t count = 0;
+ nsTArray<nsCString> keys;
+ nsTArray<V> values;
+ if constexpr (std::is_same_v<M, BooleanMetric>) {
+ fog_labeled_boolean_test_get_value(mId, &aPingName, &count, &keys,
+ &values);
+ } else if constexpr (std::is_same_v<
+ M, CounterMetric<CounterType::eBaseOrLabeled>>) {
+ fog_labeled_counter_test_get_value(mId, &aPingName, &count, &keys,
+ &values);
+ } else if constexpr (std::is_same_v<M, StringMetric>) {
+ fog_labeled_string_test_get_value(mId, &aPingName, &count, &keys,
+ &values);
+ } else if constexpr (std::is_same_v<M, QuantityMetric>) {
+ fog_labeled_quantity_test_get_value(mId, &aPingName, &count, &keys,
+ &values);
+ } else if constexpr (std::is_same_v<M, CustomDistributionMetric>) {
+ nsTArray<FfiDistributionData> ffi_values;
+ fog_labeled_custom_distribution_test_get_value(mId, &aPingName, &count,
+ &keys, &ffi_values);
+ DistributionData::fromFFIArray(ffi_values, values);
+ } else if constexpr (std::is_same_v<M, MemoryDistributionMetric>) {
+ nsTArray<FfiDistributionData> ffi_values;
+ fog_labeled_memory_distribution_test_get_value(mId, &aPingName, &count,
+ &keys, &ffi_values);
+ DistributionData::fromFFIArray(ffi_values, values);
+ } else if constexpr (std::is_same_v<M, TimingDistributionMetric>) {
+ nsTArray<FfiDistributionData> ffi_values;
+ fog_labeled_timing_distribution_test_get_value(mId, &aPingName, &count,
+ &keys, &ffi_values);
+ DistributionData::fromFFIArray(ffi_values, values);
+ } else {
+ static_assert(std::is_same_v<M, void>,
+ "There should be a block handling this type");
+ }
+
+ nsTHashMap<nsCStringHashKey, V> result;
+ for (uint64_t i = 0; i < count; i++) {
+ result.InsertOrUpdate(keys[i], std::move(values[i]));
+ }
+
+ if (result.Count() == 0) {
+ return Maybe<nsTHashMap<nsCString, V>>();
+ }
+ return Some(std::move(result));
+ }
+
// This is a workaround only needed in a rare case, ensure it's only compiled
// there.
template <typename U = T, typename V = E>
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py b/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py
@@ -189,6 +189,28 @@ def get_schedule_reverse_map(objs):
return ping_schedule_reverse_map
+def test_get_type(metric_type):
+ """
+ Returns the type name returned from the `test_get_value` Rust implementation for a given metric type.
+ """
+ if metric_type == "boolean":
+ return "bool"
+ elif metric_type == "counter":
+ return "i32"
+ elif metric_type in {
+ "custom_distribution",
+ "memory_distribution",
+ "timing_distribution",
+ }:
+ return "DistributionData"
+ elif metric_type == "string":
+ return "String"
+ elif metric_type == "quantity":
+ return "i64"
+ else:
+ return "UNSUPPORTED"
+
+
def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
"""
Given a tree of objects, output Rust code to the file-like object `output_fd`.
@@ -213,6 +235,7 @@ def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
)
env.filters["camelize"] = util.camelize
env.filters["Camelize"] = util.Camelize
+ env.filters["test_get_type"] = test_get_type
for filter_name, filter_func in filters:
env.filters[filter_name] = filter_func
return env.get_template(template_name)
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
@@ -501,6 +501,61 @@ pub(crate) mod __glean_metric_maps {
_ => panic!("No labeled_{{labeled_type}} for metric id {}", metric_id),
}
}
+
+ /// Gets the value from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - The label identifying the {{labeled_type}} submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns a map of all values in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_{{labeled_type}} by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_{{labeled_type}}_test_get(metric_id: u32, ping_name: &str) -> Option<HashMap<String, {{labeled_type|test_get_type}}>> {
+ let ping_name = if ping_name.len() == 0 {
+ None
+ } else {
+ Some(ping_name.to_string())
+ };
+ let metric = match metric_id {
+{% for metric_id, (labeled, _) in labeleds_by_id.items() %}
+ {{metric_id}} => &*super::{{labeled}} as &dyn ::glean::TestGetValue<Output=HashMap<String, {{labeled_type|test_get_type}}>>,
+{% endfor %}
+ _ => panic!("No labeled_{{labeled_type}} for metric id {}", metric_id),
+ };
+
+ metric.test_get_value(ping_name)
+ }
+
+ /// Gets the number of reported errors from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `error_type` - The error type for which to look
+ ///
+ /// # Returns
+ ///
+ /// Returns the number of recorded errors in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_{{labeled_type}} by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_{{labeled_type}}_test_get_num_recorded_errors(metric_id: u32, error_type: glean::ErrorType) -> i32 {
+ match metric_id {
+{% for metric_id, (labeled, _) in labeleds_by_id.items() %}
+ {{metric_id}} => super::{{labeled}}.test_get_num_recorded_errors(error_type),
+{% endfor %}
+ _ => panic!("No labeled_{{labeled_type}} for metric id {}", metric_id),
+ }
+ }
{% endfor %}
pub(crate) fn labeled_enum_to_str(metric_id: u32, label: u16) -> &'static str {
diff --git a/toolkit/components/glean/tests/gtest/TestFog.cpp b/toolkit/components/glean/tests/gtest/TestFog.cpp
@@ -737,3 +737,26 @@ TEST_F(FOGFixture, IsCamelCaseWorks) {
ASSERT_FALSE(IsCamelCase(u"1234"_ns));
ASSERT_FALSE(IsCamelCase(u"some#Name"_ns));
}
+
+TEST_F(FOGFixture, TestLabeledTestGetValue) {
+ test_only::mabels_labeled_counters.Get("next_to_the_fridge"_ns).Add(5);
+ test_only::mabels_labeled_counters.Get("clean"_ns).Add(10);
+
+ nsTHashMap<nsCString, int32_t> value;
+ value = test_only::mabels_labeled_counters.TestGetValue().unwrap().value();
+ ASSERT_EQ(5, value.Get("next_to_the_fridge"_ns));
+ ASSERT_EQ(10, value.Get("clean"_ns));
+}
+
+TEST_F(FOGFixture, TestLabeledTestGetValueReturnsNothingIfEmpty) {
+ auto result = test_only::mabels_labeled_counters.TestGetValue().unwrap();
+ ASSERT_TRUE(result.isNothing());
+}
+
+TEST_F(FOGFixture, TestLabeledTestGetValueWithError) {
+ test_only::mabels_labeled_counters.Get("clean"_ns).Add(-1);
+
+ nsCString error;
+ error = test_only::mabels_labeled_counters.TestGetValue().unwrapErr();
+ ASSERT_EQ("Metric had 1 error(s) of type invalid_value!"_ns, error);
+}
diff --git a/toolkit/components/glean/tests/pytest/metrics_test_output b/toolkit/components/glean/tests/pytest/metrics_test_output
@@ -1936,6 +1936,67 @@ pub(crate) mod __glean_metric_maps {
_ => panic!("No labeled_boolean for metric id {}", metric_id),
}
}
+
+ /// Gets the value from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - The label identifying the boolean submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns a map of all values in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_boolean by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_boolean_test_get(metric_id: u32, ping_name: &str) -> Option<HashMap<String, bool>> {
+ let ping_name = if ping_name.len() == 0 {
+ None
+ } else {
+ Some(ping_name.to_string())
+ };
+ let metric = match metric_id {
+ 4 => &*super::test::labeled_boolean_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ 5 => &*super::test::labeled_boolean_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ 17 => &*super::test::unordered_labeled_boolean_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ 33 => &*super::test2::labeled_boolean_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ 34 => &*super::test2::labeled_boolean_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ 46 => &*super::test2::unordered_labeled_boolean_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, bool>>,
+ _ => panic!("No labeled_boolean for metric id {}", metric_id),
+ };
+
+ metric.test_get_value(ping_name)
+ }
+
+ /// Gets the number of reported errors from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `error_type` - The error type for which to look
+ ///
+ /// # Returns
+ ///
+ /// Returns the number of recorded errors in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_boolean by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_boolean_test_get_num_recorded_errors(metric_id: u32, error_type: glean::ErrorType) -> i32 {
+ match metric_id {
+ 4 => super::test::labeled_boolean_metric.test_get_num_recorded_errors(error_type),
+ 5 => super::test::labeled_boolean_metric_labels.test_get_num_recorded_errors(error_type),
+ 17 => super::test::unordered_labeled_boolean_metric.test_get_num_recorded_errors(error_type),
+ 33 => super::test2::labeled_boolean_metric.test_get_num_recorded_errors(error_type),
+ 34 => super::test2::labeled_boolean_metric_labels.test_get_num_recorded_errors(error_type),
+ 46 => super::test2::unordered_labeled_boolean_metric.test_get_num_recorded_errors(error_type),
+ _ => panic!("No labeled_boolean for metric id {}", metric_id),
+ }
+ }
/// Gets the submetric from the specified labeled_counter metric.
///
/// # Arguments
@@ -1960,6 +2021,63 @@ pub(crate) mod __glean_metric_maps {
_ => panic!("No labeled_counter for metric id {}", metric_id),
}
}
+
+ /// Gets the value from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - The label identifying the counter submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns a map of all values in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_counter by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_counter_test_get(metric_id: u32, ping_name: &str) -> Option<HashMap<String, i32>> {
+ let ping_name = if ping_name.len() == 0 {
+ None
+ } else {
+ Some(ping_name.to_string())
+ };
+ let metric = match metric_id {
+ 6 => &*super::test::labeled_counter_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, i32>>,
+ 7 => &*super::test::labeled_counter_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, i32>>,
+ 35 => &*super::test2::labeled_counter_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, i32>>,
+ 36 => &*super::test2::labeled_counter_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, i32>>,
+ _ => panic!("No labeled_counter for metric id {}", metric_id),
+ };
+
+ metric.test_get_value(ping_name)
+ }
+
+ /// Gets the number of reported errors from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `error_type` - The error type for which to look
+ ///
+ /// # Returns
+ ///
+ /// Returns the number of recorded errors in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_counter by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_counter_test_get_num_recorded_errors(metric_id: u32, error_type: glean::ErrorType) -> i32 {
+ match metric_id {
+ 6 => super::test::labeled_counter_metric.test_get_num_recorded_errors(error_type),
+ 7 => super::test::labeled_counter_metric_labels.test_get_num_recorded_errors(error_type),
+ 35 => super::test2::labeled_counter_metric.test_get_num_recorded_errors(error_type),
+ 36 => super::test2::labeled_counter_metric_labels.test_get_num_recorded_errors(error_type),
+ _ => panic!("No labeled_counter for metric id {}", metric_id),
+ }
+ }
/// Gets the submetric from the specified labeled_string metric.
///
/// # Arguments
@@ -1985,6 +2103,63 @@ pub(crate) mod __glean_metric_maps {
}
}
+ /// Gets the value from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - The label identifying the string submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns a map of all values in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_string by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_string_test_get(metric_id: u32, ping_name: &str) -> Option<HashMap<String, String>> {
+ let ping_name = if ping_name.len() == 0 {
+ None
+ } else {
+ Some(ping_name.to_string())
+ };
+ let metric = match metric_id {
+ 8 => &*super::test::labeled_string_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, String>>,
+ 9 => &*super::test::labeled_string_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, String>>,
+ 37 => &*super::test2::labeled_string_metric as &dyn ::glean::TestGetValue<Output=HashMap<String, String>>,
+ 38 => &*super::test2::labeled_string_metric_labels as &dyn ::glean::TestGetValue<Output=HashMap<String, String>>,
+ _ => panic!("No labeled_string for metric id {}", metric_id),
+ };
+
+ metric.test_get_value(ping_name)
+ }
+
+ /// Gets the number of reported errors from the specified labeled metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `error_type` - The error type for which to look
+ ///
+ /// # Returns
+ ///
+ /// Returns the number of recorded errors in the labeled metric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_string by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_string_test_get_num_recorded_errors(metric_id: u32, error_type: glean::ErrorType) -> i32 {
+ match metric_id {
+ 8 => super::test::labeled_string_metric.test_get_num_recorded_errors(error_type),
+ 9 => super::test::labeled_string_metric_labels.test_get_num_recorded_errors(error_type),
+ 37 => super::test2::labeled_string_metric.test_get_num_recorded_errors(error_type),
+ 38 => super::test2::labeled_string_metric_labels.test_get_num_recorded_errors(error_type),
+ _ => panic!("No labeled_string for metric id {}", metric_id),
+ }
+ }
+
pub(crate) fn labeled_enum_to_str(metric_id: u32, label: u16) -> &'static str {
match metric_id {
5 => super::test::LabeledBooleanMetricLabelsLabel::from(label).into(),