☠☠ backed out by 1d72ff0f809f ☠ ☠ | |
author | Nicholas Nethercote <nnethercote@mozilla.com> |
Thu, 19 Sep 2013 15:52:28 -0700 | |
changeset 148608 | 09c71a3e7b85211747236c168ec6524fe57c352b |
parent 148607 | 8a8691a260121ee5863f44ce3883507e177db417 |
child 148609 | 9195be8a50cbb0478e07c1b29d419b6fc78768a8 |
push id | 25349 |
push user | ryanvm@gmail.com |
push date | Wed, 25 Sep 2013 18:52:12 +0000 |
treeherder | mozilla-central@39f30376058c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mccr8 |
bugs | 913260 |
milestone | 27.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/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1588,57 +1588,29 @@ private: int64_t Amount() MOZ_OVERRIDE { JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * js::gc::ChunkSize; } }; -// Nb: js-main-runtime-compartments/system + js-main-runtime-compartments/user -// could be different to the number of compartments reported by JSReporter if a -// garbage collection occurred between them being consulted. We could move -// these reporters into JSReporter to avoid that problem, but then we couldn't -// easily report them via telemetry, so we live with the small risk of -// inconsistencies. - -class RedundantJSMainRuntimeCompartmentsSystemReporter MOZ_FINAL : public MemoryUniReporter +static int64_t +JSMainRuntimeCompartmentsSystemDistinguishedAmount() { -public: - // An empty description is ok because this is a "redundant/"-prefixed - // reporter and so is ignored by about:memory. - RedundantJSMainRuntimeCompartmentsSystemReporter() - : MemoryUniReporter("redundant/js-main-runtime-compartments/system", - KIND_OTHER, UNITS_COUNT, "") - {} -private: - int64_t Amount() MOZ_OVERRIDE - { - JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); - return JS::SystemCompartmentCount(rt); - } -}; - -class RedundantJSMainRuntimeCompartmentsUserReporter MOZ_FINAL : public MemoryUniReporter + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); + return JS::SystemCompartmentCount(rt); +} + +static int64_t +JSMainRuntimeCompartmentsUserDistinguishedAmount() { -public: - // An empty description is ok because this is a "redundant/"-prefixed - // reporter and so is ignored by about:memory. - RedundantJSMainRuntimeCompartmentsUserReporter() - : MemoryUniReporter("redundant/js-main-runtime-compartments/user", - KIND_OTHER, UNITS_COUNT, -"The number of JavaScript compartments for user code in the main JSRuntime.") - {} -private: - int64_t Amount() MOZ_OVERRIDE - { - JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); - return JS::UserCompartmentCount(rt); - } -}; + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); + return JS::UserCompartmentCount(rt); +} // This is also a single reporter so it can be used by telemetry. class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public MemoryUniReporter { public: JSMainRuntimeTemporaryPeakReporter() : MemoryUniReporter("js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES, @@ -3051,21 +3023,22 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* js::SetSourceHook(runtime, new XPCJSSourceHook); // Set up locale information and callbacks for the newly-created runtime so // that the various toLocaleString() methods, localeCompare(), and other // internationalization APIs work as desired. if (!xpc_LocalizeRuntime(runtime)) NS_RUNTIMEABORT("xpc_LocalizeRuntime failed."); + // Register memory reporters and distinguished amount functions. NS_RegisterMemoryReporter(new JSMainRuntimeGCHeapReporter()); - NS_RegisterMemoryReporter(new RedundantJSMainRuntimeCompartmentsSystemReporter()); - NS_RegisterMemoryReporter(new RedundantJSMainRuntimeCompartmentsUserReporter()); NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter()); NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter); + RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount); + RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount); // Install a JavaScript 'debugger' keyword handler in debug builds only #ifdef DEBUG if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler) xpc_InstallJSDebuggerKeywordHandler(runtime); #endif }
--- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -126,66 +126,50 @@ function onUnload() "child-memory-reporter-update"); } //--------------------------------------------------------------------------- /** * Iterates over each reporter. * - * @param aIgnoreReporter - * Function that indicates if we should skip an entire reporter, based - * on its name. - * @param aIgnoreReport - * Function that indicates if we should skip a single report from a - * reporter, based on its path. * @param aHandleReport * The function that's called for each non-skipped report. */ -function processMemoryReporters(aIgnoreReporter, aIgnoreReport, aHandleReport) +function processMemoryReporters(aHandleReport) { let handleReport = function(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription) { - if (!aIgnoreReport(aUnsafePath)) { - aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, - aDescription, /* presence = */ undefined); - } + aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, + aDescription, /* presence = */ undefined); } let e = gMgr.enumerateReporters(); while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); - if (!aIgnoreReporter(mr.name)) { - // |collectReports| never passes in a |presence| argument. - mr.collectReports(handleReport, null); - } + mr.collectReports(handleReport, null); } } /** * Iterates over each report. * * @param aReports * Array of reports, read from a file or the clipboard. - * @param aIgnoreReport - * Function that indicates if we should skip a single report, based - * on its path. * @param aHandleReport * The function that's called for each report. */ -function processMemoryReportsFromFile(aReports, aIgnoreReport, aHandleReport) +function processMemoryReportsFromFile(aReports, aHandleReport) { // Process each memory reporter with aHandleReport. for (let i = 0; i < aReports.length; i++) { let r = aReports[i]; - if (!aIgnoreReport(r.path)) { - aHandleReport(r.process, r.path, r.kind, r.units, r.amount, - r.description, r._presence); - } + aHandleReport(r.process, r.path, r.kind, r.units, r.amount, + r.description, r._presence); } } //--------------------------------------------------------------------------- // The <div> holding everything but the header and footer (if they're present). // It's what is updated each time the page changes. let gMain; @@ -194,17 +178,16 @@ let gMain; let gFooter; // The "verbose" checkbox. let gVerbose; // Values for the second argument to updateMainAndFooter. let HIDE_FOOTER = 0; let SHOW_FOOTER = 1; -let IGNORE_FOOTER = 2; function updateMainAndFooter(aMsg, aFooterAction, aClassName) { // Clear gMain by replacing it with an empty node. let tmp = gMain.cloneNode(false); gMain.parentNode.replaceChild(tmp, gMain); gMain = tmp; @@ -221,17 +204,16 @@ function updateMainAndFooter(aMsg, aFoot className = className + " " + aClassName; } appendElementWithText(gMain, 'div', className, aMsg); } switch (aFooterAction) { case HIDE_FOOTER: gFooter.classList.add('hidden'); break; case SHOW_FOOTER: gFooter.classList.remove('hidden'); break; - case IGNORE_FOOTER: break; default: assertInput(false, "bad footer action in updateMainAndFooter"); } } function appendTextNode(aP, aText) { let e = document.createTextNode(aText); aP.appendChild(e); @@ -499,18 +481,18 @@ function updateAboutMemoryFromJSONObject { try { assertInput(aObj.version === gCurrentFileFormatVersion, "data version number missing or doesn't match"); assertInput(aObj.hasMozMallocUsableSize !== undefined, "missing 'hasMozMallocUsableSize' property"); assertInput(aObj.reports && aObj.reports instanceof Array, "missing or non-array 'reports' property"); - let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) { - processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport); + let process = function(aHandleReport) { + processMemoryReportsFromFile(aObj.reports, aHandleReport); } appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize); } catch (ex) { handleException(ex); } } /** @@ -932,29 +914,16 @@ function getPCollsByProcess(aProcessRepo { let pcollsByProcess = {}; // This regexp matches sentences and sentence fragments, i.e. strings that // start with a capital letter and ends with a '.'. (The final sentence may // be in parentheses, so a ')' might appear after the '.'.) const gSentenceRegExp = /^[A-Z].*\.\)?$/m; - // Ignore any "redundant/"-prefixed reporters and reports, which are only - // used by telemetry. - - function ignoreReporter(aName) - { - return aName.startsWith("redundant/"); - } - - function ignoreReport(aUnsafePath) - { - return aUnsafePath.startsWith("redundant/"); - } - function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription, aPresence) { if (aUnsafePath.startsWith("explicit/")) { assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP, "bad explicit kind"); assertInput(aUnits === UNITS_BYTES, "bad explicit units"); assertInput(gSentenceRegExp.test(aDescription), @@ -983,17 +952,18 @@ function getPCollsByProcess(aProcessRepo assertInput(gSentenceRegExp.test(aDescription), "non-sentence other description: " + aUnsafePath + ", " + aDescription); } } assert(aPresence === undefined || aPresence == DReport.PRESENT_IN_FIRST_ONLY || - aPresence == DReport.PRESENT_IN_SECOND_ONLY); + aPresence == DReport.PRESENT_IN_SECOND_ONLY, + "bad presence"); let process = aProcess === "" ? gUnnamedProcessStr : aProcess; let unsafeNames = aUnsafePath.split('/'); let unsafeName0 = unsafeNames[0]; let isDegenerate = unsafeNames.length === 1; // Get the PColl table for the process, creating it if necessary. let pcoll = pcollsByProcess[process]; @@ -1041,17 +1011,17 @@ function getPCollsByProcess(aProcessRepo t._amount = aAmount; t._description = aDescription; if (aPresence !== undefined) { t._presence = aPresence; } } } - aProcessReports(ignoreReporter, ignoreReport, handleReport); + aProcessReports(handleReport); return pcollsByProcess; } //--------------------------------------------------------------------------- // There are two kinds of TreeNode. // - Leaf TreeNodes correspond to reports.
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -145,41 +145,32 @@ is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception"); } // The main process always comes first when we display about:memory. The // remaining processes are sorted by their |resident| values (starting with // the largest). Processes without a |resident| memory reporter are saved // for the end. let fakeReporters2 = [ - { name: "redundant/foobar", - collectReports: function(aCbObj, aClosure) { - // Shouldn't reach here; aboutMemory.js should skip the reporter. - // (Nb: this must come after |mgr.explicit| is accessed, otherwise it - // *will* be run by nsMemoryReporterManager::GetExplicit()). - ok(false, "'redundant/foobar' reporter was run"); - } - }, { name: "fake3", collectReports: function(aCbObj, aClosure) { function f(aP1, aP2, aK, aU, aA) { aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure); } f("2nd", "heap-allocated", OTHER, BYTES,1000* MB); f("2nd", "heap-unallocated",OTHER, BYTES,100 * MB); f("2nd", "explicit/a/b/c", HEAP, BYTES,497 * MB); f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge f("2nd", "explicit/a/b/c", HEAP, BYTES, 1 * MB); // dup: merge f("2nd", "explicit/flip\\the\\backslashes", HEAP, BYTES,200 * MB); f("2nd", "explicit/compartment(compartment-url)", HEAP, BYTES,200 * MB); f("2nd", "other0", OTHER, BYTES,666 * MB); f("2nd", "other1", OTHER, BYTES,111 * MB); - f("2nd", "redundant/blah", NONHEAP, BYTES,24*4*KB); // ignored! // Check that we can handle "heap-allocated" not being present. f("3rd", "explicit/a/b", HEAP, BYTES,333 * MB); f("3rd", "explicit/a/c", HEAP, BYTES,444 * MB); f("3rd", "other1", OTHER, BYTES, 1 * MB); f("3rd", "resident", OTHER, BYTES,100 * MB); // Invalid values (negative, too-big) should be identified.
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -40,26 +40,17 @@ const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE; let vsizeAmounts = []; let residentAmounts = []; let jsGcHeapAmounts = []; let heapAllocatedAmounts = []; let storageSqliteAmounts = []; - let areJsNonWindowCompartmentsPresent = false; - let areWindowObjectsJsCompartmentsPresent = false; - let isSandboxLocationShown = false; - let isPlacesPresent = false; - let isImagesPresent = false; - let isXptiWorkingSetPresent = false; - let isAtomTablePresent = false; - let isBigStringPresent = false; - let isSmallString1Present = false; - let isSmallString2Present = false; + let present = {} // Generate a long, random string. We'll check that this string is // reported in at least one of the memory reporters. let bigString = ""; while (bigString.length < 10000) { bigString += Math.random(); } let bigStringPrefix = bigString.substring(0, 100); @@ -87,58 +78,62 @@ jsGcHeapAmounts.push(aAmount); } else if (aPath === "heap-allocated") { heapAllocatedAmounts.push(aAmount); } else if (aPath === "storage-sqlite") { storageSqliteAmounts.push(aAmount); // Check the presence of some other notable reporters. } else if (aPath.search(/^explicit\/js-non-window\/.*compartment\(/) >= 0) { - areJsNonWindowCompartmentsPresent = true; + present.jsNonWindowCompartments = true; } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-compartment\(/) >= 0) { - areWindowObjectsJsCompartmentsPresent = true; + present.windowObjectsJsCompartments = true; } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) { - isPlacesPresent = true; + present.places = true; } else if (aPath.search(/^explicit\/images/) >= 0) { - isImagesPresent = true; + present.images = true; } else if (aPath.search(/^explicit\/xpti-working-set$/) >= 0) { - isXptiWorkingSetPresent = true; + present.xptiWorkingSet = true; } else if (aPath.search(/^explicit\/atom-tables$/) >= 0) { - isAtomTablePresent = true; + present.atomTable = true; } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) { // A system compartment with a location (such as a sandbox) should // show that location. - isSandboxLocationShown = true; + present.sandboxLocation = true; } else if (aPath.contains(bigStringPrefix)) { - isBigStringPresent = true; + present.bigString = true; } else if (aPath.contains("!)(*&")) { - isSmallString1Present = true; + present.smallString1 = true; } else if (aPath.contains("@)(*&")) { - isSmallString2Present = true; + present.smallString2 = true; } } let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. getService(Ci.nsIMemoryReporterManager); - // Access mgr.explicit and mgr.resident just to make sure they don't crash. - // We can't check their actual values because they're non-deterministic. + // Access the distinguished amounts (mgr.explicit et al.) just to make sure + // they don't crash. We can't check their actual values because they're + // non-deterministic. // // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a // --enable-trace-malloc build. Allow for that exception, but *only* that // exception. let dummy; let haveExplicit = true; try { dummy = mgr.explicit; } catch (ex) { is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception"); haveExplicit = false; } dummy = mgr.resident; + dummy = mgr.residentFast; + dummy = mgr.JSMainRuntimeCompartmentsSystem; + dummy = mgr.JSMainRuntimeCompartmentsUser; let e = mgr.enumerateReporters(); while (e.hasMoreElements()) { let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter); r.collectReports(handleReport, null); // Access |name| to make sure it doesn't crash or assert. dummy = r.name; @@ -157,23 +152,23 @@ if (haveExplicit) { checkSpecialReport("heap-allocated", heapAllocatedAmounts); } checkSpecialReport("vsize", vsizeAmounts); checkSpecialReport("resident", residentAmounts); checkSpecialReport("js-main-runtime-gc-heap-committed/used/gc-things", jsGcHeapAmounts); checkSpecialReport("storage-sqlite", storageSqliteAmounts); - ok(areJsNonWindowCompartmentsPresent, "js-non-window compartments are present"); - ok(areWindowObjectsJsCompartmentsPresent, "window-objects/.../js compartments are present"); - ok(isSandboxLocationShown, "sandbox locations are present"); - ok(isPlacesPresent, "places is present"); - ok(isImagesPresent, "images is present"); - ok(isXptiWorkingSetPresent, "xpti-working-set is present"); - ok(isAtomTablePresent, "atom-table is present"); - ok(isBigStringPresent, "large string is present"); - ok(isSmallString1Present, "small string 1 is present"); - ok(isSmallString2Present, "small string 2 is present"); + ok(present.jsNonWindowCompartments, "js-non-window compartments are present"); + ok(present.windowObjectsJsCompartments, "window-objects/.../js compartments are present"); + ok(present.places, "places is present"); + ok(present.images, "images is present"); + ok(present.xptiWorkingSet, "xpti-working-set is present"); + ok(present.atomTable, "atom-table is present"); + ok(present.sandboxLocation, "sandbox locations are present"); + ok(present.bigString, "large string is present"); + ok(present.smallString1, "small string 1 is present"); + ok(present.smallString2, "small string 2 is present"); ]]> </script> </window>
--- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -36,34 +36,28 @@ const PREF_PREVIOUS_BUILDID = PREF_BRANC // Do not gather data more than once a minute const TELEMETRY_INTERVAL = 60000; // Delay before intializing telemetry (ms) const TELEMETRY_DELAY = 60000; // MEM_HISTOGRAMS lists the memory reporters we turn into histograms. // -// Note that we currently handle only vanilla memory reporters, not memory -// multi-reporters. -// // test_TelemetryPing.js relies on some of these memory reporters // being here. If you remove any of the following histograms from // MEM_HISTOGRAMS, you'll have to modify test_TelemetryPing.js: // // * MEMORY_JS_GC_HEAP, and // * MEMORY_JS_COMPARTMENTS_SYSTEM. // // We used to measure "explicit" too, but it could cause hangs, and the data // was always really noisy anyway. See bug 859657. const MEM_HISTOGRAMS = { "js-main-runtime-gc-heap": "MEMORY_JS_GC_HEAP", - "redundant/js-main-runtime-compartments/system": "MEMORY_JS_COMPARTMENTS_SYSTEM", - "redundant/js-main-runtime-compartments/user": "MEMORY_JS_COMPARTMENTS_USER", "js-main-runtime-temporary-peak": "MEMORY_JS_MAIN_RUNTIME_TEMPORARY_PEAK", - "redundant/resident-fast": "MEMORY_RESIDENT", "vsize": "MEMORY_VSIZE", "storage-sqlite": "MEMORY_STORAGE_SQLITE", "images-content-used-uncompressed": "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED", "heap-allocated": "MEMORY_HEAP_ALLOCATED", "heap-overhead": "MEMORY_HEAP_COMMITTED_UNUSED", "heap-overhead-ratio": "MEMORY_HEAP_COMMITTED_UNUSED_RATIO", "page-faults-hard": "PAGE_FAULTS_HARD", @@ -442,16 +436,18 @@ TelemetryPing.prototype = { getService(Ci.nsIMemoryReporterManager); } catch (e) { // OK to skip memory reporters in xpcshell return; } let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS"); let startTime = new Date(); + + // Get memory measurements from reporters. let e = mgr.enumerateReporters(); while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); let id = MEM_HISTOGRAMS[mr.name]; if (!id) { continue; } @@ -475,16 +471,27 @@ TelemetryPing.prototype = { "reporter " + mr.name + " has made more than one report"); } } mr.collectReports(h, null); } catch (e) { } } + + // Get memory measurements from distinguished amount attributes. + let h = this.handleMemoryReport.bind(this); + let b = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_BYTES, n); + let c = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_COUNT, n); + let p = (id, n) => h(id, Ci.nsIMemoryReporter.UNITS_PERCENTAGE, n); + + try { b("MEMORY_RESIDENT", mgr.residentFast); } catch (e) {} + try { c("MEMORY_JS_COMPARTMENTS_SYSTEM", mgr.JSMainRuntimeCompartmentsSystem); } catch (e) {} + try { c("MEMORY_JS_COMPARTMENTS_USER", mgr.JSMainRuntimeCompartmentsUser); } catch (e) {} + histogram.add(new Date() - startTime); }, handleMemoryReport: function(id, units, amount) { let val; if (units == Ci.nsIMemoryReporter.UNITS_BYTES) { val = Math.floor(amount / 1024); }
--- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -138,21 +138,16 @@ interface nsIMemoryReporterCallback : ns * - There must be an "explicit" tree. It represents non-overlapping * regions of memory that have been explicitly allocated with an * OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a * heap-level allocation (e.g. malloc/calloc/operator new). Reporters * in this tree must have kind HEAP or NONHEAP, units BYTES, and a * description that is a sentence (i.e. starts with a capital letter and * ends with a period, or similar). * - * - The "redundant" tree is optional, and can be used for reports that are - * redundant w.r.t. other reports. These are useful for telemetry, and are - * not shown in about:memory. Reports in this tree are entirely - * unconstrained. - * * - All other reports are unconstrained except that they must have a * description that is a sentence. */ [scriptable, uuid(53248304-124b-43cd-99dc-6e5797b91618)] interface nsIMemoryReporter : nsISupports { /* * The name of the reporter. Useful when only one reporter needs to be run. @@ -184,58 +179,73 @@ interface nsIMemoryReporter : nsISupport }; [scriptable, builtinclass, uuid(4db7040a-16f9-4249-879b-fe72729c7ef5)] interface nsIMemoryReporterManager : nsISupports { /* * Return an enumerator of nsIMemoryReporters that are currently registered. */ - nsISimpleEnumerator enumerateReporters (); + nsISimpleEnumerator enumerateReporters(); /* * Register the given nsIMemoryReporter. After a reporter is registered, * it will be available via enumerateReporters(). The Manager service * will hold a strong reference to the given reporter. */ - void registerReporter (in nsIMemoryReporter reporter); + void registerReporter(in nsIMemoryReporter reporter); /* * Unregister the given memory reporter. */ - void unregisterReporter (in nsIMemoryReporter reporter); + void unregisterReporter(in nsIMemoryReporter reporter); - /** + /* * These functions should only be used for testing purposes. */ void blockRegistration(); void unblockRegistration(); void registerReporterEvenIfBlocked(in nsIMemoryReporter aReporter); /* * Initialize. */ - void init (); + void init(); /* - * Get the resident size (aka. RSS, physical memory used). This reporter - * is special-cased because it's interesting and is available on most - * platforms. Accesses can fail. - */ - readonly attribute int64_t resident; - - /* - * Get the total size of explicit memory allocations, both at the OS-level - * (eg. via mmap, VirtualAlloc) and at the heap level (eg. via malloc, - * calloc, operator new). (Nb: it covers all heap allocations, but will - * miss any OS-level ones not covered by memory reporters.) This reporter - * is special-cased because it's interesting, and is difficult to compute - * from JavaScript code. Accesses can fail. + * The memory reporter manager, for the most part, treats reporters + * registered with it as a black box. However, there are some + * "distinguished" amounts (as could be reported by a memory reporter) that + * the manager provides as attributes, because they are sufficiently + * interesting that we want external code to be able to rely on them. + * + * Note that these are not reporters and so enumerateReporters() does not + * look at them. However, they can be embedded in a reporter. + * + * |explicit| (UNIT_BYTES) The total size of explicit memory allocations, + * both at the OS-level (eg. via mmap, VirtualAlloc) and at the heap level + * (eg. via malloc, calloc, operator new). It covers all heap allocations, + * but will miss any OS-level ones not covered by memory reporters. + * + * |resident| (UNIT_BYTES) The resident size (a.k.a. RSS or physical memory + * used). + * + * |residentFast| (UNIT_BYTES) This is like |resident|, but on Mac OS + * |resident| can purge pages, which is slow. It also affects the result of + * |residentFast|, and so |resident| and |residentFast| should not be used + * together. It is used by telemetry. + * + * |JSMainRuntimeCompartments{System,User}| (UNIT_COUNTS) The number of + * {system,user} compartments in the main JS runtime. */ readonly attribute int64_t explicit; + readonly attribute int64_t resident; + readonly attribute int64_t residentFast; + readonly attribute int64_t JSMainRuntimeCompartmentsSystem; + readonly attribute int64_t JSMainRuntimeCompartmentsUser; /* * This attribute indicates if moz_malloc_usable_size() works. */ [infallible] readonly attribute boolean hasMozMallocUsableSize; /* * Run a series of GC/CC's in an attempt to minimize the application's memory @@ -251,16 +261,37 @@ interface nsIMemoryReporterManager : nsI // Note that the memory reporters are held in an nsCOMArray, which means // that individual reporters should be referenced with |nsIMemoryReporter *| // instead of nsCOMPtr<nsIMemoryReporter>. XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter); XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter); +namespace mozilla { + +// The memory reporter manager provides access to several distinguished +// amounts via attributes. Some of these amounts are provided by Gecko +// components that cannot be accessed directly from XPCOM code. So we provide +// the following functions for those components to be registered with the +// manager. + +typedef int64_t (*InfallibleAmountFn)(); +typedef nsresult (*FallibleAmountFn)(int64_t* aAmount); + +#define REGISTER_DISTINGUISHED_AMOUNT(kind, name) \ + nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn); + +REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem) +REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser) + +#undef REGISTER_DISTINGUISHED_AMOUNT + +} + #if defined(MOZ_DMD) namespace mozilla { namespace dmd { // This runs all the memory reporters but does nothing with the results; i.e. // it does the minimal amount of work possible for DMD to do its thing. void RunReporters(); } } @@ -388,10 +419,9 @@ protected: const nsCString mNameAndPath; const int32_t mKind; const int32_t mUnits; const nsCString mDescription; }; } // namespace mozilla - %}
--- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -11,19 +11,20 @@ #include "nsServiceManagerUtils.h" #include "nsMemoryReporterManager.h" #include "nsISimpleEnumerator.h" #include "nsThreadUtils.h" #include "nsIObserverService.h" #if defined(XP_LINUX) #include "nsMemoryInfoDumper.h" #endif +#include "mozilla/Attributes.h" +#include "mozilla/PodOperations.h" +#include "mozilla/Services.h" #include "mozilla/Telemetry.h" -#include "mozilla/Attributes.h" -#include "mozilla/Services.h" #ifndef XP_WIN #include <unistd.h> #endif using namespace mozilla; #if defined(MOZ_MEMORY) @@ -421,36 +422,16 @@ public: "considering the memory resources used by the process, but it depends both on " "other processes being run and details of the OS kernel and so is best used " "for comparing the memory usage of a single process at different points in " "time.") {} NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResident(aAmount); } }; - -// This is a "redundant/"-prefixed reporter, which means it's ignored by -// about:memory. This is good because the "resident" reporter can purge pages -// on MacOS, which affects the "resident-fast" results, and we don't want the -// measurements shown in about:memory to be affected by the (arbitrary) order -// of memory reporter execution. This reporter is used by telemetry. -class ResidentFastReporter MOZ_FINAL : public MemoryUniReporter -{ -public: - ResidentFastReporter() - : MemoryUniReporter("redundant/resident-fast", KIND_OTHER, UNITS_BYTES, -"This is the same measurement as 'resident', but it tries to be as fast as " -"possible at the expense of accuracy. On most platforms this is identical to " -"the 'resident' measurement, but on Mac it may over-count. You should use " -"'resident-fast' where you care about latency of collection (e.g. in " -"telemetry). Otherwise you should use 'resident'.") - {} - - NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResidentFast(aAmount); } -}; #endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS #ifdef XP_UNIX #include <sys/resource.h> #define HAVE_PAGE_FAULT_REPORTERS 1 @@ -747,17 +728,16 @@ nsMemoryReporterManager::Init() RegisterReporter(new HeapOverheadPageCacheReporter); RegisterReporter(new HeapCommittedReporter); RegisterReporter(new HeapOverheadRatioReporter); #endif #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS RegisterReporter(new VsizeReporter); RegisterReporter(new ResidentReporter); - RegisterReporter(new ResidentFastReporter); #endif #ifdef HAVE_RESIDENT_UNIQUE_REPORTER RegisterReporter(new ResidentUniqueReporter); #endif #ifdef HAVE_PAGE_FAULT_REPORTERS RegisterReporter(new PageFaultsSoftReporter); @@ -843,16 +823,17 @@ HashtableEnumerator::GetNext(nsISupports } } // anonymous namespace nsMemoryReporterManager::nsMemoryReporterManager() : mMutex("nsMemoryReporterManager::mMutex"), mIsRegistrationBlocked(false) { + PodZero(&mAmountFns); } nsMemoryReporterManager::~nsMemoryReporterManager() { } NS_IMETHODIMP nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult) @@ -959,27 +940,16 @@ nsMemoryReporterManager::UnblockRegistra mozilla::MutexAutoLock autoLock(mMutex); if (!mIsRegistrationBlocked) { return NS_ERROR_FAILURE; } mIsRegistrationBlocked = false; return NS_OK; } -NS_IMETHODIMP -nsMemoryReporterManager::GetResident(int64_t* aResident) -{ -#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS - return ::GetResident(aResident); -#else - *aResident = 0; - return NS_ERROR_NOT_AVAILABLE; -#endif -} - // This is just a wrapper for int64_t that implements nsISupports, so it can be // passed to nsIMemoryReporter::CollectReports. class Int64Wrapper MOZ_FINAL : public nsISupports { public: NS_DECL_ISUPPORTS Int64Wrapper() : mValue(0) { } int64_t mValue; }; @@ -1004,20 +974,20 @@ public: wrappedInt64->mValue += aAmount; } return NS_OK; } }; NS_IMPL_ISUPPORTS1(ExplicitCallback, nsIMemoryReporterCallback) NS_IMETHODIMP -nsMemoryReporterManager::GetExplicit(int64_t* aExplicit) +nsMemoryReporterManager::GetExplicit(int64_t* aAmount) { - NS_ENSURE_ARG_POINTER(aExplicit); - *aExplicit = 0; + NS_ENSURE_ARG_POINTER(aAmount); + *aAmount = 0; #ifndef HAVE_JEMALLOC_STATS return NS_ERROR_NOT_AVAILABLE; #else bool more; // For each reporter we call CollectReports and filter out the // non-explicit, non-NONHEAP measurements (except for "heap-allocated"). // That's lots of wasted work, and we used to have a GetExplicitNonHeap() @@ -1030,23 +1000,70 @@ nsMemoryReporterManager::GetExplicit(int nsCOMPtr<nsISimpleEnumerator> e; EnumerateReporters(getter_AddRefs(e)); while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr<nsIMemoryReporter> r; e->GetNext(getter_AddRefs(r)); r->CollectReports(cb, wrappedExplicitSize); } - *aExplicit = wrappedExplicitSize->mValue; + *aAmount = wrappedExplicitSize->mValue; return NS_OK; #endif // HAVE_JEMALLOC_STATS } NS_IMETHODIMP +nsMemoryReporterManager::GetResident(int64_t* aAmount) +{ +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS + return ::GetResident(aAmount); +#else + *aAmount = 0; + return NS_ERROR_NOT_AVAILABLE; +#endif +} + +NS_IMETHODIMP +nsMemoryReporterManager::GetResidentFast(int64_t* aAmount) +{ +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS + return ::GetResidentFast(aAmount); +#else + *aAmount = 0; + return NS_ERROR_NOT_AVAILABLE; +#endif +} + +static nsresult +GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount) +{ + if (aAmountFn) { + *aAmount = aAmountFn(); + return NS_OK; + } + *aAmount = 0; + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount) +{ + return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem, + aAmount); +} + +NS_IMETHODIMP +nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount) +{ + return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser, + aAmount); +} + +NS_IMETHODIMP nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas) { void* p = malloc(16); if (!p) { return NS_ERROR_OUT_OF_MEMORY; } size_t usable = moz_malloc_usable_size(p); free(p); @@ -1158,16 +1175,42 @@ NS_UnregisterMemoryReporter(nsIMemoryRep { nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); if (!mgr) { return NS_ERROR_FAILURE; } return mgr->UnregisterReporter(aReporter); } +namespace mozilla { + +// Macro for generating functions that register distinguished amount functions +// with the memory reporter manager. +#define REGISTER_DISTINGUISHED_AMOUNT(kind, name) \ + nsresult \ + Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \ + { \ + nsCOMPtr<nsIMemoryReporterManager> imgr = \ + do_GetService("@mozilla.org/memory-reporter-manager;1"); \ + nsRefPtr<nsMemoryReporterManager> mgr = \ + static_cast<nsMemoryReporterManager*>(imgr.get()); \ + if (!mgr) { \ + return NS_ERROR_FAILURE; \ + } \ + mgr->mAmountFns.m##name = aAmountFn; \ + return NS_OK; \ + } + +REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem) +REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser) + +#undef REGISTER_DISTINGUISHED_AMOUNT + +} + #if defined(MOZ_DMD) namespace mozilla { namespace dmd { class NullReporterCallback : public nsIMemoryReporterCallback { public:
--- a/xpcom/base/nsMemoryReporterManager.h +++ b/xpcom/base/nsMemoryReporterManager.h @@ -15,18 +15,26 @@ class nsMemoryReporterManager : public n { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIMEMORYREPORTERMANAGER nsMemoryReporterManager(); virtual ~nsMemoryReporterManager(); + // Functions that (a) implement distinguished amounts, and (b) are outside of + // this module. + struct AmountFns { + mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem; + mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser; + }; + AmountFns mAmountFns; + private: - nsresult RegisterReporterHelper(nsIMemoryReporter *reporter, bool aForce); + nsresult RegisterReporterHelper(nsIMemoryReporter *aReporter, bool aForce); nsTHashtable<nsISupportsHashKey> mReporters; Mutex mMutex; bool mIsRegistrationBlocked; }; #define NS_MEMORY_REPORTER_MANAGER_CID \ { 0xfb97e4f5, 0x32dd, 0x497a, \