|
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/. */ |
|
6 |
|
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 |
|
29 |
|
30 #ifndef XP_WIN |
|
31 #include <unistd.h> |
|
32 #endif |
|
33 |
|
34 using namespace mozilla; |
|
35 |
|
36 #if defined(MOZ_MEMORY) |
|
37 # define HAVE_JEMALLOC_STATS 1 |
|
38 # include "mozmemory.h" |
|
39 #endif // MOZ_MEMORY |
|
40 |
|
41 #if defined(XP_LINUX) |
|
42 |
|
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 } |
|
62 |
|
63 #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1 |
|
64 static nsresult |
|
65 VsizeDistinguishedAmount(int64_t* aN) |
|
66 { |
|
67 return GetProcSelfStatmField(0, aN); |
|
68 } |
|
69 |
|
70 static nsresult |
|
71 ResidentDistinguishedAmount(int64_t* aN) |
|
72 { |
|
73 return GetProcSelfStatmField(1, aN); |
|
74 } |
|
75 |
|
76 static nsresult |
|
77 ResidentFastDistinguishedAmount(int64_t* aN) |
|
78 { |
|
79 return ResidentDistinguishedAmount(aN); |
|
80 } |
|
81 |
|
82 #define HAVE_RESIDENT_UNIQUE_REPORTER |
|
83 class ResidentUniqueReporter MOZ_FINAL : public nsIMemoryReporter |
|
84 { |
|
85 public: |
|
86 NS_DECL_ISUPPORTS |
|
87 |
|
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. |
|
97 |
|
98 FILE* f = fopen("/proc/self/smaps", "r"); |
|
99 if (NS_WARN_IF(!f)) { |
|
100 return NS_ERROR_UNEXPECTED; |
|
101 } |
|
102 |
|
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 } |
|
112 |
|
113 fclose(f); |
|
114 |
|
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) |
|
124 |
|
125 #elif defined(__DragonFly__) || defined(__FreeBSD__) \ |
|
126 || defined(__NetBSD__) || defined(__OpenBSD__) \ |
|
127 || defined(__FreeBSD_kernel__) |
|
128 |
|
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 |
|
134 |
|
135 #include <unistd.h> |
|
136 |
|
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 |
|
144 |
|
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 |
|
159 |
|
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 } |
|
180 |
|
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 } |
|
192 |
|
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 } |
|
203 |
|
204 static nsresult |
|
205 ResidentFastDistinguishedAmount(int64_t* aN) |
|
206 { |
|
207 return ResidentDistinguishedAmount(aN); |
|
208 } |
|
209 |
|
210 #ifdef __FreeBSD__ |
|
211 #include <libutil.h> |
|
212 #include <algorithm> |
|
213 |
|
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 } |
|
228 |
|
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 } |
|
238 |
|
239 free(vmmap); |
|
240 return NS_OK; |
|
241 } |
|
242 |
|
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 } |
|
253 |
|
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 |
|
266 |
|
267 #elif defined(SOLARIS) |
|
268 |
|
269 #include <procfs.h> |
|
270 #include <fcntl.h> |
|
271 #include <unistd.h> |
|
272 |
|
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 } |
|
313 |
|
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 } |
|
326 |
|
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 } |
|
338 |
|
339 static nsresult |
|
340 ResidentFastDistinguishedAmount(int64_t* aN) |
|
341 { |
|
342 return ResidentDistinguishedAmount(aN); |
|
343 } |
|
344 |
|
345 #elif defined(XP_MACOSX) |
|
346 |
|
347 #include <mach/mach_init.h> |
|
348 #include <mach/task.h> |
|
349 |
|
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 } |
|
358 |
|
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 } |
|
373 |
|
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 |
|
390 |
|
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 } |
|
398 |
|
399 static nsresult |
|
400 ResidentFastDistinguishedAmount(int64_t* aN) |
|
401 { |
|
402 return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false); |
|
403 } |
|
404 |
|
405 static nsresult |
|
406 ResidentDistinguishedAmount(int64_t* aN) |
|
407 { |
|
408 return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true); |
|
409 } |
|
410 |
|
411 #elif defined(XP_WIN) |
|
412 |
|
413 #include <windows.h> |
|
414 #include <psapi.h> |
|
415 #include <algorithm> |
|
416 |
|
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); |
|
423 |
|
424 if (!GlobalMemoryStatusEx(&s)) { |
|
425 return NS_ERROR_FAILURE; |
|
426 } |
|
427 |
|
428 *aN = s.ullTotalVirtual - s.ullAvailVirtual; |
|
429 return NS_OK; |
|
430 } |
|
431 |
|
432 static nsresult |
|
433 ResidentDistinguishedAmount(int64_t* aN) |
|
434 { |
|
435 PROCESS_MEMORY_COUNTERS pmc; |
|
436 pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS); |
|
437 |
|
438 if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { |
|
439 return NS_ERROR_FAILURE; |
|
440 } |
|
441 |
|
442 *aN = pmc.WorkingSetSize; |
|
443 return NS_OK; |
|
444 } |
|
445 |
|
446 static nsresult |
|
447 ResidentFastDistinguishedAmount(int64_t* aN) |
|
448 { |
|
449 return ResidentDistinguishedAmount(aN); |
|
450 } |
|
451 |
|
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 } |
|
463 |
|
464 if (vmemInfo.State == MEM_FREE) { |
|
465 biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize); |
|
466 } |
|
467 |
|
468 SIZE_T lastAddress = currentAddress; |
|
469 currentAddress += vmemInfo.RegionSize; |
|
470 |
|
471 // If we overflow, we've examined all of the address space. |
|
472 if (currentAddress < lastAddress) { |
|
473 break; |
|
474 } |
|
475 } |
|
476 |
|
477 *aN = biggestRegion; |
|
478 return NS_OK; |
|
479 } |
|
480 |
|
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); |
|
487 |
|
488 if (!GetProcessMemoryInfo(GetCurrentProcess(), |
|
489 (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) { |
|
490 return NS_ERROR_FAILURE; |
|
491 } |
|
492 |
|
493 *aN = pmcex.PrivateUsage; |
|
494 return NS_OK; |
|
495 } |
|
496 #endif // XP_<PLATFORM> |
|
497 |
|
498 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER |
|
499 class VsizeMaxContiguousReporter MOZ_FINAL : public nsIMemoryReporter |
|
500 { |
|
501 public: |
|
502 NS_DECL_ISUPPORTS |
|
503 |
|
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 |
|
518 |
|
519 #ifdef HAVE_PRIVATE_REPORTER |
|
520 class PrivateReporter MOZ_FINAL : public nsIMemoryReporter |
|
521 { |
|
522 public: |
|
523 NS_DECL_ISUPPORTS |
|
524 |
|
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 |
|
540 |
|
541 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS |
|
542 class VsizeReporter MOZ_FINAL : public nsIMemoryReporter |
|
543 { |
|
544 public: |
|
545 NS_DECL_ISUPPORTS |
|
546 |
|
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); |
|
553 |
|
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) |
|
566 |
|
567 class ResidentReporter MOZ_FINAL : public nsIMemoryReporter |
|
568 { |
|
569 public: |
|
570 NS_DECL_ISUPPORTS |
|
571 |
|
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); |
|
578 |
|
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) |
|
590 |
|
591 #endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS |
|
592 |
|
593 #ifdef XP_UNIX |
|
594 |
|
595 #include <sys/resource.h> |
|
596 |
|
597 #define HAVE_PAGE_FAULT_REPORTERS 1 |
|
598 |
|
599 class PageFaultsSoftReporter MOZ_FINAL : public nsIMemoryReporter |
|
600 { |
|
601 public: |
|
602 NS_DECL_ISUPPORTS |
|
603 |
|
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; |
|
613 |
|
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) |
|
628 |
|
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 } |
|
640 |
|
641 class PageFaultsHardReporter MOZ_FINAL : public nsIMemoryReporter |
|
642 { |
|
643 public: |
|
644 NS_DECL_ISUPPORTS |
|
645 |
|
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); |
|
652 |
|
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) |
|
668 |
|
669 #endif // HAVE_PAGE_FAULT_REPORTERS |
|
670 |
|
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 **/ |
|
676 |
|
677 #ifdef HAVE_JEMALLOC_STATS |
|
678 |
|
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 } |
|
687 |
|
688 class JemallocHeapReporter MOZ_FINAL : public nsIMemoryReporter |
|
689 { |
|
690 public: |
|
691 NS_DECL_ISUPPORTS |
|
692 |
|
693 NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport, |
|
694 nsISupports* aData) |
|
695 { |
|
696 jemalloc_stats_t stats; |
|
697 jemalloc_stats(&stats); |
|
698 |
|
699 nsresult rv; |
|
700 |
|
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); |
|
708 |
|
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); |
|
721 |
|
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); |
|
727 |
|
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); |
|
736 |
|
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); |
|
746 |
|
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); |
|
754 |
|
755 return NS_OK; |
|
756 } |
|
757 }; |
|
758 NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter) |
|
759 |
|
760 #endif // HAVE_JEMALLOC_STATS |
|
761 |
|
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) |
|
770 |
|
771 public: |
|
772 NS_DECL_ISUPPORTS |
|
773 |
|
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) |
|
784 |
|
785 #ifdef MOZ_DMD |
|
786 |
|
787 namespace mozilla { |
|
788 namespace dmd { |
|
789 |
|
790 class DMDReporter MOZ_FINAL : public nsIMemoryReporter |
|
791 { |
|
792 public: |
|
793 NS_DECL_ISUPPORTS |
|
794 |
|
795 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, |
|
796 nsISupports* aData) |
|
797 { |
|
798 dmd::Sizes sizes; |
|
799 dmd::SizeOf(&sizes); |
|
800 |
|
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) |
|
811 |
|
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."); |
|
816 |
|
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."); |
|
821 |
|
822 REPORT("explicit/dmd/stack-traces/table", |
|
823 sizes.mStackTraceTable, |
|
824 "Memory used by DMD's stack trace table."); |
|
825 |
|
826 REPORT("explicit/dmd/block-table", |
|
827 sizes.mBlockTable, |
|
828 "Memory used by DMD's live block table."); |
|
829 |
|
830 #undef REPORT |
|
831 |
|
832 return NS_OK; |
|
833 } |
|
834 }; |
|
835 NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter) |
|
836 |
|
837 } // namespace dmd |
|
838 } // namespace mozilla |
|
839 |
|
840 #endif // MOZ_DMD |
|
841 |
|
842 /** |
|
843 ** nsMemoryReporterManager implementation |
|
844 **/ |
|
845 |
|
846 NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager) |
|
847 |
|
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 |
|
856 |
|
857 #ifdef HAVE_JEMALLOC_STATS |
|
858 RegisterStrongReporter(new JemallocHeapReporter()); |
|
859 #endif |
|
860 |
|
861 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS |
|
862 RegisterStrongReporter(new VsizeReporter()); |
|
863 RegisterStrongReporter(new ResidentReporter()); |
|
864 #endif |
|
865 |
|
866 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER |
|
867 RegisterStrongReporter(new VsizeMaxContiguousReporter()); |
|
868 #endif |
|
869 |
|
870 #ifdef HAVE_RESIDENT_UNIQUE_REPORTER |
|
871 RegisterStrongReporter(new ResidentUniqueReporter()); |
|
872 #endif |
|
873 |
|
874 #ifdef HAVE_PAGE_FAULT_REPORTERS |
|
875 RegisterStrongReporter(new PageFaultsSoftReporter()); |
|
876 RegisterStrongReporter(new PageFaultsHardReporter()); |
|
877 #endif |
|
878 |
|
879 #ifdef HAVE_PRIVATE_REPORTER |
|
880 RegisterStrongReporter(new PrivateReporter()); |
|
881 #endif |
|
882 |
|
883 RegisterStrongReporter(new AtomTablesReporter()); |
|
884 |
|
885 #ifdef MOZ_DMD |
|
886 RegisterStrongReporter(new mozilla::dmd::DMDReporter()); |
|
887 #endif |
|
888 |
|
889 #ifdef XP_UNIX |
|
890 nsMemoryInfoDumper::Initialize(); |
|
891 #endif |
|
892 |
|
893 return NS_OK; |
|
894 } |
|
895 |
|
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 } |
|
908 |
|
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 } |
|
916 |
|
917 //#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1 |
|
918 |
|
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 |
|
925 |
|
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 } |
|
936 |
|
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 } |
|
948 |
|
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 } |
|
961 |
|
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; |
|
972 |
|
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 } |
|
978 |
|
979 uint32_t generation = mNextGeneration++; |
|
980 |
|
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 } |
|
988 |
|
989 MEMORY_REPORTING_LOG("GetReports (gen=%u, %d child(ren) present)\n", |
|
990 generation, mNumChildProcesses); |
|
991 |
|
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); |
|
999 |
|
1000 nsPrintfCString genStr("generation=%x minimize=%d DMDident=", |
|
1001 generation, aMinimize ? 1 : 0); |
|
1002 nsAutoString msg = NS_ConvertUTF8toUTF16(genStr); |
|
1003 msg += aDMDDumpIdent; |
|
1004 |
|
1005 obs->NotifyObservers(nullptr, "child-memory-reporter-request", |
|
1006 msg.get()); |
|
1007 |
|
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); |
|
1014 |
|
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 } |
|
1033 |
|
1034 if (aMinimize) { |
|
1035 rv = MinimizeMemoryUsage(NS_NewRunnableMethod(this, &nsMemoryReporterManager::StartGettingReports)); |
|
1036 } else { |
|
1037 rv = StartGettingReports(); |
|
1038 } |
|
1039 return rv; |
|
1040 } |
|
1041 |
|
1042 nsresult |
|
1043 nsMemoryReporterManager::StartGettingReports() |
|
1044 { |
|
1045 GetReportsState *s = mGetReportsState; |
|
1046 |
|
1047 // Get reports for this process. |
|
1048 GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData, |
|
1049 s->mDMDDumpIdent); |
|
1050 s->mParentDone = true; |
|
1051 |
|
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 } |
|
1057 |
|
1058 typedef nsCOMArray<nsIMemoryReporter> MemoryReporterArray; |
|
1059 |
|
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 } |
|
1067 |
|
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 } |
|
1075 |
|
1076 NS_IMETHODIMP |
|
1077 nsMemoryReporterManager::GetReportsForThisProcess( |
|
1078 nsIHandleReportCallback* aHandleReport, |
|
1079 nsISupports* aHandleReportData) |
|
1080 { |
|
1081 return GetReportsForThisProcessExtended(aHandleReport, |
|
1082 aHandleReportData, |
|
1083 nsString()); |
|
1084 } |
|
1085 |
|
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 } |
|
1097 |
|
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 |
|
1105 |
|
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 } |
|
1115 |
|
1116 #ifdef MOZ_DMD |
|
1117 if (!aDMDDumpIdent.IsEmpty()) { |
|
1118 return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent); |
|
1119 } |
|
1120 #endif |
|
1121 |
|
1122 return NS_OK; |
|
1123 } |
|
1124 |
|
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 } |
|
1137 |
|
1138 GetReportsState* s = mGetReportsState; |
|
1139 |
|
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 } |
|
1157 |
|
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 } |
|
1168 |
|
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]; |
|
1172 |
|
1173 // Child reports should have a non-empty process. |
|
1174 MOZ_ASSERT(!r.process().IsEmpty()); |
|
1175 |
|
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 } |
|
1181 |
|
1182 // If all the child processes have reported, we can cancel the timer and |
|
1183 // finish up. Otherwise, just return. |
|
1184 |
|
1185 s->mNumChildProcessesCompleted++; |
|
1186 MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n", |
|
1187 aGeneration, s->mNumChildProcessesCompleted); |
|
1188 |
|
1189 if (s->mNumChildProcessesCompleted >= s->mNumChildProcesses && |
|
1190 s->mParentDone) { |
|
1191 s->mTimer->Cancel(); |
|
1192 FinishReporting(); |
|
1193 } |
|
1194 } |
|
1195 |
|
1196 /* static */ void |
|
1197 nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData) |
|
1198 { |
|
1199 nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData); |
|
1200 GetReportsState* s = mgr->mGetReportsState; |
|
1201 |
|
1202 MOZ_ASSERT(mgr->mGetReportsState); |
|
1203 MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u)\n", |
|
1204 s->mGeneration); |
|
1205 |
|
1206 // We don't bother sending any kind of cancellation message to the child |
|
1207 // processes that haven't reported back. |
|
1208 |
|
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 } |
|
1219 |
|
1220 nsresult |
|
1221 nsMemoryReporterManager::FinishReporting() |
|
1222 { |
|
1223 // Memory reporting only happens on the main thread. |
|
1224 if (!NS_IsMainThread()) { |
|
1225 MOZ_CRASH(); |
|
1226 } |
|
1227 |
|
1228 MOZ_ASSERT(mGetReportsState); |
|
1229 MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u)\n", |
|
1230 mGetReportsState->mGeneration); |
|
1231 |
|
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); |
|
1237 |
|
1238 delete mGetReportsState; |
|
1239 mGetReportsState = nullptr; |
|
1240 return rv; |
|
1241 } |
|
1242 |
|
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 } |
|
1253 |
|
1254 nsresult |
|
1255 nsMemoryReporterManager::RegisterReporterHelper( |
|
1256 nsIMemoryReporter* aReporter, bool aForce, bool aStrong) |
|
1257 { |
|
1258 // This method is thread-safe. |
|
1259 mozilla::MutexAutoLock autoLock(mMutex); |
|
1260 |
|
1261 if (mIsRegistrationBlocked && !aForce) { |
|
1262 return NS_ERROR_FAILURE; |
|
1263 } |
|
1264 |
|
1265 if (mStrongReporters->Contains(aReporter) || |
|
1266 mWeakReporters->Contains(aReporter)) |
|
1267 { |
|
1268 return NS_ERROR_FAILURE; |
|
1269 } |
|
1270 |
|
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 } |
|
1299 |
|
1300 return NS_OK; |
|
1301 } |
|
1302 |
|
1303 NS_IMETHODIMP |
|
1304 nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter) |
|
1305 { |
|
1306 return RegisterReporterHelper(aReporter, /* force = */ false, |
|
1307 /* strong = */ true); |
|
1308 } |
|
1309 |
|
1310 NS_IMETHODIMP |
|
1311 nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter) |
|
1312 { |
|
1313 return RegisterReporterHelper(aReporter, /* force = */ false, |
|
1314 /* strong = */ false); |
|
1315 } |
|
1316 |
|
1317 NS_IMETHODIMP |
|
1318 nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked( |
|
1319 nsIMemoryReporter* aReporter) |
|
1320 { |
|
1321 return RegisterReporterHelper(aReporter, /* force = */ true, |
|
1322 /* strong = */ true); |
|
1323 } |
|
1324 |
|
1325 NS_IMETHODIMP |
|
1326 nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter) |
|
1327 { |
|
1328 // This method is thread-safe. |
|
1329 mozilla::MutexAutoLock autoLock(mMutex); |
|
1330 |
|
1331 MOZ_ASSERT(!mWeakReporters->Contains(aReporter)); |
|
1332 |
|
1333 if (mStrongReporters->Contains(aReporter)) { |
|
1334 mStrongReporters->RemoveEntry(aReporter); |
|
1335 return NS_OK; |
|
1336 } |
|
1337 |
|
1338 return NS_ERROR_FAILURE; |
|
1339 } |
|
1340 |
|
1341 NS_IMETHODIMP |
|
1342 nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter) |
|
1343 { |
|
1344 // This method is thread-safe. |
|
1345 mozilla::MutexAutoLock autoLock(mMutex); |
|
1346 |
|
1347 MOZ_ASSERT(!mStrongReporters->Contains(aReporter)); |
|
1348 |
|
1349 if (mWeakReporters->Contains(aReporter)) { |
|
1350 mWeakReporters->RemoveEntry(aReporter); |
|
1351 return NS_OK; |
|
1352 } |
|
1353 |
|
1354 return NS_ERROR_FAILURE; |
|
1355 } |
|
1356 |
|
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; |
|
1366 |
|
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(); |
|
1374 |
|
1375 return NS_OK; |
|
1376 } |
|
1377 |
|
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 } |
|
1386 |
|
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; |
|
1394 |
|
1395 mIsRegistrationBlocked = false; |
|
1396 return NS_OK; |
|
1397 } |
|
1398 |
|
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 }; |
|
1408 |
|
1409 NS_IMPL_ISUPPORTS0(Int64Wrapper) |
|
1410 |
|
1411 class ExplicitCallback MOZ_FINAL : public nsIHandleReportCallback |
|
1412 { |
|
1413 public: |
|
1414 NS_DECL_ISUPPORTS |
|
1415 |
|
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 }; |
|
1437 |
|
1438 NS_IMPL_ISUPPORTS(ExplicitCallback, nsIHandleReportCallback) |
|
1439 |
|
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 |
|
1450 |
|
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. |
|
1456 |
|
1457 nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback(); |
|
1458 nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper(); |
|
1459 |
|
1460 GetReportsForThisProcess(handleReport, wrappedExplicitSize); |
|
1461 |
|
1462 *aAmount = wrappedExplicitSize->mValue; |
|
1463 |
|
1464 return NS_OK; |
|
1465 #endif // HAVE_JEMALLOC_STATS |
|
1466 } |
|
1467 |
|
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 } |
|
1478 |
|
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 } |
|
1489 |
|
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 } |
|
1500 |
|
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 } |
|
1511 |
|
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 } |
|
1523 |
|
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 } |
|
1537 |
|
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 } |
|
1552 |
|
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 } |
|
1563 |
|
1564 NS_IMETHODIMP |
|
1565 nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount) |
|
1566 { |
|
1567 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount); |
|
1568 } |
|
1569 |
|
1570 NS_IMETHODIMP |
|
1571 nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount) |
|
1572 { |
|
1573 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount); |
|
1574 } |
|
1575 |
|
1576 NS_IMETHODIMP |
|
1577 nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount) |
|
1578 { |
|
1579 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem, |
|
1580 aAmount); |
|
1581 } |
|
1582 |
|
1583 NS_IMETHODIMP |
|
1584 nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount) |
|
1585 { |
|
1586 return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser, |
|
1587 aAmount); |
|
1588 } |
|
1589 |
|
1590 NS_IMETHODIMP |
|
1591 nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount) |
|
1592 { |
|
1593 return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed, |
|
1594 aAmount); |
|
1595 } |
|
1596 |
|
1597 NS_IMETHODIMP |
|
1598 nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount) |
|
1599 { |
|
1600 return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount); |
|
1601 } |
|
1602 |
|
1603 NS_IMETHODIMP |
|
1604 nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount) |
|
1605 { |
|
1606 return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount); |
|
1607 } |
|
1608 |
|
1609 NS_IMETHODIMP |
|
1610 nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount) |
|
1611 { |
|
1612 return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount); |
|
1613 } |
|
1614 |
|
1615 NS_IMETHODIMP |
|
1616 nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount) |
|
1617 { |
|
1618 return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount); |
|
1619 } |
|
1620 |
|
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 } |
|
1631 |
|
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 } |
|
1644 |
|
1645 namespace { |
|
1646 |
|
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 {} |
|
1662 |
|
1663 NS_IMETHOD Run() |
|
1664 { |
|
1665 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); |
|
1666 if (!os) { |
|
1667 return NS_ERROR_FAILURE; |
|
1668 } |
|
1669 |
|
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 } |
|
1678 |
|
1679 os->NotifyObservers(nullptr, "memory-pressure", |
|
1680 MOZ_UTF16("heap-minimize")); |
|
1681 mRemainingIters--; |
|
1682 NS_DispatchToMainThread(this); |
|
1683 |
|
1684 return NS_OK; |
|
1685 } |
|
1686 |
|
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; |
|
1692 |
|
1693 nsCOMPtr<nsIRunnable> mCallback; |
|
1694 uint32_t mRemainingIters; |
|
1695 }; |
|
1696 |
|
1697 } // anonymous namespace |
|
1698 |
|
1699 NS_IMETHODIMP |
|
1700 nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback) |
|
1701 { |
|
1702 nsRefPtr<MinimizeMemoryUsageRunnable> runnable = |
|
1703 new MinimizeMemoryUsageRunnable(aCallback); |
|
1704 |
|
1705 return NS_DispatchToMainThread(runnable); |
|
1706 } |
|
1707 |
|
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 } |
|
1725 |
|
1726 TimeStamp t1 = TimeStamp::Now(); |
|
1727 |
|
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 } |
|
1737 |
|
1738 TimeStamp t2 = TimeStamp::Now(); |
|
1739 |
|
1740 // Measure non-JS memory consumption. |
|
1741 size_t domSize, styleSize, otherSize; |
|
1742 mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize); |
|
1743 |
|
1744 TimeStamp t3 = TimeStamp::Now(); |
|
1745 |
|
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 |
|
1755 |
|
1756 *aJSMilliseconds = (t2 - t1).ToMilliseconds(); |
|
1757 *aNonJSMilliseconds = (t3 - t2).ToMilliseconds(); |
|
1758 |
|
1759 return NS_OK; |
|
1760 } |
|
1761 |
|
1762 namespace mozilla { |
|
1763 |
|
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; |
|
1770 |
|
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 } |
|
1778 |
|
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 } |
|
1789 |
|
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 } |
|
1800 |
|
1801 #define GET_MEMORY_REPORTER_MANAGER(mgr) \ |
|
1802 nsRefPtr<nsMemoryReporterManager> mgr = \ |
|
1803 nsMemoryReporterManager::GetOrCreate(); \ |
|
1804 if (!mgr) { \ |
|
1805 return NS_ERROR_FAILURE; \ |
|
1806 } |
|
1807 |
|
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 } |
|
1818 |
|
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 } |
|
1827 |
|
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) |
|
1832 |
|
1833 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed) |
|
1834 |
|
1835 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite) |
|
1836 DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite) |
|
1837 |
|
1838 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual) |
|
1839 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical) |
|
1840 |
|
1841 DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows) |
|
1842 |
|
1843 #undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT |
|
1844 #undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT |
|
1845 |
|
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 } |
|
1854 |
|
1855 DEFINE_REGISTER_SIZE_OF_TAB(JS); |
|
1856 DEFINE_REGISTER_SIZE_OF_TAB(NonJS); |
|
1857 |
|
1858 #undef DEFINE_REGISTER_SIZE_OF_TAB |
|
1859 |
|
1860 #undef GET_MEMORY_REPORTER_MANAGER |
|
1861 |
|
1862 } |
|
1863 |
|
1864 #if defined(MOZ_DMD) |
|
1865 |
|
1866 namespace mozilla { |
|
1867 namespace dmd { |
|
1868 |
|
1869 class DoNothingCallback MOZ_FINAL : public nsIHandleReportCallback |
|
1870 { |
|
1871 public: |
|
1872 NS_DECL_ISUPPORTS |
|
1873 |
|
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 }; |
|
1883 |
|
1884 NS_IMPL_ISUPPORTS(DoNothingCallback, nsIHandleReportCallback) |
|
1885 |
|
1886 void |
|
1887 RunReportersForThisProcess() |
|
1888 { |
|
1889 nsCOMPtr<nsIMemoryReporterManager> mgr = |
|
1890 do_GetService("@mozilla.org/memory-reporter-manager;1"); |
|
1891 |
|
1892 nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback(); |
|
1893 |
|
1894 mgr->GetReportsForThisProcess(doNothing, nullptr); |
|
1895 } |
|
1896 |
|
1897 } // namespace dmd |
|
1898 } // namespace mozilla |
|
1899 |
|
1900 #endif // defined(MOZ_DMD) |
|
1901 |