Bug 1194061 - Implement "system-heap-allocated" reporter for Windows. r=dmajor.
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 19 Aug 2015 20:35:17 -0700
changeset 291184 8daef66bd87d37ca3eaf3bb0c60d7736e0344a80
parent 291183 0e14511ed4a374b14b0a1e6fa1f0f7a97693f13c
child 291185 6c1d2577dcb305ca390917057343cafe404da7c5
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmajor
bugs1194061
milestone43.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 1194061 - Implement "system-heap-allocated" reporter for Windows. r=dmajor.
xpcom/base/nsMemoryReporterManager.cpp
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -172,32 +172,33 @@ public:
 "shared with any other processes.  This is also known as the process's unique "
 "set size (USS).  This is the amount of RAM we'd expect to be freed if we "
 "closed this process.");
   }
 };
 NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
 
 #define HAVE_SYSTEM_HEAP_REPORTER 1
-size_t
-SystemHeapSize()
+nsresult
+SystemHeapSize(int64_t* aSizeOut)
 {
     struct mallinfo info = mallinfo();
 
     // The documentation in the glibc man page makes it sound like |uordblks|
     // would suffice, but that only gets the small allocations that are put in
     // the brk heap. We need |hblkhd| as well to get the larger allocations
     // that are mmapped.
     //
     // The fields in |struct mallinfo| are all |int|, <sigh>, so it is
     // unreliable if memory usage gets high. However, the system heap size on
     // Linux should usually be zero (so long as jemalloc is enabled) so that
     // shouldn't be a problem. Nonetheless, cast the |int|s to |size_t| before
     // adding them to provide a small amount of extra overflow protection.
-    return size_t(info.hblkhd) + size_t(info.uordblks);
+    *aSizeOut = size_t(info.hblkhd) + size_t(info.uordblks);
+    return NS_OK;
 }
 
 #elif defined(__DragonFly__) || defined(__FreeBSD__) \
     || defined(__NetBSD__) || defined(__OpenBSD__) \
     || defined(__FreeBSD_kernel__)
 
 #include <sys/param.h>
 #include <sys/sysctl.h>
@@ -563,16 +564,67 @@ PrivateDistinguishedAmount(int64_t* aN)
                             (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
     return NS_ERROR_FAILURE;
   }
 
   *aN = pmcex.PrivateUsage;
   return NS_OK;
 }
 
+#define HAVE_SYSTEM_HEAP_REPORTER 1
+// Windows can have multiple separate heaps. During testing there were multiple
+// heaps present but the non-default ones had sizes no more than a few 10s of
+// KiBs. So we combine their sizes into a single measurement.
+nsresult
+SystemHeapSize(int64_t* aSizeOut)
+{
+  // Get the number of heaps.
+  DWORD nHeaps = GetProcessHeaps(0, nullptr);
+  NS_ENSURE_TRUE(nHeaps != 0, NS_ERROR_FAILURE);
+
+  // Get handles to all heaps, checking that the number of heaps hasn't
+  // changed in the meantime.
+  UniquePtr<HANDLE[]> heaps(new HANDLE[nHeaps]);
+  DWORD nHeaps2 = GetProcessHeaps(nHeaps, heaps.get());
+  NS_ENSURE_TRUE(nHeaps2 != 0 && nHeaps2 == nHeaps, NS_ERROR_FAILURE);
+
+  // Lock and iterate over each heap to get its size.
+  int64_t heapsSize = 0;
+  for (DWORD i = 0; i < nHeaps; i++) {
+    HANDLE heap = heaps[i];
+
+    NS_ENSURE_TRUE(HeapLock(heap), NS_ERROR_FAILURE);
+
+    int64_t heapSize = 0;
+    PROCESS_HEAP_ENTRY entry;
+    entry.lpData = nullptr;
+    while (HeapWalk(heap, &entry)) {
+      // We don't count entry.cbOverhead, because we just want to measure the
+      // space available to the program.
+      if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) {
+        heapSize += entry.cbData;
+      }
+    }
+
+    // Check this result only after unlocking the heap, so that we don't leave
+    // the heap locked if there was an error.
+    DWORD lastError = GetLastError();
+
+    // I have no idea how things would proceed if unlocking this heap failed...
+    NS_ENSURE_TRUE(HeapUnlock(heap), NS_ERROR_FAILURE);
+
+    NS_ENSURE_TRUE(lastError == ERROR_NO_MORE_ITEMS, NS_ERROR_FAILURE);
+
+    heapsSize += heapSize;
+  }
+
+  *aSizeOut = heapsSize;
+  return NS_OK;
+}
+
 class WindowsAddressSpaceReporter final : public nsIMemoryReporter
 {
   ~WindowsAddressSpaceReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
@@ -830,17 +882,19 @@ class SystemHeapReporter final : public 
   ~SystemHeapReporter() {}
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                            nsISupports* aData, bool aAnonymize) override
   {
-    int64_t amount = SystemHeapSize();
+    int64_t amount;
+    nsresult rv = SystemHeapSize(&amount);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     return MOZ_COLLECT_REPORT(
       "system-heap-allocated", KIND_OTHER, UNITS_BYTES, amount,
 "Memory used by the system allocator that is currently allocated to the "
 "application. This is distinct from the jemalloc heap that Firefox uses for "
 "most or all of its heap allocations. Ideally this number is zero, but "
 "on some platforms we cannot force every heap allocation through jemalloc.");
   }