Bug 700508 - Disallow non-leaf memory reporters (attempt 2). r=jlebar.
☠☠ backed out by 1f17624f1ab7 ☠ ☠
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 05 Jan 2012 19:41:31 -0800
changeset 83918 4779d8df054b671e7e44deaed81a83ae9bb6b0e0
parent 83917 8dc46cdc401b476c0904498fb29363d98f91f194
child 83919 9e534250c98b00aea8c8000e4967bdd0626806e4
push idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs700508
milestone12.0a1
Bug 700508 - Disallow non-leaf memory reporters (attempt 2). r=jlebar.
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/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
@@ -363,29 +363,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++) {
@@ -416,28 +417,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)) {
@@ -471,55 +472,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
@@ -537,59 +518,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/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -12,21 +12,26 @@
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   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
     // that the reporter doesn't crash or anything.
     dummy += r.amount;
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
@@ -75,39 +80,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/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
           f("explicit/e",       NONHEAP, BYTES, 100 * KB); // omitted
           f("explicit/f/g/h/i", HEAP,    BYTES,  20 * MB);
        },
        explicitNonHeap: (100 + 13 + 10)*MB + (499 + 100)*KB
      },
      { 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);
        },
        explicitNonHeap: 0
      },
      { collectReports: function(cbObj, closure) {
@@ -132,20 +136,16 @@
 
   // mgr.explicit sums "heap-allocated" and all the appropriate NONHEAP ones:
   // - "explicit/c", "explicit/cc" x 2, "explicit/d", "explicit/e"
   // - but *not* "explicit/c/d" x 2
   // Check explicit now before we add the fake reporters for the fake 2nd
   // 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),
     f("2nd", "explicit/a/b/c",  HEAP,    497 * MB),
     f("2nd", "explicit/a/b/c",  HEAP,      1 * MB), // dup: merge
     f("2nd", "explicit/a/b/c",  HEAP,      1 * MB), // dup: merge
     f("2nd", "explicit/flip\\the\\backslashes",
                                 HEAP,    200 * MB),
@@ -154,17 +154,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)
   ];
@@ -196,17 +195,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\
@@ -248,17 +247,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\
@@ -281,17 +280,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\
@@ -334,17 +333,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
@@ -676,203 +676,149 @@ struct MemoryReport {
     {
         MOZ_COUNT_DTOR(MemoryReport);
     }
     const nsCString path;
     PRInt64 amount;
 };
 
 #ifdef DEBUG
-// This is just a wrapper for InfallibleTArray<MemoryReport> 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<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 ExplicitNonHeapCountingCallback : 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 *aWrappedExplicitNonHeap)
     {
         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);
+            PRInt64Wrapper *wrappedPRInt64 =
+                static_cast<PRInt64Wrapper *>(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<MemoryReport> explicitNonHeapNormalReports;
+    PRInt64 explicitNonHeapNormalSize = 0;
     nsCOMPtr<nsISimpleEnumerator> e;
     EnumerateReporters(getter_AddRefs(e));
-    bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsIMemoryReporter> r;
         e->GetNext(getter_AddRefs(r));
 
         PRInt32 kind;
         rv = r->GetKind(&kind);
         NS_ENSURE_SUCCESS(rv, rv);
 
         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);
-                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,
     // so we instead use GetExplicitNonHeap() which exists purely for this
     // purpose.
     //
     // (Actually, in debug builds we also do it the slow way and compare the
     // result to the result obtained from GetExplicitNonHeap().  This
     // guarantees the two measurement paths are equivalent.  This is wise
     // because it's easy for memory reporters to have bugs.)
 
+    PRInt64 explicitNonHeapMultiSize = 0;
     nsCOMPtr<nsISimpleEnumerator> e2;
     EnumerateMultiReporters(getter_AddRefs(e2));
-    PRInt64 explicitNonHeapMultiSize = 0;
     while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryMultiReporter> r;
       e2->GetNext(getter_AddRefs(r));
       PRInt64 n;
       rv = r->GetExplicitNonHeap(&n);
       NS_ENSURE_SUCCESS(rv, rv);
       explicitNonHeapMultiSize += n;
     }
 
 #ifdef DEBUG
-    InfallibleTArray<MemoryReport> explicitNonHeapMultiReports;
-    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
-    nsRefPtr<MemoryReportsWrapper> wrappedMRs =
-        new MemoryReportsWrapper(&explicitNonHeapMultiReports);
+    nsRefPtr<ExplicitNonHeapCountingCallback> cb =
+      new ExplicitNonHeapCountingCallback();
+    nsRefPtr<PRInt64Wrapper> wrappedExplicitNonHeapMultiSize2 =
+      new PRInt64Wrapper();
     nsCOMPtr<nsISimpleEnumerator> e3;
     EnumerateMultiReporters(getter_AddRefs(e3));
     while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryMultiReporter> r;
       e3->GetNext(getter_AddRefs(r));
-      r->CollectReports(cb, wrappedMRs);
+      r->CollectReports(cb, wrappedExplicitNonHeapMultiSize2);
     }
-
-    // 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,
                  "The two measurements of 'explicit' memory usage don't match");
 #endif
 
     *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize;
-
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsACString& process,
                                    nsACString& path,
                                    PRInt32 kind,