tor-browser

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

commit d3869e5259f94ab88bf4cc06c39c36589ac0b9e6
parent 913045c3a687b810208fb4e1b010994eaa9d05ef
Author: Tarek Ziadé <tarek@ziade.org>
Date:   Wed,  5 Nov 2025 12:13:31 +0000

Bug 1996801 - Add an API to get the current process used memory r=florian

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

Diffstat:
Mdom/base/ChromeUtils.cpp | 22++++++++++++++++++++++
Mdom/base/ChromeUtils.h | 5+++++
Mdom/chrome-webidl/ChromeUtils.webidl | 14++++++++++++++
Mtoolkit/components/ml/actors/MLEngineParent.sys.mjs | 5+++++
Mtoolkit/components/processtools/ProcInfo.h | 9+++++++++
Mtoolkit/components/processtools/ProcInfo.mm | 18++++++++++++++++++
Mtoolkit/components/processtools/ProcInfo_bsd.cpp | 4++++
Mtoolkit/components/processtools/ProcInfo_linux.cpp | 20++++++++++++++++++++
Mtoolkit/components/processtools/ProcInfo_solaris.cpp | 4++++
Mtoolkit/components/processtools/ProcInfo_win.cpp | 16++++++++++++++++
Atoolkit/components/processtools/tests/xpcshell/test_proc_info.js | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/processtools/tests/xpcshell/xpcshell.toml | 2++
12 files changed, 200 insertions(+), 0 deletions(-)

diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp @@ -2054,6 +2054,28 @@ already_AddRefed<Promise> ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, } /* static */ +uint64_t ChromeUtils::GetCurrentProcessMemoryUsage(GlobalObject& aGlobal, + ErrorResult& aRv) { + uint64_t retVal = 0; + nsresult rv = mozilla::GetCurrentProcessMemoryUsage(&retVal); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + } + return retVal; +} + +/* static */ +uint64_t ChromeUtils::GetCpuTimeSinceProcessStart(GlobalObject& aGlobal, + ErrorResult& aRv) { + uint64_t retVal = 0; + nsresult rv = mozilla::GetCpuTimeSinceProcessStartInMs(&retVal); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + } + return retVal; +} + +/* static */ bool ChromeUtils::VsyncEnabled(GlobalObject& aGlobal) { return mozilla::gfx::VsyncSource::GetFastestVsyncRate().isSome(); } diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h @@ -216,6 +216,11 @@ class ChromeUtils { static already_AddRefed<Promise> RequestProcInfo(GlobalObject& aGlobal, ErrorResult& aRv); + static uint64_t GetCurrentProcessMemoryUsage(GlobalObject& aGlobal, + ErrorResult& aRv); + static uint64_t GetCpuTimeSinceProcessStart(GlobalObject& aGlobal, + ErrorResult& aRv); + static bool VsyncEnabled(GlobalObject& aGlobal); static void Import(const GlobalObject& aGlobal, diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl @@ -442,6 +442,20 @@ namespace ChromeUtils { UTF8String encodeURIForSrcset(UTF8String uri); /** + * Returns, in bytes, a platform-normalized estimate of the process's private physical memory usage. + * Any error when calling the underlying platform-specific API will be thrown. + */ + [Throws] + readonly attribute unsigned long long currentProcessMemoryUsage; + + /** + * Return the number of milliseconds of CPU time used since process start. + * Any error when calling the underlying platform-specific API will be thrown. + */ + [Throws] + readonly attribute unsigned long long cpuTimeSinceProcessStart; + + /** * IF YOU ADD NEW METHODS HERE, MAKE SURE THEY ARE THREAD-SAFE. */ }; diff --git a/toolkit/components/ml/actors/MLEngineParent.sys.mjs b/toolkit/components/ml/actors/MLEngineParent.sys.mjs @@ -1427,6 +1427,11 @@ export class MLEngine { * @returns {Promise<null | { cpuTime: null | number, memory: null | number}>} */ async getInferenceResources() { + // TODO(Greg): ask that question directly to the inference process *or* move your metrics down into the child process + // so you don't have to do any IPC at all. + // you can get the memory with ChromeUtils.currentProcessMemoryUsage and the CPU since start with ChromeUtils.cpuTimeSinceProcessStart + // theses call can be done anywhere in the inference process including the workers, which means you can Glean metrics in any place with + // no IPC try { const { children } = await ChromeUtils.requestProcInfo(); const [inference] = children.filter(child => child.type == "inference"); diff --git a/toolkit/components/processtools/ProcInfo.h b/toolkit/components/processtools/ProcInfo.h @@ -21,6 +21,15 @@ class GeckoChildProcessHost; } /** + * Returns, in bytes, a platform-normalized estimate of the process's + * private physical memory usage, that is, how much RAM the process + * alone consumes. + * + * @return NS_OK on success. + */ +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult); + +/** * Return the number of milliseconds of CPU time used since process start. * * @return NS_OK on success. diff --git a/toolkit/components/processtools/ProcInfo.mm b/toolkit/components/processtools/ProcInfo.mm @@ -28,6 +28,24 @@ static void GetTimeBase(mach_timebase_info_data_t* timebase) { namespace mozilla { +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult) { + if (!aResult) { + return NS_ERROR_INVALID_ARG; + } + task_vm_info_data_t info; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + + kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO, + reinterpret_cast<task_info_t>(&info), &count); + + if (kr != KERN_SUCCESS) { + return NS_ERROR_FAILURE; + } + // phys_footprint matches Activity Monitor’s “Memory” column on macOS 10.11+ + *aResult = info.phys_footprint; + return NS_OK; +} + nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { task_power_info_data_t task_power_info; mach_msg_type_number_t count = TASK_POWER_INFO_COUNT; diff --git a/toolkit/components/processtools/ProcInfo_bsd.cpp b/toolkit/components/processtools/ProcInfo_bsd.cpp @@ -37,6 +37,10 @@ nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { return NS_ERROR_FAILURE; } +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} + nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/toolkit/components/processtools/ProcInfo_linux.cpp b/toolkit/components/processtools/ProcInfo_linux.cpp @@ -22,6 +22,26 @@ namespace mozilla { +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult) { + if (!aResult) { + return NS_ERROR_INVALID_ARG; + } + FILE* f = fopen("/proc/self/statm", "r"); + if (!f) { + return NS_ERROR_FAILURE; + } + size_t vmSize = 0, resident = 0, shared = 0; + const int kExpected = 3; + int nread = fscanf(f, "%zu %zu %zu", &vmSize, &resident, &shared); + fclose(f); + + if (nread != kExpected) { + return NS_ERROR_FAILURE; + } + *aResult = uint64_t(resident - shared) * getpagesize(); + return NS_OK; +} + int GetCycleTimeFrequencyMHz() { return 0; } // StatReader can parse and tokenize a POSIX stat file. diff --git a/toolkit/components/processtools/ProcInfo_solaris.cpp b/toolkit/components/processtools/ProcInfo_solaris.cpp @@ -28,6 +28,10 @@ namespace mozilla { +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult) { + return NS_ERROR_NOT_IMPLEMENTED; +} + int GetCycleTimeFrequencyMHz() { return 0; } nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult) { diff --git a/toolkit/components/processtools/ProcInfo_win.cpp b/toolkit/components/processtools/ProcInfo_win.cpp @@ -32,6 +32,22 @@ static uint64_t ToNanoSeconds(const FILETIME& aFileTime) { return usec.QuadPart * 100; } +nsresult GetCurrentProcessMemoryUsage(uint64_t* aResult) { + if (!aResult) { + return NS_ERROR_INVALID_ARG; + } + + PROCESS_MEMORY_COUNTERS_EX pmc; + if (!GetProcessMemoryInfo(GetCurrentProcess(), + reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), + sizeof(pmc))) { + return NS_ERROR_FAILURE; + } + + *aResult = static_cast<uint64_t>(pmc.PrivateUsage); + return NS_OK; +} + int GetCpuFrequencyMHz() { static const int frequency = []() { // Get the nominal CPU frequency. diff --git a/toolkit/components/processtools/tests/xpcshell/test_proc_info.js b/toolkit/components/processtools/tests/xpcshell/test_proc_info.js @@ -0,0 +1,81 @@ +"use strict"; + +// Keep allocations alive across the whole test so GC can't reclaim them. +const KEEP_ALIVE = []; + +// Allocate N MiB using ArrayBuffers and *touch* the memory so it's committed. +function allocateTypedMiB(totalMiB = 64, chunkMiB = 8) { + const chunkSize = chunkMiB * 1024 * 1024; + const count = Math.ceil(totalMiB / chunkMiB); + for (let i = 0; i < count; i++) { + const buf = new ArrayBuffer(chunkSize); + // Touch one byte per page to ensure RSS growth. + const view = new Uint8Array(buf); + for (let j = 0; j < view.length; j += 4096) { + view[j] = 1; + } + KEEP_ALIVE.push(buf); + } +} + +add_task(async function test_currentProcessMemoryUsage() { + // Simulate memory allocation (strongly reachable + committed) + Cu.forceGC(); + Cu.forceCC(); + let initialMemory = ChromeUtils.currentProcessMemoryUsage; + let arr = []; + for (let i = 0; i < 1_000_000; i++) { + arr.push({ v: i }); + } + + KEEP_ALIVE.push(arr); + // Plus a guaranteed 64 MiB of typed-array memory to make growth obvious + allocateTypedMiB(64, 8); + + let finalMemory = ChromeUtils.currentProcessMemoryUsage; + + info(`Initial memory: ${initialMemory}, final memory: ${finalMemory}`); + + Assert.greater( + finalMemory, + initialMemory, + "Memory usage should increase after allocations" + ); + + Assert.greater( + finalMemory - initialMemory, + 64 * 1024 * 1024, + "Memory usage should have growned over 64MiB" + ); +}); + +async function getCurrentProcessInfo() { + let info = await ChromeUtils.requestProcInfo(); + // nanoseconds to milliseconds + return Math.floor(info.cpuTime / 1000000); +} + +add_task(async function test_cpuTimeSinceProcessStart() { + let initialCPUTimeFromRequestProcInfo = await getCurrentProcessInfo(); + let initialCPUTime = ChromeUtils.cpuTimeSinceProcessStart; + let secondCPUTimeFromRequestProcInfo = await getCurrentProcessInfo(); + Assert.greaterOrEqual(initialCPUTime, initialCPUTimeFromRequestProcInfo); + Assert.greaterOrEqual(secondCPUTimeFromRequestProcInfo, initialCPUTime); + + // Simulate some CPU load + let sum = [0]; + for (let i = 0; i < 5_000_000; i++) { + sum[0] += Math.sqrt(i); + } + let finalCPUTimeFromRequestProcInfo = await getCurrentProcessInfo(); + let finalCPUTime = ChromeUtils.cpuTimeSinceProcessStart; + Assert.greaterOrEqual(finalCPUTime, finalCPUTimeFromRequestProcInfo); + + info(`Initial CPU time: ${initialCPUTime}, final CPU time: ${finalCPUTime}`); + + Assert.greater( + finalCPUTime, + initialCPUTime, + "CPU time should increase after computation" + ); +}); diff --git a/toolkit/components/processtools/tests/xpcshell/xpcshell.toml b/toolkit/components/processtools/tests/xpcshell/xpcshell.toml @@ -2,6 +2,8 @@ firefox-appdir = "browser" subprocess = true +["test_proc_info.js"] + ["test_process_kill.js"] skip-if = ["os == 'android'"]