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 idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs715453
milestone12.0a1
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)