Bug 744311 (part 2) - Don't use -1 to represent "unknown" in memory reporters. r=jlebar.

--HG--
extra : rebase_source : 8f726aef17e5b814f5b740b453c9a67a1ef7646d
This commit is contained in:
Nicholas Nethercote 2012-04-11 19:38:31 -07:00
parent 08dc2dae6b
commit 3a4aa126ba
6 changed files with 276 additions and 274 deletions

View File

@ -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");
}

View File

@ -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\

View File

@ -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");

View File

@ -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);
}
},

View File

@ -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

View File

@ -58,55 +58,62 @@ using namespace mozilla;
#include <sys/time.h>
#include <sys/resource.h>
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 <unistd.h>
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 <fcntl.h>
#include <unistd.h>
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 <windows.h>
#include <psapi.h>
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_<PLATFORM>
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 <malloc/malloc.h>
#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<nsIMemoryReporterManager> 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