Bug 703143 - backout 61d41436beb3 so that bug 700508 can be backed out. r=crashes.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Dec 2011 11:34:18 -0800
changeset 82304 8b63bb9f4422085433140487eafade9eb98b327e
parent 82303 c79de7ae8a5772a058371621800fe5b3b3c81511
child 82305 c52a726dc939193b71c49609b70869326a052d62
push id21591
push userbmo@edmorley.co.uk
push dateFri, 09 Dec 2011 11:33:41 +0000
treeherdermozilla-central@63bff373cb94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscrashes
bugs703143, 700508
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 703143 - backout 61d41436beb3 so that bug 700508 can be backed out. r=crashes.
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,30 +360,29 @@ 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.
-// - 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.
   this._name = aName;
   this._kids = [];
   // All TreeNodes have these properties added later:
   // - _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)
 }
 
 TreeNode.prototype = {
   findKid: function(aName) {
     for (var i = 0; i < this._kids.length; i++) {
@@ -414,28 +413,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.
 
-  // 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)) {
       foundReporter = true;
       break;
     }
   }
+
   if (!foundReporter) {
-    assert(aTreeName !== 'explicit');
+    // We didn't find any reporters for this tree, so bail.
     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)) {
@@ -469,35 +468,55 @@ 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 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) {
         aT._amount = amount;
       } else {
         aT._amount = 0;
         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;
   }
 
   fillInTree(t, "");
 
   // Reduce the depth of the tree by the number of occurrences of '/' in
@@ -515,56 +534,59 @@ 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.
+  // 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;
 }
 
 /**
  * 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,37 +73,38 @@
   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/c/other",   NONHEAP,  77 * MB), // subsumed by parent
+    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,  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.
@@ -148,16 +149,17 @@
     // 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)
   ];
@@ -189,17 +191,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\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\
 ├───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\
@@ -241,17 +243,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\
@@ -274,17 +276,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\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\
 ├───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\
@@ -327,17 +329,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 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
    *   |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,55 +621,68 @@ 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 PRInt64Wrapper : public nsISupports {
+class MemoryReportsWrapper : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
-    PRInt64Wrapper() : mValue(0) { }
-    PRInt64 mValue;
+    MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
+    InfallibleTArray<MemoryReport> *mReports;
 };
-NS_IMPL_ISUPPORTS0(PRInt64Wrapper)
+NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
 
-class NonHeapCountingCallback : public nsIMemoryMultiReporterCallback
+class MemoryReportCallback : 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 *aWrappedExplicit)
+                        nsISupports *aWrappedMRs)
     {
         if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
             PromiseFlatCString(aPath).Find("explicit") == 0 &&
-            aAmount != PRInt64(-1))
-        {
-            PRInt64Wrapper *wrappedPRInt64 =
-                static_cast<PRInt64Wrapper *>(aWrappedExplicit);
-            wrappedPRInt64->mValue += aAmount;
+            aAmount != PRInt64(-1)) {
+
+            MemoryReportsWrapper *wrappedMRs =
+                static_cast<MemoryReportsWrapper *>(aWrappedMRs);
+            MemoryReport mr(aPath, aAmount);
+            wrappedMRs->mReports->AppendElement(mr);
         }
         return NS_OK;
     }
 };
 NS_IMPL_ISUPPORTS1(
-  NonHeapCountingCallback
+  MemoryReportCallback
 , 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)
 {
-    NS_ENSURE_ARG_POINTER(aExplicit);
-    *aExplicit = 0;
+    InfallibleTArray<MemoryReport> nonheap;
+    PRInt64 heapUsed = PRInt64(-1);
 
     // 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) {
@@ -682,54 +695,76 @@ 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)) {
-                *aExplicit += amount;
+                MemoryReport mr(path, amount);
+                nonheap.AppendElement(mr);
             }
         } else if (path.Equals("heap-allocated")) {
-            PRInt64 heapAllocated;
-            rv = r->GetAmount(&heapAllocated);
+            rv = r->GetAmount(&heapUsed);
             NS_ENSURE_SUCCESS(rv, rv);
             // If "heap-allocated" fails, we give up, because the result
             // would be horribly inaccurate.
-            if (heapAllocated == PRInt64(-1)) {
+            if (heapUsed == 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<PRInt64Wrapper> wrappedNonHeapTotal = new PRInt64Wrapper();
-    nsRefPtr<NonHeapCountingCallback> cb = new NonHeapCountingCallback();
+    nsRefPtr<MemoryReportsWrapper> wrappedMRs =
+        new MemoryReportsWrapper(&nonheap);
+
+    // This callback adds only NONHEAP explicit reporters.
+    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
 
     while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryMultiReporter> r;
       e2->GetNext(getter_AddRefs(r));
-      r->CollectReports(cb, wrappedNonHeapTotal);
+      r->CollectReports(cb, wrappedMRs);
     }
-    *aExplicit += wrappedNonHeapTotal->mValue;
+
+    // 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;
+    }
 
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsACString& process,
                                    nsACString& path,