diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index a6c2408c43e..13233c535a8 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -211,40 +211,22 @@ function processMemoryReporters(aMgr, aIgnoreSingle, aIgnoreMulti, aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription); } - function handleException(aReporterStr, aUnsafePathOrName, aE) - { - // There are two exception cases that must be distinguished here. - // - // - We want to halt proceedings on exceptions thrown within this file - // (i.e. assertion failures in handleReport); such exceptions contain - // gAssertionFailureMsgPrefix in their string representation. - // - // - We want to continue on when faced with exceptions thrown outside this - // file (e.g. within a multi-reporter). - - let str = aE.toString(); - if (str.search(gAssertionFailureMsgPrefix) >= 0) { - throw(aE); - } else { - debug("Bad memory " + aReporterStr + " '" + aUnsafePathOrName + - "': " + aE); - } - } - let e = aMgr.enumerateReporters(); while (e.hasMoreElements()) { let rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter); - let unsafePath = rOrig.path; + let unsafePath; try { + unsafePath = rOrig.path; if (!aIgnoreSingle(unsafePath)) { - handleReport(rOrig.process, unsafePath, rOrig.kind, rOrig.units, + handleReport(rOrig.process, unsafePath, rOrig.kind, rOrig.units, rOrig.amount, rOrig.description); } } - catch (e) { - handleException("reporter", unsafePath, e); + catch (ex) { + debug("Exception thrown by memory reporter: " + unsafePath + ": " + ex); } } + let e = aMgr.enumerateMultiReporters(); while (e.hasMoreElements()) { let mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter); @@ -254,8 +236,22 @@ function processMemoryReporters(aMgr, aIgnoreSingle, aIgnoreMulti, mrOrig.collectReports(handleReport, null); } } - catch (e) { - handleException("multi-reporter", name, e); + catch (ex) { + // There are two exception cases that must be distinguished here. + // + // - We want to halt proceedings on exceptions thrown within this file + // (i.e. assertion failures in handleReport); such exceptions contain + // gAssertionFailureMsgPrefix in their string representation. + // + // - We want to continue on when faced with exceptions thrown outside + // this file (i.e. when measuring an amount in collectReports). + let str = ex.toString(); + if (str.search(gAssertionFailureMsgPrefix) >= 0) { + throw(ex); + } else { + debug("Exception thrown within memory multi-reporter: " + name + ": " + + ex); + } } } } @@ -330,8 +326,6 @@ function appendElementWithText(aP, aTagName, aClassName, aText) // Code specific to about:memory //--------------------------------------------------------------------------- -const kUnknown = -1; // used for an unknown _amount - const kTreeDescriptions = { 'explicit' : "This tree covers explicit memory allocations by the application, both at the \ @@ -515,16 +509,11 @@ function Report(aUnsafePath, aKind, aUnits, aAmount, aDescription) } Report.prototype = { - // Sum the values (accounting for possible kUnknown amounts), and mark |this| - // as a dup. We mark dups because it's useful to know when a report is - // duplicated; it might be worth investigating and splitting up to have - // non-duplicated names. + // Sum the values and mark |this| as a dup. We mark dups because it's useful + // to know when a report is duplicated; it might be worth investigating and + // splitting up to have non-duplicated names. merge: function(r) { - if (this._amount !== kUnknown && r._amount !== kUnknown) { - this._amount += r._amount; - } else if (this._amount === kUnknown && r._amount !== kUnknown) { - this._amount = r._amount; - } + this._amount += r._amount; this._nMerged = this._nMerged ? this._nMerged + 1 : 2; }, @@ -595,14 +584,13 @@ function TreeNode(aUnsafeName) this._unsafeName = aUnsafeName; this._kids = []; // Leaf TreeNodes have these properties added immediately after construction: - // - _amount (which is never |kUnknown|) + // - _amount // - _description // - _kind // - _nMerged (only defined if > 1) - // - _isUnknown (only defined if true) // // Non-leaf TreeNodes have these properties added later: - // - _amount (which is never |kUnknown|) + // - _amount // - _description // - _hideKids (only defined if true) } @@ -676,12 +664,7 @@ function buildTree(aReports, aTreeName) } } // Fill in extra details in the leaf node from the Report object. - if (r._amount !== kUnknown) { - u._amount = r._amount; - } else { - u._amount = 0; - u._isUnknown = true; - } + u._amount = r._amount; u._description = r._description; u._kind = r._kind; if (r._nMerged) { @@ -696,7 +679,6 @@ function buildTree(aReports, aTreeName) t = t._kids[0]; // Next, fill in the remaining properties bottom-up. - // Note that this function never returns kUnknown. function fillInNonLeafNodes(aT) { if (aT._kids.length === 0) { @@ -714,7 +696,6 @@ function buildTree(aReports, aTreeName) aT._description = "The sum of all entries below '" + flipBackslashes(aT._unsafeName) + "'."; } - assert(aT._amount !== kUnknown, "aT._amount !== kUnknown"); return aT._amount; } @@ -784,17 +765,12 @@ function fixUpExplicitTree(aT, aReports) // mark "heap-allocated" when we get its size because we want it to appear // in the "Other Measurements" list. let heapAllocatedReport = aReports["heap-allocated"]; - assert(heapAllocatedReport, "no 'heap-allocated' report"); + if (heapAllocatedReport === undefined) + return false; + let heapAllocatedBytes = heapAllocatedReport._amount; let heapUnclassifiedT = new TreeNode("heap-unclassified"); - let hasKnownHeapAllocated = heapAllocatedBytes !== kUnknown; - if (hasKnownHeapAllocated) { - heapUnclassifiedT._amount = - heapAllocatedBytes - getKnownHeapUsedBytes(aT); - } else { - heapUnclassifiedT._amount = 0; - heapUnclassifiedT._isUnknown = true; - } + heapUnclassifiedT._amount = heapAllocatedBytes - getKnownHeapUsedBytes(aT); // This kindToString() ensures the "(Heap)" prefix is set without having to // set the _kind property, which would mean that there is a corresponding // Report object for this TreeNode object (which isn't true) @@ -802,11 +778,9 @@ function fixUpExplicitTree(aT, aReports) "Memory not classified by a more specific reporter. This includes " + "slop bytes due to internal fragmentation in the heap allocator " + "(caused when the allocator rounds up request sizes)."; - aT._kids.push(heapUnclassifiedT); aT._amount += heapUnclassifiedT._amount; - - return hasKnownHeapAllocated; + return true; } /** @@ -825,7 +799,6 @@ function sortTreeAndInsertAggregateNodes(aTotalBytes, aT) function isInsignificant(aT) { return !gVerbose && - aTotalBytes !== kUnknown && (100 * aT._amount / aTotalBytes) < kSignificanceThresholdPerc; } @@ -897,15 +870,15 @@ function appendWarningElements(aP, aHasKnownHeapAllocated, appendElementWithText(aP, "p", "", "WARNING: the 'heap-allocated' memory reporter and the " + "moz_malloc_usable_size() function do not work for this platform " + - "and/or configuration. This means that 'heap-unclassified' is zero " + - "and the 'explicit' tree shows much less memory than it should."); + "and/or configuration. This means that 'heap-unclassified' is not " + + "shown and the 'explicit' tree shows much less memory than it should."); appendTextNode(aP, "\n\n"); } else if (!aHasKnownHeapAllocated) { appendElementWithText(aP, "p", "", "WARNING: the 'heap-allocated' memory reporter does not work for this " + "platform and/or configuration. This means that 'heap-unclassified' " + - "is zero and the 'explicit' tree shows less memory than it should."); + "is not shown and the 'explicit' tree shows less memory than it should."); appendTextNode(aP, "\n\n"); } else if (!aHasMozMallocUsableSize) { @@ -1151,7 +1124,7 @@ const kHideKids = 1; const kShowKids = 2; function appendMrNameSpan(aP, aKind, aKidsState, aDescription, aUnsafeName, - aIsUnknown, aIsInvalid, aNMerged) + aIsInvalid, aNMerged) { let text = ""; if (aKidsState === kNoKids) { @@ -1170,11 +1143,6 @@ function appendMrNameSpan(aP, aKind, aKidsState, aDescription, aUnsafeName, flipBackslashes(aUnsafeName)); nameSpan.title = kindToString(aKind) + aDescription; - if (aIsUnknown) { - let noteSpan = appendElementWithText(aP, "span", "mrNote", " [*]"); - noteSpan.title = - "Warning: this memory reporter was unable to compute a useful value. "; - } if (aIsInvalid) { let noteSpan = appendElementWithText(aP, "span", "mrNote", " [?!]"); noteSpan.title = @@ -1372,7 +1340,7 @@ function appendTreeElements(aPOuter, aT, aProcess) // the whole tree is non-heap. let kind = isExplicitTree ? aT._kind : undefined; appendMrNameSpan(d, kind, kidsState, aT._description, aT._unsafeName, - aT._isUnknown, tIsInvalid, aT._nMerged); + tIsInvalid, aT._nMerged); appendTextNode(d, "\n"); // In non-verbose mode, invalid nodes can be hidden in collapsed sub-trees. @@ -1424,12 +1392,7 @@ function OtherReport(aUnsafePath, aUnits, aAmount, aDescription, aNMerged) // Nb: _kind is not needed, it's always KIND_OTHER. this._unsafePath = aUnsafePath; this._units = aUnits; - if (aAmount === kUnknown) { - this._amount = 0; - this._isUnknown = true; - } else { - this._amount = aAmount; - } + this._amount = aAmount; this._description = aDescription; this._asString = this.toString(); } @@ -1451,9 +1414,8 @@ OtherReport.prototype = { switch (this._units) { case UNITS_BYTES: case UNITS_COUNT: - case UNITS_COUNT_CUMULATIVE: return (n !== kUnknown && n < 0); - case UNITS_PERCENTAGE: return (n !== kUnknown && - !(0 <= n && n <= 10000)); + case UNITS_COUNT_CUMULATIVE: return n < 0; + case UNITS_PERCENTAGE: return !(0 <= n && n <= 10000); default: assert(false, "bad units in OtherReport.isInvalid"); } @@ -1514,7 +1476,7 @@ function appendOtherElements(aP, aReportsByProcess) } appendMrValueSpan(pre, pad(o._asString, maxStringLength, ' '), oIsInvalid); appendMrNameSpan(pre, KIND_OTHER, kNoKids, o._description, o._unsafePath, - o._isUnknown, oIsInvalid); + oIsInvalid); appendTextNode(pre, "\n"); } diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index bc5bc7bd519..92f9bc36318 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -21,6 +21,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; + const Cr = Components.results; let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. getService(Ci.nsIMemoryReporterManager); @@ -44,7 +45,6 @@ // Setup various fake-but-deterministic reporters. const KB = 1024; const MB = KB * KB; - const kUnknown = -1; const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP; const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; @@ -154,7 +154,15 @@ // - but *not* "explicit/c/d" x 2 // Check explicit now before we add the fake reporters for the fake 2nd // and subsequent processes. - is(mgr.explicit, 500*MB + (100 + 13 + 10)*MB + 599*KB, "mgr.explicit"); + // + // 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. + try { + is(mgr.explicit, 500*MB + (100 + 13 + 10)*MB + 599*KB, "mgr.explicit"); + } catch (ex) { + is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception"); + } let fakeReporters2 = [ f("2nd", "heap-allocated", OTHER, 1000 * MB), @@ -176,19 +184,9 @@ f("2nd", "smaps/vsize/e", NONHEAP, 24*4*KB), f("2nd", "smaps/vsize/f", NONHEAP, 24*4*KB), - // kUnknown should be handled gracefully for "heap-allocated", non-leaf - // reporters, leaf-reporters, "other" reporters, and duplicated reporters. - f("3rd", "heap-allocated", OTHER, kUnknown), + // Check that we can handle "heap-allocated" not being present. f("3rd", "explicit/a/b", HEAP, 333 * MB), f("3rd", "explicit/a/c", HEAP, 444 * MB), - f("3rd", "explicit/a/c", HEAP, kUnknown), // dup: merge - f("3rd", "explicit/a/d", HEAP, kUnknown), - f("3rd", "explicit/a/d", HEAP, kUnknown), // dup: merge - f("3rd", "explicit/b", NONHEAP, kUnknown), - f2("3rd", "other1", OTHER, BYTES, kUnknown), - f2("3rd", "other2", OTHER, COUNT, kUnknown), - f2("3rd", "other3", OTHER, COUNT_CUMULATIVE, kUnknown), - f2("3rd", "other4", OTHER, PERCENTAGE, kUnknown), // Invalid values (negative, too-big) should be identified. f("4th", "heap-allocated", OTHER, 100 * MB), @@ -285,22 +283,15 @@ Other Measurements\n\ \n\ 3rd Process\n\ \n\ -WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is zero and the 'explicit' tree shows less memory than it should.\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ \n\ Explicit Allocations\n\ 777.00 MB (100.0%) -- explicit\n\ -├──777.00 MB (100.0%) -- a\n\ -│ ├──444.00 MB (57.14%) ── c [2]\n\ -│ ├──333.00 MB (42.86%) ── b\n\ -│ └────0.00 MB (00.00%) ── d [*] [2]\n\ -└────0.00 MB (00.00%) ++ (2 tiny)\n\ +└──777.00 MB (100.0%) -- a\n\ + ├──444.00 MB (57.14%) ── c\n\ + └──333.00 MB (42.86%) ── b\n\ \n\ Other Measurements\n\ -0.00 MB ── heap-allocated [*]\n\ -0.00 MB ── other1 [*]\n\ - 0 ── other2 [*]\n\ - 0 ── other3 [*]\n\ - 0.00% ── other4 [*]\n\ \n\ 4th Process\n\ \n\ @@ -444,23 +435,15 @@ Other Measurements\n\ \n\ 3rd Process\n\ \n\ -WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is zero and the 'explicit' tree shows less memory than it should.\n\ +WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ \n\ Explicit Allocations\n\ 814,743,552 B (100.0%) -- explicit\n\ -├──814,743,552 B (100.0%) -- a\n\ -│ ├──465,567,744 B (57.14%) ── c [2]\n\ -│ ├──349,175,808 B (42.86%) ── b\n\ -│ └────────────0 B (00.00%) ── d [*] [2]\n\ -├────────────0 B (00.00%) ── b [*]\n\ -└────────────0 B (00.00%) ── heap-unclassified [*]\n\ +└──814,743,552 B (100.0%) -- a\n\ + ├──465,567,744 B (57.14%) ── c\n\ + └──349,175,808 B (42.86%) ── b\n\ \n\ Other Measurements\n\ - 0 B ── heap-allocated [*]\n\ - 0 B ── other1 [*]\n\ - 0 ── other2 [*]\n\ - 0 ── other3 [*]\n\ -0.00% ── other4 [*]\n\ \n\ 4th Process\n\ \n\ diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul index 96fbb73886c..55b765d20f1 100644 --- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -24,6 +24,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; + const Cr = Components.results; const kUnknown = -1; const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP; @@ -94,7 +95,18 @@ // 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. - let dummy = mgr.explicit; + // + // 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; let e = mgr.enumerateReporters(); @@ -122,12 +134,15 @@ aName + "'s size is reasonable"); } - checkSpecialReport("explicit", explicitAmounts); - checkSpecialReport("vsize", vsizeAmounts); - checkSpecialReport("resident", residentAmounts); + // If mgr.explicit failed, we won't have "heap-allocated" either. + if (haveExplicit) { + checkSpecialReport("explicit", explicitAmounts); + checkSpecialReport("heap-allocated", heapAllocatedAmounts); + } + checkSpecialReport("vsize", vsizeAmounts); + checkSpecialReport("resident", residentAmounts); checkSpecialReport("js-main-runtime-gc-heap-committed", jsGcHeapAmounts); - checkSpecialReport("heap-allocated", heapAllocatedAmounts); - checkSpecialReport("storage-sqlite", storageSqliteAmounts); + checkSpecialReport("storage-sqlite", storageSqliteAmounts); ok(areJsCompartmentsPresent, "js compartments are present"); ok(isSandboxLocationShown, "sandbox locations are present"); diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index af0c3f80e74..7d458731415 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -370,43 +370,46 @@ TelemetryPing.prototype = { let e = mgr.enumerateReporters(); while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); - let id = MEM_HISTOGRAMS[mr.path]; - if (!id) { - continue; - } - // mr.amount is expensive to read in some cases, so get it only once. - let amount = mr.amount; - if (amount == -1) { + let id, mrPath, mrAmount, mrUnits; + try { + mrPath = mr.path; + id = MEM_HISTOGRAMS[mrPath]; + if (!id) { + continue; + } + mrAmount = mr.amount; + mrUnits = mr.units; + } catch (ex) { continue; } let val; - if (mr.units == Ci.nsIMemoryReporter.UNITS_BYTES) { - val = Math.floor(amount / 1024); + if (mrUnits == Ci.nsIMemoryReporter.UNITS_BYTES) { + val = Math.floor(mrAmount / 1024); } - else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT) { - val = amount; + else if (mrUnits == Ci.nsIMemoryReporter.UNITS_COUNT) { + val = mrAmount; } - else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) { + else if (mrUnits == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) { // If the reporter gives us a cumulative count, we'll report the // difference in its value between now and our previous ping. - if (!(mr.path in this._prevValues)) { + if (!(mrPath in this._prevValues)) { // If this is the first time we're reading this reporter, store its // current value but don't report it in the telemetry ping, so we // ignore the effect startup had on the reporter. - this._prevValues[mr.path] = amount; + this._prevValues[mrPath] = mrAmount; continue; } - val = amount - this._prevValues[mr.path]; - this._prevValues[mr.path] = amount; + val = mrAmount - this._prevValues[mrPath]; + this._prevValues[mrPath] = mrAmount; } else { - NS_ASSERT(false, "Can't handle memory reporter with units " + mr.units); + NS_ASSERT(false, "Can't handle memory reporter with units " + mrUnits); continue; } - this.addValue(mr.path, id, val); + this.addValue(mrPath, id, val); } }, diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 24df056a587..ef7acd66488 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -209,8 +209,8 @@ interface nsIMemoryReporter : nsISupports readonly attribute PRInt32 units; /* - * The numeric value reported by this memory reporter. -1 means "unknown", - * ie. something went wrong when getting the amount. + * The numeric value reported by this memory reporter. Accesses can fail if + * something goes wrong when getting the amount. */ readonly attribute PRInt64 amount; @@ -317,7 +317,7 @@ interface nsIMemoryReporterManager : nsISupports * Get the resident size (aka. RSS, physical memory used). This reporter * is special-cased because it's interesting, is available on all * platforms, and returns a meaningful result on all common platforms. - * -1 means unknown. + * Accesses can fail. */ readonly attribute PRInt64 resident; @@ -327,7 +327,7 @@ interface nsIMemoryReporterManager : nsISupports * 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 moderately difficult - * to compute in JS. -1 means unknown. + * to compute in JS. Accesses can fail. */ readonly attribute PRInt64 explicit; @@ -346,12 +346,29 @@ interface nsIMemoryReporterManager : nsISupports class MemoryReporter_##_classname MOZ_FINAL : public nsIMemoryReporter { \ public: \ NS_DECL_ISUPPORTS \ - NS_IMETHOD GetProcess(nsACString &process) { process.Truncate(); return NS_OK; } \ - NS_IMETHOD GetPath(nsACString &memoryPath) { memoryPath.Assign(_path); return NS_OK; } \ + NS_IMETHOD GetProcess(nsACString &process) { process.Truncate(); return NS_OK; } \ + NS_IMETHOD GetPath(nsACString &memoryPath) { memoryPath.Assign(_path); return NS_OK; } \ NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; } \ NS_IMETHOD GetUnits(int *units) { *units = _units; return NS_OK; } \ - NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _amountFunction(); return NS_OK; } \ - NS_IMETHOD GetDescription(nsACString &desc) { desc.Assign(_desc); return NS_OK; } \ + NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _amountFunction(); return NS_OK; } \ + NS_IMETHOD GetDescription(nsACString &desc) { desc.Assign(_desc); return NS_OK; } \ + }; \ + NS_IMPL##_ts##ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter) + +/* + * The only difference between this and NS_MEMORY_REPORTER_IMPLEMENT_HELPER + * is that the function used to implement GetAmount is fallible. + */ +#define NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT_HELPER(_classname, _path, _kind, _units, _amountFunction, _desc, _ts) \ + class MemoryReporter_##_classname MOZ_FINAL : public nsIMemoryReporter { \ + public: \ + NS_DECL_ISUPPORTS \ + NS_IMETHOD GetProcess(nsACString &process) { process.Truncate(); return NS_OK; } \ + NS_IMETHOD GetPath(nsACString &memoryPath) { memoryPath.Assign(_path); return NS_OK; } \ + NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; } \ + NS_IMETHOD GetUnits(int *units) { *units = _units; return NS_OK; } \ + NS_IMETHOD GetAmount(PRInt64 *amount) { return _amountFunction(amount); } \ + NS_IMETHOD GetDescription(nsACString &desc) { desc.Assign(_desc); return NS_OK; } \ }; \ NS_IMPL##_ts##ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter) @@ -359,6 +376,10 @@ interface nsIMemoryReporterManager : nsISupports NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _) #define NS_THREADSAFE_MEMORY_REPORTER_IMPLEMENT(_c, _p, _k, _u, _a, _d) \ NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _THREADSAFE_) +#define NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(_c, _p, _k, _u, _a, _d) \ + NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _) +#define NS_FALLIBLE_THREADSAFE_MEMORY_REPORTER_IMPLEMENT(_c, _p, _k, _u, _a, _d) \ + NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _THREADSAFE_) #define NS_MEMORY_REPORTER_NAME(_classname) MemoryReporter_##_classname diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index a63eb3cf177..78b187fb604 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -58,55 +58,62 @@ using namespace mozilla; #include #include -static PRInt64 GetHardPageFaults() +#define HAVE_PAGE_FAULT_REPORTERS 1 +static nsresult GetHardPageFaults(PRInt64 *n) { struct rusage usage; int err = getrusage(RUSAGE_SELF, &usage); if (err != 0) { - return PRInt64(-1); + return NS_ERROR_FAILURE; } - return usage.ru_majflt; + *n = usage.ru_majflt; + return NS_OK; } -static PRInt64 GetSoftPageFaults() +static nsresult GetSoftPageFaults(PRInt64 *n) { struct rusage usage; int err = getrusage(RUSAGE_SELF, &usage); if (err != 0) { - return PRInt64(-1); + return NS_ERROR_FAILURE; } - return usage.ru_minflt; + *n = usage.ru_minflt; + return NS_OK; } -#endif +#endif // HAVE_PAGE_FAULT_REPORTERS #if defined(XP_LINUX) #include -static PRInt64 GetProcSelfStatmField(int n) +static nsresult GetProcSelfStatmField(int field, PRInt64 *n) { // There are more than two fields, but we're only interested in the first // two. static const int MAX_FIELD = 2; size_t fields[MAX_FIELD]; - NS_ASSERTION(n < MAX_FIELD, "bad field number"); + MOZ_ASSERT(field < MAX_FIELD, "bad field number"); FILE *f = fopen("/proc/self/statm", "r"); if (f) { int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]); fclose(f); - return (PRInt64) ((nread == MAX_FIELD) ? fields[n]*getpagesize() : -1); + if (nread == MAX_FIELD) { + *n = fields[field] * getpagesize(); + return NS_OK; + } } - return (PRInt64) -1; + return NS_ERROR_FAILURE; } -static PRInt64 GetVsize() +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1 +static nsresult GetVsize(PRInt64 *n) { - return GetProcSelfStatmField(0); + return GetProcSelfStatmField(0, n); } -static PRInt64 GetResident() +static nsresult GetResident(PRInt64 *n) { - return GetProcSelfStatmField(1); + return GetProcSelfStatmField(1, n); } #elif defined(SOLARIS) @@ -115,10 +122,10 @@ static PRInt64 GetResident() #include #include -static void XMappingIter(PRInt64& Vsize, PRInt64& Resident) +static void XMappingIter(PRInt64& vsize, PRInt64& resident) { - Vsize = -1; - Resident = -1; + vsize = -1; + resident = -1; int mapfd = open("/proc/self/xmap", O_RDONLY); struct stat st; prxmap_t *prmapp = NULL; @@ -140,11 +147,11 @@ static void XMappingIter(PRInt64& Vsize, PRInt64& Resident) break; } if (nmap >= n / sizeof (prxmap_t)) { - Vsize = 0; - Resident = 0; + vsize = 0; + resident = 0; for (int i = 0; i < n / sizeof (prxmap_t); i++) { - Vsize += prmapp[i].pr_size; - Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize; + vsize += prmapp[i].pr_size; + resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize; } break; } @@ -156,18 +163,27 @@ static void XMappingIter(PRInt64& Vsize, PRInt64& Resident) } } -static PRInt64 GetVsize() +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1 +static nsresult GetVsize(PRInt64 *n) { - PRInt64 Vsize, Resident; - XMappingIter(Vsize, Resident); - return Vsize; + PRInt64 vsize, resident; + XMappingIter(vsize, resident); + if (vsize == -1) { + return NS_ERROR_FAILURE; + } + *n = vsize; + return NS_OK; } -static PRInt64 GetResident() +static nsresult GetResident(PRInt64 *n) { - PRInt64 Vsize, Resident; - XMappingIter(Vsize, Resident); - return Resident; + PRInt64 vsize, resident; + XMappingIter(vsize, resident); + if (resident == -1) { + return NS_ERROR_FAILURE; + } + *n = resident; + return NS_OK; } #elif defined(XP_MACOSX) @@ -186,13 +202,18 @@ static bool GetTaskBasicInfo(struct task_basic_info *ti) // The VSIZE figure on Mac includes huge amounts of shared memory and is always // absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report // it, so we might as well too. -static PRInt64 GetVsize() +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1 +static nsresult GetVsize(PRInt64 *n) { task_basic_info ti; - return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.virtual_size : -1); + if (!GetTaskBasicInfo(&ti)) + return NS_ERROR_FAILURE; + + *n = ti.virtual_size; + return NS_OK; } -static PRInt64 GetResident() +static nsresult GetResident(PRInt64 *n) { #ifdef HAVE_JEMALLOC_STATS // If we're using jemalloc on Mac, we need to instruct jemalloc to purge @@ -209,7 +230,11 @@ static PRInt64 GetResident() #endif task_basic_info ti; - return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1); + if (!GetTaskBasicInfo(&ti)) + return NS_ERROR_FAILURE; + + *n = ti.resident_size; + return NS_OK; } #elif defined(XP_WIN) @@ -217,42 +242,50 @@ static PRInt64 GetResident() #include #include -static PRInt64 GetVsize() +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1 +static nsresult GetVsize(PRInt64 *n) { MEMORYSTATUSEX s; s.dwLength = sizeof(s); - bool success = GlobalMemoryStatusEx(&s); - if (!success) - return -1; + if (!GlobalMemoryStatusEx(&s)) { + return NS_ERROR_FAILURE; + } - return s.ullTotalVirtual - s.ullAvailVirtual; + *n = s.ullTotalVirtual - s.ullAvailVirtual; + return NS_OK; } -static PRInt64 GetResident() +static nsresult GetResident(PRInt64 *n) { PROCESS_MEMORY_COUNTERS pmc; pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS); - if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) - return (PRInt64) -1; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + return NS_ERROR_FAILURE; + } - return pmc.WorkingSetSize; + *n = pmc.WorkingSetSize; + return NS_OK; } -static PRInt64 GetPrivate() +#define HAVE_PRIVATE_REPORTER +static nsresult GetPrivate(PRInt64 *n) { PROCESS_MEMORY_COUNTERS_EX pmcex; pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX); if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) - return (PRInt64) -1; + { + return NS_ERROR_FAILURE; + } - return pmcex.PrivateUsage; + *n = pmcex.PrivateUsage; + return NS_OK; } -NS_MEMORY_REPORTER_IMPLEMENT(Private, +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(Private, "private", KIND_OTHER, UNITS_BYTES, @@ -261,17 +294,10 @@ NS_MEMORY_REPORTER_IMPLEMENT(Private, "is committed and marked MEM_PRIVATE, data that is not mapped, and " "executable pages that have been written to.") -#else +#endif // XP_ -static PRInt64 GetResident() -{ - return (PRInt64) -1; -} - -#endif - -#if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS) -NS_MEMORY_REPORTER_IMPLEMENT(Vsize, +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(Vsize, "vsize", KIND_OTHER, UNITS_BYTES, @@ -284,7 +310,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(Vsize, "another. But even on other operating systems, 'resident' is a much better " "measure of the memory resources used by the process.") -NS_MEMORY_REPORTER_IMPLEMENT(Resident, +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(Resident, "resident", KIND_OTHER, UNITS_BYTES, @@ -295,10 +321,10 @@ NS_MEMORY_REPORTER_IMPLEMENT(Resident, "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.") -#endif +#endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS -#if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS) -NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsSoft, +#ifdef HAVE_PAGE_FAULT_REPORTERS +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(PageFaultsSoft, "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, @@ -313,7 +339,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsSoft, "and because the OS services a soft page fault without accessing the disk, " "they impact performance much less than hard page faults.") -NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, @@ -328,7 +354,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, "accessing the disk is up to a million times slower than accessing RAM, " "the program may run very slowly when it is experiencing more than 100 or " "so hard page faults a second.") -#endif +#endif // HAVE_PAGE_FAULT_REPORTERS /** ** memory reporter implementation for jemalloc and OSX malloc, @@ -338,11 +364,13 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, #if HAVE_JEMALLOC_STATS +#define HAVE_HEAP_ALLOCATED_REPORTERS 1 + static PRInt64 GetHeapUnallocated() { jemalloc_stats_t stats; jemalloc_stats(&stats); - return (PRInt64) stats.mapped - stats.allocated; + return (PRInt64) (stats.mapped - stats.allocated); } static PRInt64 GetHeapAllocated() @@ -363,7 +391,7 @@ static PRInt64 GetHeapCommittedFragmentation() { jemalloc_stats_t stats; jemalloc_stats(&stats); - return (PRInt64) 10000 * (1 - stats.allocated / (double)stats.committed); + return (PRInt64) (10000 * (1 - stats.allocated / (double)stats.committed)); } static PRInt64 GetHeapDirty() @@ -408,42 +436,37 @@ NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty, #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) #include +#define HAVE_HEAP_ALLOCATED_REPORTERS 1 + static PRInt64 GetHeapUnallocated() { struct mstats stats = mstats(); - return (PRInt64) (stats.bytes_total - stats.bytes_used); + return stats.bytes_total - stats.bytes_used; } static PRInt64 GetHeapAllocated() { struct mstats stats = mstats(); - return (PRInt64) stats.bytes_used; + return stats.bytes_used; } +// malloc_zone_statistics() crashes when run under DMD because Valgrind doesn't +// intercept it. This measurement isn't important for DMD, so don't even try +// to get it. +#ifndef MOZ_DMD +#define HAVE_HEAP_ZONE0_REPORTERS 1 static PRInt64 GetHeapZone0Committed() { -#ifdef MOZ_DMD - // malloc_zone_statistics() crashes when run under DMD because Valgrind - // doesn't intercept it. This measurement isn't important for DMD, so - // don't even try. - return (PRInt64) -1; -#else malloc_statistics_t stats; malloc_zone_statistics(malloc_default_zone(), &stats); return stats.size_in_use; -#endif } static PRInt64 GetHeapZone0Used() { -#ifdef MOZ_DMD - // See comment in GetHeapZone0Committed above. - return (PRInt64) -1; -#else malloc_statistics_t stats; malloc_zone_statistics(malloc_default_zone(), &stats); return stats.size_allocated; -#endif } NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed, @@ -461,29 +484,19 @@ NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Used, GetHeapZone0Used, "Memory mapped by the heap allocator in the default zone that is " "allocated to the application.") - -#else - -static PRInt64 GetHeapAllocated() -{ - return (PRInt64) -1; -} - -static PRInt64 GetHeapUnallocated() -{ - return (PRInt64) -1; -} +#endif // MOZ_DMD #endif +#ifdef HAVE_HEAP_ALLOCATED_REPORTERS NS_MEMORY_REPORTER_IMPLEMENT(HeapUnallocated, "heap-unallocated", KIND_OTHER, UNITS_BYTES, GetHeapUnallocated, "Memory mapped by the heap allocator that is not part of an active " - "allocation. Much of this memory may be uncommitted -- that is, it does not " - "take up space in physical memory or in the swap file.") + "allocation. Much of this memory may be uncommitted -- that is, it does " + "not take up space in physical memory or in the swap file.") NS_MEMORY_REPORTER_IMPLEMENT(HeapAllocated, "heap-allocated", @@ -495,20 +508,19 @@ NS_MEMORY_REPORTER_IMPLEMENT(HeapAllocated, "application because the allocator regularly rounds up request sizes. (The " "exact amount requested is not recorded.)") -static PRInt64 GetExplicit() +// The computation of "explicit" fails if "heap-allocated" isn't available, +// which is why this is depends on HAVE_HEAP_ALLOCATED_AND_EXPLICIT_REPORTERS. +#define HAVE_EXPLICIT_REPORTER 1 +static nsresult GetExplicit(PRInt64 *n) { nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); if (mgr == nsnull) - return (PRInt64)-1; + return NS_ERROR_FAILURE; - PRInt64 n; - nsresult rv = mgr->GetExplicit(&n); - NS_ENSURE_SUCCESS(rv, rv); - - return n; + return mgr->GetExplicit(n); } -NS_MEMORY_REPORTER_IMPLEMENT(Explicit, +NS_FALLIBLE_MEMORY_REPORTER_IMPLEMENT(Explicit, "explicit", KIND_OTHER, UNITS_BYTES, @@ -516,6 +528,7 @@ NS_MEMORY_REPORTER_IMPLEMENT(Explicit, "This is the same measurement as the root of the 'explicit' tree. " "However, it is measured at a different time and so gives slightly " "different results.") +#endif // HAVE_HEAP_ALLOCATED_REPORTERS NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(AtomTableMallocSizeOf, "atom-table") @@ -551,21 +564,26 @@ nsMemoryReporterManager::Init() #define REGISTER(_x) RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x)) +#ifdef HAVE_HEAP_ALLOCATED_REPORTERS REGISTER(HeapAllocated); REGISTER(HeapUnallocated); - REGISTER(Explicit); - REGISTER(Resident); - -#if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS) - REGISTER(Vsize); #endif -#if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS) +#ifdef HAVE_EXPLICIT_REPORTER + REGISTER(Explicit); +#endif + +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS + REGISTER(Vsize); + REGISTER(Resident); +#endif + +#ifdef HAVE_PAGE_FAULT_REPORTERS REGISTER(PageFaultsSoft); REGISTER(PageFaultsHard); #endif -#if defined(XP_WIN) +#ifdef HAVE_PRIVATE_REPORTER REGISTER(Private); #endif @@ -573,7 +591,7 @@ nsMemoryReporterManager::Init() REGISTER(HeapCommitted); REGISTER(HeapCommittedFragmentation); REGISTER(HeapDirty); -#elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) +#elif defined(HAVE_HEAP_ZONE0_REPORTERS) REGISTER(HeapZone0Committed); REGISTER(HeapZone0Used); #endif @@ -655,8 +673,12 @@ nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporte NS_IMETHODIMP nsMemoryReporterManager::GetResident(PRInt64 *aResident) { - *aResident = ::GetResident(); - return NS_OK; +#if HAVE_VSIZE_AND_RESIDENT_REPORTERS + return ::GetResident(aResident); +#else + *aResident = 0; + return NS_ERROR_NOT_AVAILABLE; +#endif } struct MemoryReport { @@ -721,7 +743,9 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) { NS_ENSURE_ARG_POINTER(aExplicit); *aExplicit = 0; - +#ifndef HAVE_EXPLICIT_REPORTER + return NS_ERROR_NOT_AVAILABLE; +#else nsresult rv; bool more; @@ -748,25 +772,18 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) if (kind == nsIMemoryReporter::KIND_NONHEAP && path.Find("explicit") == 0) { - PRInt64 amount; - rv = r->GetAmount(&amount); - NS_ENSURE_SUCCESS(rv, rv); - // Just skip any NONHEAP reporters that fail, because // "heap-allocated" is the most important one. - if (amount != PRInt64(-1)) { + PRInt64 amount; + rv = r->GetAmount(&amount); + if (NS_SUCCEEDED(rv)) { explicitNonHeapNormalSize += amount; } } else if (path.Equals("heap-allocated")) { + // If we don't have "heap-allocated", give up, because the result + // would be horribly inaccurate. rv = r->GetAmount(&heapAllocated); NS_ENSURE_SUCCESS(rv, rv); - - // If we don't have "heap-allocated", give up, because the result would be - // horribly inaccurate. - if (heapAllocated == PRInt64(-1)) { - *aExplicit = PRInt64(-1); - return NS_OK; - } } } @@ -817,10 +834,11 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) NS_WARNING(msg); PR_smprintf_free(msg); } -#endif +#endif // DEBUG *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize; return NS_OK; +#endif // HAVE_HEAP_ALLOCATED_AND_EXPLICIT_REPORTERS } NS_IMETHODIMP