1 : /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * mozilla.org
19 : * Portions created by the Initial Developer are Copyright (C) 2008
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Vladimir Vukicevic <vladimir@pobox.com> (original author)
24 : * Nicholas Nethercote <nnethercote@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsAtomTable.h"
41 : #include "nsAutoPtr.h"
42 : #include "nsCOMPtr.h"
43 : #include "nsServiceManagerUtils.h"
44 : #include "nsMemoryReporterManager.h"
45 : #include "nsArrayEnumerator.h"
46 : #include "nsISimpleEnumerator.h"
47 : #include "mozilla/Telemetry.h"
48 :
49 : using namespace mozilla;
50 :
51 3 : static PRInt64 GetExplicit()
52 : {
53 6 : nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
54 3 : if (mgr == nsnull)
55 0 : return (PRInt64)-1;
56 :
57 : PRInt64 n;
58 3 : nsresult rv = mgr->GetExplicit(&n);
59 3 : NS_ENSURE_SUCCESS(rv, rv);
60 :
61 3 : return n;
62 : }
63 :
64 : #if defined(MOZ_MEMORY)
65 : # if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX)
66 : # define HAVE_JEMALLOC_STATS 1
67 : # include "jemalloc.h"
68 : # elif defined(XP_LINUX)
69 : # define HAVE_JEMALLOC_STATS 1
70 : # include "jemalloc_types.h"
71 : // jemalloc is directly linked into firefox-bin; libxul doesn't link
72 : // with it. So if we tried to use jemalloc_stats directly here, it
73 : // wouldn't be defined. Instead, we don't include the jemalloc header
74 : // and weakly link against jemalloc_stats.
75 : extern "C" {
76 : extern void jemalloc_stats(jemalloc_stats_t* stats)
77 : NS_VISIBILITY_DEFAULT __attribute__((weak));
78 : }
79 : # endif // XP_LINUX
80 : #endif // MOZ_MEMORY
81 :
82 : #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS)
83 :
84 : #include <sys/time.h>
85 : #include <sys/resource.h>
86 :
87 3 : static PRInt64 GetHardPageFaults()
88 : {
89 : struct rusage usage;
90 3 : int err = getrusage(RUSAGE_SELF, &usage);
91 3 : if (err != 0) {
92 0 : return PRInt64(-1);
93 : }
94 :
95 3 : return usage.ru_majflt;
96 : }
97 :
98 0 : static PRInt64 GetSoftPageFaults()
99 : {
100 : struct rusage usage;
101 0 : int err = getrusage(RUSAGE_SELF, &usage);
102 0 : if (err != 0) {
103 0 : return PRInt64(-1);
104 : }
105 :
106 0 : return usage.ru_minflt;
107 : }
108 :
109 : #endif
110 :
111 : #if defined(XP_LINUX)
112 :
113 : #include <unistd.h>
114 3 : static PRInt64 GetProcSelfStatmField(int n)
115 : {
116 : // There are more than two fields, but we're only interested in the first
117 : // two.
118 : static const int MAX_FIELD = 2;
119 : size_t fields[MAX_FIELD];
120 3 : NS_ASSERTION(n < MAX_FIELD, "bad field number");
121 3 : FILE *f = fopen("/proc/self/statm", "r");
122 3 : if (f) {
123 3 : int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
124 3 : fclose(f);
125 3 : return (PRInt64) ((nread == MAX_FIELD) ? fields[n]*getpagesize() : -1);
126 : }
127 0 : return (PRInt64) -1;
128 : }
129 :
130 0 : static PRInt64 GetVsize()
131 : {
132 0 : return GetProcSelfStatmField(0);
133 : }
134 :
135 3 : static PRInt64 GetResident()
136 : {
137 3 : return GetProcSelfStatmField(1);
138 : }
139 :
140 : #elif defined(SOLARIS)
141 :
142 : #include <procfs.h>
143 : #include <fcntl.h>
144 : #include <unistd.h>
145 :
146 : static void XMappingIter(PRInt64& Vsize, PRInt64& Resident)
147 : {
148 : Vsize = -1;
149 : Resident = -1;
150 : int mapfd = open("/proc/self/xmap", O_RDONLY);
151 : struct stat st;
152 : prxmap_t *prmapp = NULL;
153 : if (mapfd >= 0) {
154 : if (!fstat(mapfd, &st)) {
155 : int nmap = st.st_size / sizeof(prxmap_t);
156 : while (1) {
157 : // stat(2) on /proc/<pid>/xmap returns an incorrect value,
158 : // prior to the release of Solaris 11.
159 : // Here is a workaround for it.
160 : nmap *= 2;
161 : prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
162 : if (!prmapp) {
163 : // out of memory
164 : break;
165 : }
166 : int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
167 : if (n < 0) {
168 : break;
169 : }
170 : if (nmap >= n / sizeof (prxmap_t)) {
171 : Vsize = 0;
172 : Resident = 0;
173 : for (int i = 0; i < n / sizeof (prxmap_t); i++) {
174 : Vsize += prmapp[i].pr_size;
175 : Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
176 : }
177 : break;
178 : }
179 : free(prmapp);
180 : }
181 : free(prmapp);
182 : }
183 : close(mapfd);
184 : }
185 : }
186 :
187 : static PRInt64 GetVsize()
188 : {
189 : PRInt64 Vsize, Resident;
190 : XMappingIter(Vsize, Resident);
191 : return Vsize;
192 : }
193 :
194 : static PRInt64 GetResident()
195 : {
196 : PRInt64 Vsize, Resident;
197 : XMappingIter(Vsize, Resident);
198 : return Resident;
199 : }
200 :
201 : #elif defined(XP_MACOSX)
202 :
203 : #include <mach/mach_init.h>
204 : #include <mach/task.h>
205 :
206 : static bool GetTaskBasicInfo(struct task_basic_info *ti)
207 : {
208 : mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
209 : kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
210 : (task_info_t)ti, &count);
211 : return kr == KERN_SUCCESS;
212 : }
213 :
214 : // The VSIZE figure on Mac includes huge amounts of shared memory and is always
215 : // absurdly high, eg. 2GB+ even at start-up. But both 'top' and 'ps' report
216 : // it, so we might as well too.
217 : static PRInt64 GetVsize()
218 : {
219 : task_basic_info ti;
220 : return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.virtual_size : -1);
221 : }
222 :
223 : static PRInt64 GetResident()
224 : {
225 : #ifdef HAVE_JEMALLOC_STATS
226 : // If we're using jemalloc on Mac, we need to instruct jemalloc to purge
227 : // the pages it has madvise(MADV_FREE)'d before we read our RSS. The OS
228 : // will take away MADV_FREE'd pages when there's memory pressure, so they
229 : // shouldn't count against our RSS.
230 : //
231 : // Purging these pages shouldn't take more than 10ms or so, but we want to
232 : // keep an eye on it since GetResident() is called on each Telemetry ping.
233 : {
234 : Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
235 : jemalloc_purge_freed_pages();
236 : }
237 : #endif
238 :
239 : task_basic_info ti;
240 : return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1);
241 : }
242 :
243 : #elif defined(XP_WIN)
244 :
245 : #include <windows.h>
246 : #include <psapi.h>
247 :
248 : static PRInt64 GetVsize()
249 : {
250 : MEMORYSTATUSEX s;
251 : s.dwLength = sizeof(s);
252 :
253 : bool success = GlobalMemoryStatusEx(&s);
254 : if (!success)
255 : return -1;
256 :
257 : return s.ullTotalVirtual - s.ullAvailVirtual;
258 : }
259 :
260 : static PRInt64 GetPrivate()
261 : {
262 : PROCESS_MEMORY_COUNTERS_EX pmcex;
263 : pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
264 :
265 : if (!GetProcessMemoryInfo(GetCurrentProcess(),
266 : (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex)))
267 : return (PRInt64) -1;
268 :
269 : return pmcex.PrivateUsage;
270 : }
271 :
272 : NS_MEMORY_REPORTER_IMPLEMENT(Private,
273 : "private",
274 : KIND_OTHER,
275 : UNITS_BYTES,
276 : GetPrivate,
277 : "Memory that cannot be shared with other processes, including memory that "
278 : "is committed and marked MEM_PRIVATE, data that is not mapped, and "
279 : "executable pages that have been written to.")
280 :
281 : static PRInt64 GetResident()
282 : {
283 : PROCESS_MEMORY_COUNTERS pmc;
284 : pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
285 :
286 : if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
287 : return (PRInt64) -1;
288 :
289 : return pmc.WorkingSetSize;
290 : }
291 :
292 : #else
293 :
294 : static PRInt64 GetResident()
295 : {
296 : return (PRInt64) -1;
297 : }
298 :
299 : #endif
300 :
301 : #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS)
302 1428 : NS_MEMORY_REPORTER_IMPLEMENT(Vsize,
303 : "vsize",
304 : KIND_OTHER,
305 : UNITS_BYTES,
306 : GetVsize,
307 : "Memory mapped by the process, including code and data segments, the "
308 : "heap, thread stacks, memory explicitly mapped by the process via mmap "
309 : "and similar operations, and memory shared with other processes. "
310 : "This is the vsize figure as reported by 'top' and 'ps'. This figure is of "
311 : "limited use on Mac, where processes share huge amounts of memory with one "
312 : "another. But even on other operating systems, 'resident' is a much better "
313 4323 : "measure of the memory resources used by the process.")
314 : #endif
315 :
316 : #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS)
317 1428 : NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsSoft,
318 : "page-faults-soft",
319 : KIND_OTHER,
320 : UNITS_COUNT_CUMULATIVE,
321 : GetSoftPageFaults,
322 : "The number of soft page faults (also known as \"minor page faults\") that "
323 : "have occurred since the process started. A soft page fault occurs when the "
324 : "process tries to access a page which is present in physical memory but is "
325 : "not mapped into the process's address space. For instance, a process might "
326 : "observe soft page faults when it loads a shared library which is already "
327 : "present in physical memory. A process may experience many thousands of soft "
328 : "page faults even when the machine has plenty of available physical memory, "
329 : "and because the OS services a soft page fault without accessing the disk, "
330 4323 : "they impact performance much less than hard page faults.")
331 :
332 1450 : NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard,
333 : "page-faults-hard",
334 : KIND_OTHER,
335 : UNITS_COUNT_CUMULATIVE,
336 : GetHardPageFaults,
337 : "The number of hard page faults (also known as \"major page faults\") that "
338 : "have occurred since the process started. A hard page fault occurs when a "
339 : "process tries to access a page which is not present in physical memory. "
340 : "The operating system must access the disk in order to fulfill a hard page "
341 : "fault. When memory is plentiful, you should see very few hard page faults. "
342 : "But if the process tries to use more memory than your machine has "
343 : "available, you may see many thousands of hard page faults. Because "
344 : "accessing the disk is up to a million times slower than accessing RAM, "
345 : "the program may run very slowly when it is experiencing more than 100 or "
346 4323 : "so hard page faults a second.")
347 : #endif
348 :
349 1437 : NS_MEMORY_REPORTER_IMPLEMENT(Explicit,
350 : "explicit",
351 : KIND_OTHER,
352 : UNITS_BYTES,
353 : GetExplicit,
354 : "This is the same measurement as the root of the 'explicit' tree. "
355 : "However, it is measured at a different time and so gives slightly "
356 4323 : "different results.")
357 :
358 1437 : NS_MEMORY_REPORTER_IMPLEMENT(Resident,
359 : "resident",
360 : KIND_OTHER,
361 : UNITS_BYTES,
362 : GetResident,
363 : "Memory mapped by the process that is present in physical memory, "
364 : "also known as the resident set size (RSS). This is the best single "
365 : "figure to use when considering the memory resources used by the process, "
366 : "but it depends both on other processes being run and details of the OS "
367 : "kernel and so is best used for comparing the memory usage of a single "
368 4323 : "process at different points in time.")
369 :
370 : /**
371 : ** memory reporter implementation for jemalloc and OSX malloc,
372 : ** to obtain info on total memory in use (that we know about,
373 : ** at least -- on OSX, there are sometimes other zones in use).
374 : **/
375 :
376 : #if HAVE_JEMALLOC_STATS
377 :
378 0 : static PRInt64 GetHeapUnallocated()
379 : {
380 : jemalloc_stats_t stats;
381 0 : jemalloc_stats(&stats);
382 0 : return (PRInt64) stats.mapped - stats.allocated;
383 : }
384 :
385 6 : static PRInt64 GetHeapAllocated()
386 : {
387 : jemalloc_stats_t stats;
388 6 : jemalloc_stats(&stats);
389 6 : return (PRInt64) stats.allocated;
390 : }
391 :
392 0 : static PRInt64 GetHeapCommitted()
393 : {
394 : jemalloc_stats_t stats;
395 0 : jemalloc_stats(&stats);
396 0 : return (PRInt64) stats.committed;
397 : }
398 :
399 0 : static PRInt64 GetHeapCommittedFragmentation()
400 : {
401 : jemalloc_stats_t stats;
402 0 : jemalloc_stats(&stats);
403 0 : return (PRInt64) 10000 * (1 - stats.allocated / (double)stats.committed);
404 : }
405 :
406 0 : static PRInt64 GetHeapDirty()
407 : {
408 : jemalloc_stats_t stats;
409 0 : jemalloc_stats(&stats);
410 0 : return (PRInt64) stats.dirty;
411 : }
412 :
413 1428 : NS_MEMORY_REPORTER_IMPLEMENT(HeapCommitted,
414 : "heap-committed",
415 : KIND_OTHER,
416 : UNITS_BYTES,
417 : GetHeapCommitted,
418 : "Memory mapped by the heap allocator that is committed, i.e. in physical "
419 : "memory or paged to disk. When heap-committed is larger than "
420 : "heap-allocated, the difference between the two values is likely due to "
421 : "external fragmentation; that is, the allocator allocated a large block of "
422 : "memory and is unable to decommit it because a small part of that block is "
423 4323 : "currently in use.")
424 :
425 1428 : NS_MEMORY_REPORTER_IMPLEMENT(HeapCommittedFragmentation,
426 : "heap-committed-fragmentation",
427 : KIND_OTHER,
428 : UNITS_PERCENTAGE,
429 : GetHeapCommittedFragmentation,
430 : "Fraction of committed bytes which do not correspond to an active "
431 : "allocation; i.e., 1 - (heap-allocated / heap-committed). Although the "
432 : "allocator will waste some space under any circumstances, a large value here "
433 4323 : "may indicate that the heap is highly fragmented.")
434 :
435 1428 : NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty,
436 : "heap-dirty",
437 : KIND_OTHER,
438 : UNITS_BYTES,
439 : GetHeapDirty,
440 : "Memory which the allocator could return to the operating system, but "
441 : "hasn't. The allocator keeps this memory around as an optimization, so it "
442 : "doesn't have to ask the OS the next time it needs to fulfill a request. "
443 4323 : "This value is typically not larger than a few megabytes.")
444 :
445 : #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
446 : #include <malloc/malloc.h>
447 :
448 : static PRInt64 GetHeapUnallocated()
449 : {
450 : struct mstats stats = mstats();
451 : return (PRInt64) (stats.bytes_total - stats.bytes_used);
452 : }
453 :
454 : static PRInt64 GetHeapAllocated()
455 : {
456 : struct mstats stats = mstats();
457 : return (PRInt64) stats.bytes_used;
458 : }
459 :
460 : static PRInt64 GetHeapZone0Committed()
461 : {
462 : #ifdef MOZ_DMD
463 : // malloc_zone_statistics() crashes when run under DMD because Valgrind
464 : // doesn't intercept it. This measurement isn't important for DMD, so
465 : // don't even try.
466 : return (PRInt64) -1;
467 : #else
468 : malloc_statistics_t stats;
469 : malloc_zone_statistics(malloc_default_zone(), &stats);
470 : return stats.size_in_use;
471 : #endif
472 : }
473 :
474 : static PRInt64 GetHeapZone0Used()
475 : {
476 : #ifdef MOZ_DMD
477 : // See comment in GetHeapZone0Committed above.
478 : return (PRInt64) -1;
479 : #else
480 : malloc_statistics_t stats;
481 : malloc_zone_statistics(malloc_default_zone(), &stats);
482 : return stats.size_allocated;
483 : #endif
484 : }
485 :
486 : NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
487 : "heap-zone0-committed",
488 : KIND_OTHER,
489 : UNITS_BYTES,
490 : GetHeapZone0Committed,
491 : "Memory mapped by the heap allocator that is committed in the default "
492 : "zone.")
493 :
494 : NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Used,
495 : "heap-zone0-used",
496 : KIND_OTHER,
497 : UNITS_BYTES,
498 : GetHeapZone0Used,
499 : "Memory mapped by the heap allocator in the default zone that is "
500 : "allocated to the application.")
501 :
502 : #else
503 :
504 : static PRInt64 GetHeapAllocated()
505 : {
506 : return (PRInt64) -1;
507 : }
508 :
509 : static PRInt64 GetHeapUnallocated()
510 : {
511 : return (PRInt64) -1;
512 : }
513 :
514 : #endif
515 :
516 1428 : NS_MEMORY_REPORTER_IMPLEMENT(HeapUnallocated,
517 : "heap-unallocated",
518 : KIND_OTHER,
519 : UNITS_BYTES,
520 : GetHeapUnallocated,
521 : "Memory mapped by the heap allocator that is not part of an active "
522 : "allocation. Much of this memory may be uncommitted -- that is, it does not "
523 4323 : "take up space in physical memory or in the swap file.")
524 :
525 1440 : NS_MEMORY_REPORTER_IMPLEMENT(HeapAllocated,
526 : "heap-allocated",
527 : KIND_OTHER,
528 : UNITS_BYTES,
529 : GetHeapAllocated,
530 : "Memory mapped by the heap allocator that is currently allocated to the "
531 : "application. This may exceed the amount of memory requested by the "
532 : "application because the allocator regularly rounds up request sizes. (The "
533 4323 : "exact amount requested is not recorded.)")
534 :
535 0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(AtomTableMallocSizeOf, "atom-table")
536 :
537 0 : static PRInt64 GetAtomTableSize() {
538 0 : return NS_SizeOfAtomTableIncludingThis(AtomTableMallocSizeOf);
539 : }
540 :
541 : // Why is this here? At first glance, you'd think it could be defined and
542 : // registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
543 : // However, the obvious time to register it is when the table is initialized,
544 : // and that happens before XPCOM components are initialized, which means the
545 : // NS_RegisterMemoryReporter call fails. So instead we do it here.
546 1428 : NS_MEMORY_REPORTER_IMPLEMENT(AtomTable,
547 : "explicit/atom-table",
548 : KIND_HEAP,
549 : UNITS_BYTES,
550 : GetAtomTableSize,
551 4323 : "Memory used by the atoms table.")
552 :
553 : /**
554 : ** nsMemoryReporterManager implementation
555 : **/
556 :
557 152571 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsMemoryReporterManager, nsIMemoryReporterManager)
558 :
559 : NS_IMETHODIMP
560 1419 : nsMemoryReporterManager::Init()
561 : {
562 : #if HAVE_JEMALLOC_STATS && defined(XP_LINUX)
563 1419 : if (!jemalloc_stats)
564 0 : return NS_ERROR_FAILURE;
565 : #endif
566 :
567 : #define REGISTER(_x) RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x))
568 :
569 2838 : REGISTER(HeapAllocated);
570 2838 : REGISTER(HeapUnallocated);
571 2838 : REGISTER(Explicit);
572 2838 : REGISTER(Resident);
573 :
574 : #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS)
575 2838 : REGISTER(Vsize);
576 : #endif
577 :
578 : #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS)
579 2838 : REGISTER(PageFaultsSoft);
580 2838 : REGISTER(PageFaultsHard);
581 : #endif
582 :
583 : #if defined(XP_WIN)
584 : REGISTER(Private);
585 : #endif
586 :
587 : #if defined(HAVE_JEMALLOC_STATS)
588 2838 : REGISTER(HeapCommitted);
589 2838 : REGISTER(HeapCommittedFragmentation);
590 2838 : REGISTER(HeapDirty);
591 : #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
592 : REGISTER(HeapZone0Committed);
593 : REGISTER(HeapZone0Used);
594 : #endif
595 :
596 2838 : REGISTER(AtomTable);
597 :
598 1419 : return NS_OK;
599 : }
600 :
601 1419 : nsMemoryReporterManager::nsMemoryReporterManager()
602 1419 : : mMutex("nsMemoryReporterManager::mMutex")
603 : {
604 1419 : }
605 :
606 2838 : nsMemoryReporterManager::~nsMemoryReporterManager()
607 : {
608 5676 : }
609 :
610 : NS_IMETHODIMP
611 6 : nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator **result)
612 : {
613 : nsresult rv;
614 12 : mozilla::MutexAutoLock autoLock(mMutex);
615 6 : rv = NS_NewArrayEnumerator(result, mReporters);
616 6 : return rv;
617 : }
618 :
619 : NS_IMETHODIMP
620 6 : nsMemoryReporterManager::EnumerateMultiReporters(nsISimpleEnumerator **result)
621 : {
622 : nsresult rv;
623 12 : mozilla::MutexAutoLock autoLock(mMutex);
624 6 : rv = NS_NewArrayEnumerator(result, mMultiReporters);
625 6 : return rv;
626 : }
627 :
628 : NS_IMETHODIMP
629 25146 : nsMemoryReporterManager::RegisterReporter(nsIMemoryReporter *reporter)
630 : {
631 50292 : mozilla::MutexAutoLock autoLock(mMutex);
632 25146 : if (mReporters.IndexOf(reporter) != -1)
633 0 : return NS_ERROR_FAILURE;
634 :
635 25146 : mReporters.AppendObject(reporter);
636 25146 : return NS_OK;
637 : }
638 :
639 : NS_IMETHODIMP
640 6527 : nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter *reporter)
641 : {
642 13054 : mozilla::MutexAutoLock autoLock(mMutex);
643 6527 : if (mMultiReporters.IndexOf(reporter) != -1)
644 0 : return NS_ERROR_FAILURE;
645 :
646 6527 : mMultiReporters.AppendObject(reporter);
647 6527 : return NS_OK;
648 : }
649 :
650 : NS_IMETHODIMP
651 13305 : nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter *reporter)
652 : {
653 26610 : mozilla::MutexAutoLock autoLock(mMutex);
654 13305 : if (!mReporters.RemoveObject(reporter))
655 11835 : return NS_ERROR_FAILURE;
656 :
657 1470 : return NS_OK;
658 : }
659 :
660 : NS_IMETHODIMP
661 0 : nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter *reporter)
662 : {
663 0 : mozilla::MutexAutoLock autoLock(mMutex);
664 0 : if (!mMultiReporters.RemoveObject(reporter))
665 0 : return NS_ERROR_FAILURE;
666 :
667 0 : return NS_OK;
668 : }
669 :
670 : NS_IMETHODIMP
671 0 : nsMemoryReporterManager::GetResident(PRInt64 *aResident)
672 : {
673 0 : *aResident = ::GetResident();
674 0 : return NS_OK;
675 : }
676 :
677 : struct MemoryReport {
678 : MemoryReport(const nsACString &path, PRInt64 amount)
679 : : path(path), amount(amount)
680 : {
681 : MOZ_COUNT_CTOR(MemoryReport);
682 : }
683 : MemoryReport(const MemoryReport& rhs)
684 : : path(rhs.path), amount(rhs.amount)
685 : {
686 : MOZ_COUNT_CTOR(MemoryReport);
687 : }
688 : ~MemoryReport()
689 : {
690 : MOZ_COUNT_DTOR(MemoryReport);
691 : }
692 : const nsCString path;
693 : PRInt64 amount;
694 : };
695 :
696 : #ifdef DEBUG
697 : // This is just a wrapper for PRInt64 that implements nsISupports, so it can be
698 : // passed to nsIMemoryMultiReporter::CollectReports.
699 : class PRInt64Wrapper : public nsISupports {
700 : public:
701 : NS_DECL_ISUPPORTS
702 3 : PRInt64Wrapper() : mValue(0) { }
703 : PRInt64 mValue;
704 : };
705 9 : NS_IMPL_ISUPPORTS0(PRInt64Wrapper)
706 :
707 : class ExplicitNonHeapCountingCallback : public nsIMemoryMultiReporterCallback
708 3 : {
709 : public:
710 : NS_DECL_ISUPPORTS
711 :
712 1703 : NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
713 : PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
714 : const nsACString &aDescription,
715 : nsISupports *aWrappedExplicitNonHeap)
716 : {
717 6476 : if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
718 4773 : PromiseFlatCString(aPath).Find("explicit") == 0 &&
719 : aAmount != PRInt64(-1))
720 : {
721 : PRInt64Wrapper *wrappedPRInt64 =
722 96 : static_cast<PRInt64Wrapper *>(aWrappedExplicitNonHeap);
723 96 : wrappedPRInt64->mValue += aAmount;
724 : }
725 1703 : return NS_OK;
726 : }
727 : };
728 9 : NS_IMPL_ISUPPORTS1(
729 : ExplicitNonHeapCountingCallback
730 : , nsIMemoryMultiReporterCallback
731 : )
732 : #endif
733 :
734 : NS_IMETHODIMP
735 3 : nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
736 : {
737 3 : NS_ENSURE_ARG_POINTER(aExplicit);
738 3 : *aExplicit = 0;
739 :
740 : nsresult rv;
741 : bool more;
742 :
743 : // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal
744 : // (i.e. non-multi) "explicit" reporters.
745 3 : PRInt64 heapAllocated = PRInt64(-1);
746 3 : PRInt64 explicitNonHeapNormalSize = 0;
747 6 : nsCOMPtr<nsISimpleEnumerator> e;
748 3 : EnumerateReporters(getter_AddRefs(e));
749 63 : while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
750 114 : nsCOMPtr<nsIMemoryReporter> r;
751 57 : e->GetNext(getter_AddRefs(r));
752 :
753 : PRInt32 kind;
754 57 : rv = r->GetKind(&kind);
755 57 : NS_ENSURE_SUCCESS(rv, rv);
756 :
757 114 : nsCString path;
758 57 : rv = r->GetPath(path);
759 57 : NS_ENSURE_SUCCESS(rv, rv);
760 :
761 : // We're only interested in NONHEAP explicit reporters and
762 : // the 'heap-allocated' reporter.
763 60 : if (kind == nsIMemoryReporter::KIND_NONHEAP &&
764 3 : path.Find("explicit") == 0)
765 : {
766 : PRInt64 amount;
767 3 : rv = r->GetAmount(&amount);
768 3 : NS_ENSURE_SUCCESS(rv, rv);
769 :
770 : // Just skip any NONHEAP reporters that fail, because
771 : // "heap-allocated" is the most important one.
772 3 : if (amount != PRInt64(-1)) {
773 3 : explicitNonHeapNormalSize += amount;
774 : }
775 54 : } else if (path.Equals("heap-allocated")) {
776 3 : rv = r->GetAmount(&heapAllocated);
777 3 : NS_ENSURE_SUCCESS(rv, rv);
778 :
779 : // If we don't have "heap-allocated", give up, because the result would be
780 : // horribly inaccurate.
781 3 : if (heapAllocated == PRInt64(-1)) {
782 0 : *aExplicit = PRInt64(-1);
783 0 : return NS_OK;
784 : }
785 : }
786 : }
787 :
788 : // For each multi-reporter we could call CollectReports and filter out the
789 : // non-explicit, non-NONHEAP measurements. But that's lots of wasted work,
790 : // so we instead use GetExplicitNonHeap() which exists purely for this
791 : // purpose.
792 : //
793 : // (Actually, in debug builds we also do it the slow way and compare the
794 : // result to the result obtained from GetExplicitNonHeap(). This
795 : // guarantees the two measurement paths are equivalent. This is wise
796 : // because it's easy for memory reporters to have bugs.)
797 :
798 3 : PRInt64 explicitNonHeapMultiSize = 0;
799 6 : nsCOMPtr<nsISimpleEnumerator> e2;
800 3 : EnumerateMultiReporters(getter_AddRefs(e2));
801 21 : while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
802 30 : nsCOMPtr<nsIMemoryMultiReporter> r;
803 15 : e2->GetNext(getter_AddRefs(r));
804 : PRInt64 n;
805 15 : rv = r->GetExplicitNonHeap(&n);
806 15 : NS_ENSURE_SUCCESS(rv, rv);
807 30 : explicitNonHeapMultiSize += n;
808 : }
809 :
810 : #ifdef DEBUG
811 : nsRefPtr<ExplicitNonHeapCountingCallback> cb =
812 6 : new ExplicitNonHeapCountingCallback();
813 : nsRefPtr<PRInt64Wrapper> wrappedExplicitNonHeapMultiSize2 =
814 6 : new PRInt64Wrapper();
815 6 : nsCOMPtr<nsISimpleEnumerator> e3;
816 3 : EnumerateMultiReporters(getter_AddRefs(e3));
817 21 : while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) {
818 30 : nsCOMPtr<nsIMemoryMultiReporter> r;
819 15 : e3->GetNext(getter_AddRefs(r));
820 15 : r->CollectReports(cb, wrappedExplicitNonHeapMultiSize2);
821 : }
822 3 : PRInt64 explicitNonHeapMultiSize2 = wrappedExplicitNonHeapMultiSize2->mValue;
823 :
824 : // Check the two measurements give the same result. This was an
825 : // NS_ASSERTION but they occasionally don't match due to races (bug
826 : // 728990).
827 3 : if (explicitNonHeapMultiSize != explicitNonHeapMultiSize2) {
828 : char *msg = PR_smprintf("The two measurements of 'explicit' memory "
829 : "usage don't match (%lld vs %lld)",
830 : explicitNonHeapMultiSize,
831 0 : explicitNonHeapMultiSize2);
832 0 : NS_WARNING(msg);
833 0 : PR_smprintf_free(msg);
834 : }
835 : #endif
836 :
837 3 : *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize;
838 3 : return NS_OK;
839 : }
840 :
841 : NS_IMETHODIMP
842 0 : nsMemoryReporterManager::GetHasMozMallocUsableSize(bool *aHas)
843 : {
844 0 : void *p = malloc(16);
845 0 : if (!p) {
846 0 : return NS_ERROR_OUT_OF_MEMORY;
847 : }
848 0 : size_t usable = moz_malloc_usable_size(p);
849 0 : free(p);
850 0 : *aHas = !!(usable > 0);
851 0 : return NS_OK;
852 : }
853 :
854 0 : NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
855 :
856 0 : nsMemoryReporter::nsMemoryReporter(nsACString& process,
857 : nsACString& path,
858 : PRInt32 kind,
859 : PRInt32 units,
860 : PRInt64 amount,
861 : nsACString& desc)
862 : : mProcess(process)
863 : , mPath(path)
864 : , mKind(kind)
865 : , mUnits(units)
866 : , mAmount(amount)
867 0 : , mDesc(desc)
868 : {
869 0 : }
870 :
871 0 : nsMemoryReporter::~nsMemoryReporter()
872 : {
873 0 : }
874 :
875 0 : NS_IMETHODIMP nsMemoryReporter::GetProcess(nsACString &aProcess)
876 : {
877 0 : aProcess.Assign(mProcess);
878 0 : return NS_OK;
879 : }
880 :
881 0 : NS_IMETHODIMP nsMemoryReporter::GetPath(nsACString &aPath)
882 : {
883 0 : aPath.Assign(mPath);
884 0 : return NS_OK;
885 : }
886 :
887 0 : NS_IMETHODIMP nsMemoryReporter::GetKind(PRInt32 *aKind)
888 : {
889 0 : *aKind = mKind;
890 0 : return NS_OK;
891 : }
892 :
893 0 : NS_IMETHODIMP nsMemoryReporter::GetUnits(PRInt32 *aUnits)
894 : {
895 0 : *aUnits = mUnits;
896 0 : return NS_OK;
897 : }
898 :
899 0 : NS_IMETHODIMP nsMemoryReporter::GetAmount(PRInt64 *aAmount)
900 : {
901 0 : *aAmount = mAmount;
902 0 : return NS_OK;
903 : }
904 :
905 0 : NS_IMETHODIMP nsMemoryReporter::GetDescription(nsACString &aDescription)
906 : {
907 0 : aDescription.Assign(mDesc);
908 0 : return NS_OK;
909 : }
910 :
911 : nsresult
912 9537 : NS_RegisterMemoryReporter (nsIMemoryReporter *reporter)
913 : {
914 19074 : nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
915 9537 : if (mgr == nsnull)
916 0 : return NS_ERROR_FAILURE;
917 9537 : return mgr->RegisterReporter(reporter);
918 : }
919 :
920 : nsresult
921 6527 : NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
922 : {
923 13054 : nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
924 6527 : if (mgr == nsnull)
925 0 : return NS_ERROR_FAILURE;
926 6527 : return mgr->RegisterMultiReporter(reporter);
927 : }
928 :
929 : nsresult
930 14108 : NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter)
931 : {
932 28216 : nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
933 14108 : if (mgr == nsnull)
934 803 : return NS_ERROR_FAILURE;
935 13305 : return mgr->UnregisterReporter(reporter);
936 : }
937 :
938 : nsresult
939 794 : NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
940 : {
941 1588 : nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
942 794 : if (mgr == nsnull)
943 794 : return NS_ERROR_FAILURE;
944 0 : return mgr->UnregisterMultiReporter(reporter);
945 : }
946 :
947 : namespace mozilla {
948 :
949 : #ifdef MOZ_DMD
950 :
951 : class NullMultiReporterCallback : public nsIMemoryMultiReporterCallback
952 : {
953 : public:
954 : NS_DECL_ISUPPORTS
955 :
956 : NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
957 : PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
958 : const nsACString &aDescription,
959 : nsISupports *aData)
960 : {
961 : // Do nothing; the reporter has already reported to DMD.
962 : return NS_OK;
963 : }
964 : };
965 : NS_IMPL_ISUPPORTS1(
966 : NullMultiReporterCallback
967 : , nsIMemoryMultiReporterCallback
968 : )
969 :
970 : void
971 : DMDCheckAndDump()
972 : {
973 : nsCOMPtr<nsIMemoryReporterManager> mgr =
974 : do_GetService("@mozilla.org/memory-reporter-manager;1");
975 :
976 : // Do vanilla reporters.
977 : nsCOMPtr<nsISimpleEnumerator> e;
978 : mgr->EnumerateReporters(getter_AddRefs(e));
979 : bool more;
980 : while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
981 : nsCOMPtr<nsIMemoryReporter> r;
982 : e->GetNext(getter_AddRefs(r));
983 :
984 : // Just getting the amount is enough for the reporter to report to DMD.
985 : PRInt64 amount;
986 : (void)r->GetAmount(&amount);
987 : }
988 :
989 : // Do multi-reporters.
990 : nsCOMPtr<nsISimpleEnumerator> e2;
991 : mgr->EnumerateMultiReporters(getter_AddRefs(e2));
992 : nsRefPtr<NullMultiReporterCallback> cb = new NullMultiReporterCallback();
993 : while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
994 : nsCOMPtr<nsIMemoryMultiReporter> r;
995 : e2->GetNext(getter_AddRefs(r));
996 : r->CollectReports(cb, nsnull);
997 : }
998 :
999 : VALGRIND_DMD_CHECK_REPORTING;
1000 : }
1001 :
1002 : #endif /* defined(MOZ_DMD) */
1003 :
1004 : }
|