Bug 700508 - Disallow non-leaf memory reporters. r=jlebar.

This commit is contained in:
Nicholas Nethercote 2011-12-06 16:20:17 -08:00
parent 6cceeeb39e
commit 75ed0e4814
4 changed files with 75 additions and 134 deletions

View File

@ -365,9 +365,10 @@ function update()
content.appendChild(div);
}
// There are two kinds of TreeNode. Those that correspond to Reporters
// have more properties. The remainder are just scaffolding nodes for the
// tree, whose values are derived from their children.
// There are two kinds of TreeNode.
// - Leaf TreeNodes correspond to Reporters and have more properties.
// - Non-leaf TreeNodes are just scaffolding nodes for the tree; their values
// are derived from their children.
function TreeNode(aName)
{
// Nb: _units is not needed, it's always UNITS_BYTES.
@ -377,7 +378,7 @@ function TreeNode(aName)
// - _amount (which is never |kUnknown|)
// - _description
//
// TreeNodes corresponding to Reporters have these properties added later:
// Leaf TreeNodes have these properties added later:
// - _kind
// - _nMerged (if > 1)
// - _hasProblem (only defined if true)
@ -418,8 +419,9 @@ function buildTree(aReporters, aTreeName)
// build the tree but only fill the properties that we can with a top-down
// traversal.
// Is there any reporter which matches aTreeName? If not, we'll create a
// dummy one.
// There should always be at least one matching reporter when |aTreeName| is
// "explicit". But there may be zero for "map" trees; if that happens,
// bail.
var foundReporter = false;
for (var path in aReporters) {
if (aReporters[path].treeNameMatches(aTreeName)) {
@ -427,9 +429,8 @@ function buildTree(aReporters, aTreeName)
break;
}
}
if (!foundReporter) {
// We didn't find any reporters for this tree, so bail.
assert(aTreeName !== 'explicit');
return null;
}
@ -473,7 +474,7 @@ function buildTree(aReporters, aTreeName)
var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
if (aT._kids.length === 0) {
// Leaf node. Must have a reporter.
assert(aT._kind !== undefined, "aT._kind !== undefined");
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
aT._description = getDescription(aReporters, path);
var amount = getBytes(aReporters, path);
if (amount !== kUnknown) {
@ -483,35 +484,15 @@ function buildTree(aReporters, aTreeName)
aT._hasProblem = true;
}
} else {
// Non-leaf node. Get the size of the children.
// Non-leaf node. Derive its size and description entirely from its
// children.
assert(aT._kind === undefined, "aT._kind is defined for non-leaf node");
var childrenBytes = 0;
for (var i = 0; i < aT._kids.length; i++) {
// Allow for kUnknown, treat it like 0.
childrenBytes += fillInTree(aT._kids[i], path);
}
if (aT._kind !== undefined) {
aT._description = getDescription(aReporters, path);
var amount = getBytes(aReporters, path);
if (amount !== kUnknown) {
// Non-leaf node with its own reporter. Use the reporter and add
// an "other" child node.
aT._amount = amount;
var other = new TreeNode("other");
other._description = "All unclassified " + aT._name + " memory.",
other._amount = aT._amount - childrenBytes,
aT._kids.push(other);
} else {
// Non-leaf node with a reporter that returns kUnknown.
// Use the sum of the children and mark it as problematic.
aT._amount = childrenBytes;
aT._hasProblem = true;
}
} else {
// Non-leaf node without its own reporter. Derive its size and
// description entirely from its children.
aT._amount = childrenBytes;
aT._description = "The sum of all entries below '" + aT._name + "'.";
}
aT._amount = childrenBytes;
aT._description = "The sum of all entries below '" + aT._name + "'.";
}
assert(aT._amount !== kUnknown, "aT._amount !== kUnknown");
return aT._amount;
@ -539,49 +520,46 @@ function buildTree(aReporters, aTreeName)
* Do some work which only makes sense for the 'explicit' tree.
*/
function fixUpExplicitTree(aT, aReporters) {
// Determine how many bytes are reported by heap reporters. Be careful
// with non-leaf reporters; if we count a non-leaf reporter we don't want
// to count any of its child reporters.
// Determine how many bytes are reported by heap reporters.
var s = "";
function getKnownHeapUsedBytes(aT)
{
if (aT._kind === KIND_HEAP) {
return aT._amount;
var n = 0;
if (aT._kids.length === 0) {
// Leaf node.
assert(aT._kind !== undefined, "aT._kind is undefined for leaf node");
n = aT._kind === KIND_HEAP ? aT._amount : 0;
} else {
var n = 0;
for (var i = 0; i < aT._kids.length; i++) {
n += getKnownHeapUsedBytes(aT._kids[i]);
}
return n;
}
return n;
}
// A special case: compute the derived "heap-unclassified" value. Don't
// mark "heap-allocated" when we get its size because we want it to appear
// in the "Other Measurements" list.
var heapUsedBytes = getBytes(aReporters, "heap-allocated", true);
var unknownHeapUsedBytes = 0;
var hasProblem = true;
if (heapUsedBytes !== kUnknown) {
unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(aT);
hasProblem = false;
var heapAllocatedBytes = getBytes(aReporters, "heap-allocated", true);
var heapUnclassifiedT = new TreeNode("heap-unclassified");
if (heapAllocatedBytes !== kUnknown) {
heapUnclassifiedT._amount =
heapAllocatedBytes - getKnownHeapUsedBytes(aT);
} else {
heapUnclassifiedT._amount = 0;
heapUnclassifiedT._hasProblem = true;
}
var heapUnclassified = new TreeNode("heap-unclassified");
// This kindToString() ensures the "(Heap)" prefix is set without having to
// set the _kind property, which would mean that there is a corresponding
// Reporter for this TreeNode (which isn't true).
heapUnclassified._description =
heapUnclassifiedT._description =
kindToString(KIND_HEAP) +
"Memory not classified by a more specific reporter. This includes " +
"waste due to internal fragmentation in the heap allocator (caused " +
"when the allocator rounds up request sizes).";
heapUnclassified._amount = unknownHeapUsedBytes;
if (hasProblem) {
heapUnclassified._hasProblem = true;
}
"slop bytes due to internal fragmentation in the heap allocator "
"(caused when the allocator rounds up request sizes).";
aT._kids.push(heapUnclassified);
aT._amount += unknownHeapUsedBytes;
aT._kids.push(heapUnclassifiedT);
aT._amount += heapUnclassifiedT._amount;
}
/**

View File

@ -78,11 +78,11 @@
f("", "explicit/b/b", HEAP, 75 * MB),
f("", "explicit/b/c/a", HEAP, 70 * MB),
f("", "explicit/b/c/b", HEAP, 2 * MB), // omitted
f("", "explicit/c", NONHEAP, 100 * MB),
f("", "explicit/c/d", NONHEAP, 13 * MB), // subsumed by parent
f("", "explicit/g", HEAP, 1 * MB), // internal, dup: merge
f("", "explicit/c/other", NONHEAP, 77 * MB), // subsumed by parent
f("", "explicit/g/a", HEAP, 6 * MB),
f("", "explicit/g/b", HEAP, 5 * MB),
f("", "explicit/g/other", HEAP, 4 * MB),
f("", "other1", OTHER, 111 * MB),
f2("", "other4", OTHER, COUNT_CUMULATIVE, 888)
];
@ -99,7 +99,6 @@
},
{ collectReports: function(cbObj, closure) {
function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); }
f("explicit/g", HEAP, BYTES, 14 * MB); // internal
f("other3", OTHER, COUNT, 777);
f("other2", OTHER, BYTES, 222 * MB);
f("perc2", OTHER, PERCENTAGE, 10000);
@ -154,7 +153,6 @@
// kUnknown should be handled gracefully for "heap-allocated", non-leaf
// reporters, leaf-reporters, "other" reporters, and duplicated reporters.
f("3rd", "heap-allocated", OTHER, kUnknown),
f("3rd", "explicit/a", HEAP, kUnknown),
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
@ -196,7 +194,7 @@ Explicit Allocations\n\
│ └──20.00 MB (03.21%) -- g\n\
│ └──20.00 MB (03.21%) -- h\n\
│ └──20.00 MB (03.21%) -- i\n\
├───15.00 MB (02.41%) -- g [2]\n\
├───15.00 MB (02.41%) -- g\n\
│ ├───6.00 MB (00.96%) -- a\n\
│ ├───5.00 MB (00.80%) -- b\n\
│ └───4.00 MB (00.64%) -- other\n\
@ -248,7 +246,7 @@ Other Measurements\n\
\n\
Explicit Allocations\n\
777.00 MB (100.0%) -- explicit\n\
├──777.00 MB (100.0%) -- a [*]\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%) -- (1 omitted)\n\
@ -281,7 +279,7 @@ Explicit Allocations\n\
│ └──20,971,520 B (03.21%) -- g\n\
│ └──20,971,520 B (03.21%) -- h\n\
│ └──20,971,520 B (03.21%) -- i\n\
├───15,728,640 B (02.41%) -- g [2]\n\
├───15,728,640 B (02.41%) -- g\n\
│ ├───6,291,456 B (00.96%) -- a\n\
│ ├───5,242,880 B (00.80%) -- b\n\
│ └───4,194,304 B (00.64%) -- other\n\
@ -334,7 +332,7 @@ Other Measurements\n\
\n\
Explicit Allocations\n\
814,743,552 B (100.0%) -- explicit\n\
├──814,743,552 B (100.0%) -- a [*]\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\

View File

@ -79,21 +79,21 @@ interface nsIMemoryReporter : nsISupports
* mmap/VirtualAlloc/vm_allocate) or a heap-level allocation (eg.
* malloc/calloc/operator new).
*
* Each reporter can be viewed as representing a node in a tree rooted at
* "explicit". Not all nodes of the tree need have an associated reporter.
* So, for example, the reporters "explicit/a/b", "explicit/a/c",
* "explicit/d", "explicit/d/e", and "explicit/d/f" define this tree:
* Each reporter can be viewed as representing a leaf node in a tree
* rooted at "explicit". Internal nodes of the tree don't have
* reporters. So, for example, the reporters "explicit/a/b",
* "explicit/a/c", "explicit/d/e", and "explicit/d/f" define this tree:
*
* explicit
* |--a
* | |--b [*]
* | \--c [*]
* \--d [*]
* \--d
* |--e [*]
* \--f [*]
*
* Nodes marked with a [*] have a reporter. Notice that "explicit/a" is
* implicitly defined.
* Nodes marked with a [*] have a reporter. Notice that the internal
* nodes are implicitly defined by the paths.
*
* A node's children divide their parent's memory into disjoint pieces.
* So in the example above, |a| may not count any allocations counted by
@ -119,7 +119,7 @@ interface nsIMemoryReporter : nsISupports
* calloc, realloc, memalign, operator new, or operator new[]. Reporters
* in this category must have units UNITS_BYTES and must have a path
* starting with "explicit".
*
* - NONHEAP: memory which the program explicitly allocated, but does not
* live on the heap. Such memory is commonly allocated by calling one of
* the OS's memory-mapping functions (e.g. mmap, VirtualAlloc, or

View File

@ -626,15 +626,15 @@ struct MemoryReport {
// This is just a wrapper for InfallibleTArray<MemoryReport> that implements
// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports.
class MemoryReportsWrapper : public nsISupports {
class PRInt64Wrapper : public nsISupports {
public:
NS_DECL_ISUPPORTS
MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
InfallibleTArray<MemoryReport> *mReports;
PRInt64Wrapper() : mValue(0) { }
PRInt64 mValue;
};
NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
NS_IMPL_ISUPPORTS0(PRInt64Wrapper)
class MemoryReportCallback : public nsIMemoryMultiReporterCallback
class NonHeapCountingCallback : public nsIMemoryMultiReporterCallback
{
public:
NS_DECL_ISUPPORTS
@ -642,42 +642,29 @@ public:
NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
const nsACString &aDescription,
nsISupports *aWrappedMRs)
nsISupports *aWrappedExplicit)
{
if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
PromiseFlatCString(aPath).Find("explicit") == 0 &&
aAmount != PRInt64(-1)) {
MemoryReportsWrapper *wrappedMRs =
static_cast<MemoryReportsWrapper *>(aWrappedMRs);
MemoryReport mr(aPath, aAmount);
wrappedMRs->mReports->AppendElement(mr);
aAmount != PRInt64(-1))
{
PRInt64Wrapper *wrappedPRInt64 =
static_cast<PRInt64Wrapper *>(aWrappedExplicit);
wrappedPRInt64->mValue += aAmount;
}
return NS_OK;
}
};
NS_IMPL_ISUPPORTS1(
MemoryReportCallback
NonHeapCountingCallback
, nsIMemoryMultiReporterCallback
)
// Is path1 a prefix, and thus a parent, of path2? Eg. "a/b" is a parent of
// "a/b/c", but "a/bb" is not.
static bool
isParent(const nsACString &path1, const nsACString &path2)
{
if (path1.Length() >= path2.Length())
return false;
const nsACString& subStr = Substring(path2, 0, path1.Length());
return subStr.Equals(path1) && path2[path1.Length()] == '/';
}
NS_IMETHODIMP
nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
{
InfallibleTArray<MemoryReport> nonheap;
PRInt64 heapUsed = PRInt64(-1);
NS_ENSURE_ARG_POINTER(aExplicit);
*aExplicit = 0;
// Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla
// "explicit" reporters.
@ -700,8 +687,8 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
// We're only interested in NONHEAP explicit reporters and
// the 'heap-allocated' reporter.
if (kind == nsIMemoryReporter::KIND_NONHEAP &&
path.Find("explicit") == 0) {
path.Find("explicit") == 0)
{
PRInt64 amount;
rv = r->GetAmount(&amount);
NS_ENSURE_SUCCESS(rv, rv);
@ -709,17 +696,19 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
// Just skip any NONHEAP reporters that fail, because
// "heap-allocated" is the most important one.
if (amount != PRInt64(-1)) {
MemoryReport mr(path, amount);
nonheap.AppendElement(mr);
*aExplicit += amount;
}
} else if (path.Equals("heap-allocated")) {
rv = r->GetAmount(&heapUsed);
PRInt64 heapAllocated;
rv = r->GetAmount(&heapAllocated);
NS_ENSURE_SUCCESS(rv, rv);
// If "heap-allocated" fails, we give up, because the result
// would be horribly inaccurate.
if (heapUsed == PRInt64(-1)) {
if (heapAllocated == PRInt64(-1)) {
*aExplicit = PRInt64(-1);
return NS_OK;
} else {
*aExplicit += heapAllocated;
}
}
}
@ -727,39 +716,15 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
// Get KIND_NONHEAP measurements from multi-reporters, too.
nsCOMPtr<nsISimpleEnumerator> e2;
EnumerateMultiReporters(getter_AddRefs(e2));
nsRefPtr<MemoryReportsWrapper> wrappedMRs =
new MemoryReportsWrapper(&nonheap);
// This callback adds only NONHEAP explicit reporters.
nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
nsRefPtr<PRInt64Wrapper> wrappedNonHeapTotal = new PRInt64Wrapper();
nsRefPtr<NonHeapCountingCallback> cb = new NonHeapCountingCallback();
while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
nsCOMPtr<nsIMemoryMultiReporter> r;
e2->GetNext(getter_AddRefs(r));
r->CollectReports(cb, wrappedMRs);
}
// Ignore (by zeroing its amount) any reporter that is a child of another
// reporter. Eg. if we have "explicit/a" and "explicit/a/b", zero the
// latter. This is quadratic in the number of explicit NONHEAP reporters,
// but there shouldn't be many.
for (PRUint32 i = 0; i < nonheap.Length(); i++) {
const nsCString &iPath = nonheap[i].path;
for (PRUint32 j = i + 1; j < nonheap.Length(); j++) {
const nsCString &jPath = nonheap[j].path;
if (isParent(iPath, jPath)) {
nonheap[j].amount = 0;
} else if (isParent(jPath, iPath)) {
nonheap[i].amount = 0;
}
}
}
// Sum all the nonheap reporters and heapUsed.
*aExplicit = heapUsed;
for (PRUint32 i = 0; i < nonheap.Length(); i++) {
*aExplicit += nonheap[i].amount;
r->CollectReports(cb, wrappedNonHeapTotal);
}
*aExplicit += wrappedNonHeapTotal->mValue;
return NS_OK;
}