xpcom/base/nsMemoryReporterManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/base/nsMemoryReporterManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1901 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsAtomTable.h"
    1.11 +#include "nsAutoPtr.h"
    1.12 +#include "nsCOMPtr.h"
    1.13 +#include "nsCOMArray.h"
    1.14 +#include "nsPrintfCString.h"
    1.15 +#include "nsServiceManagerUtils.h"
    1.16 +#include "nsMemoryReporterManager.h"
    1.17 +#include "nsITimer.h"
    1.18 +#include "nsThreadUtils.h"
    1.19 +#include "nsIDOMWindow.h"
    1.20 +#include "nsPIDOMWindow.h"
    1.21 +#include "nsIObserverService.h"
    1.22 +#include "nsIGlobalObject.h"
    1.23 +#include "nsIXPConnect.h"
    1.24 +#if defined(XP_UNIX) || defined(MOZ_DMD)
    1.25 +#include "nsMemoryInfoDumper.h"
    1.26 +#endif
    1.27 +#include "mozilla/Attributes.h"
    1.28 +#include "mozilla/PodOperations.h"
    1.29 +#include "mozilla/Services.h"
    1.30 +#include "mozilla/Telemetry.h"
    1.31 +#include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport
    1.32 +
    1.33 +#ifndef XP_WIN
    1.34 +#include <unistd.h>
    1.35 +#endif
    1.36 +
    1.37 +using namespace mozilla;
    1.38 +
    1.39 +#if defined(MOZ_MEMORY)
    1.40 +#  define HAVE_JEMALLOC_STATS 1
    1.41 +#  include "mozmemory.h"
    1.42 +#endif  // MOZ_MEMORY
    1.43 +
    1.44 +#if defined(XP_LINUX)
    1.45 +
    1.46 +static nsresult
    1.47 +GetProcSelfStatmField(int aField, int64_t* aN)
    1.48 +{
    1.49 +  // There are more than two fields, but we're only interested in the first
    1.50 +  // two.
    1.51 +  static const int MAX_FIELD = 2;
    1.52 +  size_t fields[MAX_FIELD];
    1.53 +  MOZ_ASSERT(aField < MAX_FIELD, "bad field number");
    1.54 +  FILE* f = fopen("/proc/self/statm", "r");
    1.55 +  if (f) {
    1.56 +    int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
    1.57 +    fclose(f);
    1.58 +    if (nread == MAX_FIELD) {
    1.59 +      *aN = fields[aField] * getpagesize();
    1.60 +      return NS_OK;
    1.61 +    }
    1.62 +  }
    1.63 +  return NS_ERROR_FAILURE;
    1.64 +}
    1.65 +
    1.66 +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
    1.67 +static nsresult
    1.68 +VsizeDistinguishedAmount(int64_t* aN)
    1.69 +{
    1.70 +  return GetProcSelfStatmField(0, aN);
    1.71 +}
    1.72 +
    1.73 +static nsresult
    1.74 +ResidentDistinguishedAmount(int64_t* aN)
    1.75 +{
    1.76 +  return GetProcSelfStatmField(1, aN);
    1.77 +}
    1.78 +
    1.79 +static nsresult
    1.80 +ResidentFastDistinguishedAmount(int64_t* aN)
    1.81 +{
    1.82 +  return ResidentDistinguishedAmount(aN);
    1.83 +}
    1.84 +
    1.85 +#define HAVE_RESIDENT_UNIQUE_REPORTER
    1.86 +class ResidentUniqueReporter MOZ_FINAL : public nsIMemoryReporter
    1.87 +{
    1.88 +public:
    1.89 +  NS_DECL_ISUPPORTS
    1.90 +
    1.91 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1.92 +                           nsISupports* aData)
    1.93 +  {
    1.94 +    // You might be tempted to calculate USS by subtracting the "shared"
    1.95 +    // value from the "resident" value in /proc/<pid>/statm. But at least
    1.96 +    // on Linux, statm's "shared" value actually counts pages backed by
    1.97 +    // files, which has little to do with whether the pages are actually
    1.98 +    // shared. /proc/self/smaps on the other hand appears to give us the
    1.99 +    // correct information.
   1.100 +
   1.101 +    FILE* f = fopen("/proc/self/smaps", "r");
   1.102 +    if (NS_WARN_IF(!f)) {
   1.103 +      return NS_ERROR_UNEXPECTED;
   1.104 +    }
   1.105 +
   1.106 +    int64_t amount = 0;
   1.107 +    char line[256];
   1.108 +    while (fgets(line, sizeof(line), f)) {
   1.109 +      long long val = 0;
   1.110 +      if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 ||
   1.111 +          sscanf(line, "Private_Clean: %lld kB", &val) == 1) {
   1.112 +        amount += val * 1024; // convert from kB to bytes
   1.113 +      }
   1.114 +    }
   1.115 +
   1.116 +    fclose(f);
   1.117 +
   1.118 +    return MOZ_COLLECT_REPORT(
   1.119 +      "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
   1.120 +"Memory mapped by the process that is present in physical memory and not "
   1.121 +"shared with any other processes.  This is also known as the process's unique "
   1.122 +"set size (USS).  This is the amount of RAM we'd expect to be freed if we "
   1.123 +"closed this process.");
   1.124 +  }
   1.125 +};
   1.126 +NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
   1.127 +
   1.128 +#elif defined(__DragonFly__) || defined(__FreeBSD__) \
   1.129 +    || defined(__NetBSD__) || defined(__OpenBSD__) \
   1.130 +    || defined(__FreeBSD_kernel__)
   1.131 +
   1.132 +#include <sys/param.h>
   1.133 +#include <sys/sysctl.h>
   1.134 +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
   1.135 +#include <sys/user.h>
   1.136 +#endif
   1.137 +
   1.138 +#include <unistd.h>
   1.139 +
   1.140 +#if defined(__NetBSD__)
   1.141 +#undef KERN_PROC
   1.142 +#define KERN_PROC KERN_PROC2
   1.143 +#define KINFO_PROC struct kinfo_proc2
   1.144 +#else
   1.145 +#define KINFO_PROC struct kinfo_proc
   1.146 +#endif
   1.147 +
   1.148 +#if defined(__DragonFly__)
   1.149 +#define KP_SIZE(kp) (kp.kp_vm_map_size)
   1.150 +#define KP_RSS(kp) (kp.kp_vm_rssize * getpagesize())
   1.151 +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
   1.152 +#define KP_SIZE(kp) (kp.ki_size)
   1.153 +#define KP_RSS(kp) (kp.ki_rssize * getpagesize())
   1.154 +#elif defined(__NetBSD__)
   1.155 +#define KP_SIZE(kp) (kp.p_vm_msize * getpagesize())
   1.156 +#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
   1.157 +#elif defined(__OpenBSD__)
   1.158 +#define KP_SIZE(kp) ((kp.p_vm_dsize + kp.p_vm_ssize                     \
   1.159 +                      + kp.p_vm_tsize) * getpagesize())
   1.160 +#define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
   1.161 +#endif
   1.162 +
   1.163 +static nsresult
   1.164 +GetKinfoProcSelf(KINFO_PROC* aProc)
   1.165 +{
   1.166 +  int mib[] = {
   1.167 +    CTL_KERN,
   1.168 +    KERN_PROC,
   1.169 +    KERN_PROC_PID,
   1.170 +    getpid(),
   1.171 +#if defined(__NetBSD__) || defined(__OpenBSD__)
   1.172 +    sizeof(KINFO_PROC),
   1.173 +    1,
   1.174 +#endif
   1.175 +  };
   1.176 +  u_int miblen = sizeof(mib) / sizeof(mib[0]);
   1.177 +  size_t size = sizeof(KINFO_PROC);
   1.178 +  if (sysctl(mib, miblen, aProc, &size, nullptr, 0)) {
   1.179 +    return NS_ERROR_FAILURE;
   1.180 +  }
   1.181 +  return NS_OK;
   1.182 +}
   1.183 +
   1.184 +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
   1.185 +static nsresult
   1.186 +VsizeDistinguishedAmount(int64_t* aN)
   1.187 +{
   1.188 +  KINFO_PROC proc;
   1.189 +  nsresult rv = GetKinfoProcSelf(&proc);
   1.190 +  if (NS_SUCCEEDED(rv)) {
   1.191 +    *aN = KP_SIZE(proc);
   1.192 +  }
   1.193 +  return rv;
   1.194 +}
   1.195 +
   1.196 +static nsresult
   1.197 +ResidentDistinguishedAmount(int64_t* aN)
   1.198 +{
   1.199 +  KINFO_PROC proc;
   1.200 +  nsresult rv = GetKinfoProcSelf(&proc);
   1.201 +  if (NS_SUCCEEDED(rv)) {
   1.202 +    *aN = KP_RSS(proc);
   1.203 +  }
   1.204 +  return rv;
   1.205 +}
   1.206 +
   1.207 +static nsresult
   1.208 +ResidentFastDistinguishedAmount(int64_t* aN)
   1.209 +{
   1.210 +  return ResidentDistinguishedAmount(aN);
   1.211 +}
   1.212 +
   1.213 +#ifdef __FreeBSD__
   1.214 +#include <libutil.h>
   1.215 +#include <algorithm>
   1.216 +
   1.217 +static nsresult
   1.218 +GetKinfoVmentrySelf(int64_t* aPrss, uint64_t* aMaxreg)
   1.219 +{
   1.220 +  int cnt;
   1.221 +  struct kinfo_vmentry *vmmap, *kve;
   1.222 +  if ((vmmap = kinfo_getvmmap(getpid(), &cnt)) == nullptr) {
   1.223 +    return NS_ERROR_FAILURE;
   1.224 +  }
   1.225 +  if (aPrss) {
   1.226 +    *aPrss = 0;
   1.227 +  }
   1.228 +  if (aMaxreg) {
   1.229 +    *aMaxreg = 0;
   1.230 +  }
   1.231 +
   1.232 +  for (int i = 0; i < cnt; i++) {
   1.233 +    kve = &vmmap[i];
   1.234 +    if (aPrss) {
   1.235 +      *aPrss += kve->kve_private_resident;
   1.236 +    }
   1.237 +    if (aMaxreg) {
   1.238 +      *aMaxreg = std::max(*aMaxreg, kve->kve_end - kve->kve_start);
   1.239 +    }
   1.240 +  }
   1.241 +
   1.242 +  free(vmmap);
   1.243 +  return NS_OK;
   1.244 +}
   1.245 +
   1.246 +#define HAVE_PRIVATE_REPORTER
   1.247 +static nsresult
   1.248 +PrivateDistinguishedAmount(int64_t* aN)
   1.249 +{
   1.250 +  int64_t priv;
   1.251 +  nsresult rv = GetKinfoVmentrySelf(&priv, nullptr);
   1.252 +  NS_ENSURE_SUCCESS(rv, rv);
   1.253 +  *aN = priv * getpagesize();
   1.254 +  return NS_OK;
   1.255 +}
   1.256 +
   1.257 +#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
   1.258 +static nsresult
   1.259 +VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
   1.260 +{
   1.261 +  uint64_t biggestRegion;
   1.262 +  nsresult rv = GetKinfoVmentrySelf(nullptr, &biggestRegion);
   1.263 +  if (NS_SUCCEEDED(rv)) {
   1.264 +    *aN = biggestRegion;
   1.265 +  }
   1.266 +  return NS_OK;
   1.267 +}
   1.268 +#endif // FreeBSD
   1.269 +
   1.270 +#elif defined(SOLARIS)
   1.271 +
   1.272 +#include <procfs.h>
   1.273 +#include <fcntl.h>
   1.274 +#include <unistd.h>
   1.275 +
   1.276 +static void XMappingIter(int64_t& aVsize, int64_t& aResident)
   1.277 +{
   1.278 +  aVsize = -1;
   1.279 +  aResident = -1;
   1.280 +  int mapfd = open("/proc/self/xmap", O_RDONLY);
   1.281 +  struct stat st;
   1.282 +  prxmap_t* prmapp = nullptr;
   1.283 +  if (mapfd >= 0) {
   1.284 +    if (!fstat(mapfd, &st)) {
   1.285 +      int nmap = st.st_size / sizeof(prxmap_t);
   1.286 +      while (1) {
   1.287 +        // stat(2) on /proc/<pid>/xmap returns an incorrect value,
   1.288 +        // prior to the release of Solaris 11.
   1.289 +        // Here is a workaround for it.
   1.290 +        nmap *= 2;
   1.291 +        prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
   1.292 +        if (!prmapp) {
   1.293 +          // out of memory
   1.294 +          break;
   1.295 +        }
   1.296 +        int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
   1.297 +        if (n < 0) {
   1.298 +          break;
   1.299 +        }
   1.300 +        if (nmap >= n / sizeof(prxmap_t)) {
   1.301 +          aVsize = 0;
   1.302 +          aResident = 0;
   1.303 +          for (int i = 0; i < n / sizeof(prxmap_t); i++) {
   1.304 +            aVsize += prmapp[i].pr_size;
   1.305 +            aResident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
   1.306 +          }
   1.307 +          break;
   1.308 +        }
   1.309 +        free(prmapp);
   1.310 +      }
   1.311 +      free(prmapp);
   1.312 +    }
   1.313 +    close(mapfd);
   1.314 +  }
   1.315 +}
   1.316 +
   1.317 +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
   1.318 +static nsresult
   1.319 +VsizeDistinguishedAmount(int64_t* aN)
   1.320 +{
   1.321 +  int64_t vsize, resident;
   1.322 +  XMappingIter(vsize, resident);
   1.323 +  if (vsize == -1) {
   1.324 +    return NS_ERROR_FAILURE;
   1.325 +  }
   1.326 +  *aN = vsize;
   1.327 +  return NS_OK;
   1.328 +}
   1.329 +
   1.330 +static nsresult
   1.331 +ResidentDistinguishedAmount(int64_t* aN)
   1.332 +{
   1.333 +  int64_t vsize, resident;
   1.334 +  XMappingIter(vsize, resident);
   1.335 +  if (resident == -1) {
   1.336 +    return NS_ERROR_FAILURE;
   1.337 +  }
   1.338 +  *aN = resident;
   1.339 +  return NS_OK;
   1.340 +}
   1.341 +
   1.342 +static nsresult
   1.343 +ResidentFastDistinguishedAmount(int64_t* aN)
   1.344 +{
   1.345 +  return ResidentDistinguishedAmount(aN);
   1.346 +}
   1.347 +
   1.348 +#elif defined(XP_MACOSX)
   1.349 +
   1.350 +#include <mach/mach_init.h>
   1.351 +#include <mach/task.h>
   1.352 +
   1.353 +static bool
   1.354 +GetTaskBasicInfo(struct task_basic_info* aTi)
   1.355 +{
   1.356 +  mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
   1.357 +  kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
   1.358 +                               (task_info_t)aTi, &count);
   1.359 +  return kr == KERN_SUCCESS;
   1.360 +}
   1.361 +
   1.362 +// The VSIZE figure on Mac includes huge amounts of shared memory and is always
   1.363 +// absurdly high, eg. 2GB+ even at start-up.  But both 'top' and 'ps' report
   1.364 +// it, so we might as well too.
   1.365 +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
   1.366 +static nsresult
   1.367 +VsizeDistinguishedAmount(int64_t* aN)
   1.368 +{
   1.369 +  task_basic_info ti;
   1.370 +  if (!GetTaskBasicInfo(&ti)) {
   1.371 +    return NS_ERROR_FAILURE;
   1.372 +  }
   1.373 +  *aN = ti.virtual_size;
   1.374 +  return NS_OK;
   1.375 +}
   1.376 +
   1.377 +// If we're using jemalloc on Mac, we need to instruct jemalloc to purge the
   1.378 +// pages it has madvise(MADV_FREE)'d before we read our RSS in order to get
   1.379 +// an accurate result.  The OS will take away MADV_FREE'd pages when there's
   1.380 +// memory pressure, so ideally, they shouldn't count against our RSS.
   1.381 +//
   1.382 +// Purging these pages can take a long time for some users (see bug 789975),
   1.383 +// so we provide the option to get the RSS without purging first.
   1.384 +static nsresult
   1.385 +ResidentDistinguishedAmountHelper(int64_t* aN, bool aDoPurge)
   1.386 +{
   1.387 +#ifdef HAVE_JEMALLOC_STATS
   1.388 +  if (aDoPurge) {
   1.389 +    Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
   1.390 +    jemalloc_purge_freed_pages();
   1.391 +  }
   1.392 +#endif
   1.393 +
   1.394 +  task_basic_info ti;
   1.395 +  if (!GetTaskBasicInfo(&ti)) {
   1.396 +    return NS_ERROR_FAILURE;
   1.397 +  }
   1.398 +  *aN = ti.resident_size;
   1.399 +  return NS_OK;
   1.400 +}
   1.401 +
   1.402 +static nsresult
   1.403 +ResidentFastDistinguishedAmount(int64_t* aN)
   1.404 +{
   1.405 +  return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false);
   1.406 +}
   1.407 +
   1.408 +static nsresult
   1.409 +ResidentDistinguishedAmount(int64_t* aN)
   1.410 +{
   1.411 +  return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
   1.412 +}
   1.413 +
   1.414 +#elif defined(XP_WIN)
   1.415 +
   1.416 +#include <windows.h>
   1.417 +#include <psapi.h>
   1.418 +#include <algorithm>
   1.419 +
   1.420 +#define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
   1.421 +static nsresult
   1.422 +VsizeDistinguishedAmount(int64_t* aN)
   1.423 +{
   1.424 +  MEMORYSTATUSEX s;
   1.425 +  s.dwLength = sizeof(s);
   1.426 +
   1.427 +  if (!GlobalMemoryStatusEx(&s)) {
   1.428 +    return NS_ERROR_FAILURE;
   1.429 +  }
   1.430 +
   1.431 +  *aN = s.ullTotalVirtual - s.ullAvailVirtual;
   1.432 +  return NS_OK;
   1.433 +}
   1.434 +
   1.435 +static nsresult
   1.436 +ResidentDistinguishedAmount(int64_t* aN)
   1.437 +{
   1.438 +  PROCESS_MEMORY_COUNTERS pmc;
   1.439 +  pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
   1.440 +
   1.441 +  if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
   1.442 +    return NS_ERROR_FAILURE;
   1.443 +  }
   1.444 +
   1.445 +  *aN = pmc.WorkingSetSize;
   1.446 +  return NS_OK;
   1.447 +}
   1.448 +
   1.449 +static nsresult
   1.450 +ResidentFastDistinguishedAmount(int64_t* aN)
   1.451 +{
   1.452 +  return ResidentDistinguishedAmount(aN);
   1.453 +}
   1.454 +
   1.455 +#define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
   1.456 +static nsresult
   1.457 +VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
   1.458 +{
   1.459 +  SIZE_T biggestRegion = 0;
   1.460 +  MEMORY_BASIC_INFORMATION vmemInfo = { 0 };
   1.461 +  for (size_t currentAddress = 0; ; ) {
   1.462 +    if (!VirtualQuery((LPCVOID)currentAddress, &vmemInfo, sizeof(vmemInfo))) {
   1.463 +      // Something went wrong, just return whatever we've got already.
   1.464 +      break;
   1.465 +    }
   1.466 +
   1.467 +    if (vmemInfo.State == MEM_FREE) {
   1.468 +      biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize);
   1.469 +    }
   1.470 +
   1.471 +    SIZE_T lastAddress = currentAddress;
   1.472 +    currentAddress += vmemInfo.RegionSize;
   1.473 +
   1.474 +    // If we overflow, we've examined all of the address space.
   1.475 +    if (currentAddress < lastAddress) {
   1.476 +      break;
   1.477 +    }
   1.478 +  }
   1.479 +
   1.480 +  *aN = biggestRegion;
   1.481 +  return NS_OK;
   1.482 +}
   1.483 +
   1.484 +#define HAVE_PRIVATE_REPORTER
   1.485 +static nsresult
   1.486 +PrivateDistinguishedAmount(int64_t* aN)
   1.487 +{
   1.488 +  PROCESS_MEMORY_COUNTERS_EX pmcex;
   1.489 +  pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
   1.490 +
   1.491 +  if (!GetProcessMemoryInfo(GetCurrentProcess(),
   1.492 +                            (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
   1.493 +    return NS_ERROR_FAILURE;
   1.494 +  }
   1.495 +
   1.496 +  *aN = pmcex.PrivateUsage;
   1.497 +  return NS_OK;
   1.498 +}
   1.499 +#endif  // XP_<PLATFORM>
   1.500 +
   1.501 +#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
   1.502 +class VsizeMaxContiguousReporter MOZ_FINAL : public nsIMemoryReporter
   1.503 +{
   1.504 +public:
   1.505 +  NS_DECL_ISUPPORTS
   1.506 +
   1.507 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.508 +                           nsISupports* aData)
   1.509 +  {
   1.510 +    int64_t amount;
   1.511 +    nsresult rv = VsizeMaxContiguousDistinguishedAmount(&amount);
   1.512 +    NS_ENSURE_SUCCESS(rv, rv);
   1.513 +    return MOZ_COLLECT_REPORT(
   1.514 +      "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
   1.515 +      "Size of the maximum contiguous block of available virtual "
   1.516 +      "memory.");
   1.517 +  }
   1.518 +};
   1.519 +NS_IMPL_ISUPPORTS(VsizeMaxContiguousReporter, nsIMemoryReporter)
   1.520 +#endif
   1.521 +
   1.522 +#ifdef HAVE_PRIVATE_REPORTER
   1.523 +class PrivateReporter MOZ_FINAL : public nsIMemoryReporter
   1.524 +{
   1.525 +public:
   1.526 +  NS_DECL_ISUPPORTS
   1.527 +
   1.528 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.529 +                           nsISupports* aData)
   1.530 +  {
   1.531 +    int64_t amount;
   1.532 +    nsresult rv = PrivateDistinguishedAmount(&amount);
   1.533 +    NS_ENSURE_SUCCESS(rv, rv);
   1.534 +    return MOZ_COLLECT_REPORT(
   1.535 +      "private", KIND_OTHER, UNITS_BYTES, amount,
   1.536 +"Memory that cannot be shared with other processes, including memory that is "
   1.537 +"committed and marked MEM_PRIVATE, data that is not mapped, and executable "
   1.538 +"pages that have been written to.");
   1.539 +  }
   1.540 +};
   1.541 +NS_IMPL_ISUPPORTS(PrivateReporter, nsIMemoryReporter)
   1.542 +#endif
   1.543 +
   1.544 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
   1.545 +class VsizeReporter MOZ_FINAL : public nsIMemoryReporter
   1.546 +{
   1.547 +public:
   1.548 +  NS_DECL_ISUPPORTS
   1.549 +
   1.550 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.551 +                           nsISupports* aData)
   1.552 +  {
   1.553 +    int64_t amount;
   1.554 +    nsresult rv = VsizeDistinguishedAmount(&amount);
   1.555 +    NS_ENSURE_SUCCESS(rv, rv);
   1.556 +
   1.557 +    return MOZ_COLLECT_REPORT(
   1.558 +      "vsize", KIND_OTHER, UNITS_BYTES, amount,
   1.559 +"Memory mapped by the process, including code and data segments, the heap, "
   1.560 +"thread stacks, memory explicitly mapped by the process via mmap and similar "
   1.561 +"operations, and memory shared with other processes. This is the vsize figure "
   1.562 +"as reported by 'top' and 'ps'.  This figure is of limited use on Mac, where "
   1.563 +"processes share huge amounts of memory with one another.  But even on other "
   1.564 +"operating systems, 'resident' is a much better measure of the memory "
   1.565 +"resources used by the process.");
   1.566 +  }
   1.567 +};
   1.568 +NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
   1.569 +
   1.570 +class ResidentReporter MOZ_FINAL : public nsIMemoryReporter
   1.571 +{
   1.572 +public:
   1.573 +  NS_DECL_ISUPPORTS
   1.574 +
   1.575 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.576 +                           nsISupports* aData)
   1.577 +  {
   1.578 +    int64_t amount;
   1.579 +    nsresult rv = ResidentDistinguishedAmount(&amount);
   1.580 +    NS_ENSURE_SUCCESS(rv, rv);
   1.581 +
   1.582 +    return MOZ_COLLECT_REPORT(
   1.583 +      "resident", KIND_OTHER, UNITS_BYTES, amount,
   1.584 +"Memory mapped by the process that is present in physical memory, also known "
   1.585 +"as the resident set size (RSS).  This is the best single figure to use when "
   1.586 +"considering the memory resources used by the process, but it depends both on "
   1.587 +"other processes being run and details of the OS kernel and so is best used "
   1.588 +"for comparing the memory usage of a single process at different points in "
   1.589 +"time.");
   1.590 +    }
   1.591 +};
   1.592 +NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter)
   1.593 +
   1.594 +#endif  // HAVE_VSIZE_AND_RESIDENT_REPORTERS
   1.595 +
   1.596 +#ifdef XP_UNIX
   1.597 +
   1.598 +#include <sys/resource.h>
   1.599 +
   1.600 +#define HAVE_PAGE_FAULT_REPORTERS 1
   1.601 +
   1.602 +class PageFaultsSoftReporter MOZ_FINAL : public nsIMemoryReporter
   1.603 +{
   1.604 +public:
   1.605 +  NS_DECL_ISUPPORTS
   1.606 +
   1.607 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.608 +                           nsISupports* aData)
   1.609 +  {
   1.610 +    struct rusage usage;
   1.611 +    int err = getrusage(RUSAGE_SELF, &usage);
   1.612 +    if (err != 0) {
   1.613 +      return NS_ERROR_FAILURE;
   1.614 +    }
   1.615 +    int64_t amount = usage.ru_minflt;
   1.616 +
   1.617 +    return MOZ_COLLECT_REPORT(
   1.618 +      "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
   1.619 +"The number of soft page faults (also known as 'minor page faults') that "
   1.620 +"have occurred since the process started.  A soft page fault occurs when the "
   1.621 +"process tries to access a page which is present in physical memory but is "
   1.622 +"not mapped into the process's address space.  For instance, a process might "
   1.623 +"observe soft page faults when it loads a shared library which is already "
   1.624 +"present in physical memory. A process may experience many thousands of soft "
   1.625 +"page faults even when the machine has plenty of available physical memory, "
   1.626 +"and because the OS services a soft page fault without accessing the disk, "
   1.627 +"they impact performance much less than hard page faults.");
   1.628 +  }
   1.629 +};
   1.630 +NS_IMPL_ISUPPORTS(PageFaultsSoftReporter, nsIMemoryReporter)
   1.631 +
   1.632 +static nsresult
   1.633 +PageFaultsHardDistinguishedAmount(int64_t* aAmount)
   1.634 +{
   1.635 +  struct rusage usage;
   1.636 +  int err = getrusage(RUSAGE_SELF, &usage);
   1.637 +  if (err != 0) {
   1.638 +    return NS_ERROR_FAILURE;
   1.639 +  }
   1.640 +  *aAmount = usage.ru_majflt;
   1.641 +  return NS_OK;
   1.642 +}
   1.643 +
   1.644 +class PageFaultsHardReporter MOZ_FINAL : public nsIMemoryReporter
   1.645 +{
   1.646 +public:
   1.647 +  NS_DECL_ISUPPORTS
   1.648 +
   1.649 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.650 +                           nsISupports* aData)
   1.651 +  {
   1.652 +    int64_t amount = 0;
   1.653 +    nsresult rv = PageFaultsHardDistinguishedAmount(&amount);
   1.654 +    NS_ENSURE_SUCCESS(rv, rv);
   1.655 +
   1.656 +    return MOZ_COLLECT_REPORT(
   1.657 +      "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
   1.658 +"The number of hard page faults (also known as 'major page faults') that have "
   1.659 +"occurred since the process started.  A hard page fault occurs when a process "
   1.660 +"tries to access a page which is not present in physical memory. The "
   1.661 +"operating system must access the disk in order to fulfill a hard page fault. "
   1.662 +"When memory is plentiful, you should see very few hard page faults. But if "
   1.663 +"the process tries to use more memory than your machine has available, you "
   1.664 +"may see many thousands of hard page faults. Because accessing the disk is up "
   1.665 +"to a million times slower than accessing RAM, the program may run very "
   1.666 +"slowly when it is experiencing more than 100 or so hard page faults a "
   1.667 +"second.");
   1.668 +  }
   1.669 +};
   1.670 +NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter)
   1.671 +
   1.672 +#endif  // HAVE_PAGE_FAULT_REPORTERS
   1.673 +
   1.674 +/**
   1.675 + ** memory reporter implementation for jemalloc and OSX malloc,
   1.676 + ** to obtain info on total memory in use (that we know about,
   1.677 + ** at least -- on OSX, there are sometimes other zones in use).
   1.678 + **/
   1.679 +
   1.680 +#ifdef HAVE_JEMALLOC_STATS
   1.681 +
   1.682 +// This has UNITS_PERCENTAGE, so it is multiplied by 100.
   1.683 +static int64_t
   1.684 +HeapOverheadRatio(jemalloc_stats_t* aStats)
   1.685 +{
   1.686 +  return (int64_t) 10000 *
   1.687 +    (aStats->waste + aStats->bookkeeping + aStats->page_cache) /
   1.688 +    ((double)aStats->allocated);
   1.689 +}
   1.690 +
   1.691 +class JemallocHeapReporter MOZ_FINAL : public nsIMemoryReporter
   1.692 +{
   1.693 +public:
   1.694 +  NS_DECL_ISUPPORTS
   1.695 +
   1.696 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.697 +                           nsISupports* aData)
   1.698 +  {
   1.699 +    jemalloc_stats_t stats;
   1.700 +    jemalloc_stats(&stats);
   1.701 +
   1.702 +    nsresult rv;
   1.703 +
   1.704 +    rv = MOZ_COLLECT_REPORT(
   1.705 +      "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
   1.706 +"Memory mapped by the heap allocator that is currently allocated to the "
   1.707 +"application.  This may exceed the amount of memory requested by the "
   1.708 +"application because the allocator regularly rounds up request sizes. (The "
   1.709 +"exact amount requested is not recorded.)");
   1.710 +    NS_ENSURE_SUCCESS(rv, rv);
   1.711 +
   1.712 +    // We mark this and the other heap-overhead reporters as KIND_NONHEAP
   1.713 +    // because KIND_HEAP memory means "counted in heap-allocated", which
   1.714 +    // this is not.
   1.715 +    rv = MOZ_COLLECT_REPORT(
   1.716 +      "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
   1.717 +      stats.waste,
   1.718 +"Committed bytes which do not correspond to an active allocation and which the "
   1.719 +"allocator is not intentionally keeping alive (i.e., not 'heap-bookkeeping' or "
   1.720 +"'heap-page-cache').  Although the allocator will waste some space under any "
   1.721 +"circumstances, a large value here may indicate that the heap is highly "
   1.722 +"fragmented, or that allocator is performing poorly for some other reason.");
   1.723 +    NS_ENSURE_SUCCESS(rv, rv);
   1.724 +
   1.725 +    rv = MOZ_COLLECT_REPORT(
   1.726 +      "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
   1.727 +      stats.bookkeeping,
   1.728 +"Committed bytes which the heap allocator uses for internal data structures.");
   1.729 +    NS_ENSURE_SUCCESS(rv, rv);
   1.730 +
   1.731 +    rv = MOZ_COLLECT_REPORT(
   1.732 +      "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES,
   1.733 +      stats.page_cache,
   1.734 +"Memory which the allocator could return to the operating system, but hasn't. "
   1.735 +"The allocator keeps this memory around as an optimization, so it doesn't "
   1.736 +"have to ask the OS the next time it needs to fulfill a request. This value "
   1.737 +"is typically not larger than a few megabytes.");
   1.738 +    NS_ENSURE_SUCCESS(rv, rv);
   1.739 +
   1.740 +    rv = MOZ_COLLECT_REPORT(
   1.741 +      "heap-committed", KIND_OTHER, UNITS_BYTES,
   1.742 +      stats.allocated + stats.waste + stats.bookkeeping + stats.page_cache,
   1.743 +"Memory mapped by the heap allocator that is committed, i.e. in physical "
   1.744 +"memory or paged to disk.  This value corresponds to 'heap-allocated' + "
   1.745 +"'heap-waste' + 'heap-bookkeeping' + 'heap-page-cache', but because "
   1.746 +"these values are read at different times, the result probably won't match "
   1.747 +"exactly.");
   1.748 +    NS_ENSURE_SUCCESS(rv, rv);
   1.749 +
   1.750 +    rv = MOZ_COLLECT_REPORT(
   1.751 +      "heap-overhead-ratio", KIND_OTHER, UNITS_PERCENTAGE,
   1.752 +      HeapOverheadRatio(&stats),
   1.753 +"Ratio of committed, unused bytes to allocated bytes; i.e., "
   1.754 +"'heap-overhead' / 'heap-allocated'.  This measures the overhead of "
   1.755 +"the heap allocator relative to amount of memory allocated.");
   1.756 +    NS_ENSURE_SUCCESS(rv, rv);
   1.757 +
   1.758 +    return NS_OK;
   1.759 +  }
   1.760 +};
   1.761 +NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
   1.762 +
   1.763 +#endif  // HAVE_JEMALLOC_STATS
   1.764 +
   1.765 +// Why is this here?  At first glance, you'd think it could be defined and
   1.766 +// registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
   1.767 +// However, the obvious time to register it is when the table is initialized,
   1.768 +// and that happens before XPCOM components are initialized, which means the
   1.769 +// RegisterStrongMemoryReporter call fails.  So instead we do it here.
   1.770 +class AtomTablesReporter MOZ_FINAL : public nsIMemoryReporter
   1.771 +{
   1.772 +  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
   1.773 +
   1.774 +public:
   1.775 +  NS_DECL_ISUPPORTS
   1.776 +
   1.777 +  NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.778 +                           nsISupports* aData)
   1.779 +  {
   1.780 +    return MOZ_COLLECT_REPORT(
   1.781 +      "explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
   1.782 +      NS_SizeOfAtomTablesIncludingThis(MallocSizeOf),
   1.783 +      "Memory used by the dynamic and static atoms tables.");
   1.784 +  }
   1.785 +};
   1.786 +NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
   1.787 +
   1.788 +#ifdef MOZ_DMD
   1.789 +
   1.790 +namespace mozilla {
   1.791 +namespace dmd {
   1.792 +
   1.793 +class DMDReporter MOZ_FINAL : public nsIMemoryReporter
   1.794 +{
   1.795 +public:
   1.796 +  NS_DECL_ISUPPORTS
   1.797 +
   1.798 +  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1.799 +                            nsISupports* aData)
   1.800 +  {
   1.801 +    dmd::Sizes sizes;
   1.802 +    dmd::SizeOf(&sizes);
   1.803 +
   1.804 +#define REPORT(_path, _amount, _desc)                                         \
   1.805 +    do {                                                                      \
   1.806 +      nsresult rv;                                                            \
   1.807 +      rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
   1.808 +                                   KIND_HEAP, UNITS_BYTES, _amount,           \
   1.809 +                                   NS_LITERAL_CSTRING(_desc), aData);         \
   1.810 +      if (NS_WARN_IF(NS_FAILED(rv))) {                                        \
   1.811 +        return rv;                                                            \
   1.812 +      }                                                                       \
   1.813 +    } while (0)
   1.814 +
   1.815 +    REPORT("explicit/dmd/stack-traces/used",
   1.816 +           sizes.mStackTracesUsed,
   1.817 +           "Memory used by stack traces which correspond to at least "
   1.818 +           "one heap block DMD is tracking.");
   1.819 +
   1.820 +    REPORT("explicit/dmd/stack-traces/unused",
   1.821 +           sizes.mStackTracesUnused,
   1.822 +           "Memory used by stack traces which don't correspond to any heap "
   1.823 +           "blocks DMD is currently tracking.");
   1.824 +
   1.825 +    REPORT("explicit/dmd/stack-traces/table",
   1.826 +           sizes.mStackTraceTable,
   1.827 +           "Memory used by DMD's stack trace table.");
   1.828 +
   1.829 +    REPORT("explicit/dmd/block-table",
   1.830 +           sizes.mBlockTable,
   1.831 +           "Memory used by DMD's live block table.");
   1.832 +
   1.833 +#undef REPORT
   1.834 +
   1.835 +    return NS_OK;
   1.836 +  }
   1.837 +};
   1.838 +NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter)
   1.839 +
   1.840 +} // namespace dmd
   1.841 +} // namespace mozilla
   1.842 +
   1.843 +#endif  // MOZ_DMD
   1.844 +
   1.845 +/**
   1.846 + ** nsMemoryReporterManager implementation
   1.847 + **/
   1.848 +
   1.849 +NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager)
   1.850 +
   1.851 +NS_IMETHODIMP
   1.852 +nsMemoryReporterManager::Init()
   1.853 +{
   1.854 +#if defined(HAVE_JEMALLOC_STATS) && defined(XP_LINUX)
   1.855 +  if (!jemalloc_stats) {
   1.856 +    return NS_ERROR_FAILURE;
   1.857 +  }
   1.858 +#endif
   1.859 +
   1.860 +#ifdef HAVE_JEMALLOC_STATS
   1.861 +  RegisterStrongReporter(new JemallocHeapReporter());
   1.862 +#endif
   1.863 +
   1.864 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
   1.865 +  RegisterStrongReporter(new VsizeReporter());
   1.866 +  RegisterStrongReporter(new ResidentReporter());
   1.867 +#endif
   1.868 +
   1.869 +#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
   1.870 +  RegisterStrongReporter(new VsizeMaxContiguousReporter());
   1.871 +#endif
   1.872 +
   1.873 +#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
   1.874 +  RegisterStrongReporter(new ResidentUniqueReporter());
   1.875 +#endif
   1.876 +
   1.877 +#ifdef HAVE_PAGE_FAULT_REPORTERS
   1.878 +  RegisterStrongReporter(new PageFaultsSoftReporter());
   1.879 +  RegisterStrongReporter(new PageFaultsHardReporter());
   1.880 +#endif
   1.881 +
   1.882 +#ifdef HAVE_PRIVATE_REPORTER
   1.883 +  RegisterStrongReporter(new PrivateReporter());
   1.884 +#endif
   1.885 +
   1.886 +  RegisterStrongReporter(new AtomTablesReporter());
   1.887 +
   1.888 +#ifdef MOZ_DMD
   1.889 +  RegisterStrongReporter(new mozilla::dmd::DMDReporter());
   1.890 +#endif
   1.891 +
   1.892 +#ifdef XP_UNIX
   1.893 +  nsMemoryInfoDumper::Initialize();
   1.894 +#endif
   1.895 +
   1.896 +  return NS_OK;
   1.897 +}
   1.898 +
   1.899 +nsMemoryReporterManager::nsMemoryReporterManager()
   1.900 +  : mMutex("nsMemoryReporterManager::mMutex")
   1.901 +  , mIsRegistrationBlocked(false)
   1.902 +  , mStrongReporters(new StrongReportersTable())
   1.903 +  , mWeakReporters(new WeakReportersTable())
   1.904 +  , mSavedStrongReporters(nullptr)
   1.905 +  , mSavedWeakReporters(nullptr)
   1.906 +  , mNumChildProcesses(0)
   1.907 +  , mNextGeneration(1)
   1.908 +  , mGetReportsState(nullptr)
   1.909 +{
   1.910 +}
   1.911 +
   1.912 +nsMemoryReporterManager::~nsMemoryReporterManager()
   1.913 +{
   1.914 +  delete mStrongReporters;
   1.915 +  delete mWeakReporters;
   1.916 +  NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
   1.917 +  NS_ASSERTION(!mSavedWeakReporters, "failed to restore weak reporters");
   1.918 +}
   1.919 +
   1.920 +//#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
   1.921 +
   1.922 +#ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
   1.923 +#define MEMORY_REPORTING_LOG(format, ...) \
   1.924 +  fprintf(stderr, "++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
   1.925 +#else
   1.926 +#define MEMORY_REPORTING_LOG(...)
   1.927 +#endif
   1.928 +
   1.929 +void
   1.930 +nsMemoryReporterManager::IncrementNumChildProcesses()
   1.931 +{
   1.932 +  if (!NS_IsMainThread()) {
   1.933 +    MOZ_CRASH();
   1.934 +  }
   1.935 +  mNumChildProcesses++;
   1.936 +  MEMORY_REPORTING_LOG("IncrementNumChildProcesses --> %d\n",
   1.937 +                       mNumChildProcesses);
   1.938 +}
   1.939 +
   1.940 +void
   1.941 +nsMemoryReporterManager::DecrementNumChildProcesses()
   1.942 +{
   1.943 +  if (!NS_IsMainThread()) {
   1.944 +    MOZ_CRASH();
   1.945 +  }
   1.946 +  MOZ_ASSERT(mNumChildProcesses > 0);
   1.947 +  mNumChildProcesses--;
   1.948 +  MEMORY_REPORTING_LOG("DecrementNumChildProcesses --> %d\n",
   1.949 +                       mNumChildProcesses);
   1.950 +}
   1.951 +
   1.952 +NS_IMETHODIMP
   1.953 +nsMemoryReporterManager::GetReports(
   1.954 +  nsIHandleReportCallback* aHandleReport,
   1.955 +  nsISupports* aHandleReportData,
   1.956 +  nsIFinishReportingCallback* aFinishReporting,
   1.957 +  nsISupports* aFinishReportingData)
   1.958 +{
   1.959 +  return GetReportsExtended(aHandleReport, aHandleReportData,
   1.960 +                            aFinishReporting, aFinishReportingData,
   1.961 +                            /* minimize = */ false,
   1.962 +                            /* DMDident = */ nsString());
   1.963 +}
   1.964 +
   1.965 +NS_IMETHODIMP
   1.966 +nsMemoryReporterManager::GetReportsExtended(
   1.967 +  nsIHandleReportCallback* aHandleReport,
   1.968 +  nsISupports* aHandleReportData,
   1.969 +  nsIFinishReportingCallback* aFinishReporting,
   1.970 +  nsISupports* aFinishReportingData,
   1.971 +  bool aMinimize,
   1.972 +  const nsAString& aDMDDumpIdent)
   1.973 +{
   1.974 +  nsresult rv;
   1.975 +
   1.976 +  // Memory reporters are not necessarily threadsafe, so this function must
   1.977 +  // be called from the main thread.
   1.978 +  if (!NS_IsMainThread()) {
   1.979 +    MOZ_CRASH();
   1.980 +  }
   1.981 +
   1.982 +  uint32_t generation = mNextGeneration++;
   1.983 +
   1.984 +  if (mGetReportsState) {
   1.985 +    // A request is in flight.  Don't start another one.  And don't report
   1.986 +    // an error;  just ignore it, and let the in-flight request finish.
   1.987 +    MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
   1.988 +                         generation, mGetReportsState->mGeneration);
   1.989 +    return NS_OK;
   1.990 +  }
   1.991 +
   1.992 +  MEMORY_REPORTING_LOG("GetReports (gen=%u, %d child(ren) present)\n",
   1.993 +                       generation, mNumChildProcesses);
   1.994 +
   1.995 +  if (mNumChildProcesses > 0) {
   1.996 +    // Request memory reports from child processes.  We do this *before*
   1.997 +    // collecting reports for this process so each process can collect
   1.998 +    // reports in parallel.
   1.999 +    nsCOMPtr<nsIObserverService> obs =
  1.1000 +      do_GetService("@mozilla.org/observer-service;1");
  1.1001 +    NS_ENSURE_STATE(obs);
  1.1002 +
  1.1003 +    nsPrintfCString genStr("generation=%x minimize=%d DMDident=",
  1.1004 +                           generation, aMinimize ? 1 : 0);
  1.1005 +    nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
  1.1006 +    msg += aDMDDumpIdent;
  1.1007 +
  1.1008 +    obs->NotifyObservers(nullptr, "child-memory-reporter-request",
  1.1009 +                         msg.get());
  1.1010 +
  1.1011 +    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1.1012 +    NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
  1.1013 +    rv = timer->InitWithFuncCallback(TimeoutCallback,
  1.1014 +                                     this, kTimeoutLengthMS,
  1.1015 +                                     nsITimer::TYPE_ONE_SHOT);
  1.1016 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1017 +
  1.1018 +    mGetReportsState = new GetReportsState(generation,
  1.1019 +                                           timer,
  1.1020 +                                           mNumChildProcesses,
  1.1021 +                                           aHandleReport,
  1.1022 +                                           aHandleReportData,
  1.1023 +                                           aFinishReporting,
  1.1024 +                                           aFinishReportingData,
  1.1025 +                                           aDMDDumpIdent);
  1.1026 +  } else {
  1.1027 +    mGetReportsState = new GetReportsState(generation,
  1.1028 +                                           nullptr,
  1.1029 +                                           /* mNumChildProcesses = */ 0,
  1.1030 +                                           aHandleReport,
  1.1031 +                                           aHandleReportData,
  1.1032 +                                           aFinishReporting,
  1.1033 +                                           aFinishReportingData,
  1.1034 +                                           aDMDDumpIdent);
  1.1035 +  }
  1.1036 +
  1.1037 +  if (aMinimize) {
  1.1038 +    rv = MinimizeMemoryUsage(NS_NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports));
  1.1039 +  } else {
  1.1040 +    rv = StartGettingReports();
  1.1041 +  }
  1.1042 +  return rv;
  1.1043 +}
  1.1044 +
  1.1045 +nsresult
  1.1046 +nsMemoryReporterManager::StartGettingReports()
  1.1047 +{
  1.1048 +  GetReportsState *s = mGetReportsState;
  1.1049 +
  1.1050 +  // Get reports for this process.
  1.1051 +  GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
  1.1052 +                                   s->mDMDDumpIdent);
  1.1053 +  s->mParentDone = true;
  1.1054 +
  1.1055 +  // If there are no remaining child processes, we can finish up immediately.
  1.1056 +  return (s->mNumChildProcessesCompleted >= s->mNumChildProcesses)
  1.1057 +    ? FinishReporting()
  1.1058 +    : NS_OK;
  1.1059 +}
  1.1060 +
  1.1061 +typedef nsCOMArray<nsIMemoryReporter> MemoryReporterArray;
  1.1062 +
  1.1063 +static PLDHashOperator
  1.1064 +StrongEnumerator(nsRefPtrHashKey<nsIMemoryReporter>* aElem, void* aData)
  1.1065 +{
  1.1066 +  MemoryReporterArray *allReporters = static_cast<MemoryReporterArray*>(aData);
  1.1067 +  allReporters->AppendElement(aElem->GetKey());
  1.1068 +  return PL_DHASH_NEXT;
  1.1069 +}
  1.1070 +
  1.1071 +static PLDHashOperator
  1.1072 +WeakEnumerator(nsPtrHashKey<nsIMemoryReporter>* aElem, void* aData)
  1.1073 +{
  1.1074 +  MemoryReporterArray *allReporters = static_cast<MemoryReporterArray*>(aData);
  1.1075 +  allReporters->AppendElement(aElem->GetKey());
  1.1076 +  return PL_DHASH_NEXT;
  1.1077 +}
  1.1078 +
  1.1079 +NS_IMETHODIMP
  1.1080 +nsMemoryReporterManager::GetReportsForThisProcess(
  1.1081 +  nsIHandleReportCallback* aHandleReport,
  1.1082 +  nsISupports* aHandleReportData)
  1.1083 +{
  1.1084 +  return GetReportsForThisProcessExtended(aHandleReport,
  1.1085 +                                          aHandleReportData,
  1.1086 +                                          nsString());
  1.1087 +}
  1.1088 +
  1.1089 +NS_IMETHODIMP
  1.1090 +nsMemoryReporterManager::GetReportsForThisProcessExtended(
  1.1091 +  nsIHandleReportCallback* aHandleReport,
  1.1092 +  nsISupports* aHandleReportData,
  1.1093 +  const nsAString& aDMDDumpIdent)
  1.1094 +{
  1.1095 +  // Memory reporters are not necessarily threadsafe, so this function must
  1.1096 +  // be called from the main thread.
  1.1097 +  if (!NS_IsMainThread()) {
  1.1098 +    MOZ_CRASH();
  1.1099 +  }
  1.1100 +
  1.1101 +#ifdef MOZ_DMD
  1.1102 +  if (!aDMDDumpIdent.IsEmpty()) {
  1.1103 +    // Clear DMD's reportedness state before running the memory
  1.1104 +    // reporters, to avoid spurious twice-reported warnings.
  1.1105 +    dmd::ClearReports();
  1.1106 +  }
  1.1107 +#endif
  1.1108 +
  1.1109 +  MemoryReporterArray allReporters;
  1.1110 +  {
  1.1111 +    mozilla::MutexAutoLock autoLock(mMutex);
  1.1112 +    mStrongReporters->EnumerateEntries(StrongEnumerator, &allReporters);
  1.1113 +    mWeakReporters->EnumerateEntries(WeakEnumerator, &allReporters);
  1.1114 +  }
  1.1115 +  for (uint32_t i = 0; i < allReporters.Length(); i++) {
  1.1116 +    allReporters[i]->CollectReports(aHandleReport, aHandleReportData);
  1.1117 +  }
  1.1118 +
  1.1119 +#ifdef MOZ_DMD
  1.1120 +  if (!aDMDDumpIdent.IsEmpty()) {
  1.1121 +    return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
  1.1122 +  }
  1.1123 +#endif
  1.1124 +
  1.1125 +  return NS_OK;
  1.1126 +}
  1.1127 +
  1.1128 +// This function has no return value.  If something goes wrong, there's no
  1.1129 +// clear place to report the problem to, but that's ok -- we will end up
  1.1130 +// hitting the timeout and executing TimeoutCallback().
  1.1131 +void
  1.1132 +nsMemoryReporterManager::HandleChildReports(
  1.1133 +  const uint32_t& aGeneration,
  1.1134 +  const InfallibleTArray<dom::MemoryReport>& aChildReports)
  1.1135 +{
  1.1136 +  // Memory reporting only happens on the main thread.
  1.1137 +  if (!NS_IsMainThread()) {
  1.1138 +    MOZ_CRASH();
  1.1139 +  }
  1.1140 +
  1.1141 +  GetReportsState* s = mGetReportsState;
  1.1142 +
  1.1143 +  if (!s) {
  1.1144 +    // If we reach here, either:
  1.1145 +    //
  1.1146 +    // - A child process reported back too late, and no subsequent request
  1.1147 +    //   is in flight.
  1.1148 +    //
  1.1149 +    // - (Unlikely) A "child-memory-reporter-request" notification was
  1.1150 +    //   triggered from somewhere other than GetReports(), causing child
  1.1151 +    //   processes to report back when the nsMemoryReporterManager wasn't
  1.1152 +    //   expecting it.
  1.1153 +    //
  1.1154 +    // Either way, there's nothing to be done.  Just ignore it.
  1.1155 +    MEMORY_REPORTING_LOG(
  1.1156 +      "HandleChildReports: no request in flight (aGen=%u)\n",
  1.1157 +      aGeneration);
  1.1158 +    return;
  1.1159 +  }
  1.1160 +
  1.1161 +  if (aGeneration != s->mGeneration) {
  1.1162 +    // If we reach here, a child process must have reported back, too late,
  1.1163 +    // while a subsequent (higher-numbered) request is in flight.  Again,
  1.1164 +    // ignore it.
  1.1165 +    MOZ_ASSERT(aGeneration < s->mGeneration);
  1.1166 +    MEMORY_REPORTING_LOG(
  1.1167 +      "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
  1.1168 +      aGeneration, s->mGeneration);
  1.1169 +    return;
  1.1170 +  }
  1.1171 +
  1.1172 +  // Process the reports from the child process.
  1.1173 +  for (uint32_t i = 0; i < aChildReports.Length(); i++) {
  1.1174 +    const dom::MemoryReport& r = aChildReports[i];
  1.1175 +
  1.1176 +    // Child reports should have a non-empty process.
  1.1177 +    MOZ_ASSERT(!r.process().IsEmpty());
  1.1178 +
  1.1179 +    // If the call fails, ignore and continue.
  1.1180 +    s->mHandleReport->Callback(r.process(), r.path(), r.kind(),
  1.1181 +                               r.units(), r.amount(), r.desc(),
  1.1182 +                               s->mHandleReportData);
  1.1183 +  }
  1.1184 +
  1.1185 +  // If all the child processes have reported, we can cancel the timer and
  1.1186 +  // finish up.  Otherwise, just return.
  1.1187 +
  1.1188 +  s->mNumChildProcessesCompleted++;
  1.1189 +  MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n",
  1.1190 +                       aGeneration, s->mNumChildProcessesCompleted);
  1.1191 +
  1.1192 +  if (s->mNumChildProcessesCompleted >= s->mNumChildProcesses &&
  1.1193 +      s->mParentDone) {
  1.1194 +    s->mTimer->Cancel();
  1.1195 +    FinishReporting();
  1.1196 +  }
  1.1197 +}
  1.1198 +
  1.1199 +/* static */ void
  1.1200 +nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
  1.1201 +{
  1.1202 +  nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
  1.1203 +  GetReportsState* s = mgr->mGetReportsState;
  1.1204 +
  1.1205 +  MOZ_ASSERT(mgr->mGetReportsState);
  1.1206 +  MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u)\n",
  1.1207 +                       s->mGeneration);
  1.1208 +
  1.1209 +  // We don't bother sending any kind of cancellation message to the child
  1.1210 +  // processes that haven't reported back.
  1.1211 +
  1.1212 +  if (s->mParentDone) {
  1.1213 +    mgr->FinishReporting();
  1.1214 +  } else {
  1.1215 +    // This is unlikely -- the timeout expired during MinimizeMemoryUsage.
  1.1216 +    MEMORY_REPORTING_LOG("Timeout expired before parent report started!");
  1.1217 +    // Let the parent continue with its report, but ensure that
  1.1218 +    // StartGettingReports gives up immediately after that.
  1.1219 +    s->mNumChildProcesses = s->mNumChildProcessesCompleted;
  1.1220 +  }
  1.1221 +}
  1.1222 +
  1.1223 +nsresult
  1.1224 +nsMemoryReporterManager::FinishReporting()
  1.1225 +{
  1.1226 +  // Memory reporting only happens on the main thread.
  1.1227 +  if (!NS_IsMainThread()) {
  1.1228 +    MOZ_CRASH();
  1.1229 +  }
  1.1230 +
  1.1231 +  MOZ_ASSERT(mGetReportsState);
  1.1232 +  MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u)\n",
  1.1233 +                       mGetReportsState->mGeneration);
  1.1234 +
  1.1235 +  // Call this before deleting |mGetReportsState|.  That way, if
  1.1236 +  // |mFinishReportData| calls GetReports(), it will silently abort, as
  1.1237 +  // required.
  1.1238 +  nsresult rv = mGetReportsState->mFinishReporting->Callback(
  1.1239 +    mGetReportsState->mFinishReportingData);
  1.1240 +
  1.1241 +  delete mGetReportsState;
  1.1242 +  mGetReportsState = nullptr;
  1.1243 +  return rv;
  1.1244 +}
  1.1245 +
  1.1246 +static void
  1.1247 +CrashIfRefcountIsZero(nsISupports* aObj)
  1.1248 +{
  1.1249 +  // This will probably crash if the object's refcount is 0.
  1.1250 +  uint32_t refcnt = NS_ADDREF(aObj);
  1.1251 +  if (refcnt <= 1) {
  1.1252 +    MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
  1.1253 +  }
  1.1254 +  NS_RELEASE(aObj);
  1.1255 +}
  1.1256 +
  1.1257 +nsresult
  1.1258 +nsMemoryReporterManager::RegisterReporterHelper(
  1.1259 +  nsIMemoryReporter* aReporter, bool aForce, bool aStrong)
  1.1260 +{
  1.1261 +  // This method is thread-safe.
  1.1262 +  mozilla::MutexAutoLock autoLock(mMutex);
  1.1263 +
  1.1264 +  if (mIsRegistrationBlocked && !aForce) {
  1.1265 +    return NS_ERROR_FAILURE;
  1.1266 +  }
  1.1267 +
  1.1268 +  if (mStrongReporters->Contains(aReporter) ||
  1.1269 +      mWeakReporters->Contains(aReporter))
  1.1270 +  {
  1.1271 +    return NS_ERROR_FAILURE;
  1.1272 +  }
  1.1273 +
  1.1274 +  // If |aStrong| is true, |aReporter| may have a refcnt of 0, so we take
  1.1275 +  // a kung fu death grip before calling PutEntry.  Otherwise, if PutEntry
  1.1276 +  // addref'ed and released |aReporter| before finally addref'ing it for
  1.1277 +  // good, it would free aReporter!  The kung fu death grip could itself be
  1.1278 +  // problematic if PutEntry didn't addref |aReporter| (because then when the
  1.1279 +  // death grip goes out of scope, we would delete the reporter).  In debug
  1.1280 +  // mode, we check that this doesn't happen.
  1.1281 +  //
  1.1282 +  // If |aStrong| is false, we require that |aReporter| have a non-zero
  1.1283 +  // refcnt.
  1.1284 +  //
  1.1285 +  if (aStrong) {
  1.1286 +    nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
  1.1287 +    mStrongReporters->PutEntry(aReporter);
  1.1288 +    CrashIfRefcountIsZero(aReporter);
  1.1289 +  } else {
  1.1290 +    CrashIfRefcountIsZero(aReporter);
  1.1291 +    nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
  1.1292 +    if (jsComponent) {
  1.1293 +      // We cannot allow non-native reporters (WrappedJS), since we'll be
  1.1294 +      // holding onto a raw pointer, which would point to the wrapper,
  1.1295 +      // and that wrapper is likely to go away as soon as this register
  1.1296 +      // call finishes.  This would then lead to subsequent crashes in
  1.1297 +      // CollectReports().
  1.1298 +      return NS_ERROR_XPC_BAD_CONVERT_JS;
  1.1299 +    }
  1.1300 +    mWeakReporters->PutEntry(aReporter);
  1.1301 +  }
  1.1302 +
  1.1303 +  return NS_OK;
  1.1304 +}
  1.1305 +
  1.1306 +NS_IMETHODIMP
  1.1307 +nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
  1.1308 +{
  1.1309 +  return RegisterReporterHelper(aReporter, /* force = */ false,
  1.1310 +                                /* strong = */ true);
  1.1311 +}
  1.1312 +
  1.1313 +NS_IMETHODIMP
  1.1314 +nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
  1.1315 +{
  1.1316 +  return RegisterReporterHelper(aReporter, /* force = */ false,
  1.1317 +                                /* strong = */ false);
  1.1318 +}
  1.1319 +
  1.1320 +NS_IMETHODIMP
  1.1321 +nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
  1.1322 +  nsIMemoryReporter* aReporter)
  1.1323 +{
  1.1324 +  return RegisterReporterHelper(aReporter, /* force = */ true,
  1.1325 +                                /* strong = */ true);
  1.1326 +}
  1.1327 +
  1.1328 +NS_IMETHODIMP
  1.1329 +nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
  1.1330 +{
  1.1331 +  // This method is thread-safe.
  1.1332 +  mozilla::MutexAutoLock autoLock(mMutex);
  1.1333 +
  1.1334 +  MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
  1.1335 +
  1.1336 +  if (mStrongReporters->Contains(aReporter)) {
  1.1337 +    mStrongReporters->RemoveEntry(aReporter);
  1.1338 +    return NS_OK;
  1.1339 +  }
  1.1340 +
  1.1341 +  return NS_ERROR_FAILURE;
  1.1342 +}
  1.1343 +
  1.1344 +NS_IMETHODIMP
  1.1345 +nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
  1.1346 +{
  1.1347 +  // This method is thread-safe.
  1.1348 +  mozilla::MutexAutoLock autoLock(mMutex);
  1.1349 +
  1.1350 +  MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
  1.1351 +
  1.1352 +  if (mWeakReporters->Contains(aReporter)) {
  1.1353 +    mWeakReporters->RemoveEntry(aReporter);
  1.1354 +    return NS_OK;
  1.1355 +  }
  1.1356 +
  1.1357 +  return NS_ERROR_FAILURE;
  1.1358 +}
  1.1359 +
  1.1360 +NS_IMETHODIMP
  1.1361 +nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
  1.1362 +{
  1.1363 +  // This method is thread-safe.
  1.1364 +  mozilla::MutexAutoLock autoLock(mMutex);
  1.1365 +  if (mIsRegistrationBlocked) {
  1.1366 +    return NS_ERROR_FAILURE;
  1.1367 +  }
  1.1368 +  mIsRegistrationBlocked = true;
  1.1369 +
  1.1370 +  // Hide the existing reporters, saving them for later restoration.
  1.1371 +  MOZ_ASSERT(!mSavedStrongReporters);
  1.1372 +  MOZ_ASSERT(!mSavedWeakReporters);
  1.1373 +  mSavedStrongReporters = mStrongReporters;
  1.1374 +  mSavedWeakReporters = mWeakReporters;
  1.1375 +  mStrongReporters = new StrongReportersTable();
  1.1376 +  mWeakReporters = new WeakReportersTable();
  1.1377 +
  1.1378 +  return NS_OK;
  1.1379 +}
  1.1380 +
  1.1381 +NS_IMETHODIMP
  1.1382 +nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
  1.1383 +{
  1.1384 +  // This method is thread-safe.
  1.1385 +  mozilla::MutexAutoLock autoLock(mMutex);
  1.1386 +  if (!mIsRegistrationBlocked) {
  1.1387 +    return NS_ERROR_FAILURE;
  1.1388 +  }
  1.1389 +
  1.1390 +  // Banish the current reporters, and restore the hidden ones.
  1.1391 +  delete mStrongReporters;
  1.1392 +  delete mWeakReporters;
  1.1393 +  mStrongReporters = mSavedStrongReporters;
  1.1394 +  mWeakReporters = mSavedWeakReporters;
  1.1395 +  mSavedStrongReporters = nullptr;
  1.1396 +  mSavedWeakReporters = nullptr;
  1.1397 +
  1.1398 +  mIsRegistrationBlocked = false;
  1.1399 +  return NS_OK;
  1.1400 +}
  1.1401 +
  1.1402 +// This is just a wrapper for int64_t that implements nsISupports, so it can be
  1.1403 +// passed to nsIMemoryReporter::CollectReports.
  1.1404 +class Int64Wrapper MOZ_FINAL : public nsISupports
  1.1405 +{
  1.1406 +public:
  1.1407 +  NS_DECL_ISUPPORTS
  1.1408 +  Int64Wrapper() : mValue(0) { }
  1.1409 +  int64_t mValue;
  1.1410 +};
  1.1411 +
  1.1412 +NS_IMPL_ISUPPORTS0(Int64Wrapper)
  1.1413 +
  1.1414 +class ExplicitCallback MOZ_FINAL : public nsIHandleReportCallback
  1.1415 +{
  1.1416 +public:
  1.1417 +  NS_DECL_ISUPPORTS
  1.1418 +
  1.1419 +  NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
  1.1420 +                      int32_t aKind, int32_t aUnits, int64_t aAmount,
  1.1421 +                      const nsACString& aDescription,
  1.1422 +                      nsISupports* aWrappedExplicit)
  1.1423 +  {
  1.1424 +    // Using the "heap-allocated" reporter here instead of
  1.1425 +    // nsMemoryReporterManager.heapAllocated goes against the usual
  1.1426 +    // pattern.  But it's for a good reason:  in tests, we can easily
  1.1427 +    // create artificial (i.e. deterministic) reporters -- which allows us
  1.1428 +    // to precisely test nsMemoryReporterManager.explicit -- but we can't
  1.1429 +    // do that for distinguished amounts.
  1.1430 +    if (aPath.Equals("heap-allocated") ||
  1.1431 +        (aKind == nsIMemoryReporter::KIND_NONHEAP &&
  1.1432 +         PromiseFlatCString(aPath).Find("explicit") == 0))
  1.1433 +    {
  1.1434 +      Int64Wrapper* wrappedInt64 = static_cast<Int64Wrapper*>(aWrappedExplicit);
  1.1435 +      wrappedInt64->mValue += aAmount;
  1.1436 +    }
  1.1437 +    return NS_OK;
  1.1438 +  }
  1.1439 +};
  1.1440 +
  1.1441 +NS_IMPL_ISUPPORTS(ExplicitCallback, nsIHandleReportCallback)
  1.1442 +
  1.1443 +NS_IMETHODIMP
  1.1444 +nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
  1.1445 +{
  1.1446 +  if (NS_WARN_IF(!aAmount)) {
  1.1447 +    return NS_ERROR_INVALID_ARG;
  1.1448 +  }
  1.1449 +  *aAmount = 0;
  1.1450 +#ifndef HAVE_JEMALLOC_STATS
  1.1451 +  return NS_ERROR_NOT_AVAILABLE;
  1.1452 +#else
  1.1453 +
  1.1454 +  // For each reporter we call CollectReports and filter out the
  1.1455 +  // non-explicit, non-NONHEAP measurements (except for "heap-allocated").
  1.1456 +  // That's lots of wasted work, and we used to have a GetExplicitNonHeap()
  1.1457 +  // method which did this more efficiently, but it ended up being more
  1.1458 +  // trouble than it was worth.
  1.1459 +
  1.1460 +  nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback();
  1.1461 +  nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
  1.1462 +
  1.1463 +  GetReportsForThisProcess(handleReport, wrappedExplicitSize);
  1.1464 +
  1.1465 +  *aAmount = wrappedExplicitSize->mValue;
  1.1466 +
  1.1467 +  return NS_OK;
  1.1468 +#endif // HAVE_JEMALLOC_STATS
  1.1469 +}
  1.1470 +
  1.1471 +NS_IMETHODIMP
  1.1472 +nsMemoryReporterManager::GetVsize(int64_t* aVsize)
  1.1473 +{
  1.1474 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
  1.1475 +  return VsizeDistinguishedAmount(aVsize);
  1.1476 +#else
  1.1477 +  *aVsize = 0;
  1.1478 +  return NS_ERROR_NOT_AVAILABLE;
  1.1479 +#endif
  1.1480 +}
  1.1481 +
  1.1482 +NS_IMETHODIMP
  1.1483 +nsMemoryReporterManager::GetVsizeMaxContiguous(int64_t* aAmount)
  1.1484 +{
  1.1485 +#ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
  1.1486 +  return VsizeMaxContiguousDistinguishedAmount(aAmount);
  1.1487 +#else
  1.1488 +  *aAmount = 0;
  1.1489 +  return NS_ERROR_NOT_AVAILABLE;
  1.1490 +#endif
  1.1491 +}
  1.1492 +
  1.1493 +NS_IMETHODIMP
  1.1494 +nsMemoryReporterManager::GetResident(int64_t* aAmount)
  1.1495 +{
  1.1496 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
  1.1497 +  return ResidentDistinguishedAmount(aAmount);
  1.1498 +#else
  1.1499 +  *aAmount = 0;
  1.1500 +  return NS_ERROR_NOT_AVAILABLE;
  1.1501 +#endif
  1.1502 +}
  1.1503 +
  1.1504 +NS_IMETHODIMP
  1.1505 +nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
  1.1506 +{
  1.1507 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
  1.1508 +  return ResidentFastDistinguishedAmount(aAmount);
  1.1509 +#else
  1.1510 +  *aAmount = 0;
  1.1511 +  return NS_ERROR_NOT_AVAILABLE;
  1.1512 +#endif
  1.1513 +}
  1.1514 +
  1.1515 +/*static*/
  1.1516 +int64_t nsMemoryReporterManager::ResidentFast()
  1.1517 +{
  1.1518 +#ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
  1.1519 +    int64_t amount;
  1.1520 +    ResidentFastDistinguishedAmount(&amount);
  1.1521 +    return amount;
  1.1522 +#else
  1.1523 +    return 0;
  1.1524 +#endif
  1.1525 +}
  1.1526 +
  1.1527 +NS_IMETHODIMP
  1.1528 +nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
  1.1529 +{
  1.1530 +#ifdef HAVE_JEMALLOC_STATS
  1.1531 +  jemalloc_stats_t stats;
  1.1532 +  jemalloc_stats(&stats);
  1.1533 +  *aAmount = stats.allocated;
  1.1534 +  return NS_OK;
  1.1535 +#else
  1.1536 +  *aAmount = 0;
  1.1537 +  return NS_ERROR_NOT_AVAILABLE;
  1.1538 +#endif
  1.1539 +}
  1.1540 +
  1.1541 +// This has UNITS_PERCENTAGE, so it is multiplied by 100x.
  1.1542 +NS_IMETHODIMP
  1.1543 +nsMemoryReporterManager::GetHeapOverheadRatio(int64_t* aAmount)
  1.1544 +{
  1.1545 +#ifdef HAVE_JEMALLOC_STATS
  1.1546 +  jemalloc_stats_t stats;
  1.1547 +  jemalloc_stats(&stats);
  1.1548 +  *aAmount = HeapOverheadRatio(&stats);
  1.1549 +  return NS_OK;
  1.1550 +#else
  1.1551 +  *aAmount = 0;
  1.1552 +  return NS_ERROR_NOT_AVAILABLE;
  1.1553 +#endif
  1.1554 +}
  1.1555 +
  1.1556 +static nsresult
  1.1557 +GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
  1.1558 +{
  1.1559 +  if (aAmountFn) {
  1.1560 +    *aAmount = aAmountFn();
  1.1561 +    return NS_OK;
  1.1562 +  }
  1.1563 +  *aAmount = 0;
  1.1564 +  return NS_ERROR_NOT_AVAILABLE;
  1.1565 +}
  1.1566 +
  1.1567 +NS_IMETHODIMP
  1.1568 +nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
  1.1569 +{
  1.1570 +  return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
  1.1571 +}
  1.1572 +
  1.1573 +NS_IMETHODIMP
  1.1574 +nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
  1.1575 +{
  1.1576 +  return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
  1.1577 +}
  1.1578 +
  1.1579 +NS_IMETHODIMP
  1.1580 +nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
  1.1581 +{
  1.1582 +  return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
  1.1583 +                             aAmount);
  1.1584 +}
  1.1585 +
  1.1586 +NS_IMETHODIMP
  1.1587 +nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
  1.1588 +{
  1.1589 +  return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
  1.1590 +                             aAmount);
  1.1591 +}
  1.1592 +
  1.1593 +NS_IMETHODIMP
  1.1594 +nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
  1.1595 +{
  1.1596 +  return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
  1.1597 +                             aAmount);
  1.1598 +}
  1.1599 +
  1.1600 +NS_IMETHODIMP
  1.1601 +nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
  1.1602 +{
  1.1603 +  return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
  1.1604 +}
  1.1605 +
  1.1606 +NS_IMETHODIMP
  1.1607 +nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
  1.1608 +{
  1.1609 +  return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
  1.1610 +}
  1.1611 +
  1.1612 +NS_IMETHODIMP
  1.1613 +nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
  1.1614 +{
  1.1615 +  return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
  1.1616 +}
  1.1617 +
  1.1618 +NS_IMETHODIMP
  1.1619 +nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
  1.1620 +{
  1.1621 +  return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
  1.1622 +}
  1.1623 +
  1.1624 +NS_IMETHODIMP
  1.1625 +nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
  1.1626 +{
  1.1627 +#ifdef HAVE_PAGE_FAULT_REPORTERS
  1.1628 +  return PageFaultsHardDistinguishedAmount(aAmount);
  1.1629 +#else
  1.1630 +  *aAmount = 0;
  1.1631 +  return NS_ERROR_NOT_AVAILABLE;
  1.1632 +#endif
  1.1633 +}
  1.1634 +
  1.1635 +NS_IMETHODIMP
  1.1636 +nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
  1.1637 +{
  1.1638 +  void* p = malloc(16);
  1.1639 +  if (!p) {
  1.1640 +    return NS_ERROR_OUT_OF_MEMORY;
  1.1641 +  }
  1.1642 +  size_t usable = moz_malloc_usable_size(p);
  1.1643 +  free(p);
  1.1644 +  *aHas = !!(usable > 0);
  1.1645 +  return NS_OK;
  1.1646 +}
  1.1647 +
  1.1648 +namespace {
  1.1649 +
  1.1650 +/**
  1.1651 + * This runnable lets us implement
  1.1652 + * nsIMemoryReporterManager::MinimizeMemoryUsage().  We fire a heap-minimize
  1.1653 + * notification, spin the event loop, and repeat this process a few times.
  1.1654 + *
  1.1655 + * When this sequence finishes, we invoke the callback function passed to the
  1.1656 + * runnable's constructor.
  1.1657 + */
  1.1658 +class MinimizeMemoryUsageRunnable : public nsRunnable
  1.1659 +{
  1.1660 +public:
  1.1661 +  MinimizeMemoryUsageRunnable(nsIRunnable* aCallback)
  1.1662 +    : mCallback(aCallback)
  1.1663 +    , mRemainingIters(sNumIters)
  1.1664 +  {}
  1.1665 +
  1.1666 +  NS_IMETHOD Run()
  1.1667 +  {
  1.1668 +    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  1.1669 +    if (!os) {
  1.1670 +      return NS_ERROR_FAILURE;
  1.1671 +    }
  1.1672 +
  1.1673 +    if (mRemainingIters == 0) {
  1.1674 +      os->NotifyObservers(nullptr, "after-minimize-memory-usage",
  1.1675 +                          MOZ_UTF16("MinimizeMemoryUsageRunnable"));
  1.1676 +      if (mCallback) {
  1.1677 +        mCallback->Run();
  1.1678 +      }
  1.1679 +      return NS_OK;
  1.1680 +    }
  1.1681 +
  1.1682 +    os->NotifyObservers(nullptr, "memory-pressure",
  1.1683 +                        MOZ_UTF16("heap-minimize"));
  1.1684 +    mRemainingIters--;
  1.1685 +    NS_DispatchToMainThread(this);
  1.1686 +
  1.1687 +    return NS_OK;
  1.1688 +  }
  1.1689 +
  1.1690 +private:
  1.1691 +  // Send sNumIters heap-minimize notifications, spinning the event
  1.1692 +  // loop after each notification (see bug 610166 comment 12 for an
  1.1693 +  // explanation), because one notification doesn't cut it.
  1.1694 +  static const uint32_t sNumIters = 3;
  1.1695 +
  1.1696 +  nsCOMPtr<nsIRunnable> mCallback;
  1.1697 +  uint32_t mRemainingIters;
  1.1698 +};
  1.1699 +
  1.1700 +} // anonymous namespace
  1.1701 +
  1.1702 +NS_IMETHODIMP
  1.1703 +nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback)
  1.1704 +{
  1.1705 +  nsRefPtr<MinimizeMemoryUsageRunnable> runnable =
  1.1706 +    new MinimizeMemoryUsageRunnable(aCallback);
  1.1707 +
  1.1708 +  return NS_DispatchToMainThread(runnable);
  1.1709 +}
  1.1710 +
  1.1711 +NS_IMETHODIMP
  1.1712 +nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
  1.1713 +                                   int64_t* aJSObjectsSize,
  1.1714 +                                   int64_t* aJSStringsSize,
  1.1715 +                                   int64_t* aJSOtherSize,
  1.1716 +                                   int64_t* aDomSize,
  1.1717 +                                   int64_t* aStyleSize,
  1.1718 +                                   int64_t* aOtherSize,
  1.1719 +                                   int64_t* aTotalSize,
  1.1720 +                                   double*  aJSMilliseconds,
  1.1721 +                                   double*  aNonJSMilliseconds)
  1.1722 +{
  1.1723 +  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
  1.1724 +  nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aTopWindow);
  1.1725 +  if (NS_WARN_IF(!global) || NS_WARN_IF(!piWindow)) {
  1.1726 +    return NS_ERROR_FAILURE;
  1.1727 +  }
  1.1728 +
  1.1729 +  TimeStamp t1 = TimeStamp::Now();
  1.1730 +
  1.1731 +  // Measure JS memory consumption (and possibly some non-JS consumption, via
  1.1732 +  // |jsPrivateSize|).
  1.1733 +  size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
  1.1734 +  nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
  1.1735 +                                  &jsObjectsSize, &jsStringsSize,
  1.1736 +                                  &jsPrivateSize, &jsOtherSize);
  1.1737 +  if (NS_WARN_IF(NS_FAILED(rv))) {
  1.1738 +    return rv;
  1.1739 +  }
  1.1740 +
  1.1741 +  TimeStamp t2 = TimeStamp::Now();
  1.1742 +
  1.1743 +  // Measure non-JS memory consumption.
  1.1744 +  size_t domSize, styleSize, otherSize;
  1.1745 +  mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
  1.1746 +
  1.1747 +  TimeStamp t3 = TimeStamp::Now();
  1.1748 +
  1.1749 +  *aTotalSize = 0;
  1.1750 +  #define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
  1.1751 +  DO(aJSObjectsSize, jsObjectsSize);
  1.1752 +  DO(aJSStringsSize, jsStringsSize);
  1.1753 +  DO(aJSOtherSize,   jsOtherSize);
  1.1754 +  DO(aDomSize,       jsPrivateSize + domSize);
  1.1755 +  DO(aStyleSize,     styleSize);
  1.1756 +  DO(aOtherSize,     otherSize);
  1.1757 +  #undef DO
  1.1758 +
  1.1759 +  *aJSMilliseconds    = (t2 - t1).ToMilliseconds();
  1.1760 +  *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
  1.1761 +
  1.1762 +  return NS_OK;
  1.1763 +}
  1.1764 +
  1.1765 +namespace mozilla {
  1.1766 +
  1.1767 +nsresult
  1.1768 +RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
  1.1769 +{
  1.1770 +  // Hold a strong reference to the argument to make sure it gets released if
  1.1771 +  // we return early below.
  1.1772 +  nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
  1.1773 +
  1.1774 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
  1.1775 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
  1.1776 +  if (!mgr) {
  1.1777 +    return NS_ERROR_FAILURE;
  1.1778 +  }
  1.1779 +  return mgr->RegisterStrongReporter(reporter);
  1.1780 +}
  1.1781 +
  1.1782 +nsresult
  1.1783 +RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
  1.1784 +{
  1.1785 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
  1.1786 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
  1.1787 +  if (!mgr) {
  1.1788 +    return NS_ERROR_FAILURE;
  1.1789 +  }
  1.1790 +  return mgr->RegisterWeakReporter(aReporter);
  1.1791 +}
  1.1792 +
  1.1793 +nsresult
  1.1794 +UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
  1.1795 +{
  1.1796 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
  1.1797 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
  1.1798 +  if (!mgr) {
  1.1799 +    return NS_ERROR_FAILURE;
  1.1800 +  }
  1.1801 +  return mgr->UnregisterWeakReporter(aReporter);
  1.1802 +}
  1.1803 +
  1.1804 +#define GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
  1.1805 +  nsRefPtr<nsMemoryReporterManager> mgr =                                     \
  1.1806 +    nsMemoryReporterManager::GetOrCreate();                                   \
  1.1807 +  if (!mgr) {                                                                 \
  1.1808 +    return NS_ERROR_FAILURE;                                                  \
  1.1809 +  }
  1.1810 +
  1.1811 +// Macro for generating functions that register distinguished amount functions
  1.1812 +// with the memory reporter manager.
  1.1813 +#define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name)                      \
  1.1814 +  nsresult                                                                    \
  1.1815 +  Register##name##DistinguishedAmount(kind##AmountFn aAmountFn)               \
  1.1816 +  {                                                                           \
  1.1817 +    GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
  1.1818 +    mgr->mAmountFns.m##name = aAmountFn;                                      \
  1.1819 +    return NS_OK;                                                             \
  1.1820 +  }
  1.1821 +
  1.1822 +#define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name)                          \
  1.1823 +  nsresult                                                                    \
  1.1824 +  Unregister##name##DistinguishedAmount()                                     \
  1.1825 +  {                                                                           \
  1.1826 +    GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
  1.1827 +    mgr->mAmountFns.m##name = nullptr;                                        \
  1.1828 +    return NS_OK;                                                             \
  1.1829 +  }
  1.1830 +
  1.1831 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
  1.1832 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
  1.1833 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
  1.1834 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
  1.1835 +
  1.1836 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
  1.1837 +
  1.1838 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
  1.1839 +DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
  1.1840 +
  1.1841 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
  1.1842 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
  1.1843 +
  1.1844 +DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
  1.1845 +
  1.1846 +#undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
  1.1847 +#undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
  1.1848 +
  1.1849 +#define DEFINE_REGISTER_SIZE_OF_TAB(name)                                     \
  1.1850 +  nsresult                                                                    \
  1.1851 +  Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn)                   \
  1.1852 +  {                                                                           \
  1.1853 +    GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
  1.1854 +    mgr->mSizeOfTabFns.m##name = aSizeOfTabFn;                                \
  1.1855 +    return NS_OK;                                                             \
  1.1856 +  }
  1.1857 +
  1.1858 +DEFINE_REGISTER_SIZE_OF_TAB(JS);
  1.1859 +DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
  1.1860 +
  1.1861 +#undef DEFINE_REGISTER_SIZE_OF_TAB
  1.1862 +
  1.1863 +#undef GET_MEMORY_REPORTER_MANAGER
  1.1864 +
  1.1865 +}
  1.1866 +
  1.1867 +#if defined(MOZ_DMD)
  1.1868 +
  1.1869 +namespace mozilla {
  1.1870 +namespace dmd {
  1.1871 +
  1.1872 +class DoNothingCallback MOZ_FINAL : public nsIHandleReportCallback
  1.1873 +{
  1.1874 +public:
  1.1875 +  NS_DECL_ISUPPORTS
  1.1876 +
  1.1877 +  NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
  1.1878 +                      int32_t aKind, int32_t aUnits, int64_t aAmount,
  1.1879 +                      const nsACString& aDescription,
  1.1880 +                      nsISupports* aData)
  1.1881 +  {
  1.1882 +    // Do nothing;  the reporter has already reported to DMD.
  1.1883 +    return NS_OK;
  1.1884 +  }
  1.1885 +};
  1.1886 +
  1.1887 +NS_IMPL_ISUPPORTS(DoNothingCallback, nsIHandleReportCallback)
  1.1888 +
  1.1889 +void
  1.1890 +RunReportersForThisProcess()
  1.1891 +{
  1.1892 +  nsCOMPtr<nsIMemoryReporterManager> mgr =
  1.1893 +    do_GetService("@mozilla.org/memory-reporter-manager;1");
  1.1894 +
  1.1895 +  nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
  1.1896 +
  1.1897 +  mgr->GetReportsForThisProcess(doNothing, nullptr);
  1.1898 +}
  1.1899 +
  1.1900 +} // namespace dmd
  1.1901 +} // namespace mozilla
  1.1902 +
  1.1903 +#endif  // defined(MOZ_DMD)
  1.1904 +

mercurial