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