diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 50be70fb34d..158e6e0fe58 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -368,9 +368,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. @@ -380,7 +381,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) @@ -421,8 +422,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)) { @@ -430,9 +432,8 @@ function buildTree(aReporters, aTreeName) break; } } - if (!foundReporter) { - // We didn't find any reporters for this tree, so bail. + assert(aTreeName !== 'explicit'); return null; } @@ -476,7 +477,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) { @@ -486,35 +487,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; @@ -542,49 +523,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/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index c1cc9ed3a64..609d62ab8ad 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -17,11 +17,16 @@ var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. getService(Ci.nsIMemoryReporterManager); + // Access mgr.explicit and mgr.resident just to make sure they don't crash. + // We can't check their actual values because they're non-deterministic. + var dummy = mgr.explicit; + dummy = mgr.resident; + // Remove all the real reporters and multi-reporters; save them to // restore at the end. var e = mgr.enumerateReporters(); var realReporters = []; - var dummy = 0; + dummy = 0; while (e.hasMoreElements()) { var r = e.getNext().QueryInterface(Ci.nsIMemoryReporter); // Get the |amount| field, even though we don't use it, just to test @@ -80,18 +85,18 @@ 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/g", HEAP, 1 * MB), // internal, dup: merge 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) ]; var fakeMultiReporters = [ { collectReports: function(cbObj, closure) { function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); } - f("explicit/c", NONHEAP, BYTES, 100 * MB), - f("explicit/c/d", NONHEAP, BYTES, 13 * MB), // subsumed by parent - f("explicit/c/d", NONHEAP, BYTES, 10 * MB), // dup, subsumed by parent + f("explicit/c/d", NONHEAP, BYTES, 13 * MB), + f("explicit/c/d", NONHEAP, BYTES, 10 * MB), // dup + f("explicit/c/other", NONHEAP, BYTES, 77 * MB), f("explicit/cc", NONHEAP, BYTES, 13 * MB); f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted @@ -102,7 +107,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); @@ -137,10 +141,6 @@ // and subsequent processes. is(mgr.explicit, 500*MB + (100 + 13 + 10)*MB + 599*KB, "mgr.explicit"); - // Access mgr.resident just to make sure it doesn't crash. We can't check - // its actual value because it's non-deterministic. - dummy = mgr.resident; - var fakeReporters2 = [ f("2nd", "heap-allocated", OTHER, 1000 * MB), f("2nd", "heap-unallocated",OTHER, 100 * MB), @@ -159,7 +159,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 @@ -201,7 +200,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\ @@ -253,7 +252,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\ @@ -286,7 +285,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\ @@ -339,7 +338,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 1ef11168712..f9d8079f9cd 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 1add056f619..2578939670e 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -681,17 +681,17 @@ struct MemoryReport { }; #ifdef DEBUG -// This is just a wrapper for InfallibleTArray that implements -// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports. -class MemoryReportsWrapper : public nsISupports { +// This is just a wrapper for PRInt64 that implements nsISupports, so it can be +// passed to nsIMemoryMultiReporter::CollectReports. +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 ExplicitNonHeapCountingCallback : public nsIMemoryMultiReporterCallback { public: NS_DECL_ISUPPORTS @@ -699,50 +699,40 @@ public: NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath, PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount, const nsACString &aDescription, - nsISupports *aWrappedMRs) + nsISupports *aWrappedExplicitNonHeap) { 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); + PRInt64Wrapper *wrappedPRInt64 = + static_cast(aWrappedExplicitNonHeap); + wrappedPRInt64->mValue += aAmount; } return NS_OK; } }; NS_IMPL_ISUPPORTS1( - MemoryReportCallback + ExplicitNonHeapCountingCallback , nsIMemoryMultiReporterCallback ) #endif -// 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) { + NS_ENSURE_ARG_POINTER(aExplicit); + *aExplicit = 0; + nsresult rv; + bool more; // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal // (i.e. non-multi) "explicit" reporters. PRInt64 heapAllocated = PRInt64(-1); - InfallibleTArray explicitNonHeapNormalReports; + PRInt64 explicitNonHeapNormalSize = 0; nsCOMPtr e; EnumerateReporters(getter_AddRefs(e)); - bool more; while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr r; e->GetNext(getter_AddRefs(r)); @@ -758,8 +748,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); @@ -767,45 +757,20 @@ 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); - explicitNonHeapNormalReports.AppendElement(mr); + explicitNonHeapNormalSize += amount; } } else if (path.Equals("heap-allocated")) { 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; - } - - // Sum all the explicit, NONHEAP reports from normal reporters. - // Ignore (by zeroing its amount) any normal reporter that is a child of - // another normal 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. - // - // XXX: bug 700508 will remove the need for this - // - for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { - const nsCString &iPath = explicitNonHeapNormalReports[i].path; - for (PRUint32 j = i + 1; j < explicitNonHeapNormalReports.Length(); j++) { - const nsCString &jPath = explicitNonHeapNormalReports[j].path; - if (isParent(iPath, jPath)) { - explicitNonHeapNormalReports[j].amount = 0; - } else if (isParent(jPath, iPath)) { - explicitNonHeapNormalReports[i].amount = 0; + // 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; } } } - PRInt64 explicitNonHeapNormalSize = 0; - for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { - explicitNonHeapNormalSize += explicitNonHeapNormalReports[i].amount; - } // For each multi-reporter we could call CollectReports and filter out the // non-explicit, non-NONHEAP measurements. But that's lots of wasted work, @@ -817,9 +782,9 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) // guarantees the two measurement paths are equivalent. This is wise // because it's easy for memory reporters to have bugs.) + PRInt64 explicitNonHeapMultiSize = 0; nsCOMPtr e2; EnumerateMultiReporters(getter_AddRefs(e2)); - PRInt64 explicitNonHeapMultiSize = 0; while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) { nsCOMPtr r; e2->GetNext(getter_AddRefs(r)); @@ -830,36 +795,18 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) } #ifdef DEBUG - InfallibleTArray explicitNonHeapMultiReports; - nsRefPtr cb = new MemoryReportCallback(); - nsRefPtr wrappedMRs = - new MemoryReportsWrapper(&explicitNonHeapMultiReports); + nsRefPtr cb = + new ExplicitNonHeapCountingCallback(); + nsRefPtr wrappedExplicitNonHeapMultiSize2 = + new PRInt64Wrapper(); nsCOMPtr e3; EnumerateMultiReporters(getter_AddRefs(e3)); while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) { nsCOMPtr r; e3->GetNext(getter_AddRefs(r)); - r->CollectReports(cb, wrappedMRs); - } - - // Sum all the explicit, NONHEAP reports from multi-reporters. - // XXX: identical to the explicitNonHeapNormalReports case above; bug - // 700508 will remove the need for this - for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { - const nsCString &iPath = explicitNonHeapMultiReports[i].path; - for (PRUint32 j = i + 1; j < explicitNonHeapMultiReports.Length(); j++) { - const nsCString &jPath = explicitNonHeapMultiReports[j].path; - if (isParent(iPath, jPath)) { - explicitNonHeapMultiReports[j].amount = 0; - } else if (isParent(jPath, iPath)) { - explicitNonHeapMultiReports[i].amount = 0; - } - } - } - PRInt64 explicitNonHeapMultiSize2 = 0; - for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { - explicitNonHeapMultiSize2 += explicitNonHeapMultiReports[i].amount; + r->CollectReports(cb, wrappedExplicitNonHeapMultiSize2); } + PRInt64 explicitNonHeapMultiSize2 = wrappedExplicitNonHeapMultiSize2->mValue; // Check the two measurements give the same result. NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2, @@ -867,7 +814,6 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) #endif *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize; - return NS_OK; }