Backout 4779d8df054b (bug 700508) for linux64 Moth orange
authorEd Morley <bmo@edmorley.co.uk>
Fri, 06 Jan 2012 16:23:05 +0000
changeset 85166 1f17624f1ab7ea7b7a0dbe847307896b4197826f
parent 85165 461a7c9235945abb04cb87e5b1aa8697a7745246
child 85167 f70cf8170972fd502b2c9b3e9cc92f69fa8d3e77
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs700508
milestone12.0a1
backs out4779d8df054b671e7e44deaed81a83ae9bb6b0e0
Backout 4779d8df054b (bug 700508) for linux64 Moth orange
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,30 +363,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++) {
@@ -417,28 +416,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)) {
@@ -472,35 +471,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
@@ -518,56 +537,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/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -12,26 +12,21 @@
   <!-- 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 = [];
-  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
     // that the reporter doesn't crash or anything.
     dummy += r.amount;
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
@@ -80,38 +75,39 @@
   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/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
           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) {
@@ -136,16 +132,20 @@
 
   // 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,16 +154,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)
   ];
@@ -195,17 +196,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\
@@ -247,17 +248,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\
@@ -280,17 +281,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\
@@ -333,17 +334,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
@@ -676,149 +676,203 @@ struct MemoryReport {
     {
         MOZ_COUNT_DTOR(MemoryReport);
     }
     const nsCString path;
     PRInt64 amount;
 };
 
 #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<MemoryReport> 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<MemoryReport> *r) : mReports(r) { }
+    InfallibleTArray<MemoryReport> *mReports;
 };
-NS_IMPL_ISUPPORTS0(PRInt64Wrapper)
+NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
 
-class ExplicitNonHeapCountingCallback : 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 *aWrappedExplicitNonHeap)
+                        nsISupports *aWrappedMRs)
     {
         if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
             PromiseFlatCString(aPath).Find("explicit") == 0 &&
             aAmount != PRInt64(-1))
         {
-            PRInt64Wrapper *wrappedPRInt64 =
-                static_cast<PRInt64Wrapper *>(aWrappedExplicitNonHeap);
-            wrappedPRInt64->mValue += aAmount;
+            MemoryReportsWrapper *wrappedMRs =
+                static_cast<MemoryReportsWrapper *>(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<MemoryReport> explicitNonHeapNormalReports;
     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)) {
-                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,
     // 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
-    nsRefPtr<ExplicitNonHeapCountingCallback> cb =
-      new ExplicitNonHeapCountingCallback();
-    nsRefPtr<PRInt64Wrapper> wrappedExplicitNonHeapMultiSize2 =
-      new PRInt64Wrapper();
+    InfallibleTArray<MemoryReport> explicitNonHeapMultiReports;
+    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
+    nsRefPtr<MemoryReportsWrapper> wrappedMRs =
+        new MemoryReportsWrapper(&explicitNonHeapMultiReports);
     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, wrappedExplicitNonHeapMultiSize2);
+      r->CollectReports(cb, wrappedMRs);
     }
-    PRInt64 explicitNonHeapMultiSize2 = wrappedExplicitNonHeapMultiSize2->mValue;
+
+    // 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;
+    }
 
     // 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,