Bug 700508 - Disallow non-leaf memory reporters. r=jlebar.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 06 Dec 2011 16:20:17 -0800
changeset 82132 61d41436beb3764b450cd930b20805738cddf2d0
parent 82131 dc2865fc9538e21fd20b2997c5e8868da4478302
child 82133 bab6fef4017c60de65f5f7213d17642b8298788e
push idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs700508
milestone11.0a1
Bug 700508 - Disallow non-leaf memory reporters. r=jlebar.
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -360,29 +360,30 @@ function update()
           "heading to expand or collapse its tree.</span>" +
           "</div>";
 
   var div = document.createElement("div");
   div.innerHTML = text;
   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.
   this._name = aName;
   this._kids = [];
   // All TreeNodes have these properties added later:
   // - _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)
 }
 
 TreeNode.prototype = {
   findKid: function(aName) {
     for (var i = 0; i < this._kids.length; i++) {
@@ -413,28 +414,28 @@ TreeNode.compare = function(a, b) {
  * @return The built tree.
  */
 function buildTree(aReporters, aTreeName)
 {
   // We want to process all reporters that begin with |aTreeName|.  First we
   // 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)) {
       foundReporter = true;
       break;
     }
   }
-
   if (!foundReporter) {
-    // We didn't find any reporters for this tree, so bail.
+    assert(aTreeName !== 'explicit');
     return null;
   }
 
   var t = new TreeNode("falseRoot");
   for (var path in aReporters) {
     // Add any missing nodes in the tree implied by the path.
     var r = aReporters[path];
     if (r.treeNameMatches(aTreeName)) {
@@ -468,55 +469,35 @@ function buildTree(aReporters, aTreeName
 
   // Next, fill in the remaining properties bottom-up.
   // Note that this function never returns kUnknown.
   function fillInTree(aT, aPrepath)
   {
     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) {
         aT._amount = amount;
       } else {
         aT._amount = 0;
         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;
   }
 
   fillInTree(t, "");
 
   // Reduce the depth of the tree by the number of occurrences of '/' in
@@ -534,59 +515,56 @@ function buildTree(aReporters, aTreeName
 
   return t;
 }
 
 /**
  * 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;
 }
 
 /**
  * Sort all kid nodes from largest to smallest and aggregate insignificant
  * nodes.
  *
  * @param aTotalBytes
  *        The size of the tree's root node.
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -73,38 +73,37 @@
   var fakeReporters = [
     f("", "heap-allocated",     OTHER,   500 * MB),
     f("", "heap-unallocated",   OTHER,   100 * MB),
     f("", "explicit/a",         HEAP,    222 * MB),
     f("", "explicit/b/a",       HEAP,     85 * MB),
     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)
   ];
   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,  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
           f("explicit/e",       NONHEAP, BYTES, 100 * KB); // omitted
           f("explicit/f/g/h/i", HEAP,    BYTES,  20 * MB);
        }
      },
      { 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);
           f("perc1",            OTHER,   PERCENTAGE, 4567);
        }
      },
      { collectReports: function(cbObj, closure) {
           // The amounts are given in pages, so multiply here by 4kb.
@@ -149,17 +148,16 @@
     // The escaping of compartment names must prevent this script from running.
     f("2nd", "danger<script>window.alert(1)</script>",
                                 OTHER,   666 * MB),
     f("2nd", "other1",          OTHER,   111 * MB),
 
     // 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
     f("3rd", "explicit/a/d",    HEAP,    kUnknown),
     f("3rd", "explicit/a/d",    HEAP,    kUnknown), // dup: merge
     f("3rd", "explicit/b",      NONHEAP, kUnknown),
     f("3rd", "other1",          OTHER,   kUnknown)
   ];
@@ -191,17 +189,17 @@ 623.58 MB (100.0%) -- explicit\n\
 ├──100.00 MB (16.04%) -- c\n\
 │  ├───77.00 MB (12.35%) -- other\n\
 │  └───23.00 MB (03.69%) -- d [2]\n\
 ├───23.00 MB (03.69%) -- cc [2]\n\
 ├───20.00 MB (03.21%) -- f\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\
 ├───11.00 MB (01.76%) -- heap-unclassified\n\
 └────0.58 MB (00.09%) -- (2 omitted)\n\
 \n\
 Resident Set Size (RSS) Breakdown\n\
 0.16 MB (100.0%) -- resident\n\
@@ -243,17 +241,17 @@ Other Measurements\n\
 1,000.00 MB -- heap-allocated\n\
   100.00 MB -- heap-unallocated\n\
   111.00 MB -- other1\n\
 \n\
 3rd Process\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\
 └────0.00 MB (00.00%) -- (2 omitted)\n\
 \n\
 Other Measurements\n\
 0.00 MB -- heap-allocated [*]\n\
 0.00 MB -- other1 [*]\n\
@@ -276,17 +274,17 @@ 653,876,224 B (100.0%) -- explicit\n\
 ├──104,857,600 B (16.04%) -- c\n\
 │  ├───80,740,352 B (12.35%) -- other\n\
 │  └───24,117,248 B (03.69%) -- d [2]\n\
 ├───24,117,248 B (03.69%) -- cc [2]\n\
 ├───20,971,520 B (03.21%) -- f\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\
 ├───11,534,336 B (01.76%) -- heap-unclassified\n\
 ├──────510,976 B (00.08%) -- d\n\
 └──────102,400 B (00.02%) -- e\n\
 \n\
 Resident Set Size (RSS) Breakdown\n\
@@ -329,17 +327,17 @@ Other Measurements\n\
 1,048,576,000 B -- heap-allocated\n\
   104,857,600 B -- heap-unallocated\n\
   116,391,936 B -- other1\n\
 \n\
 3rd Process\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\
 ├────────────0 B (00.00%) -- b [*]\n\
 └────────────0 B (00.00%) -- heap-unclassified [*]\n\
 \n\
 Other Measurements\n\
 0 B -- heap-allocated [*]\n\
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -74,31 +74,31 @@ interface nsIMemoryReporter : nsISupport
    * The path that this memory usage should be reported under.  Paths are
    * '/'-delimited, eg. "a/b/c".  There are three categories of paths.
    *
    * - Paths starting with "explicit" represent regions of memory that have
    *   been explicitly allocated with an OS-level allocation (eg.
    *   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
    *   |d|, and vice versa.
    *
    * - Paths starting with "map" represent regions of virtual memory that the
    *   process has mapped.  The reporter immediately beneath "map" describes
    *   the type of measurement; for instance, the reporter "map/rss/[stack]"
@@ -114,17 +114,17 @@ interface nsIMemoryReporter : nsISupport
 
   /*
    * There are three categories of memory reporters:
    *
    *  - HEAP: memory allocated by the heap allocator, e.g. by calling malloc,
    *    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
    *    vm_allocate).  Reporters in this category must have units UNITS_BYTES
    *    and must have a path starting with "explicit" or "map".
    *
    *  - OTHER: reporters which don't fit into either of these categories. Such
    *    reporters must have a path that does not start with "explicit" or "map"
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -621,68 +621,55 @@ struct MemoryReport {
         MOZ_COUNT_DTOR(MemoryReport);
     }
     const nsCString path;
     PRInt64 amount;
 };
 
 // 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
 
     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.
     nsCOMPtr<nsISimpleEnumerator> e;
     EnumerateReporters(getter_AddRefs(e));
 
     bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
@@ -695,76 +682,54 @@ nsMemoryReporterManager::GetExplicit(PRI
 
         nsCString path;
         rv = r->GetPath(path);
         NS_ENSURE_SUCCESS(rv, rv);
 
         // 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);
 
             // 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;
             }
         }
     }
 
     // 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);
+      r->CollectReports(cb, wrappedNonHeapTotal);
     }
-
-    // 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;
-    }
+    *aExplicit += wrappedNonHeapTotal->mValue;
 
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsACString& process,
                                    nsACString& path,