Bug 689583 - Add names for memory multi-reporters. r=jlebar.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 06 Feb 2012 17:02:59 -0800
changeset 86274 4e9464928e08998b188e2db519c511d9750e9182
parent 86273 0c9c30297fadc3e78ac22af3bd25005c46f57d95
child 86275 841b4395aa6639db91a0d47e7f286ff07959cc7b
push id22008
push userbmo@edmorley.co.uk
push dateTue, 07 Feb 2012 10:32:58 +0000
treeherdermozilla-central@2b61af9d18ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar
bugs689583
milestone13.0a1
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
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