Bug 689583 - Add names for memory multi-reporters. r=jlebar.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 06 Feb 2012 17:02:59 -0800
changeset 86387 4e9464928e08998b188e2db519c511d9750e9182
parent 86386 0c9c30297fadc3e78ac22af3bd25005c46f57d95
child 86388 841b4395aa6639db91a0d47e7f286ff07959cc7b
push id94
push userbturner@mozilla.com
push dateWed, 08 Feb 2012 05:39:15 +0000
reviewersjlebar
bugs689583
milestone13.0a1
Bug 689583 - Add names for memory multi-reporters. r=jlebar.
dom/base/nsDOMMemoryReporter.cpp
dom/workers/WorkerPrivate.cpp
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/nsPresShell.cpp
storage/src/mozStorageService.cpp
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
xpcom/base/MapsMemoryReporter.cpp
xpcom/base/nsIMemoryReporter.idl
--- a/dom/base/nsDOMMemoryReporter.cpp
+++ b/dom/base/nsDOMMemoryReporter.cpp
@@ -231,16 +231,23 @@ PLDHashOperator
 GetWindows(const PRUint64& aId, nsGlobalWindow*& aWindow, void* aClosure)
 {
   ((WindowArray *)aClosure)->AppendElement(aWindow);
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
+nsDOMMemoryMultiReporter::GetName(nsACString &aName)
+{
+  aName.AssignLiteral("dom+style");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
                                          nsISupports* aClosure)
 {
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   NS_ENSURE_TRUE(windowsById, NS_OK);
 
   // Hold on to every window in memory so that window objects can't be
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -223,16 +223,22 @@ public:
         }
 #endif
         mWorkerPrivate = nsnull;
       }
     }
     return NS_OK;
   }
 
+  NS_IMETHOD GetName(nsACString &aName)
+  {
+      aName.AssignLiteral("workers");
+      return NS_OK;
+  }
+
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback* aCallback,
                  nsISupports* aClosure)
   {
     AssertIsOnMainThread();
 
     JS::RuntimeStats rtStats(JsWorkerMallocSizeOf, xpc::GetCompartmentName,
                              xpc::DestroyCompartmentName);
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1706,16 +1706,22 @@ ReportJSRuntimeExplicitTreeStats(const J
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
 
 class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter
 {
 public:
     NS_DECL_ISUPPORTS
 
+    NS_IMETHOD GetName(nsACString &name)
+    {
+        name.AssignLiteral("js");
+        return NS_OK;
+    }
+
     NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
                               nsISupports *closure)
     {
         XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
 
         // In the first step we get all the stats and stash them in a local
         // data structure.  In the second step we pass all the stashed stats to
         // the callback.  Separating these steps is important because the
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -686,16 +686,23 @@ PresShell::MemoryReporter::SizeEnumerato
   }
 
   return PL_DHASH_NEXT;
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(GfxTextrunWordCacheMallocSizeOf, "gfx/textrun-word-cache")
 
 NS_IMETHODIMP
+PresShell::MemoryReporter::GetName(nsACString &aName)
+{
+  aName.AssignLiteral("layout");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
                                           nsISupports* aClosure)
 {
   MemoryReporterData data;
   data.callback = aCb;
   data.closure = aClosure;
 
   sLiveShells->EnumerateEntries(SizeEnumerator, &data);
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -177,16 +177,22 @@ public:
       "Memory (approximate) used by all pager caches used by connections "
       "to this database.");
 
     NS_NAMED_LITERAL_CSTRING(mSchemaDesc,
       "Memory (approximate) used to store the schema for all databases "
       "associated with connections to this database.");
   }
 
+  NS_IMETHOD GetName(nsACString &aName)
+  {
+      aName.AssignLiteral("storage-sqlite");
+      return NS_OK;
+  }
+
   // Warning: To get a Connection's measurements requires holding its lock.
   // There may be a delay getting the lock if another thread is accessing the
   // Connection.  This isn't very nice if CollectReports is called from the
   // main thread!  But at the time of writing this function is only called when
   // about:memory is loaded (not, for example, when telemetry pings occur) and
   // any delays in that case aren't so bad.
   NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback,
                             nsISupports *aClosure)
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -116,17 +116,18 @@ const kTreeNames = {
   'explicit': 'Explicit Allocations',
   'resident': 'Resident Set Size (RSS) Breakdown',
   'pss':      'Proportional Set Size (PSS) Breakdown',
   'vsize':    'Virtual Size Breakdown',
   'swap':     'Swap Usage Breakdown',
   'other':    'Other Measurements'
 };
 
-const kMapTreePaths = ['map/resident', 'map/pss', 'map/vsize', 'map/swap'];
+const kMapTreePaths =
+  ['smaps/resident', 'smaps/pss', 'smaps/vsize', 'smaps/swap'];
 
 function onLoad()
 {
   var os = Cc["@mozilla.org/observer-service;1"].
       getService(Ci.nsIObserverService);
   os.notifyObservers(null, "child-memory-reporter-request", null);
 
   os.addObserver(ChildMemoryListener, "child-memory-reporter-update", false);
@@ -278,16 +279,21 @@ function getReportersByProcess(aMgr)
     catch(e) {
       debug("An error occurred when collecting results from the memory reporter " +
             rOrig.path + ": " + e);
     }
   }
   var e = aMgr.enumerateMultiReporters();
   while (e.hasMoreElements()) {
     var mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    // Ignore the "smaps" reporters in non-verbose mode.
+    if (!gVerbose && mrOrig.name === "smaps") {
+      continue;
+    }
+
     try {
       mrOrig.collectReports(addReporter, null);
     }
     catch(e) {
       debug("An error occurred when collecting a multi-reporter's results: " + e);
     }
   }
 
@@ -448,17 +454,17 @@ TreeNode.compare = function(a, b) {
  */
 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,
+  // "explicit".  But there may be zero for "smaps" trees;  if that happens,
   // bail.
   var foundReporter = false;
   for (var unsafePath in aReporters) {
     if (aReporters[unsafePath].treeNameMatches(aTreeName)) {
       foundReporter = true;
       break;
     }
   }
@@ -547,29 +553,27 @@ function buildTree(aReporters, aTreeName
 
   // Set the (unsafe) description on the root node.
   t._unsafeDescription = kTreeUnsafeDescriptions[t._unsafeName];
 
   return t;
 }
 
 /**
- * Ignore all the memory reporters that belong to a tree;  this involves
+ * Ignore all the memory reports that belong to a "smaps" tree;  this involves
  * explicitly marking them as done.
  *
  * @param aReporters
  *        The table of Reporters, indexed by _unsafePath.
- * @param aTreeName
- *        The name of the tree being built.
  */
-function ignoreTree(aReporters, aTreeName)
+function ignoreSmapsTrees(aReporters)
 {
   for (var unsafePath in aReporters) {
     var r = aReporters[unsafePath];
-    if (r.treeNameMatches(aTreeName)) {
+    if (r.treeNameMatches("smaps")) {
       var dummy = getBytes(aReporters, unsafePath);
     }
   }
 }
 
 /**
  * Do some work which only makes sense for the 'explicit' tree.
  *
@@ -780,31 +784,34 @@ function appendProcessElements(aP, aProc
   var warningsDiv = appendElement(aP, "div", "accuracyWarning");
 
   var explicitTree = buildTree(aReporters, 'explicit');
   var hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReporters);
   sortTreeAndInsertAggregateNodes(explicitTree._amount, explicitTree);
   appendTreeElements(aP, explicitTree, aProcess);
 
   // We only show these breakdown trees in verbose mode.
-  kMapTreePaths.forEach(function(t) {
-    if (gVerbose) {
+  if (gVerbose) {
+    kMapTreePaths.forEach(function(t) {
       var tree = buildTree(aReporters, t);
 
       // |tree| will be null if we don't have any reporters for the given
       // unsafePath.
       if (tree) {
         sortTreeAndInsertAggregateNodes(tree._amount, tree);
-        tree._hideKids = true;   // map trees are always initially collapsed
+        tree._hideKids = true;   // smaps trees are always initially collapsed
         appendTreeElements(aP, tree, aProcess);
       }
-    } else {
-      ignoreTree(aReporters, t);
-    }
-  });
+    });
+  } else {
+    // Although we skip the "smaps" multi-reporter in getReportersByProcess(),
+    // we might get some smaps reports from a child process, and they must be
+    // explicitly ignored.
+    ignoreSmapsTrees(aReporters);
+  }
 
   // We have to call appendOtherElements after we process all the trees,
   // because it looks at all the reporters which aren't part of a tree.
   var otherText = appendOtherElements(aP, aReporters, aProcess);
 
   // Add any warnings about inaccuracies due to platform limitations.
   // These must be computed after generating all the text.  The newlines give
   // nice spacing if we cut+paste into a text buffer.
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -87,48 +87,51 @@
     f("", "explicit/b/c/b",     HEAP,      2 * MB), // omitted
     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) {
+     { name: "fake1",
+       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/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) {
+     { name: "fake2",
+       collectReports: function(cbObj, closure) {
           function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); }
           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) {
+     { name: "smaps",
+       collectReports: function(cbObj, closure) {
           // The amounts are given in pages, so multiply here by 4kb.
           function f(p, a) { cbObj.callback("", p, NONHEAP, BYTES, a * 4 * KB, "(desc)", closure); }
-          f("map/vsize/a",     24);
-          f("map/swap/a",       1);
-          f("map/swap/a",       2);
-          f("map/vsize/a",      19);
-          f("map/swap/b/c",     10);
-          f("map/resident/a",   42);
-          f("map/pss/a",        43);
+          f("smaps/vsize/a",     24);
+          f("smaps/swap/a",       1);
+          f("smaps/swap/a",       2);
+          f("smaps/vsize/a",      19);
+          f("smaps/swap/b/c",     10);
+          f("smaps/resident/a",   42);
+          f("smaps/pss/a",        43);
        },
        explicitNonHeap: 0
      }
   ];
   for (var i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporter(fakeReporters[i]);
   }
   for (var i = 0; i < fakeMultiReporters.length; i++) {
@@ -192,20 +195,38 @@
     // If a negative value is within a collapsed sub-tree in non-verbose mode,
     // we should still get the warning at the top.
     f("5th", "heap-allocated",   OTHER,   100 * MB),
     f("5th", "explicit/big",     HEAP,     99 * MB),
     f("5th", "explicit/a/pos",   HEAP,     40 * KB),
     f("5th", "explicit/a/neg1",  NONHEAP, -20 * KB),
     f("5th", "explicit/a/neg2",  NONHEAP, -10 * KB)
   ];
+  var fakeMultiReporters2 = [
+     // Because this multi-reporter is in a child process, the fact that we
+     // skip the "smaps" multi-reporter in the parent process won't cause
+     // these to be skipped;  the fall-back skipping will be hit instead.
+     { name: "smaps",
+       collectReports: function(cbObj, closure) {
+          // The amounts are given in pages, so multiply here by 4kb.
+          function f(p, a) { cbObj.callback("2nd", p, NONHEAP, BYTES, a * 4 * KB, "(desc)", closure); }
+          f("smaps/vsize/a",     24);
+          f("smaps/vsize/b",     24);
+       },
+       explicitNonHeap: 0
+     }
+  ];
   for (var i = 0; i < fakeReporters2.length; i++) {
     mgr.registerReporter(fakeReporters2[i]);
   }
+  for (var i = 0; i < fakeMultiReporters2.length; i++) {
+    mgr.registerMultiReporter(fakeMultiReporters2[i]);
+  }
   fakeReporters = fakeReporters.concat(fakeReporters2);
+  fakeMultiReporters = fakeMultiReporters.concat(fakeMultiReporters2);
   ]]>
   </script>
 
   <iframe id="amFrame"  height="400" src="about:memory"></iframe>
   <iframe id="amvFrame" height="400" src="about:memory?verbose"></iframe>
 
   <script type="application/javascript">
   <![CDATA[
@@ -388,16 +409,19 @@ Explicit Allocations\n\
 1,048,576,000 B (100.0%) -- explicit\n\
 ├────523,239,424 B (49.90%) -- a\n\
 │    └──523,239,424 B (49.90%) -- b\n\
 │       └──523,239,424 B (49.90%) ── c [3]\n\
 ├────209,715,200 B (20.00%) ── flip/the/backslashes\n\
 ├────209,715,200 B (20.00%) ── compartment(compartment-url)\n\
 └────105,906,176 B (10.10%) ── heap-unclassified\n\
 \n\
+Virtual Size Breakdown\n\
+196,608 B (100.0%) ++ vsize\n\
+\n\
 Other Measurements\n\
   698,351,616 B ── danger<script>window.alert(1)</script>\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\
--- a/xpcom/base/MapsMemoryReporter.cpp
+++ b/xpcom/base/MapsMemoryReporter.cpp
@@ -118,17 +118,18 @@ void GetBasename(const nsCString &aPath,
     out.Assign(Substring(out, 0, out.RFind("(deleted)")));
   }
   out.StripChars(" ");
 
   aOut.Assign(out);
 }
 
 // MapsReporter::CollectReports uses this stuct to keep track of whether it's
-// seen a mapping under 'map/resident', 'map/vsize', and 'map/swap'.
+// seen a mapping under 'smaps/resident', 'smaps/pss', 'smaps/vsize', and
+// 'smaps/swap'.
 struct CategoriesSeen {
   CategoriesSeen() :
     mSeenResident(false),
     mSeenPss(false),
     mSeenVsize(false),
     mSeenSwap(false)
   {
   }
@@ -143,16 +144,22 @@ struct CategoriesSeen {
 
 class MapsReporter : public nsIMemoryMultiReporter
 {
 public:
   MapsReporter();
 
   NS_DECL_ISUPPORTS
 
+  NS_IMETHOD GetName(nsACString &aName)
+  {
+      aName.AssignLiteral("smaps");
+      return NS_OK;
+  }
+
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback *aCallback,
                  nsISupports *aClosure);
 
   NS_IMETHOD
   GetExplicitNonHeap(PRInt64 *aAmount) {
     // This reporter doesn't do any "explicit" measurements.
     *aAmount = 0;
@@ -216,26 +223,26 @@ MapsReporter::CollectReports(nsIMemoryMu
   while (true) {
     nsresult rv = ParseMapping(f, aCallback, aClosure, &categoriesSeen);
     if (NS_FAILED(rv))
       break;
   }
 
   fclose(f);
 
-  // For sure we should have created some node under 'map/resident' and
-  // 'map/vsize'; otherwise we're probably not reading smaps correctly.  If we
-  // didn't create a node under 'map/swap', create one here so about:memory
-  // knows to create an empty 'map/swap' tree.  See also bug 682735.
+  // For sure we should have created some node under 'smaps/resident' and
+  // 'smaps/vsize'; otherwise we're probably not reading smaps correctly.  If we
+  // didn't create a node under 'smaps/swap', create one here so about:memory
+  // knows to create an empty 'smaps/swap' tree.  See also bug 682735.
 
   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a vsize node?");
   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?");
   if (!categoriesSeen.mSeenSwap) {
     aCallback->Callback(NS_LITERAL_CSTRING(""),
-                        NS_LITERAL_CSTRING("map/swap/total"),
+                        NS_LITERAL_CSTRING("smaps/swap/total"),
                         nsIMemoryReporter::KIND_NONHEAP,
                         nsIMemoryReporter::UNITS_BYTES,
                         0,
                         NS_LITERAL_CSTRING("This process uses no swap space."),
                         aClosure);
   }
 
   return NS_OK;
@@ -505,17 +512,17 @@ MapsReporter::ParseMapBody(
     aCategoriesSeen->mSeenSwap = true;
   }
   else {
     // Don't report this category.
     return NS_OK;
   }
 
   nsCAutoString path;
-  path.Append("map/");
+  path.Append("smaps/");
   path.Append(category);
   path.Append("/");
   path.Append(aName);
 
   aCallback->Callback(NS_LITERAL_CSTRING(""),
                       path,
                       nsIMemoryReporter::KIND_NONHEAP,
                       nsIMemoryReporter::UNITS_BYTES,
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -211,16 +211,26 @@ interface nsIMemoryMultiReporterCallback
  * nsIMemoryReporter, but note that seeing any of these arguments requires
  * calling collectReports which will trigger all relevant computation.
  * (Compare and contrast this with nsIMemoryReporter, which allows all
  * fields except |amount| to be accessed without triggering computation.)
  */
 [scriptable, uuid(61d498d5-b460-4398-a8ea-7f75208534b4)]
 interface nsIMemoryMultiReporter : nsISupports
 {
+  /*
+   * The name of the multi-reporter.  Useful when only one multi-reporter
+   * needs to be run.  Must be unique;  if multi-reporters share names it's
+   * likely the wrong one will be called in certain circumstances.
+   */
+  readonly attribute ACString name;
+
+  /*
+   * Run the multi-reporter.
+   */
   void collectReports(in nsIMemoryMultiReporterCallback callback,
                       in nsISupports closure);
 
   /*
    * Return the sum of all this multi-reporter's measurements that have a
    * path that starts with "explicit" and are KIND_NONHEAP.
    *
    * This is a hack that's required to implement