tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 752a05fc1c2a22992bf9d11a1ee30d1224287096
parent f87f2e7d875e7a07296eb5311f95c4386332901d
Author: Charlie Humphreys <chumphreys@mozilla.com>
Date:   Tue,  4 Nov 2025 22:01:46 +0000

Bug 1997174: add TestGetValue for C++ Labeled metrics r=chutten,toolkit-telemetry-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D270583

Diffstat:
Mtoolkit/components/glean/api/src/ffi/labeled.rs | 344++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/glean/api/src/ffi/macros.rs | 32++++++++++++++++++++++++++++++++
Mtoolkit/components/glean/api/src/private/labeled.rs | 38+++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/glean/bindings/private/DistributionData.h | 19+++++++++++++++++++
Mtoolkit/components/glean/bindings/private/Labeled.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mtoolkit/components/glean/build_scripts/glean_parser_ext/rust.py | 23+++++++++++++++++++++++
Mtoolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2 | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/glean/tests/gtest/TestFog.cpp | 23+++++++++++++++++++++++
Mtoolkit/components/glean/tests/pytest/metrics_test_output | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 831 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,63 @@ 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( + false, + "A type is not implemented for fog_labeled_test_get_value_template"); + } + + 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(),