diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 8a9d90469cd..de23d5669d1 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -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; } /** diff --git a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul index cb32c7ff96a..81d013af2dd 100644 --- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul @@ -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\ diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index eee76e9ecc7..113d5a1911b 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -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 diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 89c6b4b0421..dd8a4d46696 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -626,15 +626,15 @@ struct MemoryReport { // This is just a wrapper for InfallibleTArray 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 *r) : mReports(r) { } - InfallibleTArray *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(aWrappedMRs); - MemoryReport mr(aPath, aAmount); - wrappedMRs->mReports->AppendElement(mr); + aAmount != PRInt64(-1)) + { + PRInt64Wrapper *wrappedPRInt64 = + static_cast(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 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 e2; EnumerateMultiReporters(getter_AddRefs(e2)); - nsRefPtr wrappedMRs = - new MemoryReportsWrapper(&nonheap); - - // This callback adds only NONHEAP explicit reporters. - nsRefPtr cb = new MemoryReportCallback(); + nsRefPtr wrappedNonHeapTotal = new PRInt64Wrapper(); + nsRefPtr cb = new NonHeapCountingCallback(); while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) { nsCOMPtr 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; }