xpcom/base/nsMemoryReporterManager.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsAtomTable.h"
michael@0 8 #include "nsAutoPtr.h"
michael@0 9 #include "nsCOMPtr.h"
michael@0 10 #include "nsCOMArray.h"
michael@0 11 #include "nsPrintfCString.h"
michael@0 12 #include "nsServiceManagerUtils.h"
michael@0 13 #include "nsMemoryReporterManager.h"
michael@0 14 #include "nsITimer.h"
michael@0 15 #include "nsThreadUtils.h"
michael@0 16 #include "nsIDOMWindow.h"
michael@0 17 #include "nsPIDOMWindow.h"
michael@0 18 #include "nsIObserverService.h"
michael@0 19 #include "nsIGlobalObject.h"
michael@0 20 #include "nsIXPConnect.h"
michael@0 21 #if defined(XP_UNIX) || defined(MOZ_DMD)
michael@0 22 #include "nsMemoryInfoDumper.h"
michael@0 23 #endif
michael@0 24 #include "mozilla/Attributes.h"
michael@0 25 #include "mozilla/PodOperations.h"
michael@0 26 #include "mozilla/Services.h"
michael@0 27 #include "mozilla/Telemetry.h"
michael@0 28 #include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport
michael@0 29
michael@0 30 #ifndef XP_WIN
michael@0 31 #include <unistd.h>
michael@0 32 #endif
michael@0 33
michael@0 34 using namespace mozilla;
michael@0 35
michael@0 36 #if defined(MOZ_MEMORY)
michael@0 37 # define HAVE_JEMALLOC_STATS 1
michael@0 38 # include "mozmemory.h"
michael@0 39 #endif // MOZ_MEMORY
michael@0 40
michael@0 41 #if defined(XP_LINUX)
michael@0 42
michael@0 43 static nsresult
michael@0 44 GetProcSelfStatmField(int aField, int64_t* aN)
michael@0 45 {
michael@0 46 // There are more than two fields, but we're only interested in the first
michael@0 47 // two.
michael@0 48 static const int MAX_FIELD = 2;
michael@0 49 size_t fields[MAX_FIELD];
michael@0 50 MOZ_ASSERT(aField < MAX_FIELD, "bad field number");
michael@0 51 FILE* f = fopen("/proc/self/statm", "r");
michael@0 52 if (f) {
michael@0 53 int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
michael@0 54 fclose(f);
michael@0 55 if (nread == MAX_FIELD) {
michael@0 56 *aN = fields[aField] * getpagesize();
michael@0 57 return NS_OK;
michael@0 58 }
michael@0 59 }
michael@0 60 return NS_ERROR_FAILURE;
michael@0 61 }
michael@0 62
michael@0 63 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
michael@0 64 static nsresult
michael@0 65 VsizeDistinguishedAmount(int64_t* aN)
michael@0 66 {
michael@0 67 return GetProcSelfStatmField(0, aN);
michael@0 68 }
michael@0 69
michael@0 70 static nsresult
michael@0 71 ResidentDistinguishedAmount(int64_t* aN)
michael@0 72 {
michael@0 73 return GetProcSelfStatmField(1, aN);
michael@0 74 }
michael@0 75
michael@0 76 static nsresult
michael@0 77 ResidentFastDistinguishedAmount(int64_t* aN)
michael@0 78 {
michael@0 79 return ResidentDistinguishedAmount(aN);
michael@0 80 }
michael@0 81
michael@0 82 #define HAVE_RESIDENT_UNIQUE_REPORTER
michael@0 83 class ResidentUniqueReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 84 {
michael@0 85 public:
michael@0 86 NS_DECL_ISUPPORTS
michael@0 87
michael@0 88 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 89 nsISupports* aData)
michael@0 90 {
michael@0 91 // You might be tempted to calculate USS by subtracting the "shared"
michael@0 92 // value from the "resident" value in /proc/<pid>/statm. But at least
michael@0 93 // on Linux, statm's "shared" value actually counts pages backed by
michael@0 94 // files, which has little to do with whether the pages are actually
michael@0 95 // shared. /proc/self/smaps on the other hand appears to give us the
michael@0 96 // correct information.
michael@0 97
michael@0 98 FILE* f = fopen("/proc/self/smaps", "r");
michael@0 99 if (NS_WARN_IF(!f)) {
michael@0 100 return NS_ERROR_UNEXPECTED;
michael@0 101 }
michael@0 102
michael@0 103 int64_t amount = 0;
michael@0 104 char line[256];
michael@0 105 while (fgets(line, sizeof(line), f)) {
michael@0 106 long long val = 0;
michael@0 107 if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 ||
michael@0 108 sscanf(line, "Private_Clean: %lld kB", &val) == 1) {
michael@0 109 amount += val * 1024; // convert from kB to bytes
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 fclose(f);
michael@0 114
michael@0 115 return MOZ_COLLECT_REPORT(
michael@0 116 "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
michael@0 117 "Memory mapped by the process that is present in physical memory and not "
michael@0 118 "shared with any other processes. This is also known as the process's unique "
michael@0 119 "set size (USS). This is the amount of RAM we'd expect to be freed if we "
michael@0 120 "closed this process.");
michael@0 121 }
michael@0 122 };
michael@0 123 NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
michael@0 124
michael@0 125 #elif defined(__DragonFly__) || defined(__FreeBSD__) \
michael@0 126 || defined(__NetBSD__) || defined(__OpenBSD__) \
michael@0 127 || defined(__FreeBSD_kernel__)
michael@0 128
michael@0 129 #include <sys/param.h>
michael@0 130 #include <sys/sysctl.h>
michael@0 131 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
michael@0 132 #include <sys/user.h>
michael@0 133 #endif
michael@0 134
michael@0 135 #include <unistd.h>
michael@0 136
michael@0 137 #if defined(__NetBSD__)
michael@0 138 #undef KERN_PROC
michael@0 139 #define KERN_PROC KERN_PROC2
michael@0 140 #define KINFO_PROC struct kinfo_proc2
michael@0 141 #else
michael@0 142 #define KINFO_PROC struct kinfo_proc
michael@0 143 #endif
michael@0 144
michael@0 145 #if defined(__DragonFly__)
michael@0 146 #define KP_SIZE(kp) (kp.kp_vm_map_size)
michael@0 147 #define KP_RSS(kp) (kp.kp_vm_rssize * getpagesize())
michael@0 148 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
michael@0 149 #define KP_SIZE(kp) (kp.ki_size)
michael@0 150 #define KP_RSS(kp) (kp.ki_rssize * getpagesize())
michael@0 151 #elif defined(__NetBSD__)
michael@0 152 #define KP_SIZE(kp) (kp.p_vm_msize * getpagesize())
michael@0 153 #define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
michael@0 154 #elif defined(__OpenBSD__)
michael@0 155 #define KP_SIZE(kp) ((kp.p_vm_dsize + kp.p_vm_ssize \
michael@0 156 + kp.p_vm_tsize) * getpagesize())
michael@0 157 #define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
michael@0 158 #endif
michael@0 159
michael@0 160 static nsresult
michael@0 161 GetKinfoProcSelf(KINFO_PROC* aProc)
michael@0 162 {
michael@0 163 int mib[] = {
michael@0 164 CTL_KERN,
michael@0 165 KERN_PROC,
michael@0 166 KERN_PROC_PID,
michael@0 167 getpid(),
michael@0 168 #if defined(__NetBSD__) || defined(__OpenBSD__)
michael@0 169 sizeof(KINFO_PROC),
michael@0 170 1,
michael@0 171 #endif
michael@0 172 };
michael@0 173 u_int miblen = sizeof(mib) / sizeof(mib[0]);
michael@0 174 size_t size = sizeof(KINFO_PROC);
michael@0 175 if (sysctl(mib, miblen, aProc, &size, nullptr, 0)) {
michael@0 176 return NS_ERROR_FAILURE;
michael@0 177 }
michael@0 178 return NS_OK;
michael@0 179 }
michael@0 180
michael@0 181 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
michael@0 182 static nsresult
michael@0 183 VsizeDistinguishedAmount(int64_t* aN)
michael@0 184 {
michael@0 185 KINFO_PROC proc;
michael@0 186 nsresult rv = GetKinfoProcSelf(&proc);
michael@0 187 if (NS_SUCCEEDED(rv)) {
michael@0 188 *aN = KP_SIZE(proc);
michael@0 189 }
michael@0 190 return rv;
michael@0 191 }
michael@0 192
michael@0 193 static nsresult
michael@0 194 ResidentDistinguishedAmount(int64_t* aN)
michael@0 195 {
michael@0 196 KINFO_PROC proc;
michael@0 197 nsresult rv = GetKinfoProcSelf(&proc);
michael@0 198 if (NS_SUCCEEDED(rv)) {
michael@0 199 *aN = KP_RSS(proc);
michael@0 200 }
michael@0 201 return rv;
michael@0 202 }
michael@0 203
michael@0 204 static nsresult
michael@0 205 ResidentFastDistinguishedAmount(int64_t* aN)
michael@0 206 {
michael@0 207 return ResidentDistinguishedAmount(aN);
michael@0 208 }
michael@0 209
michael@0 210 #ifdef __FreeBSD__
michael@0 211 #include <libutil.h>
michael@0 212 #include <algorithm>
michael@0 213
michael@0 214 static nsresult
michael@0 215 GetKinfoVmentrySelf(int64_t* aPrss, uint64_t* aMaxreg)
michael@0 216 {
michael@0 217 int cnt;
michael@0 218 struct kinfo_vmentry *vmmap, *kve;
michael@0 219 if ((vmmap = kinfo_getvmmap(getpid(), &cnt)) == nullptr) {
michael@0 220 return NS_ERROR_FAILURE;
michael@0 221 }
michael@0 222 if (aPrss) {
michael@0 223 *aPrss = 0;
michael@0 224 }
michael@0 225 if (aMaxreg) {
michael@0 226 *aMaxreg = 0;
michael@0 227 }
michael@0 228
michael@0 229 for (int i = 0; i < cnt; i++) {
michael@0 230 kve = &vmmap[i];
michael@0 231 if (aPrss) {
michael@0 232 *aPrss += kve->kve_private_resident;
michael@0 233 }
michael@0 234 if (aMaxreg) {
michael@0 235 *aMaxreg = std::max(*aMaxreg, kve->kve_end - kve->kve_start);
michael@0 236 }
michael@0 237 }
michael@0 238
michael@0 239 free(vmmap);
michael@0 240 return NS_OK;
michael@0 241 }
michael@0 242
michael@0 243 #define HAVE_PRIVATE_REPORTER
michael@0 244 static nsresult
michael@0 245 PrivateDistinguishedAmount(int64_t* aN)
michael@0 246 {
michael@0 247 int64_t priv;
michael@0 248 nsresult rv = GetKinfoVmentrySelf(&priv, nullptr);
michael@0 249 NS_ENSURE_SUCCESS(rv, rv);
michael@0 250 *aN = priv * getpagesize();
michael@0 251 return NS_OK;
michael@0 252 }
michael@0 253
michael@0 254 #define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
michael@0 255 static nsresult
michael@0 256 VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
michael@0 257 {
michael@0 258 uint64_t biggestRegion;
michael@0 259 nsresult rv = GetKinfoVmentrySelf(nullptr, &biggestRegion);
michael@0 260 if (NS_SUCCEEDED(rv)) {
michael@0 261 *aN = biggestRegion;
michael@0 262 }
michael@0 263 return NS_OK;
michael@0 264 }
michael@0 265 #endif // FreeBSD
michael@0 266
michael@0 267 #elif defined(SOLARIS)
michael@0 268
michael@0 269 #include <procfs.h>
michael@0 270 #include <fcntl.h>
michael@0 271 #include <unistd.h>
michael@0 272
michael@0 273 static void XMappingIter(int64_t& aVsize, int64_t& aResident)
michael@0 274 {
michael@0 275 aVsize = -1;
michael@0 276 aResident = -1;
michael@0 277 int mapfd = open("/proc/self/xmap", O_RDONLY);
michael@0 278 struct stat st;
michael@0 279 prxmap_t* prmapp = nullptr;
michael@0 280 if (mapfd >= 0) {
michael@0 281 if (!fstat(mapfd, &st)) {
michael@0 282 int nmap = st.st_size / sizeof(prxmap_t);
michael@0 283 while (1) {
michael@0 284 // stat(2) on /proc/<pid>/xmap returns an incorrect value,
michael@0 285 // prior to the release of Solaris 11.
michael@0 286 // Here is a workaround for it.
michael@0 287 nmap *= 2;
michael@0 288 prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
michael@0 289 if (!prmapp) {
michael@0 290 // out of memory
michael@0 291 break;
michael@0 292 }
michael@0 293 int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
michael@0 294 if (n < 0) {
michael@0 295 break;
michael@0 296 }
michael@0 297 if (nmap >= n / sizeof(prxmap_t)) {
michael@0 298 aVsize = 0;
michael@0 299 aResident = 0;
michael@0 300 for (int i = 0; i < n / sizeof(prxmap_t); i++) {
michael@0 301 aVsize += prmapp[i].pr_size;
michael@0 302 aResident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
michael@0 303 }
michael@0 304 break;
michael@0 305 }
michael@0 306 free(prmapp);
michael@0 307 }
michael@0 308 free(prmapp);
michael@0 309 }
michael@0 310 close(mapfd);
michael@0 311 }
michael@0 312 }
michael@0 313
michael@0 314 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
michael@0 315 static nsresult
michael@0 316 VsizeDistinguishedAmount(int64_t* aN)
michael@0 317 {
michael@0 318 int64_t vsize, resident;
michael@0 319 XMappingIter(vsize, resident);
michael@0 320 if (vsize == -1) {
michael@0 321 return NS_ERROR_FAILURE;
michael@0 322 }
michael@0 323 *aN = vsize;
michael@0 324 return NS_OK;
michael@0 325 }
michael@0 326
michael@0 327 static nsresult
michael@0 328 ResidentDistinguishedAmount(int64_t* aN)
michael@0 329 {
michael@0 330 int64_t vsize, resident;
michael@0 331 XMappingIter(vsize, resident);
michael@0 332 if (resident == -1) {
michael@0 333 return NS_ERROR_FAILURE;
michael@0 334 }
michael@0 335 *aN = resident;
michael@0 336 return NS_OK;
michael@0 337 }
michael@0 338
michael@0 339 static nsresult
michael@0 340 ResidentFastDistinguishedAmount(int64_t* aN)
michael@0 341 {
michael@0 342 return ResidentDistinguishedAmount(aN);
michael@0 343 }
michael@0 344
michael@0 345 #elif defined(XP_MACOSX)
michael@0 346
michael@0 347 #include <mach/mach_init.h>
michael@0 348 #include <mach/task.h>
michael@0 349
michael@0 350 static bool
michael@0 351 GetTaskBasicInfo(struct task_basic_info* aTi)
michael@0 352 {
michael@0 353 mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
michael@0 354 kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
michael@0 355 (task_info_t)aTi, &count);
michael@0 356 return kr == KERN_SUCCESS;
michael@0 357 }
michael@0 358
michael@0 359 // The VSIZE figure on Mac includes huge amounts of shared memory and is always
michael@0 360 // absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report
michael@0 361 // it, so we might as well too.
michael@0 362 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
michael@0 363 static nsresult
michael@0 364 VsizeDistinguishedAmount(int64_t* aN)
michael@0 365 {
michael@0 366 task_basic_info ti;
michael@0 367 if (!GetTaskBasicInfo(&ti)) {
michael@0 368 return NS_ERROR_FAILURE;
michael@0 369 }
michael@0 370 *aN = ti.virtual_size;
michael@0 371 return NS_OK;
michael@0 372 }
michael@0 373
michael@0 374 // If we're using jemalloc on Mac, we need to instruct jemalloc to purge the
michael@0 375 // pages it has madvise(MADV_FREE)'d before we read our RSS in order to get
michael@0 376 // an accurate result. The OS will take away MADV_FREE'd pages when there's
michael@0 377 // memory pressure, so ideally, they shouldn't count against our RSS.
michael@0 378 //
michael@0 379 // Purging these pages can take a long time for some users (see bug 789975),
michael@0 380 // so we provide the option to get the RSS without purging first.
michael@0 381 static nsresult
michael@0 382 ResidentDistinguishedAmountHelper(int64_t* aN, bool aDoPurge)
michael@0 383 {
michael@0 384 #ifdef HAVE_JEMALLOC_STATS
michael@0 385 if (aDoPurge) {
michael@0 386 Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
michael@0 387 jemalloc_purge_freed_pages();
michael@0 388 }
michael@0 389 #endif
michael@0 390
michael@0 391 task_basic_info ti;
michael@0 392 if (!GetTaskBasicInfo(&ti)) {
michael@0 393 return NS_ERROR_FAILURE;
michael@0 394 }
michael@0 395 *aN = ti.resident_size;
michael@0 396 return NS_OK;
michael@0 397 }
michael@0 398
michael@0 399 static nsresult
michael@0 400 ResidentFastDistinguishedAmount(int64_t* aN)
michael@0 401 {
michael@0 402 return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false);
michael@0 403 }
michael@0 404
michael@0 405 static nsresult
michael@0 406 ResidentDistinguishedAmount(int64_t* aN)
michael@0 407 {
michael@0 408 return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
michael@0 409 }
michael@0 410
michael@0 411 #elif defined(XP_WIN)
michael@0 412
michael@0 413 #include <windows.h>
michael@0 414 #include <psapi.h>
michael@0 415 #include <algorithm>
michael@0 416
michael@0 417 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
michael@0 418 static nsresult
michael@0 419 VsizeDistinguishedAmount(int64_t* aN)
michael@0 420 {
michael@0 421 MEMORYSTATUSEX s;
michael@0 422 s.dwLength = sizeof(s);
michael@0 423
michael@0 424 if (!GlobalMemoryStatusEx(&s)) {
michael@0 425 return NS_ERROR_FAILURE;
michael@0 426 }
michael@0 427
michael@0 428 *aN = s.ullTotalVirtual - s.ullAvailVirtual;
michael@0 429 return NS_OK;
michael@0 430 }
michael@0 431
michael@0 432 static nsresult
michael@0 433 ResidentDistinguishedAmount(int64_t* aN)
michael@0 434 {
michael@0 435 PROCESS_MEMORY_COUNTERS pmc;
michael@0 436 pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
michael@0 437
michael@0 438 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
michael@0 439 return NS_ERROR_FAILURE;
michael@0 440 }
michael@0 441
michael@0 442 *aN = pmc.WorkingSetSize;
michael@0 443 return NS_OK;
michael@0 444 }
michael@0 445
michael@0 446 static nsresult
michael@0 447 ResidentFastDistinguishedAmount(int64_t* aN)
michael@0 448 {
michael@0 449 return ResidentDistinguishedAmount(aN);
michael@0 450 }
michael@0 451
michael@0 452 #define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
michael@0 453 static nsresult
michael@0 454 VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
michael@0 455 {
michael@0 456 SIZE_T biggestRegion = 0;
michael@0 457 MEMORY_BASIC_INFORMATION vmemInfo = { 0 };
michael@0 458 for (size_t currentAddress = 0; ; ) {
michael@0 459 if (!VirtualQuery((LPCVOID)currentAddress, &vmemInfo, sizeof(vmemInfo))) {
michael@0 460 // Something went wrong, just return whatever we've got already.
michael@0 461 break;
michael@0 462 }
michael@0 463
michael@0 464 if (vmemInfo.State == MEM_FREE) {
michael@0 465 biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize);
michael@0 466 }
michael@0 467
michael@0 468 SIZE_T lastAddress = currentAddress;
michael@0 469 currentAddress += vmemInfo.RegionSize;
michael@0 470
michael@0 471 // If we overflow, we've examined all of the address space.
michael@0 472 if (currentAddress < lastAddress) {
michael@0 473 break;
michael@0 474 }
michael@0 475 }
michael@0 476
michael@0 477 *aN = biggestRegion;
michael@0 478 return NS_OK;
michael@0 479 }
michael@0 480
michael@0 481 #define HAVE_PRIVATE_REPORTER
michael@0 482 static nsresult
michael@0 483 PrivateDistinguishedAmount(int64_t* aN)
michael@0 484 {
michael@0 485 PROCESS_MEMORY_COUNTERS_EX pmcex;
michael@0 486 pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
michael@0 487
michael@0 488 if (!GetProcessMemoryInfo(GetCurrentProcess(),
michael@0 489 (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
michael@0 490 return NS_ERROR_FAILURE;
michael@0 491 }
michael@0 492
michael@0 493 *aN = pmcex.PrivateUsage;
michael@0 494 return NS_OK;
michael@0 495 }
michael@0 496 #endif // XP_<PLATFORM>
michael@0 497
michael@0 498 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
michael@0 499 class VsizeMaxContiguousReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 500 {
michael@0 501 public:
michael@0 502 NS_DECL_ISUPPORTS
michael@0 503
michael@0 504 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 505 nsISupports* aData)
michael@0 506 {
michael@0 507 int64_t amount;
michael@0 508 nsresult rv = VsizeMaxContiguousDistinguishedAmount(&amount);
michael@0 509 NS_ENSURE_SUCCESS(rv, rv);
michael@0 510 return MOZ_COLLECT_REPORT(
michael@0 511 "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
michael@0 512 "Size of the maximum contiguous block of available virtual "
michael@0 513 "memory.");
michael@0 514 }
michael@0 515 };
michael@0 516 NS_IMPL_ISUPPORTS(VsizeMaxContiguousReporter, nsIMemoryReporter)
michael@0 517 #endif
michael@0 518
michael@0 519 #ifdef HAVE_PRIVATE_REPORTER
michael@0 520 class PrivateReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 521 {
michael@0 522 public:
michael@0 523 NS_DECL_ISUPPORTS
michael@0 524
michael@0 525 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 526 nsISupports* aData)
michael@0 527 {
michael@0 528 int64_t amount;
michael@0 529 nsresult rv = PrivateDistinguishedAmount(&amount);
michael@0 530 NS_ENSURE_SUCCESS(rv, rv);
michael@0 531 return MOZ_COLLECT_REPORT(
michael@0 532 "private", KIND_OTHER, UNITS_BYTES, amount,
michael@0 533 "Memory that cannot be shared with other processes, including memory that is "
michael@0 534 "committed and marked MEM_PRIVATE, data that is not mapped, and executable "
michael@0 535 "pages that have been written to.");
michael@0 536 }
michael@0 537 };
michael@0 538 NS_IMPL_ISUPPORTS(PrivateReporter, nsIMemoryReporter)
michael@0 539 #endif
michael@0 540
michael@0 541 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 542 class VsizeReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 543 {
michael@0 544 public:
michael@0 545 NS_DECL_ISUPPORTS
michael@0 546
michael@0 547 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 548 nsISupports* aData)
michael@0 549 {
michael@0 550 int64_t amount;
michael@0 551 nsresult rv = VsizeDistinguishedAmount(&amount);
michael@0 552 NS_ENSURE_SUCCESS(rv, rv);
michael@0 553
michael@0 554 return MOZ_COLLECT_REPORT(
michael@0 555 "vsize", KIND_OTHER, UNITS_BYTES, amount,
michael@0 556 "Memory mapped by the process, including code and data segments, the heap, "
michael@0 557 "thread stacks, memory explicitly mapped by the process via mmap and similar "
michael@0 558 "operations, and memory shared with other processes. This is the vsize figure "
michael@0 559 "as reported by 'top' and 'ps'. This figure is of limited use on Mac, where "
michael@0 560 "processes share huge amounts of memory with one another. But even on other "
michael@0 561 "operating systems, 'resident' is a much better measure of the memory "
michael@0 562 "resources used by the process.");
michael@0 563 }
michael@0 564 };
michael@0 565 NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
michael@0 566
michael@0 567 class ResidentReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 568 {
michael@0 569 public:
michael@0 570 NS_DECL_ISUPPORTS
michael@0 571
michael@0 572 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 573 nsISupports* aData)
michael@0 574 {
michael@0 575 int64_t amount;
michael@0 576 nsresult rv = ResidentDistinguishedAmount(&amount);
michael@0 577 NS_ENSURE_SUCCESS(rv, rv);
michael@0 578
michael@0 579 return MOZ_COLLECT_REPORT(
michael@0 580 "resident", KIND_OTHER, UNITS_BYTES, amount,
michael@0 581 "Memory mapped by the process that is present in physical memory, also known "
michael@0 582 "as the resident set size (RSS). This is the best single figure to use when "
michael@0 583 "considering the memory resources used by the process, but it depends both on "
michael@0 584 "other processes being run and details of the OS kernel and so is best used "
michael@0 585 "for comparing the memory usage of a single process at different points in "
michael@0 586 "time.");
michael@0 587 }
michael@0 588 };
michael@0 589 NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter)
michael@0 590
michael@0 591 #endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 592
michael@0 593 #ifdef XP_UNIX
michael@0 594
michael@0 595 #include <sys/resource.h>
michael@0 596
michael@0 597 #define HAVE_PAGE_FAULT_REPORTERS 1
michael@0 598
michael@0 599 class PageFaultsSoftReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 600 {
michael@0 601 public:
michael@0 602 NS_DECL_ISUPPORTS
michael@0 603
michael@0 604 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 605 nsISupports* aData)
michael@0 606 {
michael@0 607 struct rusage usage;
michael@0 608 int err = getrusage(RUSAGE_SELF, &usage);
michael@0 609 if (err != 0) {
michael@0 610 return NS_ERROR_FAILURE;
michael@0 611 }
michael@0 612 int64_t amount = usage.ru_minflt;
michael@0 613
michael@0 614 return MOZ_COLLECT_REPORT(
michael@0 615 "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
michael@0 616 "The number of soft page faults (also known as 'minor page faults') that "
michael@0 617 "have occurred since the process started. A soft page fault occurs when the "
michael@0 618 "process tries to access a page which is present in physical memory but is "
michael@0 619 "not mapped into the process's address space. For instance, a process might "
michael@0 620 "observe soft page faults when it loads a shared library which is already "
michael@0 621 "present in physical memory. A process may experience many thousands of soft "
michael@0 622 "page faults even when the machine has plenty of available physical memory, "
michael@0 623 "and because the OS services a soft page fault without accessing the disk, "
michael@0 624 "they impact performance much less than hard page faults.");
michael@0 625 }
michael@0 626 };
michael@0 627 NS_IMPL_ISUPPORTS(PageFaultsSoftReporter, nsIMemoryReporter)
michael@0 628
michael@0 629 static nsresult
michael@0 630 PageFaultsHardDistinguishedAmount(int64_t* aAmount)
michael@0 631 {
michael@0 632 struct rusage usage;
michael@0 633 int err = getrusage(RUSAGE_SELF, &usage);
michael@0 634 if (err != 0) {
michael@0 635 return NS_ERROR_FAILURE;
michael@0 636 }
michael@0 637 *aAmount = usage.ru_majflt;
michael@0 638 return NS_OK;
michael@0 639 }
michael@0 640
michael@0 641 class PageFaultsHardReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 642 {
michael@0 643 public:
michael@0 644 NS_DECL_ISUPPORTS
michael@0 645
michael@0 646 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 647 nsISupports* aData)
michael@0 648 {
michael@0 649 int64_t amount = 0;
michael@0 650 nsresult rv = PageFaultsHardDistinguishedAmount(&amount);
michael@0 651 NS_ENSURE_SUCCESS(rv, rv);
michael@0 652
michael@0 653 return MOZ_COLLECT_REPORT(
michael@0 654 "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
michael@0 655 "The number of hard page faults (also known as 'major page faults') that have "
michael@0 656 "occurred since the process started. A hard page fault occurs when a process "
michael@0 657 "tries to access a page which is not present in physical memory. The "
michael@0 658 "operating system must access the disk in order to fulfill a hard page fault. "
michael@0 659 "When memory is plentiful, you should see very few hard page faults. But if "
michael@0 660 "the process tries to use more memory than your machine has available, you "
michael@0 661 "may see many thousands of hard page faults. Because accessing the disk is up "
michael@0 662 "to a million times slower than accessing RAM, the program may run very "
michael@0 663 "slowly when it is experiencing more than 100 or so hard page faults a "
michael@0 664 "second.");
michael@0 665 }
michael@0 666 };
michael@0 667 NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter)
michael@0 668
michael@0 669 #endif // HAVE_PAGE_FAULT_REPORTERS
michael@0 670
michael@0 671 /**
michael@0 672 ** memory reporter implementation for jemalloc and OSX malloc,
michael@0 673 ** to obtain info on total memory in use (that we know about,
michael@0 674 ** at least -- on OSX, there are sometimes other zones in use).
michael@0 675 **/
michael@0 676
michael@0 677 #ifdef HAVE_JEMALLOC_STATS
michael@0 678
michael@0 679 // This has UNITS_PERCENTAGE, so it is multiplied by 100.
michael@0 680 static int64_t
michael@0 681 HeapOverheadRatio(jemalloc_stats_t* aStats)
michael@0 682 {
michael@0 683 return (int64_t) 10000 *
michael@0 684 (aStats->waste + aStats->bookkeeping + aStats->page_cache) /
michael@0 685 ((double)aStats->allocated);
michael@0 686 }
michael@0 687
michael@0 688 class JemallocHeapReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 689 {
michael@0 690 public:
michael@0 691 NS_DECL_ISUPPORTS
michael@0 692
michael@0 693 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 694 nsISupports* aData)
michael@0 695 {
michael@0 696 jemalloc_stats_t stats;
michael@0 697 jemalloc_stats(&stats);
michael@0 698
michael@0 699 nsresult rv;
michael@0 700
michael@0 701 rv = MOZ_COLLECT_REPORT(
michael@0 702 "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
michael@0 703 "Memory mapped by the heap allocator that is currently allocated to the "
michael@0 704 "application. This may exceed the amount of memory requested by the "
michael@0 705 "application because the allocator regularly rounds up request sizes. (The "
michael@0 706 "exact amount requested is not recorded.)");
michael@0 707 NS_ENSURE_SUCCESS(rv, rv);
michael@0 708
michael@0 709 // We mark this and the other heap-overhead reporters as KIND_NONHEAP
michael@0 710 // because KIND_HEAP memory means "counted in heap-allocated", which
michael@0 711 // this is not.
michael@0 712 rv = MOZ_COLLECT_REPORT(
michael@0 713 "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
michael@0 714 stats.waste,
michael@0 715 "Committed bytes which do not correspond to an active allocation and which the "
michael@0 716 "allocator is not intentionally keeping alive (i.e., not 'heap-bookkeeping' or "
michael@0 717 "'heap-page-cache'). Although the allocator will waste some space under any "
michael@0 718 "circumstances, a large value here may indicate that the heap is highly "
michael@0 719 "fragmented, or that allocator is performing poorly for some other reason.");
michael@0 720 NS_ENSURE_SUCCESS(rv, rv);
michael@0 721
michael@0 722 rv = MOZ_COLLECT_REPORT(
michael@0 723 "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
michael@0 724 stats.bookkeeping,
michael@0 725 "Committed bytes which the heap allocator uses for internal data structures.");
michael@0 726 NS_ENSURE_SUCCESS(rv, rv);
michael@0 727
michael@0 728 rv = MOZ_COLLECT_REPORT(
michael@0 729 "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES,
michael@0 730 stats.page_cache,
michael@0 731 "Memory which the allocator could return to the operating system, but hasn't. "
michael@0 732 "The allocator keeps this memory around as an optimization, so it doesn't "
michael@0 733 "have to ask the OS the next time it needs to fulfill a request. This value "
michael@0 734 "is typically not larger than a few megabytes.");
michael@0 735 NS_ENSURE_SUCCESS(rv, rv);
michael@0 736
michael@0 737 rv = MOZ_COLLECT_REPORT(
michael@0 738 "heap-committed", KIND_OTHER, UNITS_BYTES,
michael@0 739 stats.allocated + stats.waste + stats.bookkeeping + stats.page_cache,
michael@0 740 "Memory mapped by the heap allocator that is committed, i.e. in physical "
michael@0 741 "memory or paged to disk. This value corresponds to 'heap-allocated' + "
michael@0 742 "'heap-waste' + 'heap-bookkeeping' + 'heap-page-cache', but because "
michael@0 743 "these values are read at different times, the result probably won't match "
michael@0 744 "exactly.");
michael@0 745 NS_ENSURE_SUCCESS(rv, rv);
michael@0 746
michael@0 747 rv = MOZ_COLLECT_REPORT(
michael@0 748 "heap-overhead-ratio", KIND_OTHER, UNITS_PERCENTAGE,
michael@0 749 HeapOverheadRatio(&stats),
michael@0 750 "Ratio of committed, unused bytes to allocated bytes; i.e., "
michael@0 751 "'heap-overhead' / 'heap-allocated'. This measures the overhead of "
michael@0 752 "the heap allocator relative to amount of memory allocated.");
michael@0 753 NS_ENSURE_SUCCESS(rv, rv);
michael@0 754
michael@0 755 return NS_OK;
michael@0 756 }
michael@0 757 };
michael@0 758 NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
michael@0 759
michael@0 760 #endif // HAVE_JEMALLOC_STATS
michael@0 761
michael@0 762 // Why is this here? At first glance, you'd think it could be defined and
michael@0 763 // registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
michael@0 764 // However, the obvious time to register it is when the table is initialized,
michael@0 765 // and that happens before XPCOM components are initialized, which means the
michael@0 766 // RegisterStrongMemoryReporter call fails. So instead we do it here.
michael@0 767 class AtomTablesReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 768 {
michael@0 769 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
michael@0 770
michael@0 771 public:
michael@0 772 NS_DECL_ISUPPORTS
michael@0 773
michael@0 774 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 775 nsISupports* aData)
michael@0 776 {
michael@0 777 return MOZ_COLLECT_REPORT(
michael@0 778 "explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
michael@0 779 NS_SizeOfAtomTablesIncludingThis(MallocSizeOf),
michael@0 780 "Memory used by the dynamic and static atoms tables.");
michael@0 781 }
michael@0 782 };
michael@0 783 NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
michael@0 784
michael@0 785 #ifdef MOZ_DMD
michael@0 786
michael@0 787 namespace mozilla {
michael@0 788 namespace dmd {
michael@0 789
michael@0 790 class DMDReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 791 {
michael@0 792 public:
michael@0 793 NS_DECL_ISUPPORTS
michael@0 794
michael@0 795 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 796 nsISupports* aData)
michael@0 797 {
michael@0 798 dmd::Sizes sizes;
michael@0 799 dmd::SizeOf(&sizes);
michael@0 800
michael@0 801 #define REPORT(_path, _amount, _desc) \
michael@0 802 do { \
michael@0 803 nsresult rv; \
michael@0 804 rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
michael@0 805 KIND_HEAP, UNITS_BYTES, _amount, \
michael@0 806 NS_LITERAL_CSTRING(_desc), aData); \
michael@0 807 if (NS_WARN_IF(NS_FAILED(rv))) { \
michael@0 808 return rv; \
michael@0 809 } \
michael@0 810 } while (0)
michael@0 811
michael@0 812 REPORT("explicit/dmd/stack-traces/used",
michael@0 813 sizes.mStackTracesUsed,
michael@0 814 "Memory used by stack traces which correspond to at least "
michael@0 815 "one heap block DMD is tracking.");
michael@0 816
michael@0 817 REPORT("explicit/dmd/stack-traces/unused",
michael@0 818 sizes.mStackTracesUnused,
michael@0 819 "Memory used by stack traces which don't correspond to any heap "
michael@0 820 "blocks DMD is currently tracking.");
michael@0 821
michael@0 822 REPORT("explicit/dmd/stack-traces/table",
michael@0 823 sizes.mStackTraceTable,
michael@0 824 "Memory used by DMD's stack trace table.");
michael@0 825
michael@0 826 REPORT("explicit/dmd/block-table",
michael@0 827 sizes.mBlockTable,
michael@0 828 "Memory used by DMD's live block table.");
michael@0 829
michael@0 830 #undef REPORT
michael@0 831
michael@0 832 return NS_OK;
michael@0 833 }
michael@0 834 };
michael@0 835 NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter)
michael@0 836
michael@0 837 } // namespace dmd
michael@0 838 } // namespace mozilla
michael@0 839
michael@0 840 #endif // MOZ_DMD
michael@0 841
michael@0 842 /**
michael@0 843 ** nsMemoryReporterManager implementation
michael@0 844 **/
michael@0 845
michael@0 846 NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager)
michael@0 847
michael@0 848 NS_IMETHODIMP
michael@0 849 nsMemoryReporterManager::Init()
michael@0 850 {
michael@0 851 #if defined(HAVE_JEMALLOC_STATS) && defined(XP_LINUX)
michael@0 852 if (!jemalloc_stats) {
michael@0 853 return NS_ERROR_FAILURE;
michael@0 854 }
michael@0 855 #endif
michael@0 856
michael@0 857 #ifdef HAVE_JEMALLOC_STATS
michael@0 858 RegisterStrongReporter(new JemallocHeapReporter());
michael@0 859 #endif
michael@0 860
michael@0 861 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 862 RegisterStrongReporter(new VsizeReporter());
michael@0 863 RegisterStrongReporter(new ResidentReporter());
michael@0 864 #endif
michael@0 865
michael@0 866 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
michael@0 867 RegisterStrongReporter(new VsizeMaxContiguousReporter());
michael@0 868 #endif
michael@0 869
michael@0 870 #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
michael@0 871 RegisterStrongReporter(new ResidentUniqueReporter());
michael@0 872 #endif
michael@0 873
michael@0 874 #ifdef HAVE_PAGE_FAULT_REPORTERS
michael@0 875 RegisterStrongReporter(new PageFaultsSoftReporter());
michael@0 876 RegisterStrongReporter(new PageFaultsHardReporter());
michael@0 877 #endif
michael@0 878
michael@0 879 #ifdef HAVE_PRIVATE_REPORTER
michael@0 880 RegisterStrongReporter(new PrivateReporter());
michael@0 881 #endif
michael@0 882
michael@0 883 RegisterStrongReporter(new AtomTablesReporter());
michael@0 884
michael@0 885 #ifdef MOZ_DMD
michael@0 886 RegisterStrongReporter(new mozilla::dmd::DMDReporter());
michael@0 887 #endif
michael@0 888
michael@0 889 #ifdef XP_UNIX
michael@0 890 nsMemoryInfoDumper::Initialize();
michael@0 891 #endif
michael@0 892
michael@0 893 return NS_OK;
michael@0 894 }
michael@0 895
michael@0 896 nsMemoryReporterManager::nsMemoryReporterManager()
michael@0 897 : mMutex("nsMemoryReporterManager::mMutex")
michael@0 898 , mIsRegistrationBlocked(false)
michael@0 899 , mStrongReporters(new StrongReportersTable())
michael@0 900 , mWeakReporters(new WeakReportersTable())
michael@0 901 , mSavedStrongReporters(nullptr)
michael@0 902 , mSavedWeakReporters(nullptr)
michael@0 903 , mNumChildProcesses(0)
michael@0 904 , mNextGeneration(1)
michael@0 905 , mGetReportsState(nullptr)
michael@0 906 {
michael@0 907 }
michael@0 908
michael@0 909 nsMemoryReporterManager::~nsMemoryReporterManager()
michael@0 910 {
michael@0 911 delete mStrongReporters;
michael@0 912 delete mWeakReporters;
michael@0 913 NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
michael@0 914 NS_ASSERTION(!mSavedWeakReporters, "failed to restore weak reporters");
michael@0 915 }
michael@0 916
michael@0 917 //#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
michael@0 918
michael@0 919 #ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
michael@0 920 #define MEMORY_REPORTING_LOG(format, ...) \
michael@0 921 fprintf(stderr, "++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
michael@0 922 #else
michael@0 923 #define MEMORY_REPORTING_LOG(...)
michael@0 924 #endif
michael@0 925
michael@0 926 void
michael@0 927 nsMemoryReporterManager::IncrementNumChildProcesses()
michael@0 928 {
michael@0 929 if (!NS_IsMainThread()) {
michael@0 930 MOZ_CRASH();
michael@0 931 }
michael@0 932 mNumChildProcesses++;
michael@0 933 MEMORY_REPORTING_LOG("IncrementNumChildProcesses --> %d\n",
michael@0 934 mNumChildProcesses);
michael@0 935 }
michael@0 936
michael@0 937 void
michael@0 938 nsMemoryReporterManager::DecrementNumChildProcesses()
michael@0 939 {
michael@0 940 if (!NS_IsMainThread()) {
michael@0 941 MOZ_CRASH();
michael@0 942 }
michael@0 943 MOZ_ASSERT(mNumChildProcesses > 0);
michael@0 944 mNumChildProcesses--;
michael@0 945 MEMORY_REPORTING_LOG("DecrementNumChildProcesses --> %d\n",
michael@0 946 mNumChildProcesses);
michael@0 947 }
michael@0 948
michael@0 949 NS_IMETHODIMP
michael@0 950 nsMemoryReporterManager::GetReports(
michael@0 951 nsIHandleReportCallback* aHandleReport,
michael@0 952 nsISupports* aHandleReportData,
michael@0 953 nsIFinishReportingCallback* aFinishReporting,
michael@0 954 nsISupports* aFinishReportingData)
michael@0 955 {
michael@0 956 return GetReportsExtended(aHandleReport, aHandleReportData,
michael@0 957 aFinishReporting, aFinishReportingData,
michael@0 958 /* minimize = */ false,
michael@0 959 /* DMDident = */ nsString());
michael@0 960 }
michael@0 961
michael@0 962 NS_IMETHODIMP
michael@0 963 nsMemoryReporterManager::GetReportsExtended(
michael@0 964 nsIHandleReportCallback* aHandleReport,
michael@0 965 nsISupports* aHandleReportData,
michael@0 966 nsIFinishReportingCallback* aFinishReporting,
michael@0 967 nsISupports* aFinishReportingData,
michael@0 968 bool aMinimize,
michael@0 969 const nsAString& aDMDDumpIdent)
michael@0 970 {
michael@0 971 nsresult rv;
michael@0 972
michael@0 973 // Memory reporters are not necessarily threadsafe, so this function must
michael@0 974 // be called from the main thread.
michael@0 975 if (!NS_IsMainThread()) {
michael@0 976 MOZ_CRASH();
michael@0 977 }
michael@0 978
michael@0 979 uint32_t generation = mNextGeneration++;
michael@0 980
michael@0 981 if (mGetReportsState) {
michael@0 982 // A request is in flight. Don't start another one. And don't report
michael@0 983 // an error; just ignore it, and let the in-flight request finish.
michael@0 984 MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
michael@0 985 generation, mGetReportsState->mGeneration);
michael@0 986 return NS_OK;
michael@0 987 }
michael@0 988
michael@0 989 MEMORY_REPORTING_LOG("GetReports (gen=%u, %d child(ren) present)\n",
michael@0 990 generation, mNumChildProcesses);
michael@0 991
michael@0 992 if (mNumChildProcesses > 0) {
michael@0 993 // Request memory reports from child processes. We do this *before*
michael@0 994 // collecting reports for this process so each process can collect
michael@0 995 // reports in parallel.
michael@0 996 nsCOMPtr<nsIObserverService> obs =
michael@0 997 do_GetService("@mozilla.org/observer-service;1");
michael@0 998 NS_ENSURE_STATE(obs);
michael@0 999
michael@0 1000 nsPrintfCString genStr("generation=%x minimize=%d DMDident=",
michael@0 1001 generation, aMinimize ? 1 : 0);
michael@0 1002 nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
michael@0 1003 msg += aDMDDumpIdent;
michael@0 1004
michael@0 1005 obs->NotifyObservers(nullptr, "child-memory-reporter-request",
michael@0 1006 msg.get());
michael@0 1007
michael@0 1008 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 1009 NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
michael@0 1010 rv = timer->InitWithFuncCallback(TimeoutCallback,
michael@0 1011 this, kTimeoutLengthMS,
michael@0 1012 nsITimer::TYPE_ONE_SHOT);
michael@0 1013 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1014
michael@0 1015 mGetReportsState = new GetReportsState(generation,
michael@0 1016 timer,
michael@0 1017 mNumChildProcesses,
michael@0 1018 aHandleReport,
michael@0 1019 aHandleReportData,
michael@0 1020 aFinishReporting,
michael@0 1021 aFinishReportingData,
michael@0 1022 aDMDDumpIdent);
michael@0 1023 } else {
michael@0 1024 mGetReportsState = new GetReportsState(generation,
michael@0 1025 nullptr,
michael@0 1026 /* mNumChildProcesses = */ 0,
michael@0 1027 aHandleReport,
michael@0 1028 aHandleReportData,
michael@0 1029 aFinishReporting,
michael@0 1030 aFinishReportingData,
michael@0 1031 aDMDDumpIdent);
michael@0 1032 }
michael@0 1033
michael@0 1034 if (aMinimize) {
michael@0 1035 rv = MinimizeMemoryUsage(NS_NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports));
michael@0 1036 } else {
michael@0 1037 rv = StartGettingReports();
michael@0 1038 }
michael@0 1039 return rv;
michael@0 1040 }
michael@0 1041
michael@0 1042 nsresult
michael@0 1043 nsMemoryReporterManager::StartGettingReports()
michael@0 1044 {
michael@0 1045 GetReportsState *s = mGetReportsState;
michael@0 1046
michael@0 1047 // Get reports for this process.
michael@0 1048 GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
michael@0 1049 s->mDMDDumpIdent);
michael@0 1050 s->mParentDone = true;
michael@0 1051
michael@0 1052 // If there are no remaining child processes, we can finish up immediately.
michael@0 1053 return (s->mNumChildProcessesCompleted >= s->mNumChildProcesses)
michael@0 1054 ? FinishReporting()
michael@0 1055 : NS_OK;
michael@0 1056 }
michael@0 1057
michael@0 1058 typedef nsCOMArray<nsIMemoryReporter> MemoryReporterArray;
michael@0 1059
michael@0 1060 static PLDHashOperator
michael@0 1061 StrongEnumerator(nsRefPtrHashKey<nsIMemoryReporter>* aElem, void* aData)
michael@0 1062 {
michael@0 1063 MemoryReporterArray *allReporters = static_cast<MemoryReporterArray*>(aData);
michael@0 1064 allReporters->AppendElement(aElem->GetKey());
michael@0 1065 return PL_DHASH_NEXT;
michael@0 1066 }
michael@0 1067
michael@0 1068 static PLDHashOperator
michael@0 1069 WeakEnumerator(nsPtrHashKey<nsIMemoryReporter>* aElem, void* aData)
michael@0 1070 {
michael@0 1071 MemoryReporterArray *allReporters = static_cast<MemoryReporterArray*>(aData);
michael@0 1072 allReporters->AppendElement(aElem->GetKey());
michael@0 1073 return PL_DHASH_NEXT;
michael@0 1074 }
michael@0 1075
michael@0 1076 NS_IMETHODIMP
michael@0 1077 nsMemoryReporterManager::GetReportsForThisProcess(
michael@0 1078 nsIHandleReportCallback* aHandleReport,
michael@0 1079 nsISupports* aHandleReportData)
michael@0 1080 {
michael@0 1081 return GetReportsForThisProcessExtended(aHandleReport,
michael@0 1082 aHandleReportData,
michael@0 1083 nsString());
michael@0 1084 }
michael@0 1085
michael@0 1086 NS_IMETHODIMP
michael@0 1087 nsMemoryReporterManager::GetReportsForThisProcessExtended(
michael@0 1088 nsIHandleReportCallback* aHandleReport,
michael@0 1089 nsISupports* aHandleReportData,
michael@0 1090 const nsAString& aDMDDumpIdent)
michael@0 1091 {
michael@0 1092 // Memory reporters are not necessarily threadsafe, so this function must
michael@0 1093 // be called from the main thread.
michael@0 1094 if (!NS_IsMainThread()) {
michael@0 1095 MOZ_CRASH();
michael@0 1096 }
michael@0 1097
michael@0 1098 #ifdef MOZ_DMD
michael@0 1099 if (!aDMDDumpIdent.IsEmpty()) {
michael@0 1100 // Clear DMD's reportedness state before running the memory
michael@0 1101 // reporters, to avoid spurious twice-reported warnings.
michael@0 1102 dmd::ClearReports();
michael@0 1103 }
michael@0 1104 #endif
michael@0 1105
michael@0 1106 MemoryReporterArray allReporters;
michael@0 1107 {
michael@0 1108 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1109 mStrongReporters->EnumerateEntries(StrongEnumerator, &allReporters);
michael@0 1110 mWeakReporters->EnumerateEntries(WeakEnumerator, &allReporters);
michael@0 1111 }
michael@0 1112 for (uint32_t i = 0; i < allReporters.Length(); i++) {
michael@0 1113 allReporters[i]->CollectReports(aHandleReport, aHandleReportData);
michael@0 1114 }
michael@0 1115
michael@0 1116 #ifdef MOZ_DMD
michael@0 1117 if (!aDMDDumpIdent.IsEmpty()) {
michael@0 1118 return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
michael@0 1119 }
michael@0 1120 #endif
michael@0 1121
michael@0 1122 return NS_OK;
michael@0 1123 }
michael@0 1124
michael@0 1125 // This function has no return value. If something goes wrong, there's no
michael@0 1126 // clear place to report the problem to, but that's ok -- we will end up
michael@0 1127 // hitting the timeout and executing TimeoutCallback().
michael@0 1128 void
michael@0 1129 nsMemoryReporterManager::HandleChildReports(
michael@0 1130 const uint32_t& aGeneration,
michael@0 1131 const InfallibleTArray<dom::MemoryReport>& aChildReports)
michael@0 1132 {
michael@0 1133 // Memory reporting only happens on the main thread.
michael@0 1134 if (!NS_IsMainThread()) {
michael@0 1135 MOZ_CRASH();
michael@0 1136 }
michael@0 1137
michael@0 1138 GetReportsState* s = mGetReportsState;
michael@0 1139
michael@0 1140 if (!s) {
michael@0 1141 // If we reach here, either:
michael@0 1142 //
michael@0 1143 // - A child process reported back too late, and no subsequent request
michael@0 1144 // is in flight.
michael@0 1145 //
michael@0 1146 // - (Unlikely) A "child-memory-reporter-request" notification was
michael@0 1147 // triggered from somewhere other than GetReports(), causing child
michael@0 1148 // processes to report back when the nsMemoryReporterManager wasn't
michael@0 1149 // expecting it.
michael@0 1150 //
michael@0 1151 // Either way, there's nothing to be done. Just ignore it.
michael@0 1152 MEMORY_REPORTING_LOG(
michael@0 1153 "HandleChildReports: no request in flight (aGen=%u)\n",
michael@0 1154 aGeneration);
michael@0 1155 return;
michael@0 1156 }
michael@0 1157
michael@0 1158 if (aGeneration != s->mGeneration) {
michael@0 1159 // If we reach here, a child process must have reported back, too late,
michael@0 1160 // while a subsequent (higher-numbered) request is in flight. Again,
michael@0 1161 // ignore it.
michael@0 1162 MOZ_ASSERT(aGeneration < s->mGeneration);
michael@0 1163 MEMORY_REPORTING_LOG(
michael@0 1164 "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
michael@0 1165 aGeneration, s->mGeneration);
michael@0 1166 return;
michael@0 1167 }
michael@0 1168
michael@0 1169 // Process the reports from the child process.
michael@0 1170 for (uint32_t i = 0; i < aChildReports.Length(); i++) {
michael@0 1171 const dom::MemoryReport& r = aChildReports[i];
michael@0 1172
michael@0 1173 // Child reports should have a non-empty process.
michael@0 1174 MOZ_ASSERT(!r.process().IsEmpty());
michael@0 1175
michael@0 1176 // If the call fails, ignore and continue.
michael@0 1177 s->mHandleReport->Callback(r.process(), r.path(), r.kind(),
michael@0 1178 r.units(), r.amount(), r.desc(),
michael@0 1179 s->mHandleReportData);
michael@0 1180 }
michael@0 1181
michael@0 1182 // If all the child processes have reported, we can cancel the timer and
michael@0 1183 // finish up. Otherwise, just return.
michael@0 1184
michael@0 1185 s->mNumChildProcessesCompleted++;
michael@0 1186 MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n",
michael@0 1187 aGeneration, s->mNumChildProcessesCompleted);
michael@0 1188
michael@0 1189 if (s->mNumChildProcessesCompleted >= s->mNumChildProcesses &&
michael@0 1190 s->mParentDone) {
michael@0 1191 s->mTimer->Cancel();
michael@0 1192 FinishReporting();
michael@0 1193 }
michael@0 1194 }
michael@0 1195
michael@0 1196 /* static */ void
michael@0 1197 nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
michael@0 1198 {
michael@0 1199 nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
michael@0 1200 GetReportsState* s = mgr->mGetReportsState;
michael@0 1201
michael@0 1202 MOZ_ASSERT(mgr->mGetReportsState);
michael@0 1203 MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u)\n",
michael@0 1204 s->mGeneration);
michael@0 1205
michael@0 1206 // We don't bother sending any kind of cancellation message to the child
michael@0 1207 // processes that haven't reported back.
michael@0 1208
michael@0 1209 if (s->mParentDone) {
michael@0 1210 mgr->FinishReporting();
michael@0 1211 } else {
michael@0 1212 // This is unlikely -- the timeout expired during MinimizeMemoryUsage.
michael@0 1213 MEMORY_REPORTING_LOG("Timeout expired before parent report started!");
michael@0 1214 // Let the parent continue with its report, but ensure that
michael@0 1215 // StartGettingReports gives up immediately after that.
michael@0 1216 s->mNumChildProcesses = s->mNumChildProcessesCompleted;
michael@0 1217 }
michael@0 1218 }
michael@0 1219
michael@0 1220 nsresult
michael@0 1221 nsMemoryReporterManager::FinishReporting()
michael@0 1222 {
michael@0 1223 // Memory reporting only happens on the main thread.
michael@0 1224 if (!NS_IsMainThread()) {
michael@0 1225 MOZ_CRASH();
michael@0 1226 }
michael@0 1227
michael@0 1228 MOZ_ASSERT(mGetReportsState);
michael@0 1229 MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u)\n",
michael@0 1230 mGetReportsState->mGeneration);
michael@0 1231
michael@0 1232 // Call this before deleting |mGetReportsState|. That way, if
michael@0 1233 // |mFinishReportData| calls GetReports(), it will silently abort, as
michael@0 1234 // required.
michael@0 1235 nsresult rv = mGetReportsState->mFinishReporting->Callback(
michael@0 1236 mGetReportsState->mFinishReportingData);
michael@0 1237
michael@0 1238 delete mGetReportsState;
michael@0 1239 mGetReportsState = nullptr;
michael@0 1240 return rv;
michael@0 1241 }
michael@0 1242
michael@0 1243 static void
michael@0 1244 CrashIfRefcountIsZero(nsISupports* aObj)
michael@0 1245 {
michael@0 1246 // This will probably crash if the object's refcount is 0.
michael@0 1247 uint32_t refcnt = NS_ADDREF(aObj);
michael@0 1248 if (refcnt <= 1) {
michael@0 1249 MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
michael@0 1250 }
michael@0 1251 NS_RELEASE(aObj);
michael@0 1252 }
michael@0 1253
michael@0 1254 nsresult
michael@0 1255 nsMemoryReporterManager::RegisterReporterHelper(
michael@0 1256 nsIMemoryReporter* aReporter, bool aForce, bool aStrong)
michael@0 1257 {
michael@0 1258 // This method is thread-safe.
michael@0 1259 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1260
michael@0 1261 if (mIsRegistrationBlocked && !aForce) {
michael@0 1262 return NS_ERROR_FAILURE;
michael@0 1263 }
michael@0 1264
michael@0 1265 if (mStrongReporters->Contains(aReporter) ||
michael@0 1266 mWeakReporters->Contains(aReporter))
michael@0 1267 {
michael@0 1268 return NS_ERROR_FAILURE;
michael@0 1269 }
michael@0 1270
michael@0 1271 // If |aStrong| is true, |aReporter| may have a refcnt of 0, so we take
michael@0 1272 // a kung fu death grip before calling PutEntry. Otherwise, if PutEntry
michael@0 1273 // addref'ed and released |aReporter| before finally addref'ing it for
michael@0 1274 // good, it would free aReporter! The kung fu death grip could itself be
michael@0 1275 // problematic if PutEntry didn't addref |aReporter| (because then when the
michael@0 1276 // death grip goes out of scope, we would delete the reporter). In debug
michael@0 1277 // mode, we check that this doesn't happen.
michael@0 1278 //
michael@0 1279 // If |aStrong| is false, we require that |aReporter| have a non-zero
michael@0 1280 // refcnt.
michael@0 1281 //
michael@0 1282 if (aStrong) {
michael@0 1283 nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
michael@0 1284 mStrongReporters->PutEntry(aReporter);
michael@0 1285 CrashIfRefcountIsZero(aReporter);
michael@0 1286 } else {
michael@0 1287 CrashIfRefcountIsZero(aReporter);
michael@0 1288 nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
michael@0 1289 if (jsComponent) {
michael@0 1290 // We cannot allow non-native reporters (WrappedJS), since we'll be
michael@0 1291 // holding onto a raw pointer, which would point to the wrapper,
michael@0 1292 // and that wrapper is likely to go away as soon as this register
michael@0 1293 // call finishes. This would then lead to subsequent crashes in
michael@0 1294 // CollectReports().
michael@0 1295 return NS_ERROR_XPC_BAD_CONVERT_JS;
michael@0 1296 }
michael@0 1297 mWeakReporters->PutEntry(aReporter);
michael@0 1298 }
michael@0 1299
michael@0 1300 return NS_OK;
michael@0 1301 }
michael@0 1302
michael@0 1303 NS_IMETHODIMP
michael@0 1304 nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
michael@0 1305 {
michael@0 1306 return RegisterReporterHelper(aReporter, /* force = */ false,
michael@0 1307 /* strong = */ true);
michael@0 1308 }
michael@0 1309
michael@0 1310 NS_IMETHODIMP
michael@0 1311 nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
michael@0 1312 {
michael@0 1313 return RegisterReporterHelper(aReporter, /* force = */ false,
michael@0 1314 /* strong = */ false);
michael@0 1315 }
michael@0 1316
michael@0 1317 NS_IMETHODIMP
michael@0 1318 nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
michael@0 1319 nsIMemoryReporter* aReporter)
michael@0 1320 {
michael@0 1321 return RegisterReporterHelper(aReporter, /* force = */ true,
michael@0 1322 /* strong = */ true);
michael@0 1323 }
michael@0 1324
michael@0 1325 NS_IMETHODIMP
michael@0 1326 nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
michael@0 1327 {
michael@0 1328 // This method is thread-safe.
michael@0 1329 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1330
michael@0 1331 MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
michael@0 1332
michael@0 1333 if (mStrongReporters->Contains(aReporter)) {
michael@0 1334 mStrongReporters->RemoveEntry(aReporter);
michael@0 1335 return NS_OK;
michael@0 1336 }
michael@0 1337
michael@0 1338 return NS_ERROR_FAILURE;
michael@0 1339 }
michael@0 1340
michael@0 1341 NS_IMETHODIMP
michael@0 1342 nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
michael@0 1343 {
michael@0 1344 // This method is thread-safe.
michael@0 1345 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1346
michael@0 1347 MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
michael@0 1348
michael@0 1349 if (mWeakReporters->Contains(aReporter)) {
michael@0 1350 mWeakReporters->RemoveEntry(aReporter);
michael@0 1351 return NS_OK;
michael@0 1352 }
michael@0 1353
michael@0 1354 return NS_ERROR_FAILURE;
michael@0 1355 }
michael@0 1356
michael@0 1357 NS_IMETHODIMP
michael@0 1358 nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
michael@0 1359 {
michael@0 1360 // This method is thread-safe.
michael@0 1361 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1362 if (mIsRegistrationBlocked) {
michael@0 1363 return NS_ERROR_FAILURE;
michael@0 1364 }
michael@0 1365 mIsRegistrationBlocked = true;
michael@0 1366
michael@0 1367 // Hide the existing reporters, saving them for later restoration.
michael@0 1368 MOZ_ASSERT(!mSavedStrongReporters);
michael@0 1369 MOZ_ASSERT(!mSavedWeakReporters);
michael@0 1370 mSavedStrongReporters = mStrongReporters;
michael@0 1371 mSavedWeakReporters = mWeakReporters;
michael@0 1372 mStrongReporters = new StrongReportersTable();
michael@0 1373 mWeakReporters = new WeakReportersTable();
michael@0 1374
michael@0 1375 return NS_OK;
michael@0 1376 }
michael@0 1377
michael@0 1378 NS_IMETHODIMP
michael@0 1379 nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
michael@0 1380 {
michael@0 1381 // This method is thread-safe.
michael@0 1382 mozilla::MutexAutoLock autoLock(mMutex);
michael@0 1383 if (!mIsRegistrationBlocked) {
michael@0 1384 return NS_ERROR_FAILURE;
michael@0 1385 }
michael@0 1386
michael@0 1387 // Banish the current reporters, and restore the hidden ones.
michael@0 1388 delete mStrongReporters;
michael@0 1389 delete mWeakReporters;
michael@0 1390 mStrongReporters = mSavedStrongReporters;
michael@0 1391 mWeakReporters = mSavedWeakReporters;
michael@0 1392 mSavedStrongReporters = nullptr;
michael@0 1393 mSavedWeakReporters = nullptr;
michael@0 1394
michael@0 1395 mIsRegistrationBlocked = false;
michael@0 1396 return NS_OK;
michael@0 1397 }
michael@0 1398
michael@0 1399 // This is just a wrapper for int64_t that implements nsISupports, so it can be
michael@0 1400 // passed to nsIMemoryReporter::CollectReports.
michael@0 1401 class Int64Wrapper MOZ_FINAL : public nsISupports
michael@0 1402 {
michael@0 1403 public:
michael@0 1404 NS_DECL_ISUPPORTS
michael@0 1405 Int64Wrapper() : mValue(0) { }
michael@0 1406 int64_t mValue;
michael@0 1407 };
michael@0 1408
michael@0 1409 NS_IMPL_ISUPPORTS0(Int64Wrapper)
michael@0 1410
michael@0 1411 class ExplicitCallback MOZ_FINAL : public nsIHandleReportCallback
michael@0 1412 {
michael@0 1413 public:
michael@0 1414 NS_DECL_ISUPPORTS
michael@0 1415
michael@0 1416 NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
michael@0 1417 int32_t aKind, int32_t aUnits, int64_t aAmount,
michael@0 1418 const nsACString& aDescription,
michael@0 1419 nsISupports* aWrappedExplicit)
michael@0 1420 {
michael@0 1421 // Using the "heap-allocated" reporter here instead of
michael@0 1422 // nsMemoryReporterManager.heapAllocated goes against the usual
michael@0 1423 // pattern. But it's for a good reason: in tests, we can easily
michael@0 1424 // create artificial (i.e. deterministic) reporters -- which allows us
michael@0 1425 // to precisely test nsMemoryReporterManager.explicit -- but we can't
michael@0 1426 // do that for distinguished amounts.
michael@0 1427 if (aPath.Equals("heap-allocated") ||
michael@0 1428 (aKind == nsIMemoryReporter::KIND_NONHEAP &&
michael@0 1429 PromiseFlatCString(aPath).Find("explicit") == 0))
michael@0 1430 {
michael@0 1431 Int64Wrapper* wrappedInt64 = static_cast<Int64Wrapper*>(aWrappedExplicit);
michael@0 1432 wrappedInt64->mValue += aAmount;
michael@0 1433 }
michael@0 1434 return NS_OK;
michael@0 1435 }
michael@0 1436 };
michael@0 1437
michael@0 1438 NS_IMPL_ISUPPORTS(ExplicitCallback, nsIHandleReportCallback)
michael@0 1439
michael@0 1440 NS_IMETHODIMP
michael@0 1441 nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
michael@0 1442 {
michael@0 1443 if (NS_WARN_IF(!aAmount)) {
michael@0 1444 return NS_ERROR_INVALID_ARG;
michael@0 1445 }
michael@0 1446 *aAmount = 0;
michael@0 1447 #ifndef HAVE_JEMALLOC_STATS
michael@0 1448 return NS_ERROR_NOT_AVAILABLE;
michael@0 1449 #else
michael@0 1450
michael@0 1451 // For each reporter we call CollectReports and filter out the
michael@0 1452 // non-explicit, non-NONHEAP measurements (except for "heap-allocated").
michael@0 1453 // That's lots of wasted work, and we used to have a GetExplicitNonHeap()
michael@0 1454 // method which did this more efficiently, but it ended up being more
michael@0 1455 // trouble than it was worth.
michael@0 1456
michael@0 1457 nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback();
michael@0 1458 nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
michael@0 1459
michael@0 1460 GetReportsForThisProcess(handleReport, wrappedExplicitSize);
michael@0 1461
michael@0 1462 *aAmount = wrappedExplicitSize->mValue;
michael@0 1463
michael@0 1464 return NS_OK;
michael@0 1465 #endif // HAVE_JEMALLOC_STATS
michael@0 1466 }
michael@0 1467
michael@0 1468 NS_IMETHODIMP
michael@0 1469 nsMemoryReporterManager::GetVsize(int64_t* aVsize)
michael@0 1470 {
michael@0 1471 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 1472 return VsizeDistinguishedAmount(aVsize);
michael@0 1473 #else
michael@0 1474 *aVsize = 0;
michael@0 1475 return NS_ERROR_NOT_AVAILABLE;
michael@0 1476 #endif
michael@0 1477 }
michael@0 1478
michael@0 1479 NS_IMETHODIMP
michael@0 1480 nsMemoryReporterManager::GetVsizeMaxContiguous(int64_t* aAmount)
michael@0 1481 {
michael@0 1482 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
michael@0 1483 return VsizeMaxContiguousDistinguishedAmount(aAmount);
michael@0 1484 #else
michael@0 1485 *aAmount = 0;
michael@0 1486 return NS_ERROR_NOT_AVAILABLE;
michael@0 1487 #endif
michael@0 1488 }
michael@0 1489
michael@0 1490 NS_IMETHODIMP
michael@0 1491 nsMemoryReporterManager::GetResident(int64_t* aAmount)
michael@0 1492 {
michael@0 1493 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 1494 return ResidentDistinguishedAmount(aAmount);
michael@0 1495 #else
michael@0 1496 *aAmount = 0;
michael@0 1497 return NS_ERROR_NOT_AVAILABLE;
michael@0 1498 #endif
michael@0 1499 }
michael@0 1500
michael@0 1501 NS_IMETHODIMP
michael@0 1502 nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
michael@0 1503 {
michael@0 1504 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 1505 return ResidentFastDistinguishedAmount(aAmount);
michael@0 1506 #else
michael@0 1507 *aAmount = 0;
michael@0 1508 return NS_ERROR_NOT_AVAILABLE;
michael@0 1509 #endif
michael@0 1510 }
michael@0 1511
michael@0 1512 /*static*/
michael@0 1513 int64_t nsMemoryReporterManager::ResidentFast()
michael@0 1514 {
michael@0 1515 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
michael@0 1516 int64_t amount;
michael@0 1517 ResidentFastDistinguishedAmount(&amount);
michael@0 1518 return amount;
michael@0 1519 #else
michael@0 1520 return 0;
michael@0 1521 #endif
michael@0 1522 }
michael@0 1523
michael@0 1524 NS_IMETHODIMP
michael@0 1525 nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
michael@0 1526 {
michael@0 1527 #ifdef HAVE_JEMALLOC_STATS
michael@0 1528 jemalloc_stats_t stats;
michael@0 1529 jemalloc_stats(&stats);
michael@0 1530 *aAmount = stats.allocated;
michael@0 1531 return NS_OK;
michael@0 1532 #else
michael@0 1533 *aAmount = 0;
michael@0 1534 return NS_ERROR_NOT_AVAILABLE;
michael@0 1535 #endif
michael@0 1536 }
michael@0 1537
michael@0 1538 // This has UNITS_PERCENTAGE, so it is multiplied by 100x.
michael@0 1539 NS_IMETHODIMP
michael@0 1540 nsMemoryReporterManager::GetHeapOverheadRatio(int64_t* aAmount)
michael@0 1541 {
michael@0 1542 #ifdef HAVE_JEMALLOC_STATS
michael@0 1543 jemalloc_stats_t stats;
michael@0 1544 jemalloc_stats(&stats);
michael@0 1545 *aAmount = HeapOverheadRatio(&stats);
michael@0 1546 return NS_OK;
michael@0 1547 #else
michael@0 1548 *aAmount = 0;
michael@0 1549 return NS_ERROR_NOT_AVAILABLE;
michael@0 1550 #endif
michael@0 1551 }
michael@0 1552
michael@0 1553 static nsresult
michael@0 1554 GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
michael@0 1555 {
michael@0 1556 if (aAmountFn) {
michael@0 1557 *aAmount = aAmountFn();
michael@0 1558 return NS_OK;
michael@0 1559 }
michael@0 1560 *aAmount = 0;
michael@0 1561 return NS_ERROR_NOT_AVAILABLE;
michael@0 1562 }
michael@0 1563
michael@0 1564 NS_IMETHODIMP
michael@0 1565 nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
michael@0 1566 {
michael@0 1567 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
michael@0 1568 }
michael@0 1569
michael@0 1570 NS_IMETHODIMP
michael@0 1571 nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
michael@0 1572 {
michael@0 1573 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
michael@0 1574 }
michael@0 1575
michael@0 1576 NS_IMETHODIMP
michael@0 1577 nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
michael@0 1578 {
michael@0 1579 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
michael@0 1580 aAmount);
michael@0 1581 }
michael@0 1582
michael@0 1583 NS_IMETHODIMP
michael@0 1584 nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
michael@0 1585 {
michael@0 1586 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
michael@0 1587 aAmount);
michael@0 1588 }
michael@0 1589
michael@0 1590 NS_IMETHODIMP
michael@0 1591 nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
michael@0 1592 {
michael@0 1593 return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
michael@0 1594 aAmount);
michael@0 1595 }
michael@0 1596
michael@0 1597 NS_IMETHODIMP
michael@0 1598 nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
michael@0 1599 {
michael@0 1600 return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
michael@0 1601 }
michael@0 1602
michael@0 1603 NS_IMETHODIMP
michael@0 1604 nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
michael@0 1605 {
michael@0 1606 return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
michael@0 1607 }
michael@0 1608
michael@0 1609 NS_IMETHODIMP
michael@0 1610 nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
michael@0 1611 {
michael@0 1612 return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
michael@0 1613 }
michael@0 1614
michael@0 1615 NS_IMETHODIMP
michael@0 1616 nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
michael@0 1617 {
michael@0 1618 return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
michael@0 1619 }
michael@0 1620
michael@0 1621 NS_IMETHODIMP
michael@0 1622 nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
michael@0 1623 {
michael@0 1624 #ifdef HAVE_PAGE_FAULT_REPORTERS
michael@0 1625 return PageFaultsHardDistinguishedAmount(aAmount);
michael@0 1626 #else
michael@0 1627 *aAmount = 0;
michael@0 1628 return NS_ERROR_NOT_AVAILABLE;
michael@0 1629 #endif
michael@0 1630 }
michael@0 1631
michael@0 1632 NS_IMETHODIMP
michael@0 1633 nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
michael@0 1634 {
michael@0 1635 void* p = malloc(16);
michael@0 1636 if (!p) {
michael@0 1637 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1638 }
michael@0 1639 size_t usable = moz_malloc_usable_size(p);
michael@0 1640 free(p);
michael@0 1641 *aHas = !!(usable > 0);
michael@0 1642 return NS_OK;
michael@0 1643 }
michael@0 1644
michael@0 1645 namespace {
michael@0 1646
michael@0 1647 /**
michael@0 1648 * This runnable lets us implement
michael@0 1649 * nsIMemoryReporterManager::MinimizeMemoryUsage(). We fire a heap-minimize
michael@0 1650 * notification, spin the event loop, and repeat this process a few times.
michael@0 1651 *
michael@0 1652 * When this sequence finishes, we invoke the callback function passed to the
michael@0 1653 * runnable's constructor.
michael@0 1654 */
michael@0 1655 class MinimizeMemoryUsageRunnable : public nsRunnable
michael@0 1656 {
michael@0 1657 public:
michael@0 1658 MinimizeMemoryUsageRunnable(nsIRunnable* aCallback)
michael@0 1659 : mCallback(aCallback)
michael@0 1660 , mRemainingIters(sNumIters)
michael@0 1661 {}
michael@0 1662
michael@0 1663 NS_IMETHOD Run()
michael@0 1664 {
michael@0 1665 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 1666 if (!os) {
michael@0 1667 return NS_ERROR_FAILURE;
michael@0 1668 }
michael@0 1669
michael@0 1670 if (mRemainingIters == 0) {
michael@0 1671 os->NotifyObservers(nullptr, "after-minimize-memory-usage",
michael@0 1672 MOZ_UTF16("MinimizeMemoryUsageRunnable"));
michael@0 1673 if (mCallback) {
michael@0 1674 mCallback->Run();
michael@0 1675 }
michael@0 1676 return NS_OK;
michael@0 1677 }
michael@0 1678
michael@0 1679 os->NotifyObservers(nullptr, "memory-pressure",
michael@0 1680 MOZ_UTF16("heap-minimize"));
michael@0 1681 mRemainingIters--;
michael@0 1682 NS_DispatchToMainThread(this);
michael@0 1683
michael@0 1684 return NS_OK;
michael@0 1685 }
michael@0 1686
michael@0 1687 private:
michael@0 1688 // Send sNumIters heap-minimize notifications, spinning the event
michael@0 1689 // loop after each notification (see bug 610166 comment 12 for an
michael@0 1690 // explanation), because one notification doesn't cut it.
michael@0 1691 static const uint32_t sNumIters = 3;
michael@0 1692
michael@0 1693 nsCOMPtr<nsIRunnable> mCallback;
michael@0 1694 uint32_t mRemainingIters;
michael@0 1695 };
michael@0 1696
michael@0 1697 } // anonymous namespace
michael@0 1698
michael@0 1699 NS_IMETHODIMP
michael@0 1700 nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback)
michael@0 1701 {
michael@0 1702 nsRefPtr<MinimizeMemoryUsageRunnable> runnable =
michael@0 1703 new MinimizeMemoryUsageRunnable(aCallback);
michael@0 1704
michael@0 1705 return NS_DispatchToMainThread(runnable);
michael@0 1706 }
michael@0 1707
michael@0 1708 NS_IMETHODIMP
michael@0 1709 nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow,
michael@0 1710 int64_t* aJSObjectsSize,
michael@0 1711 int64_t* aJSStringsSize,
michael@0 1712 int64_t* aJSOtherSize,
michael@0 1713 int64_t* aDomSize,
michael@0 1714 int64_t* aStyleSize,
michael@0 1715 int64_t* aOtherSize,
michael@0 1716 int64_t* aTotalSize,
michael@0 1717 double* aJSMilliseconds,
michael@0 1718 double* aNonJSMilliseconds)
michael@0 1719 {
michael@0 1720 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
michael@0 1721 nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aTopWindow);
michael@0 1722 if (NS_WARN_IF(!global) || NS_WARN_IF(!piWindow)) {
michael@0 1723 return NS_ERROR_FAILURE;
michael@0 1724 }
michael@0 1725
michael@0 1726 TimeStamp t1 = TimeStamp::Now();
michael@0 1727
michael@0 1728 // Measure JS memory consumption (and possibly some non-JS consumption, via
michael@0 1729 // |jsPrivateSize|).
michael@0 1730 size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
michael@0 1731 nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
michael@0 1732 &jsObjectsSize, &jsStringsSize,
michael@0 1733 &jsPrivateSize, &jsOtherSize);
michael@0 1734 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 1735 return rv;
michael@0 1736 }
michael@0 1737
michael@0 1738 TimeStamp t2 = TimeStamp::Now();
michael@0 1739
michael@0 1740 // Measure non-JS memory consumption.
michael@0 1741 size_t domSize, styleSize, otherSize;
michael@0 1742 mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
michael@0 1743
michael@0 1744 TimeStamp t3 = TimeStamp::Now();
michael@0 1745
michael@0 1746 *aTotalSize = 0;
michael@0 1747 #define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
michael@0 1748 DO(aJSObjectsSize, jsObjectsSize);
michael@0 1749 DO(aJSStringsSize, jsStringsSize);
michael@0 1750 DO(aJSOtherSize, jsOtherSize);
michael@0 1751 DO(aDomSize, jsPrivateSize + domSize);
michael@0 1752 DO(aStyleSize, styleSize);
michael@0 1753 DO(aOtherSize, otherSize);
michael@0 1754 #undef DO
michael@0 1755
michael@0 1756 *aJSMilliseconds = (t2 - t1).ToMilliseconds();
michael@0 1757 *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
michael@0 1758
michael@0 1759 return NS_OK;
michael@0 1760 }
michael@0 1761
michael@0 1762 namespace mozilla {
michael@0 1763
michael@0 1764 nsresult
michael@0 1765 RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
michael@0 1766 {
michael@0 1767 // Hold a strong reference to the argument to make sure it gets released if
michael@0 1768 // we return early below.
michael@0 1769 nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
michael@0 1770
michael@0 1771 nsCOMPtr<nsIMemoryReporterManager> mgr =
michael@0 1772 do_GetService("@mozilla.org/memory-reporter-manager;1");
michael@0 1773 if (!mgr) {
michael@0 1774 return NS_ERROR_FAILURE;
michael@0 1775 }
michael@0 1776 return mgr->RegisterStrongReporter(reporter);
michael@0 1777 }
michael@0 1778
michael@0 1779 nsresult
michael@0 1780 RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
michael@0 1781 {
michael@0 1782 nsCOMPtr<nsIMemoryReporterManager> mgr =
michael@0 1783 do_GetService("@mozilla.org/memory-reporter-manager;1");
michael@0 1784 if (!mgr) {
michael@0 1785 return NS_ERROR_FAILURE;
michael@0 1786 }
michael@0 1787 return mgr->RegisterWeakReporter(aReporter);
michael@0 1788 }
michael@0 1789
michael@0 1790 nsresult
michael@0 1791 UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
michael@0 1792 {
michael@0 1793 nsCOMPtr<nsIMemoryReporterManager> mgr =
michael@0 1794 do_GetService("@mozilla.org/memory-reporter-manager;1");
michael@0 1795 if (!mgr) {
michael@0 1796 return NS_ERROR_FAILURE;
michael@0 1797 }
michael@0 1798 return mgr->UnregisterWeakReporter(aReporter);
michael@0 1799 }
michael@0 1800
michael@0 1801 #define GET_MEMORY_REPORTER_MANAGER(mgr) \
michael@0 1802 nsRefPtr<nsMemoryReporterManager> mgr = \
michael@0 1803 nsMemoryReporterManager::GetOrCreate(); \
michael@0 1804 if (!mgr) { \
michael@0 1805 return NS_ERROR_FAILURE; \
michael@0 1806 }
michael@0 1807
michael@0 1808 // Macro for generating functions that register distinguished amount functions
michael@0 1809 // with the memory reporter manager.
michael@0 1810 #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
michael@0 1811 nsresult \
michael@0 1812 Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \
michael@0 1813 { \
michael@0 1814 GET_MEMORY_REPORTER_MANAGER(mgr) \
michael@0 1815 mgr->mAmountFns.m##name = aAmountFn; \
michael@0 1816 return NS_OK; \
michael@0 1817 }
michael@0 1818
michael@0 1819 #define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
michael@0 1820 nsresult \
michael@0 1821 Unregister##name##DistinguishedAmount() \
michael@0 1822 { \
michael@0 1823 GET_MEMORY_REPORTER_MANAGER(mgr) \
michael@0 1824 mgr->mAmountFns.m##name = nullptr; \
michael@0 1825 return NS_OK; \
michael@0 1826 }
michael@0 1827
michael@0 1828 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
michael@0 1829 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
michael@0 1830 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
michael@0 1831 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
michael@0 1832
michael@0 1833 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
michael@0 1834
michael@0 1835 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
michael@0 1836 DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
michael@0 1837
michael@0 1838 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
michael@0 1839 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
michael@0 1840
michael@0 1841 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
michael@0 1842
michael@0 1843 #undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
michael@0 1844 #undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
michael@0 1845
michael@0 1846 #define DEFINE_REGISTER_SIZE_OF_TAB(name) \
michael@0 1847 nsresult \
michael@0 1848 Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn) \
michael@0 1849 { \
michael@0 1850 GET_MEMORY_REPORTER_MANAGER(mgr) \
michael@0 1851 mgr->mSizeOfTabFns.m##name = aSizeOfTabFn; \
michael@0 1852 return NS_OK; \
michael@0 1853 }
michael@0 1854
michael@0 1855 DEFINE_REGISTER_SIZE_OF_TAB(JS);
michael@0 1856 DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
michael@0 1857
michael@0 1858 #undef DEFINE_REGISTER_SIZE_OF_TAB
michael@0 1859
michael@0 1860 #undef GET_MEMORY_REPORTER_MANAGER
michael@0 1861
michael@0 1862 }
michael@0 1863
michael@0 1864 #if defined(MOZ_DMD)
michael@0 1865
michael@0 1866 namespace mozilla {
michael@0 1867 namespace dmd {
michael@0 1868
michael@0 1869 class DoNothingCallback MOZ_FINAL : public nsIHandleReportCallback
michael@0 1870 {
michael@0 1871 public:
michael@0 1872 NS_DECL_ISUPPORTS
michael@0 1873
michael@0 1874 NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
michael@0 1875 int32_t aKind, int32_t aUnits, int64_t aAmount,
michael@0 1876 const nsACString& aDescription,
michael@0 1877 nsISupports* aData)
michael@0 1878 {
michael@0 1879 // Do nothing; the reporter has already reported to DMD.
michael@0 1880 return NS_OK;
michael@0 1881 }
michael@0 1882 };
michael@0 1883
michael@0 1884 NS_IMPL_ISUPPORTS(DoNothingCallback, nsIHandleReportCallback)
michael@0 1885
michael@0 1886 void
michael@0 1887 RunReportersForThisProcess()
michael@0 1888 {
michael@0 1889 nsCOMPtr<nsIMemoryReporterManager> mgr =
michael@0 1890 do_GetService("@mozilla.org/memory-reporter-manager;1");
michael@0 1891
michael@0 1892 nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
michael@0 1893
michael@0 1894 mgr->GetReportsForThisProcess(doNothing, nullptr);
michael@0 1895 }
michael@0 1896
michael@0 1897 } // namespace dmd
michael@0 1898 } // namespace mozilla
michael@0 1899
michael@0 1900 #endif // defined(MOZ_DMD)
michael@0 1901

mercurial