Bug 656773 - Separate the process and the path in nsIMemoryReporter so that about:memory works in private browsing mode. r=sdwilsh, sr=roc.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 06 Jun 2011 11:22:45 +1000
changeset 70577 caf81a228fff6685fc6f673251ada3f0f300b109
parent 70576 068197c2a88ec96bf0e40f369dc482b3e62eb63c
child 70578 780fb17abc1ef6ef1660b7bfbc26597749b7c7fe
push id20364
push usernnethercote@mozilla.com
push dateMon, 06 Jun 2011 01:23:21 +0000
treeherdermozilla-central@caf81a228fff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssdwilsh, roc
bugs656773
milestone7.0a1
first release with
nightly linux32
caf81a228fff / 7.0a1 / 20110606030709 / files
nightly linux64
caf81a228fff / 7.0a1 / 20110606030709 / files
nightly mac
caf81a228fff / 7.0a1 / 20110606030709 / files
nightly win32
caf81a228fff / 7.0a1 / 20110606030709 / files
nightly win64
caf81a228fff / 7.0a1 / 20110606030221 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 656773 - Separate the process and the path in nsIMemoryReporter so that about:memory works in private browsing mode. r=sdwilsh, sr=roc.
dom/ipc/ContentParent.cpp
dom/ipc/PMemoryReportRequest.ipdl
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxWindowsPlatform.cpp
modules/libpr0n/src/imgLoader.cpp
storage/src/mozStorageConnection.cpp
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -685,23 +685,23 @@ void
 ContentParent::SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     for (PRInt32 i = 0; i < mMemoryReporters.Count(); i++)
         mgr->UnregisterReporter(mMemoryReporters[i]);
 
     for (PRUint32 i = 0; i < report.Length(); i++) {
 
-        nsCString prefix = report[i].prefix();
-        nsCString path   = report[i].path();
-        PRInt32   kind   = report[i].kind();
-        nsCString desc   = report[i].desc();
+        nsCString process  = report[i].process();
+        nsCString path     = report[i].path();
+        PRInt32   kind     = report[i].kind();
+        nsCString desc     = report[i].desc();
         PRInt64 memoryUsed = report[i].memoryUsed();
         
-        nsRefPtr<nsMemoryReporter> r = new nsMemoryReporter(prefix,
+        nsRefPtr<nsMemoryReporter> r = new nsMemoryReporter(process,
                                                             path,
                                                             kind,
                                                             desc,
                                                             memoryUsed);
       mMemoryReporters.AppendObject(r);
       mgr->RegisterReporter(r);
     }
 
--- a/dom/ipc/PMemoryReportRequest.ipdl
+++ b/dom/ipc/PMemoryReportRequest.ipdl
@@ -37,17 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 include protocol PContent;
 
 namespace mozilla {
 namespace dom {
 
 struct MemoryReport {
-  nsCString prefix;
+  nsCString process;
   nsCString path;
   PRInt32 kind;
   nsCString desc;
   PRInt64 memoryUsed;
 };
 
 protocol PMemoryReportRequest {
   manager PContent;
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -595,16 +595,21 @@ class SurfaceMemoryReporter :
 {
 public:
     SurfaceMemoryReporter(gfxASurface::gfxSurfaceType aType)
         : mType(aType)
     { }
 
     NS_DECL_ISUPPORTS
 
+    NS_IMETHOD GetProcess(char **process) {
+        *process = strdup("");
+        return NS_OK;
+    }
+
     NS_IMETHOD GetPath(char **memoryPath) {
         *memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
         return NS_OK;
     }
 
     NS_IMETHOD GetKind(PRInt32 *kind) {
         *kind = MR_HEAP;
         return NS_OK;
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -90,16 +90,21 @@ class D2DCacheReporter :
     public nsIMemoryReporter
 {
 public:
     D2DCacheReporter()
     { }
 
     NS_DECL_ISUPPORTS
 
+    NS_IMETHOD GetProcess(char **process) {
+        *process = strdup("");
+        return NS_OK;
+    }
+
     NS_IMETHOD GetPath(char **memoryPath) {
         *memoryPath = strdup("gfx-d2d-surfacecache");
         return NS_OK;
     }
 
     NS_IMETHOD GetKind(PRInt32 *kind) {
         *kind = MR_OTHER;
         return NS_OK;
@@ -122,16 +127,21 @@ class D2DVRAMReporter :
     public nsIMemoryReporter
 {
 public:
     D2DVRAMReporter()
     { }
 
     NS_DECL_ISUPPORTS
 
+    NS_IMETHOD GetProcess(char **process) {
+        *process = strdup("");
+        return NS_OK;
+    }
+
     NS_IMETHOD GetPath(char **memoryPath) {
         *memoryPath = strdup("gfx-d2d-surfacevram");
         return NS_OK;
     }
 
     NS_IMETHOD GetKind(PRInt32 *kind) {
         *kind = MR_OTHER;
         return NS_OK;
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -152,16 +152,22 @@ public:
   };
 
   imgMemoryReporter(ReporterType aType)
     : mType(aType)
   { }
 
   NS_DECL_ISUPPORTS
 
+  NS_IMETHOD GetProcess(char **process)
+  {
+    *process = strdup("");
+    return NS_OK;
+  }
+
   NS_IMETHOD GetPath(char **memoryPath)
   {
     if (mType == ChromeUsedRaw) {
       *memoryPath = strdup("explicit/images/chrome/used/raw");
     } else if (mType == ChromeUsedUncompressed) {
       *memoryPath = strdup("explicit/images/chrome/used/uncompressed");
     } else if (mType == ChromeUnusedRaw) {
       *memoryPath = strdup("explicit/images/chrome/unused/raw");
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -343,16 +343,22 @@ public:
   StorageMemoryReporter(Connection &aDBConn,
                         ReporterType aType)
   : mDBConn(aDBConn)
   , mType(aType)
   {
   }
 
 
+  NS_IMETHOD GetProcess(char **process)
+  {
+    *process = strdup("");
+    return NS_OK;
+  }
+
   NS_IMETHOD GetPath(char **memoryPath)
   {
     nsCString path;
 
     path.AppendLiteral("explicit/storage/sqlite/");
     path.Append(mDBConn.getFilename());
 
     if (mType == Cache_Used) {
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -144,67 +144,60 @@ function update()
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
       getService(Ci.nsIMemoryReporterManager);
 
   // Process each memory reporter:
   // - Make a copy of it into a sub-table indexed by its process.  Each copy
   //   looks like this:
   //
-  //     interface Tmr {
-  //       _tpath: string;
+  //     interface Reporter {
+  //       _path:        string;
   //       _kind:        number;
   //       _description: string;
   //       _memoryUsed:  number;
   //     }
   //
-  // - The .path property is renamed ._tpath ("truncated path") in the copy
-  //   because the process name and ':' (if present) are removed.
-  // - Note that copying mr.memoryUsed (which calls a C++ function under the
-  //   IDL covers) to tmr._memoryUsed for every reporter now means that the
+  //   After this point we never use the original memory reporter again.
+  //
+  // - Note that copying rOrig.memoryUsed (which calls a C++ function under the
+  //   IDL covers) to r._memoryUsed for every reporter now means that the
   //   results as consistent as possible -- measurements are made all at
   //   once before most of the memory required to generate this page is
   //   allocated.
-  var tmrTable = {};
+  var reportersByProcess = {};
   var e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
-    var mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    var process;
-    var tmr = {};
-    var i = mr.path.indexOf(':');
-    if (i === -1) {
-      process = "Main";
-      tmr._tpath = mr.path;
-    } else {
-      process = mr.path.slice(0, i);
-      tmr._tpath = mr.path.slice(i + 1);
+    var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
+    var process = rOrig.process === "" ? "Main" : rOrig.process;
+    var r = {
+      _path:        rOrig.path,
+      _kind:        rOrig.kind,
+      _description: rOrig.description,
+      _memoryUsed:  rOrig.memoryUsed
+    };
+    if (!reportersByProcess[process]) {
+      reportersByProcess[process] = {};
     }
-    tmr._kind        = mr.kind;
-    tmr._description = mr.description;
-    tmr._memoryUsed  = mr.memoryUsed;
-
-    if (!tmrTable[process]) {
-      tmrTable[process] = {};
-    }
-    var tmrs = tmrTable[process];
-    if (tmrs[tmr._tpath]) {
+    var reporters = reportersByProcess[process];
+    if (reporters[r._path]) {
       // Already an entry;  must be a duplicated reporter.  This can
       // happen legitimately.  Sum the sizes.
-      tmrs[tmr._tpath]._memoryUsed += tmr._memoryUsed;
+      reporters[r._path]._memoryUsed += r._memoryUsed;
     } else {
-      tmrs[tmr._tpath] = tmr;
+      reporters[r._path] = r;
     }
   }
 
   // Generate output for one process at a time.  Always start with the
   // Main process.
-  var text = genProcessText("Main", tmrTable["Main"]);
-  for (var process in tmrTable) {
+  var text = genProcessText("Main", reportersByProcess["Main"]);
+  for (var process in reportersByProcess) {
     if (process !== "Main") {
-      text += genProcessText(process, tmrTable[process]);
+      text += genProcessText(process, reportersByProcess[process]);
     }
   }
 
   // Memory-related actions.
   const GCDesc = "Do a global garbage collection.";
   // XXX: once bug 625302 is fixed, should change this button to just do a CC.
   const CCDesc = "Do a global garbage collection followed by a cycle " +
                  "collection. (It currently is not possible to do a cycle " +
@@ -234,31 +227,31 @@ function update()
           "</div>";
 
 
   var div = document.createElement("div");
   div.innerHTML = text;
   content.appendChild(div);
 }
 
-function cmpTmrs(a, b)
+function cmp_memoryUsed(a, b)
 {
   return b._memoryUsed - a._memoryUsed
 };
 
 /**
  * Generates the text for a single process.
  *
  * @param aProcess
  *        The name of the process
- * @param aTmrs
- *        Table of Tmrs for this process
+ * @param aReporters
+ *        Table of reporters for this process, indexed by _path
  * @return The generated text
  */
-function genProcessText(aProcess, aTmrs)
+function genProcessText(aProcess, aReporters)
 {
   /**
    * From a list of memory reporters, builds a tree that mirrors the tree
    * structure that will be shown as output.
    *
    * @return The built tree.  The tree nodes have this structure:
    *         interface Node {
    *           _name: string;
@@ -288,71 +281,71 @@ function genProcessText(aProcess, aTmrs)
     // We want to process all reporters that begin with 'treeName'.
     // First we build the tree but only filling in '_name', '_kind', '_kids'
     // and maybe '._hasReporter'.  This is done top-down from the reporters.
     var t = {
       _name: "falseRoot",
       _kind: MR_OTHER,
       _kids: []
     };
-    for (var tpath in aTmrs) {
-      var tmr = aTmrs[tpath];
-      if (tmr._tpath.slice(0, treeName.length) === treeName) {
-        var names = tmr._tpath.split('/');
+    for (var path in aReporters) {
+      var r = aReporters[path];
+      if (r._path.slice(0, treeName.length) === treeName) {
+        var names = r._path.split('/');
         var u = t;
         for (var i = 0; i < names.length; i++) {
           var name = names[i];
           var uMatch = findKid(name, u._kids);
           if (uMatch) {
             u = uMatch;
           } else {
             var v = {
               _name: name,
               _kind: MR_OTHER,
               _kids: []
             };
             u._kids.push(v);
             u = v;
           }
         }
-        u._kind = tmr._kind;
+        u._kind = r._kind;
         u._hasReporter = true;
       }
     }
     // Using falseRoot makes the above code simpler.  Now discard it, leaving
     // treeName at the root.
     t = t._kids[0];
 
     // Next, fill in '_description' and '_memoryUsed', and maybe '_hasProblem'
     // for each node.  This is done bottom-up because for most non-leaf nodes
     // '_memoryUsed' and '_description' are determined from the child nodes.
-    function fillInTree(aT, aPretpath)
+    function fillInTree(aT, aPrepath)
     {
-      var tpath = aPretpath ? aPretpath + '/' + aT._name : aT._name;
+      var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
       if (aT._kids.length === 0) {
         // Leaf node.  Must have a reporter.
-        aT._description = getDescription(aTmrs, tpath);
-        var memoryUsed = getBytes(aTmrs, tpath);
+        aT._description = getDescription(aReporters, path);
+        var memoryUsed = getBytes(aReporters, path);
         if (memoryUsed !== kUnknown) {
           aT._memoryUsed = memoryUsed;
         } else {
           aT._memoryUsed = 0;
           aT._hasProblem = true;
         }
       } else {
         // 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.
-          var b = fillInTree(aT._kids[i], tpath);
+          var b = fillInTree(aT._kids[i], path);
           childrenBytes += (b === kUnknown ? 0 : b);
         }
         if (aT._hasReporter === true) {
-          aT._description = getDescription(aTmrs, tpath);
-          var memoryUsed = getBytes(aTmrs, tpath);
+          aT._description = getDescription(aReporters, path);
+          var memoryUsed = getBytes(aReporters, path);
           if (memoryUsed !== kUnknown) {
             // Non-leaf node with its own reporter.  Use the reporter and add
             // an "other" child node.
             aT._memoryUsed = memoryUsed;
             var other = {
               _name: "other",
               _kind: MR_OTHER,
               _description: "All unclassified " + aT._name + " memory.",
@@ -392,17 +385,17 @@ function genProcessText(aProcess, aTmrs)
         }
         return n;
       }
     }
 
     // A special case:  compute the derived "heap-unclassified" value.  Don't
     // mark "heap-used" when we get its size because we want it to appear in
     // the "Other Measurements" list.
-    var heapUsedBytes = getBytes(aTmrs, "heap-used", true);
+    var heapUsedBytes = getBytes(aReporters, "heap-used", true);
     var unknownHeapUsedBytes = 0;
     var hasProblem = true;
     if (heapUsedBytes !== kUnknown) {
       unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
       hasProblem = false;
     }
     var heapUnclassified = {
       _name: "heap-unclassified",
@@ -430,55 +423,55 @@ function genProcessText(aProcess, aTmrs)
      * Sort all kid nodes from largest to smallest and aggregate
      * insignificant nodes.
      *
      * @param aT
      *        The tree
      */
     function filterTree(aT)
     {
-      aT._kids.sort(cmpTmrs);
+      aT._kids.sort(cmp_memoryUsed);
 
       for (var i = 0; i < aT._kids.length; i++) {
         if (shouldOmit(aT._kids[i]._memoryUsed)) {
           // This sub-tree is below the significance threshold
           // Remove it and all remaining (smaller) sub-trees, and
           // replace them with a single aggregate node.
           var i0 = i;
           var aggBytes = 0;
           var aggNames = [];
           for ( ; i < aT._kids.length; i++) {
             aggBytes += aT._kids[i]._memoryUsed;
             aggNames.push(aT._kids[i]._name);
           }
           aT._kids.splice(i0);
           var n = i - i0;
-          var tmrSub = {
+          var rSub = {
             _name: "(" + n + " omitted)",
             _kind: MR_OTHER,
             _description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
             _memoryUsed: aggBytes,
             _kids: []
           };
-          aT._kids[i0] = tmrSub;
+          aT._kids[i0] = rSub;
           break;
         }
         filterTree(aT._kids[i]);
       }
     }
     filterTree(t);
 
     return t;
   }
 
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   var text = "";
   text += "<h1>" + aProcess + " Process</h1>\n\n";
   text += genTreeText(buildTree());
-  text += genOtherText(aTmrs);
+  text += genOtherText(aReporters);
   text += "<hr></hr>";
   return text;
 }
 
 /**
  * Converts a byte count to an appropriate string representation.
  *
  * @param aBytes
@@ -547,52 +540,52 @@ function pad(aS, aN, aC)
   }
   return padding + aS;
 }
 
 /**
  * Gets the byte count for a particular memory reporter and sets its _done
  * property.
  *
- * @param aTmrs
- *        Table of Tmrs for this process
- * @param aTpath
- *        The tpath of the memory reporter
+ * @param aReporters
+ *        Table of reporters for this process, indexed by _path
+ * @param aPath
+ *        The path of the memory reporter
  * @param aDoNotMark
  *        If set, the _done property is not set.
  * @return The byte count
  */
-function getBytes(aTmrs, aTpath, aDoNotMark)
+function getBytes(aReporters, aPath, aDoNotMark)
 {
-  var tmr = aTmrs[aTpath];
-  if (tmr) {
-    var bytes = tmr._memoryUsed;
+  var r = aReporters[aPath];
+  if (r) {
+    var bytes = r._memoryUsed;
     if (!aDoNotMark) {
-      tmr._done = true;
+      r._done = true;
     }
     return bytes;
   }
-  // Nb: this should never occur; all tpaths have been extracted from aTmrs and
-  // so the lookup will succeed.  Return an obviously wrong number that will
-  // likely be noticed.
+  // Nb: this should never occur; all paths have been extracted from
+  // the original list of reporters and so the lookup should succeed.  Return
+  // an obviously wrong number that will likely be noticed.
   return -2 * 1024 * 1024;
 }
 
 /**
  * Gets the description for a particular memory reporter.
  *
- * @param aTmrs
- *        Table of Tmrs for this process
- * @param aTpath
- *        The tpath of the memory reporter
+ * @param aReporters
+ *        Table of reporters for this process, indexed by _path
+ * @param aPath
+ *        The path of the memory reporter
  * @return The description
  */
-function getDescription(aTmrs, aTpath)
+function getDescription(aReporters, aPath)
 {
-  var r = aTmrs[aTpath];
+  var r = aReporters[aPath];
   return r ? r._description : "???";
 }
 
 function genMrValueText(aValue)
 {
   return "<span class='mrValue'>" + aValue + "</span>";
 }
 
@@ -735,59 +728,67 @@ function genTreeText(aT)
                
   return "<h2 class='hasDesc' title='" + escapeQuotes(desc) +
          "'>Explicit Allocations</h2>\n" + "<pre>" + text + "</pre>\n";
 }
 
 /**
  * Generates the text for the "Other Measurements" section.
  *
- * @param aTmrs
- *        Table of Tmrs for this process
+ * @param aReporters
+ *        Table of reporters for this process, indexed by _path
  * @return The generated text
  */
-function genOtherText(aTmrs)
+function genOtherText(aReporters)
 {
-  // Generate an array of tmr-like elements, stripping out all the tmrs that
-  // have already been handled.  Also find the width of the widest element, so
-  // we can format things nicely.
+  // Generate an array of Reporter-like elements, stripping out all the
+  // reporters that have already been handled.  Also find the width of the
+  // widest element, so we can format things nicely.
   var maxBytesLength = 0;
-  var tmrArray = [];
-  for (var tpath in aTmrs) {
-    var tmr = aTmrs[tpath];
-    if (!tmr._done) {
+  var rArray = [];
+  for (var path in aReporters) {
+    var r = aReporters[path];
+    if (!r._done) {
       var hasProblem = false;
-      if (tmr._memoryUsed === kUnknown) {
+      if (r._memoryUsed === kUnknown) {
         hasProblem = true;
       }
       var elem = {
-        _tpath:       tmr._tpath,
-        _kind:        tmr._kind,
-        _description: tmr._description,
-        _memoryUsed:  hasProblem ? 0 : tmr._memoryUsed,
+        _path:        r._path,
+        _kind:        r._kind,
+        _description: r._description,
+        _memoryUsed:  hasProblem ? 0 : r._memoryUsed,
         _hasProblem:  hasProblem
       };
-      tmrArray.push(elem);
+      rArray.push(elem);
       var thisBytesLength = formatBytes(elem._memoryUsed).length;
       if (thisBytesLength > maxBytesLength) {
         maxBytesLength = thisBytesLength;
       }
     }
   }
-  tmrArray.sort(cmpTmrs);
+  rArray.sort(cmp_memoryUsed);
 
   // Generate text for the not-yet-printed values.
   var text = "";
-  for (var i = 0; i < tmrArray.length; i++) {
-    var elem = tmrArray[i];
+  for (var i = 0; i < rArray.length; i++) {
+    var elem = rArray[i];
     text += genMrValueText(
               pad(formatBytes(elem._memoryUsed), maxBytesLength, ' ')) + " ";
-    text += genMrNameText(elem._kind, elem._description, elem._tpath,
+    text += genMrNameText(elem._kind, elem._description, elem._path,
                           elem._hasProblem);
   }
 
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   const desc = "This list contains other memory measurements that cross-cut " +
                "the requested memory measurements above."
   return "<h2 class='hasDesc' title='" + desc + "'>Other Measurements</h2>\n" +
          "<pre>" + text + "</pre>\n";
 }
 
+function debug(x)
+{
+  var content = $("content");
+  var div = document.createElement("div");
+  div.innerHTML = JSON.stringify(x);
+  content.appendChild(div);
+}
+
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -30,54 +30,63 @@
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const kUnknown = -1;
   const MAPPED = Ci.nsIMemoryReporter.MR_MAPPED;
   const HEAP   = Ci.nsIMemoryReporter.MR_HEAP;
   const OTHER  = Ci.nsIMemoryReporter.MR_OTHER;
 
+  function f(aProcess, aPath, aKind, aMemoryUsed) {
+    return {
+      process:     aProcess,
+      path:        aPath,
+      kind:        aKind,
+      description: "(description)",
+      memoryUsed:  aMemoryUsed 
+    };
+  }
+
   fakeReporters = [
-    { path: "heap-used",        kind: OTHER, memoryUsed:  500 * MB },
-    { path: "heap-unused",      kind: OTHER, memoryUsed:  100 * MB },
-    { path: "explicit/a",       kind: HEAP,  memoryUsed:  222 * MB },
-    { path: "explicit/b/a",     kind: HEAP,  memoryUsed:   85 * MB },
-    { path: "explicit/b/b",     kind: HEAP,  memoryUsed:   75 * MB },
-    { path: "explicit/b/c/a",   kind: HEAP,  memoryUsed:   70 * MB },
-    { path: "explicit/b/c/b",   kind: HEAP,  memoryUsed:    2 * MB }, // omitted
-    { path: "explicit/c",       kind: MAPPED,memoryUsed:  123 * MB },
-    { path: "explicit/d",       kind: MAPPED,memoryUsed:  499 * KB }, // omitted
-    { path: "explicit/e",       kind: MAPPED,memoryUsed:  100 * KB }, // omitted
-    { path: "explicit/f/g/h/i", kind: HEAP,  memoryUsed:   20 * MB },
-    { path: "explicit/g",       kind: HEAP,  memoryUsed:   14 * MB }, // internal
-    { path: "explicit/g",       kind: HEAP,  memoryUsed:    1 * MB }, // internal, dup: merge
-    { path: "explicit/g/a",     kind: HEAP,  memoryUsed:    6 * MB },
-    { path: "explicit/g/b",     kind: HEAP,  memoryUsed:    5 * MB },
-    { path: "other1",           kind: OTHER, memoryUsed:  111 * MB },
-    { path: "other2",           kind: OTHER, memoryUsed:  222 * MB },
+    f("", "heap-used",          OTHER,  500 * MB),
+    f("", "heap-unused",        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",         MAPPED, 123 * MB),
+    f("", "explicit/d",         MAPPED, 499 * KB), // omitted
+    f("", "explicit/e",         MAPPED, 100 * KB), // omitted
+    f("", "explicit/f/g/h/i",   HEAP,    20 * MB),
+    f("", "explicit/g",         HEAP,    14 * MB), // internal
+    f("", "explicit/g",         HEAP,     1 * MB), // internal, dup: merge
+    f("", "explicit/g/a",       HEAP,     6 * MB),
+    f("", "explicit/g/b",       HEAP,     5 * MB),
+    f("", "other1",             OTHER,  111 * MB),
+    f("", "other2",             OTHER,  222 * MB),
 
-    { path: "2nd:heap-used",    kind: OTHER, memoryUsed: 1000 * MB },
-    { path: "2nd:heap-unused",  kind: OTHER, memoryUsed:  100 * MB },
-    { path: "2nd:explicit/a/b/c",kind: HEAP, memoryUsed:  498 * MB },
-    { path: "2nd:explicit/a/b/c",kind: HEAP, memoryUsed:    1 * MB }, // dup: merge
-    { path: "2nd:explicit/b",   kind: HEAP,  memoryUsed:  400 * MB },
-    { path: "2nd:other1",       kind: OTHER, memoryUsed:  777 * MB },
+    f("2nd", "heap-used",       OTHER, 1000 * MB),
+    f("2nd", "heap-unused",     OTHER,  100 * MB),
+    f("2nd", "explicit/a/b/c",  HEAP,   498 * MB),
+    f("2nd", "explicit/a/b/c",  HEAP,     1 * MB), // dup: merge
+    f("2nd", "explicit/b",      HEAP,   400 * MB),
+    f("2nd", "other1",          OTHER,  777 * MB),
 
     // kUnknown should be handled gracefully for "heap-used", non-leaf
     // reporters, leaf-reporters, and "other" reporters.
-    { path: "3rd:heap-used",    kind: OTHER, memoryUsed: kUnknown },
-    { path: "3rd:explicit/a",   kind: HEAP,  memoryUsed: kUnknown },
-    { path: "3rd:explicit/a/b", kind: HEAP,  memoryUsed: 333 * MB },
-    { path: "3rd:explicit/a/c", kind: HEAP,  memoryUsed: 444 * MB },
-    { path: "3rd:explicit/a/d", kind: HEAP,  memoryUsed: kUnknown },
-    { path: "3rd:explicit/b",   kind: MAPPED,memoryUsed: kUnknown },
-    { path: "3rd:other1",       kind: OTHER, memoryUsed: kUnknown }
+    f("3rd", "heap-used",       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/d",    HEAP,  kUnknown),
+    f("3rd", "explicit/b",      MAPPED,kUnknown),
+    f("3rd", "other1",          OTHER, kUnknown)
   ];
   for (var i = 0; i < fakeReporters.length; i++) {
-    fakeReporters[i].description = "(description)";
     mgr.registerReporter(fakeReporters[i]);
   }
   ]]>
   </script>
 
   <iframe id="amFrame"  height="300" src="about:memory"></iframe>
   <iframe id="amvFrame" height="300" src="about:memory?verbose"></iframe>
 
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -39,19 +39,26 @@
 #include "nsISupports.idl"
 
 interface nsISimpleEnumerator;
 
 [scriptable, uuid(d298b942-3e66-4cd3-9ff5-46abc69147a7)]
 interface nsIMemoryReporter : nsISupports
 {
   /*
-   * The path that this memory usage should be reported under.  Paths can
-   * begin with a process name plus a colon, eg "Content:", but this is not
-   * necessary for the main process.  After the process name, paths are
+   * The name of the process containing this reporter.  All reporters start
+   * with "", which is short-hand for the "main" process;  this is true even
+   * for reporters in child processes.  When reporters from child reporters
+   * are copied into the main process, the copies have their 'process' field
+   * set appropriately.
+   */
+  readonly attribute string process;
+
+  /*
+   * The path that this memory usage should be reported under.  Paths are
    * '/'-delimited, eg. "a/b/c".  There are two categories of paths.
    *
    * - Paths starting with "explicit" represent non-overlapping 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 one can be viewed as representing a
    *   path in a tree from the root node ("explicit") to a node lower in the
    *   tree; this lower node does not have to be a leaf node.
@@ -127,20 +134,24 @@ interface nsIMemoryReporterManager : nsI
   /*
    * Initialize.
    */
   void init ();
 };
 
 %{C++
 
+/*
+ * Note that this defaults 'process' to "", which is usually what's desired.
+ */
 #define NS_MEMORY_REPORTER_IMPLEMENT(_classname,_path,_kind,_desc,_usageFunction,_dataptr) \
     class MemoryReporter_##_classname : public nsIMemoryReporter {      \
     public:                                                             \
       NS_DECL_ISUPPORTS                                                 \
+      NS_IMETHOD GetProcess(char **process) { *process = strdup(""); return NS_OK; } \
       NS_IMETHOD GetPath(char **memoryPath) { *memoryPath = strdup(_path); return NS_OK; } \
       NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; } \
       NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; } \
       NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) { *memoryUsed = _usageFunction(_dataptr); return NS_OK; } \
     };                                                                  \
     NS_IMPL_ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter)
 
 #define NS_MEMORY_REPORTER_NAME(_classname)  MemoryReporter_##_classname
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -393,36 +393,39 @@ nsMemoryReporterManager::UnregisterRepor
     if (!mReporters.RemoveObject(reporter))
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
-nsMemoryReporter::nsMemoryReporter(nsCString& prefix,
+nsMemoryReporter::nsMemoryReporter(nsCString& process,
                                    nsCString& path,
                                    PRInt32 kind,
                                    nsCString& desc,
                                    PRInt64 memoryUsed)
-: mKind(kind)
+: mProcess(process)
+, mPath(path)
+, mKind(kind)
 , mDesc(desc)
 , mMemoryUsed(memoryUsed)
 {
-  if (!prefix.IsEmpty()) {
-      mPath.Append(prefix);
-      mPath.Append(NS_LITERAL_CSTRING(":"));
-  }
-  mPath.Append(path);
 }
 
 nsMemoryReporter::~nsMemoryReporter()
 {
 }
 
+NS_IMETHODIMP nsMemoryReporter::GetProcess(char **aProcess)
+{
+    *aProcess = strdup(mProcess.get());
+    return NS_OK;
+}
+
 NS_IMETHODIMP nsMemoryReporter::GetPath(char **aPath)
 {
     *aPath = strdup(mPath.get());
     return NS_OK;
 }
 
 NS_IMETHODIMP nsMemoryReporter::GetKind(PRInt32 *aKind)
 {
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -6,25 +6,26 @@
 using mozilla::Mutex;
 
 class nsMemoryReporter : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
-  nsMemoryReporter(nsCString& prefix,
+  nsMemoryReporter(nsCString& process,
                    nsCString& path, 
                    PRInt32 kind,
                    nsCString& desc,
                    PRInt64 memoryUsed);
 
   ~nsMemoryReporter();
 
 protected:
+  nsCString mProcess;
   nsCString mPath;
   PRInt32   mKind;
   nsCString mDesc;
   PRInt64   mMemoryUsed;
 };
 
 
 class nsMemoryReporterManager : public nsIMemoryReporterManager