From d1f7bc7cba03f1059aecfceab258a8510f1083ba Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 6 Jan 2012 16:23:05 +0000 Subject: [PATCH] Backout 4779d8df054b (bug 700508) for linux64 Moth orange --- .../aboutmemory/content/aboutMemory.js | 92 ++++++++------ .../aboutmemory/tests/test_aboutmemory.xul | 29 ++--- xpcom/base/nsIMemoryReporter.idl | 16 +-- xpcom/base/nsMemoryReporterManager.cpp | 118 +++++++++++++----- 4 files changed, 166 insertions(+), 89 deletions(-) diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 158e6e0fe58..50be70fb34d 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -368,10 +368,9 @@ function update() content.appendChild(div); } -// 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. +// 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. function TreeNode(aName) { // Nb: _units is not needed, it's always UNITS_BYTES. @@ -381,7 +380,7 @@ function TreeNode(aName) // - _amount (which is never |kUnknown|) // - _description // - // Leaf TreeNodes have these properties added later: + // TreeNodes corresponding to Reporters have these properties added later: // - _kind // - _nMerged (if > 1) // - _hasProblem (only defined if true) @@ -422,9 +421,8 @@ function buildTree(aReporters, aTreeName) // build the tree but only fill the properties that we can with a top-down // traversal. - // 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. + // Is there any reporter which matches aTreeName? If not, we'll create a + // dummy one. var foundReporter = false; for (var path in aReporters) { if (aReporters[path].treeNameMatches(aTreeName)) { @@ -432,8 +430,9 @@ function buildTree(aReporters, aTreeName) break; } } + if (!foundReporter) { - assert(aTreeName !== 'explicit'); + // We didn't find any reporters for this tree, so bail. return null; } @@ -477,7 +476,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 is undefined for leaf node"); + assert(aT._kind !== undefined, "aT._kind !== undefined"); aT._description = getDescription(aReporters, path); var amount = getBytes(aReporters, path); if (amount !== kUnknown) { @@ -487,15 +486,35 @@ function buildTree(aReporters, aTreeName) aT._hasProblem = true; } } else { - // Non-leaf node. Derive its size and description entirely from its - // children. - assert(aT._kind === undefined, "aT._kind is defined for non-leaf node"); + // Non-leaf node. Get the size of the children. 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); } - aT._amount = childrenBytes; - aT._description = "The sum of all entries below '" + aT._name + "'."; + 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 + "'."; + } } assert(aT._amount !== kUnknown, "aT._amount !== kUnknown"); return aT._amount; @@ -523,46 +542,49 @@ 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. + // 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. var s = ""; function getKnownHeapUsedBytes(aT) { - 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; + if (aT._kind === KIND_HEAP) { + return aT._amount; } 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 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 heapUsedBytes = getBytes(aReporters, "heap-allocated", true); + var unknownHeapUsedBytes = 0; + var hasProblem = true; + if (heapUsedBytes !== kUnknown) { + unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(aT); + hasProblem = false; } + 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). - heapUnclassifiedT._description = + heapUnclassified._description = kindToString(KIND_HEAP) + "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)."; + "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; + } - aT._kids.push(heapUnclassifiedT); - aT._amount += heapUnclassifiedT._amount; + aT._kids.push(heapUnclassified); + aT._amount += unknownHeapUsedBytes; } /** diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index 609d62ab8ad..c1cc9ed3a64 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -17,16 +17,11 @@ 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 = []; - dummy = 0; + var 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 @@ -85,18 +80,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/d", NONHEAP, BYTES, 13 * MB), - f("explicit/c/d", NONHEAP, BYTES, 10 * MB), // dup - f("explicit/c/other", NONHEAP, BYTES, 77 * MB), + 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/cc", NONHEAP, BYTES, 13 * MB); f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted @@ -107,6 +102,7 @@ }, { 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); @@ -141,6 +137,10 @@ // 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,6 +159,7 @@ // 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 @@ -200,7 +201,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\n\ +├───15.00 MB (02.41%) -- g [2]\n\ │ ├───6.00 MB (00.96%) -- a\n\ │ ├───5.00 MB (00.80%) -- b\n\ │ └───4.00 MB (00.64%) -- other\n\ @@ -252,7 +253,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\ @@ -285,7 +286,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\n\ +├───15,728,640 B (02.41%) -- g [2]\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\ @@ -338,7 +339,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 f9d8079f9cd..1ef11168712 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 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: + * 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: * * explicit * |--a * | |--b [*] * | \--c [*] - * \--d + * \--d [*] * |--e [*] * \--f [*] * - * Nodes marked with a [*] have a reporter. Notice that the internal - * nodes are implicitly defined by the paths. + * Nodes marked with a [*] have a reporter. Notice that "explicit/a" is + * implicitly defined. * * 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 2578939670e..1add056f619 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 PRInt64 that implements nsISupports, so it can be -// passed to nsIMemoryMultiReporter::CollectReports. -class PRInt64Wrapper : public nsISupports { +// This is just a wrapper for InfallibleTArray that implements +// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports. +class MemoryReportsWrapper : public nsISupports { public: NS_DECL_ISUPPORTS - PRInt64Wrapper() : mValue(0) { } - PRInt64 mValue; + MemoryReportsWrapper(InfallibleTArray *r) : mReports(r) { } + InfallibleTArray *mReports; }; -NS_IMPL_ISUPPORTS0(PRInt64Wrapper) +NS_IMPL_ISUPPORTS0(MemoryReportsWrapper) -class ExplicitNonHeapCountingCallback : public nsIMemoryMultiReporterCallback +class MemoryReportCallback : public nsIMemoryMultiReporterCallback { public: NS_DECL_ISUPPORTS @@ -699,40 +699,50 @@ public: NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath, PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount, const nsACString &aDescription, - nsISupports *aWrappedExplicitNonHeap) + nsISupports *aWrappedMRs) { if (aKind == nsIMemoryReporter::KIND_NONHEAP && PromiseFlatCString(aPath).Find("explicit") == 0 && aAmount != PRInt64(-1)) { - PRInt64Wrapper *wrappedPRInt64 = - static_cast(aWrappedExplicitNonHeap); - wrappedPRInt64->mValue += aAmount; + MemoryReportsWrapper *wrappedMRs = + static_cast(aWrappedMRs); + MemoryReport mr(aPath, aAmount); + wrappedMRs->mReports->AppendElement(mr); } return NS_OK; } }; NS_IMPL_ISUPPORTS1( - ExplicitNonHeapCountingCallback + MemoryReportCallback , 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); - PRInt64 explicitNonHeapNormalSize = 0; + InfallibleTArray explicitNonHeapNormalReports; nsCOMPtr e; EnumerateReporters(getter_AddRefs(e)); + bool more; while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr r; e->GetNext(getter_AddRefs(r)); @@ -748,8 +758,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); @@ -757,20 +767,45 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) // Just skip any NONHEAP reporters that fail, because // "heap-allocated" is the most important one. if (amount != PRInt64(-1)) { - explicitNonHeapNormalSize += amount; + MemoryReport mr(path, amount); + explicitNonHeapNormalReports.AppendElement(mr); } } 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; + // 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; } } } + 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, @@ -782,9 +817,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)); @@ -795,18 +830,36 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) } #ifdef DEBUG - nsRefPtr cb = - new ExplicitNonHeapCountingCallback(); - nsRefPtr wrappedExplicitNonHeapMultiSize2 = - new PRInt64Wrapper(); + InfallibleTArray explicitNonHeapMultiReports; + nsRefPtr cb = new MemoryReportCallback(); + nsRefPtr wrappedMRs = + new MemoryReportsWrapper(&explicitNonHeapMultiReports); nsCOMPtr e3; EnumerateMultiReporters(getter_AddRefs(e3)); while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) { nsCOMPtr r; e3->GetNext(getter_AddRefs(r)); - r->CollectReports(cb, wrappedExplicitNonHeapMultiSize2); + 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; } - PRInt64 explicitNonHeapMultiSize2 = wrappedExplicitNonHeapMultiSize2->mValue; // Check the two measurements give the same result. NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2, @@ -814,6 +867,7 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) #endif *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize; + return NS_OK; }