From 9c8e0c6df65c347c90df917a17385f78de940f85 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 2 May 2025 09:24:06 +0000 Subject: [PATCH] Bug 1896607 - FOG: Memory reporter on the Rust side. r=chutten This sets up the scaffolding on the Rust side and calls that from the C++ side. To start it only measures the size of pings. This requires a Glean update that implements all that malloc_size_of. Differential Revision: https://phabricator.services.mozilla.com/D210288 --- Cargo.lock | 2 + toolkit/components/glean/api/Cargo.toml | 2 + toolkit/components/glean/api/src/lib.rs | 55 +++++++++++++++++++ .../components/glean/api/src/private/ping.rs | 9 +++ .../templates/rust_pings.jinja2 | 12 ++++ toolkit/components/glean/cbindgen.toml | 2 +- toolkit/components/glean/xpcom/FOG.cpp | 11 ++++ 7 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6eb1fd3e83e8..b1b9c536cf0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1967,6 +1967,7 @@ dependencies = [ "inherent", "log", "mozbuild", + "nserror", "nsstring", "once_cell", "serde", @@ -1975,6 +1976,7 @@ dependencies = [ "thin-vec", "time 0.1.45", "uuid", + "wr_malloc_size_of", "xpcom", ] diff --git a/toolkit/components/glean/api/Cargo.toml b/toolkit/components/glean/api/Cargo.toml index 7be88d5e18d3..a56f703c92b9 100644 --- a/toolkit/components/glean/api/Cargo.toml +++ b/toolkit/components/glean/api/Cargo.toml @@ -22,6 +22,8 @@ xpcom = { path = "../../../../xpcom/rust/xpcom", optional = true } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } mozbuild = "0.1" serde_json = "1" +nserror = { path = "../../../../xpcom/rust/nserror" } +malloc_size_of = { path = "../../../../gfx/wr/wr_malloc_size_of", package = "wr_malloc_size_of", features = ["once_cell"] } # Note, we will want to remove this when Bug 1925313 is fixed [dependencies.time] diff --git a/toolkit/components/glean/api/src/lib.rs b/toolkit/components/glean/api/src/lib.rs index 2f51b94917fe..4ef9c5044e64 100644 --- a/toolkit/components/glean/api/src/lib.rs +++ b/toolkit/components/glean/api/src/lib.rs @@ -25,3 +25,58 @@ pub mod ipc; #[cfg(test)] mod common_test; + +#[cfg(feature = "with_gecko")] +use nserror::{NS_ERROR_UNEXPECTED, NS_OK}; +#[cfg(feature = "with_gecko")] +use nsstring::{nsACString, nsCStr}; +#[cfg(feature = "with_gecko")] +use xpcom::interfaces::{nsIHandleReportCallback, nsIMemoryReporter, nsISupports}; +#[cfg(feature = "with_gecko")] +use xpcom::RefPtr; + +/// Collect a memory report for heap memory in bytes. +#[cfg(feature = "with_gecko")] +macro_rules! moz_collect_report { + ($cb:ident, $path:expr, $amount:expr, $desc:expr, $data:expr) => { + $cb.Callback( + &nsCStr::new() as &nsACString, + &nsCStr::from($path) as &nsACString, + nsIMemoryReporter::KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, + $amount as i64, + &nsCStr::from($desc) as &nsACString, + $data, + ) + }; +} + +#[cfg(feature = "with_gecko")] +#[no_mangle] +unsafe extern "C" fn fog_collect_reports( + callback: *const nsIHandleReportCallback, + data: *const nsISupports, + _anonymize: bool, +) -> nserror::nsresult { + let callback = match RefPtr::from_raw(callback) { + Some(ptr) => ptr, + None => return NS_ERROR_UNEXPECTED, + }; + + extern "C" { + fn fog_malloc_size_of(ptr: *const xpcom::reexports::libc::c_void) -> usize; + } + let mut ops = malloc_size_of::MallocSizeOfOps::new(fog_malloc_size_of, None); + + let ping_size = pings::fog_ping_alloc_size(&mut ops); + + moz_collect_report!( + callback, + "explicit/fog/pings", + ping_size, + "Memory used by all FOG pings", + data + ); + + NS_OK +} diff --git a/toolkit/components/glean/api/src/private/ping.rs b/toolkit/components/glean/api/src/private/ping.rs index cdb5bf039eed..1328b2ce19e9 100644 --- a/toolkit/components/glean/api/src/private/ping.rs +++ b/toolkit/components/glean/api/src/private/ping.rs @@ -18,6 +18,15 @@ pub enum Ping { Child, } +impl malloc_size_of::MallocSizeOf for Ping { + fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize { + match self { + Ping::Child => 0, + Ping::Parent { inner, .. } => inner.size_of(ops), + } + } +} + impl Ping { /// Create a new ping type for the given name, whether to include the client ID and whether to /// send this ping empty. diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 index f1f8bf9bab1f..2f7dea715205 100644 --- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 +++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 @@ -125,3 +125,15 @@ pub(crate) fn set_ping_enabled_by_id(id: u32, enabled: bool) { } } } + +/// Measure the allocation size of all known pings. +#[doc(hidden)] +pub fn fog_ping_alloc_size(ops: &mut malloc_size_of::MallocSizeOfOps) -> usize { + use malloc_size_of::MallocSizeOf; + + let mut n = 0; + {% for obj in all_objs['pings'].values() %} + n += ::once_cell::sync::Lazy::get(&{{ obj.name|snake_case }}).map(|p| p.size_of(ops)).unwrap_or(0); + {% endfor %} + n +} diff --git a/toolkit/components/glean/cbindgen.toml b/toolkit/components/glean/cbindgen.toml index fd3af2220cc6..127930c25c97 100644 --- a/toolkit/components/glean/cbindgen.toml +++ b/toolkit/components/glean/cbindgen.toml @@ -14,7 +14,7 @@ line_length = 100 tab_width = 2 language = "C++" namespaces = ["mozilla::glean::impl"] -includes = ["nsTArray.h", "nsString.h"] +includes = ["nsTArray.h", "nsString.h", "nsIMemoryReporter.h"] [export.rename] "ThinVec" = "nsTArray" diff --git a/toolkit/components/glean/xpcom/FOG.cpp b/toolkit/components/glean/xpcom/FOG.cpp index 49282fe6c9b4..33e1e8a79bad 100644 --- a/toolkit/components/glean/xpcom/FOG.cpp +++ b/toolkit/components/glean/xpcom/FOG.cpp @@ -655,6 +655,16 @@ void FOG::InitMemoryReporter() { RegisterWeakMemoryReporter(this); } MOZ_DEFINE_MALLOC_SIZE_OF(FOGMallocSizeOf) +// Rust doesn't support weak-linking, so MFBT_API functions like +// moz_malloc_size_of need a C++ wrapper that uses the regular ABI +// +// We're not using MOZ_DEFINE_MALLOC_SIZE_OF here because that makes the +// function `static`, which would make it not visible outside this file +extern "C" size_t fog_malloc_size_of(const void* aPtr) { + MOZ_REPORT(aPtr); + return moz_malloc_size_of(aPtr); +} + NS_IMETHODIMP FOG::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { @@ -663,6 +673,7 @@ FOG::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, MOZ_COLLECT_REPORT("explicit/fog/impl", KIND_HEAP, UNITS_BYTES, aMallocSizeOf(this), "Memory used by the FOG core implementation"); + glean::impl::fog_collect_reports(aHandleReport, aData, aAnonymize); return NS_OK; }