author | Chris H-C <chutten@mozilla.com> |
Mon, 14 Dec 2020 17:17:45 +0000 | |
changeset 560675 | cb2974d10cd2e3cdb2d872217264db9b36d67e2a |
parent 560674 | 0c03e3ad5e56201e35437734b0ae1d34faa52005 |
child 560676 | ab3cb32e5be762943ce7b9ca8292cf1c8487ee09 |
push id | 132750 |
push user | chutten@mozilla.com |
push date | Mon, 14 Dec 2020 19:47:51 +0000 |
treeherder | autoland@ab3cb32e5be7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | janerik |
bugs | 1673648 |
milestone | 85.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/toolkit/components/glean/api/src/ffi/mod.rs +++ b/toolkit/components/glean/api/src/ffi/mod.rs @@ -1,14 +1,15 @@ // 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 thin_vec::ThinVec; use {nsstring::nsACString, uuid::Uuid}; #[macro_use] mod macros; mod event; #[no_mangle] pub unsafe extern "C" fn fog_counter_add(id: u32, amount: i32) { @@ -147,8 +148,38 @@ pub extern "C" fn fog_datetime_set( minute: u32, second: u32, nano: u32, offset_seconds: i32, ) { let metric = metric_get!(DATETIME_MAP, id); metric.set_with_details(year, month, day, hour, minute, second, nano, offset_seconds); } + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_test_has_value( + id: u32, + storage_name: &nsACString, +) -> bool { + test_has!(MEMORY_DISTRIBUTION_MAP, id, storage_name) +} + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_test_get_value( + id: u32, + storage_name: &nsACString, + sum: &mut u64, + buckets: &mut ThinVec<u64>, + counts: &mut ThinVec<u64>, +) { + let val = test_get!(MEMORY_DISTRIBUTION_MAP, id, storage_name); + *sum = val.sum; + for (&bucket, &count) in val.values.iter() { + buckets.push(bucket); + counts.push(count) + } +} + +#[no_mangle] +pub extern "C" fn fog_memory_distribution_accumulate(id: u32, sample: u64) { + let metric = metric_get!(MEMORY_DISTRIBUTION_MAP, id); + metric.accumulate(sample); +}
--- a/toolkit/components/glean/api/src/private/memory_distribution.rs +++ b/toolkit/components/glean/api/src/private/memory_distribution.rs @@ -1,79 +1,78 @@ // 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/. -use std::sync::Arc; +use inherent::inherent; use super::{CommonMetricData, DistributionData, MemoryUnit, MetricId}; -use crate::dispatcher; +use glean_core::traits::MemoryDistribution; + use crate::ipc::{need_ipc, with_ipc_payload}; /// A memory distribution metric. /// /// Memory distributions are used to accumulate and store memory measurements for analyzing distributions of the memory data. -#[derive(Debug)] +#[derive(Clone)] pub enum MemoryDistributionMetric { Parent { /// The metric's ID. /// /// **TEST-ONLY** - Do not use unless gated with `#[cfg(test)]`. id: MetricId, - inner: Arc<MemoryDistributionMetricImpl>, + inner: glean::private::MemoryDistributionMetric, }, Child(MemoryDistributionMetricIpc), } -#[derive(Debug)] -pub struct MemoryDistributionMetricImpl { - inner: glean_core::metrics::MemoryDistributionMetric, -} -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct MemoryDistributionMetricIpc(MetricId); impl MemoryDistributionMetric { /// Create a new memory distribution metric. pub fn new(id: MetricId, meta: CommonMetricData, memory_unit: MemoryUnit) -> Self { if need_ipc() { MemoryDistributionMetric::Child(MemoryDistributionMetricIpc(id)) } else { - let inner = Arc::new(MemoryDistributionMetricImpl::new(meta, memory_unit)); + let inner = glean::private::MemoryDistributionMetric::new(meta, memory_unit); MemoryDistributionMetric::Parent { id, inner } } } #[cfg(test)] pub(crate) fn child_metric(&self) -> Self { match self { MemoryDistributionMetric::Parent { id, .. } => { MemoryDistributionMetric::Child(MemoryDistributionMetricIpc(*id)) } MemoryDistributionMetric::Child(_) => { panic!("Can't get a child metric from a child metric") } } } +} +#[inherent(pub)] +impl MemoryDistribution for MemoryDistributionMetric { /// Accumulates the provided sample in the metric. /// /// ## Arguments /// /// * `sample` - The sample to be recorded by the metric. The sample is assumed to be in the /// configured memory unit of the metric. /// /// ## Notes /// /// Values bigger than 1 Terabyte (2<sup>40</sup> bytes) are truncated /// and an `ErrorType::InvalidValue` error is recorded. - pub fn accumulate(&self, sample: u64) { + fn accumulate(&self, sample: u64) { match self { MemoryDistributionMetric::Parent { inner, .. } => { - let metric = Arc::clone(&inner); - dispatcher::launch(move || metric.accumulate(sample)); + MemoryDistribution::accumulate(&*inner, sample); } MemoryDistributionMetric::Child(c) => { with_ipc_payload(move |payload| { if let Some(v) = payload.memory_samples.get_mut(&c.0) { v.push(sample); } else { payload.memory_samples.insert(c.0, vec![sample]); } @@ -84,57 +83,69 @@ impl MemoryDistributionMetric { /// **Test-only API.** /// /// Get the currently-stored histogram as a DistributionData of the serialized value. /// This doesn't clear the stored value. /// /// ## Arguments /// - /// * `storage_name` - the storage name to look into. + /// * `ping_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<DistributionData> { + fn test_get_value<'a, S: Into<Option<&'a str>>>( + &self, + ping_name: S, + ) -> Option<DistributionData> { match self { - MemoryDistributionMetric::Parent { inner, .. } => { - dispatcher::block_on_queue(); - inner.test_get_value(storage_name) + MemoryDistributionMetric::Parent { inner, .. } => inner.test_get_value(ping_name), + MemoryDistributionMetric::Child(c) => { + panic!("Cannot get test value for {:?} in non-parent process!", c.0) } - MemoryDistributionMetric::Child(_c) => panic!( - "Cannot get test value for {:?} in non-parent process!", - self - ), } } -} -impl MemoryDistributionMetricImpl { - pub fn new(meta: CommonMetricData, memory_unit: MemoryUnit) -> Self { - let inner = glean_core::metrics::MemoryDistributionMetric::new(meta, memory_unit); - Self { inner } - } - - pub fn accumulate(&self, sample: u64) { - crate::with_glean(|glean| self.inner.accumulate(glean, sample)) - } - - pub fn test_get_value(&self, storage_name: &str) -> Option<DistributionData> { - crate::with_glean(move |glean| self.inner.test_get_value(glean, storage_name)) + /// **Exported for test purposes.** + /// + /// Gets the number of recorded errors for the given error type. + /// + /// # Arguments + /// + /// * `error` - The type of error + /// * `ping_name` - represents the optional name of the ping to retrieve the + /// metric for. Defaults to the first value in `send_in_pings`. + /// + /// # Returns + /// + /// The number of errors recorded. + fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( + &self, + error: glean::ErrorType, + ping_name: S, + ) -> i32 { + match self { + MemoryDistributionMetric::Parent { inner, .. } => { + inner.test_get_num_recorded_errors(error, ping_name) + } + MemoryDistributionMetric::Child(c) => panic!( + "Cannot get the number of recorded errors for {:?} in non-parent process!", + c.0 + ), + } } } #[cfg(test)] mod test { use super::*; use crate::{common_test::*, ipc, metrics}; #[test] - #[ignore] // TODO: Enable them back when bug 1677451 lands. fn smoke_test_memory_distribution() { let _lock = lock_test(); let metric = MemoryDistributionMetric::new( 0.into(), CommonMetricData { name: "memory_distribution_metric".into(), category: "telemetry".into(), @@ -149,17 +160,16 @@ mod test { let metric_data = metric.test_get_value("store1").unwrap(); assert_eq!(1, metric_data.values[&42494]); assert_eq!(0, metric_data.values[&44376]); assert_eq!(43008, metric_data.sum); } #[test] - #[ignore] // TODO: Enable them back when bug 1677451 lands. fn memory_distribution_child() { let _lock = lock_test(); let parent_metric = &metrics::test_only_ipc::a_memory_dist; parent_metric.accumulate(42); { let child_metric = parent_metric.child_metric();
--- a/toolkit/components/glean/bindings/MetricTypes.h +++ b/toolkit/components/glean/bindings/MetricTypes.h @@ -4,13 +4,14 @@ #ifndef mozilla_Glean_MetricTypes_h #define mozilla_Glean_MetricTypes_h #include "mozilla/glean/Boolean.h" #include "mozilla/glean/Counter.h" #include "mozilla/glean/Datetime.h" #include "mozilla/glean/Event.h" +#include "mozilla/glean/MemoryDistribution.h" #include "mozilla/glean/Timespan.h" #include "mozilla/glean/String.h" #include "mozilla/glean/Uuid.h" #endif // mozilla_Glean_MetricTypes_h
new file mode 100644 --- /dev/null +++ b/toolkit/components/glean/bindings/private/MemoryDistribution.cpp @@ -0,0 +1,64 @@ +/* -*- 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/MemoryDistribution.h" + +#include "mozilla/Components.h" +#include "nsIClassInfoImpl.h" +#include "nsJSUtils.h" +#include "nsPrintfCString.h" +#include "nsString.h" + +namespace mozilla::glean { + +NS_IMPL_CLASSINFO(GleanMemoryDistribution, nullptr, 0, {0}) +NS_IMPL_ISUPPORTS_CI(GleanMemoryDistribution, nsIGleanMemoryDistribution) + +NS_IMETHODIMP +GleanMemoryDistribution::Accumulate(uint64_t aSample) { + mMemoryDist.Accumulate(aSample); + return NS_OK; +} + +NS_IMETHODIMP +GleanMemoryDistribution::TestGetValue(const nsACString& aPingName, + JSContext* aCx, + JS::MutableHandleValue aResult) { + auto result = mMemoryDist.TestGetValue(aPingName); + if (result.isNothing()) { + aResult.set(JS::UndefinedValue()); + } else { + // Build return value of the form: { sum: #, values: {bucket1: count1, ...} + JS::RootedObject root(aCx, JS_NewPlainObject(aCx)); + if (!root) { + return NS_ERROR_FAILURE; + } + uint64_t sum = result.ref().sum; + if (!JS_DefineProperty(aCx, root, "sum", static_cast<double>(sum), + JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + JS::RootedObject valuesObj(aCx, JS_NewPlainObject(aCx)); + if (!valuesObj || + !JS_DefineProperty(aCx, root, "values", valuesObj, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + auto& data = result.ref().values; + for (auto iter = data.ConstIter(); !iter.Done(); iter.Next()) { + const uint64_t bucket = iter.Key(); + const uint64_t count = iter.UserData(); + if (!JS_DefineProperty(aCx, valuesObj, + nsPrintfCString("%lu", bucket).get(), + static_cast<double>(count), JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + aResult.setObject(*root); + } + return NS_OK; +} + +} // namespace mozilla::glean
new file mode 100644 --- /dev/null +++ b/toolkit/components/glean/bindings/private/MemoryDistribution.h @@ -0,0 +1,91 @@ +/* -*- 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_GleanMemoryDistribution_h +#define mozilla_glean_GleanMemoryDistribution_h + +#include "mozilla/glean/fog_ffi_generated.h" +#include "mozilla/Maybe.h" +#include "nsDataHashtable.h" +#include "nsIGleanMetrics.h" +#include "nsTArray.h" + +namespace mozilla::glean { + +struct DistributionData final { + uint64_t sum; + nsDataHashtable<nsUint64HashKey, uint64_t> values; +}; + +namespace impl { + +class MemoryDistributionMetric { + public: + constexpr explicit MemoryDistributionMetric(uint32_t aId) : mId(aId) {} + + /* + * Accumulates the provided sample in the metric. + * + * @param aSample The sample to be recorded by the metric. The sample is + * assumed to be in the confgured memory unit of the metric. + * + * Notes: Values bigger than 1 Terabyte (2^40 bytes) are truncated and an + * InvalidValue error is recorded. + */ + void Accumulate(uint64_t aSample) const { + fog_memory_distribution_accumulate(mId, aSample); + } + + /** + * **Test-only API** + * + * Gets the currently stored value as a DistributionData. + * + * 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<DistributionData> TestGetValue(const nsACString& aPingName) const { + if (!fog_memory_distribution_test_has_value(mId, &aPingName)) { + return Nothing(); + } + nsTArray<uint64_t> buckets; + nsTArray<uint64_t> counts; + DistributionData ret; + fog_memory_distribution_test_get_value(mId, &aPingName, &ret.sum, &buckets, + &counts); + for (size_t i = 0; i < buckets.Length(); ++i) { + ret.values.Put(buckets[i], counts[i]); + } + return Some(std::move(ret)); + } + + private: + const uint32_t mId; +}; +} // namespace impl + +class GleanMemoryDistribution final : public nsIGleanMemoryDistribution { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGLEANMEMORYDISTRIBUTION + + explicit GleanMemoryDistribution(uint64_t aId) : mMemoryDist(aId){}; + + private: + virtual ~GleanMemoryDistribution() = default; + + const impl::MemoryDistributionMetric mMemoryDist; +}; + +} // namespace mozilla::glean + +#endif /* mozilla_glean_GleanMemoryDistribution_h */
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/util.py +++ b/toolkit/components/glean/build_scripts/glean_parser_ext/util.py @@ -29,16 +29,17 @@ def generate_metric_ids(objs): return lambda metric: metric_id_mapping[(metric.category, metric.name)] IMPLEMENTED_CPP_TYPES = [ "boolean", "counter", "datetime", "event", + "memory_distribution", "string", "timespan", "uuid", ] def is_implemented_metric_type(typ): """
--- a/toolkit/components/glean/docs/new_metric_types.md +++ b/toolkit/components/glean/docs/new_metric_types.md @@ -87,39 +87,42 @@ Each metric type has six pieces you'll n - Using our convenient macros, define the Multi-Language Architecture's FFI layer above the Rust API in [`api/src/ffi/mod.rs`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/api/src/ffi/mod.rs). ### 2. C++ Impl - Implement a type called `XMetric` (e.g. `CounterMetric`) in `mozilla::glean::impl` in [`bindings/private/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/private/). - Its methods should be named the same as the ones in the Rust API, transformed to `CamelCase`. - They should all be public. - Multiplex the FFI's `test_have` and `test_get` functions into a single `TestGetValue` function that returns a `mozilla::Maybe` wrapping the C++ type that best fits the metric type. -- Include the new metric type in [`bindings/MetricTypes.h`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/MetricTypes.h) -- Include the new files in [`moz.build`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/MetricTypes.h). The header file should be added to `EXPORTS.mozilla.glean` and the `.cpp` file should be added to `UNIFIED_SOURCES`. +- Include the new metric type in + [`bindings/MetricTypes.h`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/MetricTypes.h) + and + [`build_scripts/glean_parser_ext/util.py`'s `IMPLEMENTED_CPP_TYPES`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/build_scripts/glean_parser_ext/util.py). +- Include the new files in [`moz.build`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/MetricTypes.h). The header file should be added to `EXPORTS.mozilla.glean.bindings` and the `.cpp` file should be added to `UNIFIED_SOURCES`. ### 3. IDL - Duplicate the public API (including its docs) to [`xpcom/nsIGleanMetrics.idl`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/xpcom/nsIGleanMetrics.idl) with the name `nsIGleanX` (e.g. `nsIGleanCounter`). - Inherit from `nsISupports`. - The naming style for members here is `lowerCamelCase`. You'll need a [GUID](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Generating_GUIDs) because this is XPCOM, but you'll only need the canonical form since we're only exposing to JS. - The `testGetValue` method will return a `jsval`. ### 4. JS Impl - Add an `nsIGleanX`-deriving, `XMetric`-owning type called `GleanX` (e.g. `GleanCounter`) in the same header and `.cpp` as `XMetric` in [`bindings/private/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/private/). - Don't declare any methods beyond a ctor (takes a `uint32_t` metric id, init-constructs a `impl::XMetric` member) and dtor (`default`): the IDL will do the rest so long as you remember to add `NS_DECL_ISUPPORTS` and `NS_DECL_NSIGLEANX`. - In the definition of `GleanX`, member identifiers are back to `CamelCase` and need macros like `NS_IMETHODIMP`. Delegate operations to the owned `XMetric`, returning `NS_OK` no matter what in non-test methods. - Test-only methods can return `NS_ERROR` codes on failures, but mostly return `NS_OK` and use `undefined` in the `JS::MutableHandleValue` result to signal no value. -### 5. Tests +### 6. Tests Two languages means two test suites. - Add a never-expiring test-only metric of your type to [`test_metrics.yaml`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/test_metrics.yaml). - Feel free to be clever with the name, but be sure to make clear that it is test-only. - **C++ Tests (GTest)** - Add a small test case to [`gtest/TestFog.cpp`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/gtest/TestFog.cpp). - For more details, peruse the [testing docs](testing.md). - **JS Tests (xpcshell)** - Add a small test case to [`xpcshell/test_Glean.js`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/xpcshell/test_Glean.js). - For more details, peruse the [testing docs](testing.md). -### 6. API Documentation +### 7. API Documentation TODO
--- a/toolkit/components/glean/gtest/TestFog.cpp +++ b/toolkit/components/glean/gtest/TestFog.cpp @@ -132,8 +132,27 @@ TEST(FOG, TestCppEventWorks) // 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()); } + +TEST(FOG, TestCppMemoryDistWorks) +{ + test_only::do_you_remember.Accumulate(7); + test_only::do_you_remember.Accumulate(17); + + DistributionData data = + test_only::do_you_remember.TestGetValue("test-ping"_ns).ref(); + // Sum is in bytes, test_only::do_you_remember is in megabytes. So + // multiplication ahoy! + ASSERT_EQ(data.sum, 24UL * 1024 * 1024); + for (auto iter = data.values.Iter(); !iter.Done(); iter.Next()) { + const uint64_t bucket = iter.Key(); + const uint64_t count = iter.UserData(); + ASSERT_TRUE(count == 0 || + (count == 1 && (bucket == 17520006 || bucket == 7053950))) + << "Only two occupied buckets"; + } +}
--- a/toolkit/components/glean/moz.build +++ b/toolkit/components/glean/moz.build @@ -27,16 +27,17 @@ if CONFIG["MOZ_GLEAN"]: "!GleanMetrics.h", "bindings/Category.h", "bindings/Glean.h", "bindings/MetricTypes.h", "bindings/private/Boolean.h", "bindings/private/Counter.h", "bindings/private/Datetime.h", "bindings/private/Event.h", + "bindings/private/MemoryDistribution.h", "bindings/private/String.h", "bindings/private/Timespan.h", "bindings/private/Uuid.h", ] if CONFIG["COMPILE_ENVIRONMENT"]: EXPORTS.mozilla.glean += [ "!fog_ffi_generated.h", @@ -47,16 +48,17 @@ if CONFIG["MOZ_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/MemoryDistribution.cpp", "bindings/private/String.cpp", "bindings/private/Timespan.cpp", "bindings/private/Uuid.cpp", "ipc/FOGIPC.cpp", ] TEST_DIRS += [ "gtest",
--- a/toolkit/components/glean/pytest/metrics_test_output_cpp +++ b/toolkit/components/glean/pytest/metrics_test_output_cpp @@ -48,16 +48,25 @@ namespace test { * generated from test.timespan_metric */ /** * A multi-line * description */ constexpr impl::TimespanMetric timespan_metric(11); + /** + * generated from test.memory_distribution_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::MemoryDistributionMetric memory_distribution_metric(13); + } namespace test_nested { /** * generated from test.nested.uuid_metric */ /** * A multi-line * description
--- a/toolkit/components/glean/pytest/metrics_test_output_js +++ b/toolkit/components/glean/pytest/metrics_test_output_js @@ -48,16 +48,20 @@ static already_AddRefed<nsISupports> New case 5: /* String */ { return MakeAndAddRef<GleanString>(metricId); } case 8: /* Timespan */ { return MakeAndAddRef<GleanTimespan>(metricId); } + case 10: /* MemoryDistribution */ + { + return MakeAndAddRef<GleanMemoryDistribution>(metricId); + } case 11: /* Uuid */ { return MakeAndAddRef<GleanUuid>(metricId); } case 12: /* Datetime */ { return MakeAndAddRef<GleanDatetime>(metricId); }
--- a/toolkit/components/glean/test_metrics.yaml +++ b/toolkit/components/glean/test_metrics.yaml @@ -106,16 +106,34 @@ test_only: data_sensitivity: - technical notification_emails: - glean-team@mozilla.com expires: never send_in_pings: - test-ping + do_you_remember: + type: memory_distribution + memory_unit: megabyte + description: | + They say it's the second thing to go. + This is a test-only metric. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1673648 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1673648#c1 + data_sensitivity: + - technical + expires: never + notification_emails: + - glean-team@mozilla.com + send_in_pings: + - test-ping + test_only.ipc: a_counter: type: counter description: | This is a test-only metric. Just counting things. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1646165
--- a/toolkit/components/glean/xpcom/nsIGleanMetrics.idl +++ b/toolkit/components/glean/xpcom/nsIGleanMetrics.idl @@ -83,16 +83,48 @@ interface nsIGleanCounter : 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. */ jsval testGetValue(in AUTF8String aStorageName); }; +[scriptable, uuid(eea5ed46-16ba-46cd-bb1f-504581987fe1)] +interface nsIGleanMemoryDistribution : nsISupports +{ + /* + * Accumulates the provided sample in the metric. + * + * @param aSample The sample to be recorded by the metric. The sample is + * assumed to be in the confgured memory unit of the metric. + * + * Notes: Values bigger than 1 Terabyte (2^40 bytes) are truncated and an + * InvalidValue error is recorded. + */ + void accumulate(in uint64_t aSample); + + /** + * **Test-only API** + * + * Gets the currently stored value as a DistributionData. + * + * 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. + */ + [implicit_jscontext] + jsval testGetValue(in ACString aStorageName); +}; + [scriptable, uuid(d84a3555-46f1-48c1-9122-e8e88b069d2b)] interface nsIGleanString : nsISupports { /* * Set to the specified value. * * @param value The string to set the metric to. */
--- a/toolkit/components/glean/xpcshell/test_Glean.js +++ b/toolkit/components/glean/xpcshell/test_Glean.js @@ -142,8 +142,23 @@ add_task(async function test_fog_event_w // 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")); }); + +add_task(async function test_fog_memory_distribution_works() { + Glean.test_only.do_you_remember.accumulate(7); + Glean.test_only.do_you_remember.accumulate(17); + + let data = Glean.test_only.do_you_remember.testGetValue("test-ping"); + // `data.sum` is in bytes, but the metric is in MB. + Assert.equal(24 * 1024 * 1024, data.sum, "Sum's correct"); + for (let [bucket, count] of Object.entries(data.values)) { + Assert.ok( + count == 0 || (count == 1 && (bucket == 17520006 || bucket == 7053950)), + "Only two buckets have a sample" + ); + } +});