Backout 4779d8df054b (bug 700508) for linux64 Moth orange
authorEd Morley <bmo@edmorley.co.uk>
Fri, 06 Jan 2012 16:23:05 +0000
changeset 83937 1f17624f1ab7ea7b7a0dbe847307896b4197826f
parent 83936 461a7c9235945abb04cb87e5b1aa8697a7745246
child 83938 f70cf8170972fd502b2c9b3e9cc92f69fa8d3e77
push id4643
push userbmo@edmorley.co.uk
push dateFri, 06 Jan 2012 16:25:02 +0000
treeherdermozilla-inbound@1f17624f1ab7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs700508
milestone12.0a1
backs out4779d8df054b671e7e44deaed81a83ae9bb6b0e0
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
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,