Bug 1223927 - Add resident-unique measurement to OS X. r=njn a=ritu
authorEric Rahm <erahm@mozilla.com>
Wed, 11 Nov 2015 14:07:30 -0800
changeset 305722 971d0fc7eef6033ec89efc62c71b256dbae87e24
parent 305721 4e62acda19b73dfd211ada6a8beef4762d632ac0
child 305723 4936d1c5c1dfe8e2d3d5d93cbe7383f199bda027
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn, ritu
bugs1223927
milestone44.0a2
Bug 1223927 - Add resident-unique measurement to OS X. r=njn a=ritu
xpcom/base/nsMemoryReporterManager.cpp
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -415,17 +415,20 @@ static nsresult
 ResidentFastDistinguishedAmount(int64_t* aN)
 {
   return ResidentDistinguishedAmount(aN);
 }
 
 #elif defined(XP_MACOSX)
 
 #include <mach/mach_init.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
 #include <mach/task.h>
+#include <sys/sysctl.h>
 
 static bool
 GetTaskBasicInfo(struct task_basic_info* aTi)
 {
   mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
   kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
                                (task_info_t)aTi, &count);
   return kr == KERN_SUCCESS;
@@ -480,16 +483,134 @@ ResidentFastDistinguishedAmount(int64_t*
 }
 
 static nsresult
 ResidentDistinguishedAmount(int64_t* aN)
 {
   return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
 }
 
+#define HAVE_RESIDENT_UNIQUE_REPORTER 1
+
+static bool
+InSharedRegion(mach_vm_address_t aAddr, cpu_type_t aType)
+{
+  mach_vm_address_t base;
+  mach_vm_address_t size;
+
+  switch (aType) {
+    case CPU_TYPE_ARM:
+      base = SHARED_REGION_BASE_ARM;
+      size = SHARED_REGION_SIZE_ARM;
+      break;
+    case CPU_TYPE_I386:
+      base = SHARED_REGION_BASE_I386;
+      size = SHARED_REGION_SIZE_I386;
+      break;
+    case CPU_TYPE_X86_64:
+      base = SHARED_REGION_BASE_X86_64;
+      size = SHARED_REGION_SIZE_X86_64;
+      break;
+    default:
+      return false;
+  }
+
+  return base <= aAddr && aAddr < (base + size);
+}
+
+static nsresult
+ResidentUniqueDistinguishedAmount(int64_t* aN)
+{
+  if (!aN) {
+    return NS_ERROR_FAILURE;
+  }
+
+  cpu_type_t cpu_type;
+  size_t len = sizeof(cpu_type);
+  if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Roughly based on libtop_update_vm_regions in
+  // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
+  size_t privatePages = 0;
+  mach_vm_size_t size = 0;
+  for (mach_vm_address_t addr = MACH_VM_MIN_ADDRESS; ; addr += size) {
+    vm_region_top_info_data_t info;
+    mach_msg_type_number_t infoCount = VM_REGION_TOP_INFO_COUNT;
+    mach_port_t objectName;
+
+    kern_return_t kr =
+        mach_vm_region(mach_task_self(), &addr, &size, VM_REGION_TOP_INFO,
+                       reinterpret_cast<vm_region_info_t>(&info),
+                       &infoCount, &objectName);
+    if (kr == KERN_INVALID_ADDRESS) {
+      // Done iterating VM regions.
+      break;
+    } else if (kr != KERN_SUCCESS) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (InSharedRegion(addr, cpu_type) && info.share_mode != SM_PRIVATE) {
+        continue;
+    }
+
+    switch (info.share_mode) {
+      case SM_LARGE_PAGE:
+        // NB: Large pages are not shareable and always resident.
+      case SM_PRIVATE:
+        privatePages += info.private_pages_resident;
+        privatePages += info.shared_pages_resident;
+        break;
+      case SM_COW:
+        privatePages += info.private_pages_resident;
+        if (info.ref_count == 1) {
+          // Treat copy-on-write pages as private if they only have one reference.
+          privatePages += info.shared_pages_resident;
+        }
+        break;
+      case SM_SHARED:
+      default:
+        break;
+    }
+  }
+
+  vm_size_t pageSize;
+  if (host_page_size(mach_host_self(), &pageSize) != KERN_SUCCESS) {
+    pageSize = PAGE_SIZE;
+  }
+
+  *aN = privatePages * pageSize;
+  return NS_OK;
+}
+
+class ResidentUniqueReporter final : public nsIMemoryReporter
+{
+  ~ResidentUniqueReporter() {}
+
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+                           nsISupports* aData, bool aAnonymize) override
+  {
+    int64_t amount = 0;
+    nsresult rv = ResidentUniqueDistinguishedAmount(&amount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return MOZ_COLLECT_REPORT(
+      "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
+"Memory mapped by the process that is present in physical memory and not "
+"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)
+
 #elif defined(XP_WIN)
 
 #include <windows.h>
 #include <psapi.h>
 #include <algorithm>
 
 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
 static nsresult