Bug 715453 (part 2) - Ignore computedSize in nsMallocSizeOfFun. r=jlebar.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 17 Jan 2012 20:23:55 -0800
changeset 84832 87ad0c8ae369121deeecf3ecbc737183bcd761c6
parent 84831 593594a73dd0b46198dbe4d5c49cabf6f3d3865e
child 84833 025fa101a51bdbb2758cccffaa790a8f38b04dd8
push id21879
push usermak77@bonardo.net
push dateThu, 19 Jan 2012 10:34:58 +0000
treeherdermozilla-central@f76b576a9e28 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar
bugs715453
milestone12.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 715453 (part 2) - Ignore computedSize in nsMallocSizeOfFun. r=jlebar.
toolkit/components/aboutmemory/content/aboutMemory.css
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.css
+++ b/toolkit/components/aboutmemory/content/aboutMemory.css
@@ -30,16 +30,20 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+.accuracyWarning {
+  color: #f00;
+}
+
 .mrValue {
   font-weight: bold;
   color: #400;
 }
 
 .mrPerc {
 }
 
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -238,21 +238,18 @@ Reporter.prototype = {
   treeNameMatches: function(aTreeName) {
     // Nb: the '/' must be present, because we have a KIND_OTHER reporter
     // called "explicit" which is not part of the "explicit" tree.
     aTreeName += "/";
     return this._path.slice(0, aTreeName.length) === aTreeName;
   }
 };
 
-function getReportersByProcess()
+function getReportersByProcess(aMgr)
 {
-  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
   //   is a Reporter object.  After this point we never use the original memory
   //   reporter again.
   //
   // - Note that copying rOrig.amount (which calls a C++ function under the
   //   IDL covers) to r._amount for every reporter now means that the
   //   results as consistent as possible -- measurements are made all at
@@ -274,29 +271,29 @@ function getReportersByProcess()
       // legitimately.  Merge them.
       reporter.merge(r);
     } else {
       reporters[r._path] = r;
     }
   }
 
   // Process vanilla reporters first, then multi-reporters.
-  var e = mgr.enumerateReporters();
+  var e = aMgr.enumerateReporters();
   while (e.hasMoreElements()) {
     var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     try {
       addReporter(rOrig.process, rOrig.path, rOrig.kind, rOrig.units,
                   rOrig.amount, rOrig.description);
     }
     catch(e) {
       debug("An error occurred when collecting results from the memory reporter " +
             rOrig.path + ": " + e);
     }
   }
-  var e = mgr.enumerateMultiReporters();
+  var e = aMgr.enumerateMultiReporters();
   while (e.hasMoreElements()) {
     var mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
     try {
       mrOrig.collectReports(addReporter, null);
     }
     catch(e) {
       debug("An error occurred when collecting a multi-reporter's results: " + e);
     }
@@ -316,23 +313,31 @@ function update()
   content.parentNode.replaceChild(content.cloneNode(false), content);
   content = $("content");
 
   if (gVerbose)
     content.parentNode.classList.add('verbose');
   else
     content.parentNode.classList.add('non-verbose');
 
+  var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
+      getService(Ci.nsIMemoryReporterManager);
+
+  var text = "";
+
   // Generate output for one process at a time.  Always start with the
   // Main process.
-  var reportersByProcess = getReportersByProcess();
-  var text = genProcessText("Main", reportersByProcess["Main"]);
+  var reportersByProcess = getReportersByProcess(mgr);
+  var hasMozMallocUsableSize = mgr.hasMozMallocUsableSize;
+  text += genProcessText("Main", reportersByProcess["Main"],
+                         hasMozMallocUsableSize);
   for (var process in reportersByProcess) {
     if (process !== "Main") {
-      text += genProcessText(process, reportersByProcess[process]);
+      text += genProcessText(process, reportersByProcess[process],
+                             hasMozMallocUsableSize);
     }
   }
 
   // Memory-related actions.
   const GCDesc = "Do a global garbage collection.";
   const CCDesc = "Do a cycle collection.";
   const MPDesc = "Send three \"heap-minimize\" notifications in a " +
                  "row.  Each notification triggers a global garbage " +
@@ -516,18 +521,25 @@ function buildTree(aReporters, aTreeName
   // Set the description on the root node.
   t._description = kTreeDescriptions[t._name];
 
   return t;
 }
 
 /**
  * Do some work which only makes sense for the 'explicit' tree.
+ *
+ * @param aT
+ *        The tree.
+ * @param aReporters
+ *        Table of Reporters for this process, indexed by _path.
+ * @return A boolean indicating if "heap-allocated" is known for the process.
  */
-function fixUpExplicitTree(aT, aReporters) {
+function fixUpExplicitTree(aT, aReporters)
+{
   // Determine how many bytes are reported by heap 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");
@@ -540,17 +552,18 @@ function fixUpExplicitTree(aT, aReporter
     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) {
+  var hasKnownHeapAllocated = heapAllocatedBytes !== kUnknown;
+  if (hasKnownHeapAllocated) {
     heapUnclassifiedT._amount =
       heapAllocatedBytes - getKnownHeapUsedBytes(aT);
   } else {
     heapUnclassifiedT._amount = 0;
     heapUnclassifiedT._hasProblem = true;
   }
   // This kindToString() ensures the "(Heap)" prefix is set without having to
   // set the _kind property, which would mean that there is a corresponding
@@ -558,16 +571,18 @@ function fixUpExplicitTree(aT, aReporter
   heapUnclassifiedT._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).";
 
   aT._kids.push(heapUnclassifiedT);
   aT._amount += heapUnclassifiedT._amount;
+
+  return hasKnownHeapAllocated;
 }
 
 /**
  * Sort all kid nodes from largest to smallest and aggregate insignificant
  * nodes.
  *
  * @param aTotalBytes
  *        The size of the tree's root node.
@@ -612,32 +627,71 @@ function filterTree(aTotalBytes, aT)
       aT._kids[i0] = rSub;
       aT._kids.sort(TreeNode.compare);
       break;
     }
     filterTree(aTotalBytes, aT._kids[i]);
   }
 }
 
+function genWarningText(aHasKnownHeapAllocated, aHasMozMallocUsableSize) 
+{
+  var warningText = "";
+
+  if (!aHasKnownHeapAllocated && !aHasMozMallocUsableSize) {
+    warningText =
+      "<p class='accuracyWarning'>WARNING: the 'heap-allocated' memory " +
+      "reporter and the moz_malloc_usable_size() function do not work for " +
+      "this platform and/or configuration.  This means that " +
+      "'heap-unclassified' is zero and the 'explicit' tree shows " +
+      "much less memory than it should.</p>\n\n";
+
+  } else if (!aHasKnownHeapAllocated) {
+    warningText =
+      "<p class='accuracyWarning'>WARNING: the 'heap-allocated' memory " +
+      "reporter does not work for this platform and/or configuration. " +
+      "This means that 'heap-unclassified' is zero and the 'explicit' tree " +
+      "shows less memory than it should.</p>\n\n";
+
+  } else if (!aHasMozMallocUsableSize) {
+    warningText =
+      "<p class='accuracyWarning'>WARNING: the moz_malloc_usable_size() " +
+      "function does not work for this platform and/or configuration. " +
+      "This means that much of the heap-allocated memory is not measured " +
+      "by individual memory reporters and so will fall under " +
+      "'heap-unclassified'.</p>\n\n";
+  }
+  return warningText;
+}
+
 /**
  * Generates the text for a single process.
  *
  * @param aProcess
  *        The name of the process.
  * @param aReporters
  *        Table of Reporters for this process, indexed by _path.
+ * @param aHasMozMallocUsableSize
+ *        Boolean indicating if moz_malloc_usable_size works.
  * @return The generated text.
  */
-function genProcessText(aProcess, aReporters)
+function genProcessText(aProcess, aReporters, aHasMozMallocUsableSize)
 {
   var explicitTree = buildTree(aReporters, 'explicit');
-  fixUpExplicitTree(explicitTree, aReporters);
+  var hasKnownHeapAllocated = fixUpExplicitTree(explicitTree, aReporters);
   filterTree(explicitTree._amount, explicitTree);
   var explicitText = genTreeText(explicitTree, aProcess);
 
+  // Generate any warnings about inaccuracies due to platform limitations.
+  // The newlines give nice spacing if we cut+paste into a text buffer.
+  var warningText = "";
+  var accuracyTagText = "<p class='accuracyWarning'>";
+  var warningText =
+        genWarningText(hasKnownHeapAllocated, aHasMozMallocUsableSize);
+
   var mapTreeText = '';
   kMapTreePaths.forEach(function(t) {
     var tree = buildTree(aReporters, t);
 
     // |tree| will be null if we don't have any reporters for the given path.
     if (tree) {
       filterTree(tree._amount, tree);
       mapTreeText += genTreeText(tree, aProcess);
@@ -645,17 +699,17 @@ function genProcessText(aProcess, aRepor
   });
 
   // We have to call genOtherText after we process all the trees, because it
   // looks at all the reporters which aren't part of a tree.
   var otherText = genOtherText(aReporters, aProcess);
 
   // The newlines give nice spacing if we cut+paste into a text buffer.
   return "<h1>" + aProcess + " Process</h1>\n\n" +
-         explicitText + mapTreeText + otherText +
+         warningText + explicitText + mapTreeText + otherText +
          "<hr></hr>";
 }
 
 /**
  * Formats an int as a human-readable string.
  *
  * @param aN
  *        The integer to format.
@@ -825,19 +879,17 @@ function prepDesc(aStr)
 
 function genMrNameText(aKind, aDesc, aName, aHasProblem, aNMerged)
 {
   var text = "-- <span class='mrName hasDesc' title='" +
              kindToString(aKind) + prepDesc(aDesc) +
              "'>" + prepName(aName) + "</span>";
   if (aHasProblem) {
     const problemDesc =
-      "Warning: this memory reporter was unable to compute a useful value. " +
-      "The reported value is the sum of all entries below '" + aName + "', " +
-      "which is probably less than the true value.";
+      "Warning: this memory reporter was unable to compute a useful value. ";
     text += " <span class='mrStar' title=\"" + problemDesc + "\">[*]</span>";
   }
   if (aNMerged) {
     const dupDesc = "This value is the sum of " + aNMerged +
                     " memory reporters that all have the same path.";
     text += " <span class='mrStar' title=\"" + dupDesc + "\">[" + 
             aNMerged + "]</span>";
   }
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -245,16 +245,18 @@ 1,000.00 MB (100.0%) -- explicit\n\
 Other Measurements\n\
   666.00 MB -- danger<script>window.alert(1)</script>\n\
 1,000.00 MB -- heap-allocated\n\
   100.00 MB -- heap-unallocated\n\
   111.00 MB -- other1\n\
 \n\
 3rd Process\n\
 \n\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is zero and the 'explicit' tree shows less memory than it should.\n\
+\n\
 Explicit Allocations\n\
 777.00 MB (100.0%) -- explicit\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\
@@ -331,16 +333,18 @@ 1,048,576,000 B (100.0%) -- explicit\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\
+WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is zero and the 'explicit' tree shows less memory than it should.\n\
+\n\
 Explicit Allocations\n\
 814,743,552 B (100.0%) -- explicit\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\
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -227,17 +227,17 @@ interface nsIMemoryMultiReporter : nsISu
    * nsIMemoryReporterManager::explicit efficiently, which is important --
    * multi-reporters can special-case this operation so it's much faster
    * than getting all the reports, filtering out the unneeded ones, and
    * summing the remainder.
    */
   readonly attribute PRInt64 explicitNonHeap;
 };
 
-[scriptable, uuid(84ba9c85-3372-4423-b7ab-74708b9269a6)]
+[scriptable, uuid(4527b1d8-a81f-4af3-9623-80e4120392c7)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
   nsISimpleEnumerator enumerateReporters ();
 
   /*
@@ -288,16 +288,21 @@ interface nsIMemoryReporterManager : nsI
    * Get the total size of explicit memory allocations, both at the OS-level
    * (eg. via mmap, VirtualAlloc) and at the heap level (eg. via malloc,
    * calloc, operator new).  (Nb: it covers all heap allocations, but will
    * miss any OS-level ones not covered by memory reporters.)  This reporter
    * is special-cased because it's interesting, and is moderately difficult
    * to compute in JS.  -1 means unknown.
    */
   readonly attribute PRInt64 explicit;
+
+  /*
+   * This attribute indicates if moz_malloc_usable_size() works.
+   */
+  readonly attribute boolean hasMozMallocUsableSize;
 };
 
 %{C++
 
 /*
  * Note that this defaults 'process' to "", which is usually what's desired.
  */
 #define NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_classname, _path, _kind, _units, _amountFunction, _desc, _ts) \
@@ -335,53 +340,45 @@ nsresult NS_UnregisterMemoryMultiReporte
 #endif
 #include "dmd.h"
 #endif
 
 namespace mozilla {
 
 /*
  * Functions generated via this macro should be used by all traversal-based
- * memory reporters.
+ * memory reporters.  Such functions return moz_malloc_usable_size(ptr),
+ * which will be 0 on obscure platforms that don't implement
+ * malloc_usable_size() or an equivalent.
  *
- * - On platforms where moz_malloc_usable_size(ptr) returns 0 it just returns
- *   |computedSize|.  This happens on platforms where malloc_usable_size() or
- *   equivalent isn't available.
- *
- * - Otherwise, it returns |moz_malloc_usable_size(ptr)|.
+ * XXX: bug 715453 will remove the unused |computedSize| argument.
  *
  * You might be wondering why we have a macro that creates multiple functions
  * distinguished only by |name|, instead of a single MemoryReporterMallocSizeOf
  * function.  It's mostly to help with DMD integration, though it sometimes
  * also helps with debugging and temporary ad hoc profiling.  The |name| chosen
  * doesn't matter greatly, but it's best to make it similar to the path used by
  * the relevant memory reporter(s).
  */
 #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name)                        \
   size_t fn(const void *ptr, size_t computedSize)                             \
   {                                                                           \
       size_t usable = moz_malloc_usable_size((void*)ptr);                     \
-      if (!usable) {                                                          \
-          return computedSize;                                                \
-      }                                                                       \
       VALGRIND_DMD_REPORT(ptr, usable, name);                                 \
       return usable;                                                          \
   }
 
 /*
  * Like NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN, but the created function sends an
  * "unreport" message to DMD.
  */
 #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(fn)                           \
   size_t fn(const void *ptr, size_t computedSize)                             \
   {                                                                           \
       size_t usable = moz_malloc_usable_size((void*)ptr);                     \
-      if (!usable) {                                                          \
-          return computedSize;                                                \
-      }                                                                       \
       VALGRIND_DMD_UNREPORT(ptr);                                             \
       return usable;                                                          \
   }
 
 #ifdef MOZ_DMD
 
 /*
  * This runs all the memory reporters but does nothing with the results;  i.e.
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -812,16 +812,29 @@ nsMemoryReporterManager::GetExplicit(PRI
     NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2,
                  "The two measurements of 'explicit' memory usage don't match");
 #endif
 
     *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsMemoryReporterManager::GetHasMozMallocUsableSize(bool *aHas)
+{
+    void *p = malloc(16);
+    if (!p) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    size_t usable = moz_malloc_usable_size(p);
+    free(p);
+    *aHas = !!(usable > 0);
+    return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsACString& process,
                                    nsACString& path,
                                    PRInt32 kind,
                                    PRInt32 units,
                                    PRInt64 amount,
                                    nsACString& desc)