LCOV - code coverage report
Current view: top level - xpcom/base - nsMemoryReporterManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 97 613 15.8 %
Date: 2017-07-14 16:53:18 Functions: 64 164 39.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "nsProxyRelease.h"
      13             : #include "nsServiceManagerUtils.h"
      14             : #include "nsMemoryReporterManager.h"
      15             : #include "nsITimer.h"
      16             : #include "nsThreadUtils.h"
      17             : #include "nsPIDOMWindow.h"
      18             : #include "nsIObserverService.h"
      19             : #include "nsIGlobalObject.h"
      20             : #include "nsIXPConnect.h"
      21             : #include "GeckoProfilerReporter.h"
      22             : #if defined(XP_UNIX) || defined(MOZ_DMD)
      23             : #include "nsMemoryInfoDumper.h"
      24             : #endif
      25             : #include "nsNetCID.h"
      26             : #include "mozilla/Attributes.h"
      27             : #include "mozilla/MemoryReportingProcess.h"
      28             : #include "mozilla/PodOperations.h"
      29             : #include "mozilla/Preferences.h"
      30             : #include "mozilla/Services.h"
      31             : #include "mozilla/Telemetry.h"
      32             : #include "mozilla/UniquePtrExtensions.h"
      33             : #include "mozilla/dom/MemoryReportTypes.h"
      34             : #include "mozilla/dom/ContentParent.h"
      35             : #include "mozilla/gfx/GPUProcessManager.h"
      36             : #include "mozilla/ipc/FileDescriptorUtils.h"
      37             : 
      38             : #ifdef XP_WIN
      39             : #include <process.h>
      40             : #ifndef getpid
      41             : #define getpid _getpid
      42             : #endif
      43             : #else
      44             : #include <unistd.h>
      45             : #endif
      46             : 
      47             : using namespace mozilla;
      48             : using namespace dom;
      49             : 
      50             : #if defined(MOZ_MEMORY)
      51             : #  define HAVE_JEMALLOC_STATS 1
      52             : #  include "mozmemory.h"
      53             : #endif  // MOZ_MEMORY
      54             : 
      55             : #if defined(XP_LINUX)
      56             : 
      57             : #include <malloc.h>
      58             : #include <string.h>
      59             : #include <stdlib.h>
      60             : 
      61             : static MOZ_MUST_USE nsresult
      62           0 : GetProcSelfStatmField(int aField, int64_t* aN)
      63             : {
      64             :   // There are more than two fields, but we're only interested in the first
      65             :   // two.
      66             :   static const int MAX_FIELD = 2;
      67             :   size_t fields[MAX_FIELD];
      68           0 :   MOZ_ASSERT(aField < MAX_FIELD, "bad field number");
      69           0 :   FILE* f = fopen("/proc/self/statm", "r");
      70           0 :   if (f) {
      71           0 :     int nread = fscanf(f, "%zu %zu", &fields[0], &fields[1]);
      72           0 :     fclose(f);
      73           0 :     if (nread == MAX_FIELD) {
      74           0 :       *aN = fields[aField] * getpagesize();
      75           0 :       return NS_OK;
      76             :     }
      77             :   }
      78           0 :   return NS_ERROR_FAILURE;
      79             : }
      80             : 
      81             : static MOZ_MUST_USE nsresult
      82           0 : GetProcSelfSmapsPrivate(int64_t* aN)
      83             : {
      84             :   // You might be tempted to calculate USS by subtracting the "shared" value
      85             :   // from the "resident" value in /proc/<pid>/statm. But at least on Linux,
      86             :   // statm's "shared" value actually counts pages backed by files, which has
      87             :   // little to do with whether the pages are actually shared. /proc/self/smaps
      88             :   // on the other hand appears to give us the correct information.
      89             : 
      90           0 :   FILE* f = fopen("/proc/self/smaps", "r");
      91           0 :   if (NS_WARN_IF(!f)) {
      92           0 :     return NS_ERROR_UNEXPECTED;
      93             :   }
      94             : 
      95             :   // We carry over the end of the buffer to the beginning to make sure we only
      96             :   // interpret complete lines.
      97             :   static const uint32_t carryOver = 32;
      98             :   static const uint32_t readSize = 4096;
      99             : 
     100           0 :   int64_t amount = 0;
     101             :   char buffer[carryOver + readSize + 1];
     102             : 
     103             :   // Fill the beginning of the buffer with spaces, as a sentinel for the first
     104             :   // iteration.
     105           0 :   memset(buffer, ' ', carryOver);
     106             : 
     107             :   for (;;) {
     108           0 :     size_t bytes = fread(buffer + carryOver, sizeof(*buffer), readSize, f);
     109           0 :     char* end = buffer + bytes;
     110           0 :     char* ptr = buffer;
     111           0 :     end[carryOver] = '\0';
     112             :     // We are looking for lines like "Private_{Clean,Dirty}: 4 kB".
     113           0 :     while ((ptr = strstr(ptr, "Private"))) {
     114           0 :       if (ptr >= end) {
     115           0 :         break;
     116             :       }
     117           0 :       ptr += sizeof("Private_Xxxxx:");
     118           0 :       amount += strtol(ptr, nullptr, 10);
     119             :     }
     120           0 :     if (bytes < readSize) {
     121             :       // We do not expect any match within the end of the buffer.
     122           0 :       MOZ_ASSERT(!strstr(end, "Private"));
     123           0 :       break;
     124             :     }
     125             :     // Carry the end of the buffer over to the beginning.
     126           0 :     memcpy(buffer, end, carryOver);
     127           0 :   }
     128             : 
     129           0 :   fclose(f);
     130             :   // Convert from kB to bytes.
     131           0 :   *aN = amount * 1024;
     132           0 :   return NS_OK;
     133             : }
     134             : 
     135             : #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
     136             : static MOZ_MUST_USE nsresult
     137           0 : VsizeDistinguishedAmount(int64_t* aN)
     138             : {
     139           0 :   return GetProcSelfStatmField(0, aN);
     140             : }
     141             : 
     142             : static MOZ_MUST_USE nsresult
     143           0 : ResidentDistinguishedAmount(int64_t* aN)
     144             : {
     145           0 :   return GetProcSelfStatmField(1, aN);
     146             : }
     147             : 
     148             : static MOZ_MUST_USE nsresult
     149           0 : ResidentFastDistinguishedAmount(int64_t* aN)
     150             : {
     151           0 :   return ResidentDistinguishedAmount(aN);
     152             : }
     153             : 
     154             : #define HAVE_RESIDENT_UNIQUE_REPORTER 1
     155             : static MOZ_MUST_USE nsresult
     156           0 : ResidentUniqueDistinguishedAmount(int64_t* aN)
     157             : {
     158           0 :   return GetProcSelfSmapsPrivate(aN);
     159             : }
     160             : 
     161             : #ifdef HAVE_MALLINFO
     162             : #define HAVE_SYSTEM_HEAP_REPORTER 1
     163             : static MOZ_MUST_USE nsresult
     164           0 : SystemHeapSize(int64_t* aSizeOut)
     165             : {
     166           0 :     struct mallinfo info = mallinfo();
     167             : 
     168             :     // The documentation in the glibc man page makes it sound like |uordblks|
     169             :     // would suffice, but that only gets the small allocations that are put in
     170             :     // the brk heap. We need |hblkhd| as well to get the larger allocations
     171             :     // that are mmapped.
     172             :     //
     173             :     // The fields in |struct mallinfo| are all |int|, <sigh>, so it is
     174             :     // unreliable if memory usage gets high. However, the system heap size on
     175             :     // Linux should usually be zero (so long as jemalloc is enabled) so that
     176             :     // shouldn't be a problem. Nonetheless, cast the |int|s to |size_t| before
     177             :     // adding them to provide a small amount of extra overflow protection.
     178           0 :     *aSizeOut = size_t(info.hblkhd) + size_t(info.uordblks);
     179           0 :     return NS_OK;
     180             : }
     181             : #endif
     182             : 
     183             : #elif defined(__DragonFly__) || defined(__FreeBSD__) \
     184             :     || defined(__NetBSD__) || defined(__OpenBSD__) \
     185             :     || defined(__FreeBSD_kernel__)
     186             : 
     187             : #include <sys/param.h>
     188             : #include <sys/sysctl.h>
     189             : #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
     190             : #include <sys/user.h>
     191             : #endif
     192             : 
     193             : #include <unistd.h>
     194             : 
     195             : #if defined(__NetBSD__)
     196             : #undef KERN_PROC
     197             : #define KERN_PROC KERN_PROC2
     198             : #define KINFO_PROC struct kinfo_proc2
     199             : #else
     200             : #define KINFO_PROC struct kinfo_proc
     201             : #endif
     202             : 
     203             : #if defined(__DragonFly__)
     204             : #define KP_SIZE(kp) (kp.kp_vm_map_size)
     205             : #define KP_RSS(kp) (kp.kp_vm_rssize * getpagesize())
     206             : #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
     207             : #define KP_SIZE(kp) (kp.ki_size)
     208             : #define KP_RSS(kp) (kp.ki_rssize * getpagesize())
     209             : #elif defined(__NetBSD__)
     210             : #define KP_SIZE(kp) (kp.p_vm_msize * getpagesize())
     211             : #define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
     212             : #elif defined(__OpenBSD__)
     213             : #define KP_SIZE(kp) ((kp.p_vm_dsize + kp.p_vm_ssize                     \
     214             :                       + kp.p_vm_tsize) * getpagesize())
     215             : #define KP_RSS(kp) (kp.p_vm_rssize * getpagesize())
     216             : #endif
     217             : 
     218             : static MOZ_MUST_USE nsresult
     219             : GetKinfoProcSelf(KINFO_PROC* aProc)
     220             : {
     221             :   int mib[] = {
     222             :     CTL_KERN,
     223             :     KERN_PROC,
     224             :     KERN_PROC_PID,
     225             :     getpid(),
     226             : #if defined(__NetBSD__) || defined(__OpenBSD__)
     227             :     sizeof(KINFO_PROC),
     228             :     1,
     229             : #endif
     230             :   };
     231             :   u_int miblen = sizeof(mib) / sizeof(mib[0]);
     232             :   size_t size = sizeof(KINFO_PROC);
     233             :   if (sysctl(mib, miblen, aProc, &size, nullptr, 0)) {
     234             :     return NS_ERROR_FAILURE;
     235             :   }
     236             :   return NS_OK;
     237             : }
     238             : 
     239             : #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
     240             : static MOZ_MUST_USE nsresult
     241             : VsizeDistinguishedAmount(int64_t* aN)
     242             : {
     243             :   KINFO_PROC proc;
     244             :   nsresult rv = GetKinfoProcSelf(&proc);
     245             :   if (NS_SUCCEEDED(rv)) {
     246             :     *aN = KP_SIZE(proc);
     247             :   }
     248             :   return rv;
     249             : }
     250             : 
     251             : static MOZ_MUST_USE nsresult
     252             : ResidentDistinguishedAmount(int64_t* aN)
     253             : {
     254             :   KINFO_PROC proc;
     255             :   nsresult rv = GetKinfoProcSelf(&proc);
     256             :   if (NS_SUCCEEDED(rv)) {
     257             :     *aN = KP_RSS(proc);
     258             :   }
     259             :   return rv;
     260             : }
     261             : 
     262             : static MOZ_MUST_USE nsresult
     263             : ResidentFastDistinguishedAmount(int64_t* aN)
     264             : {
     265             :   return ResidentDistinguishedAmount(aN);
     266             : }
     267             : 
     268             : #ifdef __FreeBSD__
     269             : #include <libutil.h>
     270             : #include <algorithm>
     271             : 
     272             : static MOZ_MUST_USE nsresult
     273             : GetKinfoVmentrySelf(int64_t* aPrss, uint64_t* aMaxreg)
     274             : {
     275             :   int cnt;
     276             :   struct kinfo_vmentry* vmmap;
     277             :   struct kinfo_vmentry* kve;
     278             :   if (!(vmmap = kinfo_getvmmap(getpid(), &cnt))) {
     279             :     return NS_ERROR_FAILURE;
     280             :   }
     281             :   if (aPrss) {
     282             :     *aPrss = 0;
     283             :   }
     284             :   if (aMaxreg) {
     285             :     *aMaxreg = 0;
     286             :   }
     287             : 
     288             :   for (int i = 0; i < cnt; i++) {
     289             :     kve = &vmmap[i];
     290             :     if (aPrss) {
     291             :       *aPrss += kve->kve_private_resident;
     292             :     }
     293             :     if (aMaxreg) {
     294             :       *aMaxreg = std::max(*aMaxreg, kve->kve_end - kve->kve_start);
     295             :     }
     296             :   }
     297             : 
     298             :   free(vmmap);
     299             :   return NS_OK;
     300             : }
     301             : 
     302             : #define HAVE_PRIVATE_REPORTER 1
     303             : static MOZ_MUST_USE nsresult
     304             : PrivateDistinguishedAmount(int64_t* aN)
     305             : {
     306             :   int64_t priv;
     307             :   nsresult rv = GetKinfoVmentrySelf(&priv, nullptr);
     308             :   NS_ENSURE_SUCCESS(rv, rv);
     309             :   *aN = priv * getpagesize();
     310             :   return NS_OK;
     311             : }
     312             : 
     313             : #define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
     314             : static MOZ_MUST_USE nsresult
     315             : VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
     316             : {
     317             :   uint64_t biggestRegion;
     318             :   nsresult rv = GetKinfoVmentrySelf(nullptr, &biggestRegion);
     319             :   if (NS_SUCCEEDED(rv)) {
     320             :     *aN = biggestRegion;
     321             :   }
     322             :   return NS_OK;
     323             : }
     324             : #endif // FreeBSD
     325             : 
     326             : #elif defined(SOLARIS)
     327             : 
     328             : #include <procfs.h>
     329             : #include <fcntl.h>
     330             : #include <unistd.h>
     331             : 
     332             : static void
     333             : XMappingIter(int64_t& aVsize, int64_t& aResident)
     334             : {
     335             :   aVsize = -1;
     336             :   aResident = -1;
     337             :   int mapfd = open("/proc/self/xmap", O_RDONLY);
     338             :   struct stat st;
     339             :   prxmap_t* prmapp = nullptr;
     340             :   if (mapfd >= 0) {
     341             :     if (!fstat(mapfd, &st)) {
     342             :       int nmap = st.st_size / sizeof(prxmap_t);
     343             :       while (1) {
     344             :         // stat(2) on /proc/<pid>/xmap returns an incorrect value,
     345             :         // prior to the release of Solaris 11.
     346             :         // Here is a workaround for it.
     347             :         nmap *= 2;
     348             :         prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t));
     349             :         if (!prmapp) {
     350             :           // out of memory
     351             :           break;
     352             :         }
     353             :         int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0);
     354             :         if (n < 0) {
     355             :           break;
     356             :         }
     357             :         if (nmap >= n / sizeof(prxmap_t)) {
     358             :           aVsize = 0;
     359             :           aResident = 0;
     360             :           for (int i = 0; i < n / sizeof(prxmap_t); i++) {
     361             :             aVsize += prmapp[i].pr_size;
     362             :             aResident += prmapp[i].pr_rss * prmapp[i].pr_pagesize;
     363             :           }
     364             :           break;
     365             :         }
     366             :         free(prmapp);
     367             :       }
     368             :       free(prmapp);
     369             :     }
     370             :     close(mapfd);
     371             :   }
     372             : }
     373             : 
     374             : #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
     375             : static MOZ_MUST_USE nsresult
     376             : VsizeDistinguishedAmount(int64_t* aN)
     377             : {
     378             :   int64_t vsize, resident;
     379             :   XMappingIter(vsize, resident);
     380             :   if (vsize == -1) {
     381             :     return NS_ERROR_FAILURE;
     382             :   }
     383             :   *aN = vsize;
     384             :   return NS_OK;
     385             : }
     386             : 
     387             : static MOZ_MUST_USE nsresult
     388             : ResidentDistinguishedAmount(int64_t* aN)
     389             : {
     390             :   int64_t vsize, resident;
     391             :   XMappingIter(vsize, resident);
     392             :   if (resident == -1) {
     393             :     return NS_ERROR_FAILURE;
     394             :   }
     395             :   *aN = resident;
     396             :   return NS_OK;
     397             : }
     398             : 
     399             : static MOZ_MUST_USE nsresult
     400             : ResidentFastDistinguishedAmount(int64_t* aN)
     401             : {
     402             :   return ResidentDistinguishedAmount(aN);
     403             : }
     404             : 
     405             : #elif defined(XP_MACOSX)
     406             : 
     407             : #include <mach/mach_init.h>
     408             : #include <mach/mach_vm.h>
     409             : #include <mach/shared_region.h>
     410             : #include <mach/task.h>
     411             : #include <sys/sysctl.h>
     412             : 
     413             : static MOZ_MUST_USE bool
     414             : GetTaskBasicInfo(struct task_basic_info* aTi)
     415             : {
     416             :   mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
     417             :   kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
     418             :                                (task_info_t)aTi, &count);
     419             :   return kr == KERN_SUCCESS;
     420             : }
     421             : 
     422             : // The VSIZE figure on Mac includes huge amounts of shared memory and is always
     423             : // absurdly high, eg. 2GB+ even at start-up.  But both 'top' and 'ps' report
     424             : // it, so we might as well too.
     425             : #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
     426             : static MOZ_MUST_USE nsresult
     427             : VsizeDistinguishedAmount(int64_t* aN)
     428             : {
     429             :   task_basic_info ti;
     430             :   if (!GetTaskBasicInfo(&ti)) {
     431             :     return NS_ERROR_FAILURE;
     432             :   }
     433             :   *aN = ti.virtual_size;
     434             :   return NS_OK;
     435             : }
     436             : 
     437             : // If we're using jemalloc on Mac, we need to instruct jemalloc to purge the
     438             : // pages it has madvise(MADV_FREE)'d before we read our RSS in order to get
     439             : // an accurate result.  The OS will take away MADV_FREE'd pages when there's
     440             : // memory pressure, so ideally, they shouldn't count against our RSS.
     441             : //
     442             : // Purging these pages can take a long time for some users (see bug 789975),
     443             : // so we provide the option to get the RSS without purging first.
     444             : static MOZ_MUST_USE nsresult
     445             : ResidentDistinguishedAmountHelper(int64_t* aN, bool aDoPurge)
     446             : {
     447             : #ifdef HAVE_JEMALLOC_STATS
     448             :   if (aDoPurge) {
     449             :     Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
     450             :     jemalloc_purge_freed_pages();
     451             :   }
     452             : #endif
     453             : 
     454             :   task_basic_info ti;
     455             :   if (!GetTaskBasicInfo(&ti)) {
     456             :     return NS_ERROR_FAILURE;
     457             :   }
     458             :   *aN = ti.resident_size;
     459             :   return NS_OK;
     460             : }
     461             : 
     462             : static MOZ_MUST_USE nsresult
     463             : ResidentFastDistinguishedAmount(int64_t* aN)
     464             : {
     465             :   return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ false);
     466             : }
     467             : 
     468             : static MOZ_MUST_USE nsresult
     469             : ResidentDistinguishedAmount(int64_t* aN)
     470             : {
     471             :   return ResidentDistinguishedAmountHelper(aN, /* doPurge = */ true);
     472             : }
     473             : 
     474             : #define HAVE_RESIDENT_UNIQUE_REPORTER 1
     475             : 
     476             : static bool
     477             : InSharedRegion(mach_vm_address_t aAddr, cpu_type_t aType)
     478             : {
     479             :   mach_vm_address_t base;
     480             :   mach_vm_address_t size;
     481             : 
     482             :   switch (aType) {
     483             :     case CPU_TYPE_ARM:
     484             :       base = SHARED_REGION_BASE_ARM;
     485             :       size = SHARED_REGION_SIZE_ARM;
     486             :       break;
     487             :     case CPU_TYPE_I386:
     488             :       base = SHARED_REGION_BASE_I386;
     489             :       size = SHARED_REGION_SIZE_I386;
     490             :       break;
     491             :     case CPU_TYPE_X86_64:
     492             :       base = SHARED_REGION_BASE_X86_64;
     493             :       size = SHARED_REGION_SIZE_X86_64;
     494             :       break;
     495             :     default:
     496             :       return false;
     497             :   }
     498             : 
     499             :   return base <= aAddr && aAddr < (base + size);
     500             : }
     501             : 
     502             : static MOZ_MUST_USE nsresult
     503             : ResidentUniqueDistinguishedAmount(int64_t* aN)
     504             : {
     505             :   if (!aN) {
     506             :     return NS_ERROR_FAILURE;
     507             :   }
     508             : 
     509             :   cpu_type_t cpu_type;
     510             :   size_t len = sizeof(cpu_type);
     511             :   if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
     512             :     return NS_ERROR_FAILURE;
     513             :   }
     514             : 
     515             :   // Roughly based on libtop_update_vm_regions in
     516             :   // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
     517             :   size_t privatePages = 0;
     518             :   mach_vm_size_t size = 0;
     519             :   for (mach_vm_address_t addr = MACH_VM_MIN_ADDRESS; ; addr += size) {
     520             :     vm_region_top_info_data_t info;
     521             :     mach_msg_type_number_t infoCount = VM_REGION_TOP_INFO_COUNT;
     522             :     mach_port_t objectName;
     523             : 
     524             :     kern_return_t kr =
     525             :         mach_vm_region(mach_task_self(), &addr, &size, VM_REGION_TOP_INFO,
     526             :                        reinterpret_cast<vm_region_info_t>(&info),
     527             :                        &infoCount, &objectName);
     528             :     if (kr == KERN_INVALID_ADDRESS) {
     529             :       // Done iterating VM regions.
     530             :       break;
     531             :     } else if (kr != KERN_SUCCESS) {
     532             :       return NS_ERROR_FAILURE;
     533             :     }
     534             : 
     535             :     if (InSharedRegion(addr, cpu_type) && info.share_mode != SM_PRIVATE) {
     536             :         continue;
     537             :     }
     538             : 
     539             :     switch (info.share_mode) {
     540             :       case SM_LARGE_PAGE:
     541             :         // NB: Large pages are not shareable and always resident.
     542             :       case SM_PRIVATE:
     543             :         privatePages += info.private_pages_resident;
     544             :         privatePages += info.shared_pages_resident;
     545             :         break;
     546             :       case SM_COW:
     547             :         privatePages += info.private_pages_resident;
     548             :         if (info.ref_count == 1) {
     549             :           // Treat copy-on-write pages as private if they only have one reference.
     550             :           privatePages += info.shared_pages_resident;
     551             :         }
     552             :         break;
     553             :       case SM_SHARED:
     554             :       default:
     555             :         break;
     556             :     }
     557             :   }
     558             : 
     559             :   vm_size_t pageSize;
     560             :   if (host_page_size(mach_host_self(), &pageSize) != KERN_SUCCESS) {
     561             :     pageSize = PAGE_SIZE;
     562             :   }
     563             : 
     564             :   *aN = privatePages * pageSize;
     565             :   return NS_OK;
     566             : }
     567             : 
     568             : #elif defined(XP_WIN)
     569             : 
     570             : #include <windows.h>
     571             : #include <psapi.h>
     572             : #include <algorithm>
     573             : 
     574             : #define HAVE_VSIZE_AND_RESIDENT_REPORTERS 1
     575             : static MOZ_MUST_USE nsresult
     576             : VsizeDistinguishedAmount(int64_t* aN)
     577             : {
     578             :   MEMORYSTATUSEX s;
     579             :   s.dwLength = sizeof(s);
     580             : 
     581             :   if (!GlobalMemoryStatusEx(&s)) {
     582             :     return NS_ERROR_FAILURE;
     583             :   }
     584             : 
     585             :   *aN = s.ullTotalVirtual - s.ullAvailVirtual;
     586             :   return NS_OK;
     587             : }
     588             : 
     589             : static MOZ_MUST_USE nsresult
     590             : ResidentDistinguishedAmount(int64_t* aN)
     591             : {
     592             :   PROCESS_MEMORY_COUNTERS pmc;
     593             :   pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
     594             : 
     595             :   if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
     596             :     return NS_ERROR_FAILURE;
     597             :   }
     598             : 
     599             :   *aN = pmc.WorkingSetSize;
     600             :   return NS_OK;
     601             : }
     602             : 
     603             : static MOZ_MUST_USE nsresult
     604             : ResidentFastDistinguishedAmount(int64_t* aN)
     605             : {
     606             :   return ResidentDistinguishedAmount(aN);
     607             : }
     608             : 
     609             : #define HAVE_RESIDENT_UNIQUE_REPORTER 1
     610             : 
     611             : static MOZ_MUST_USE nsresult
     612             : ResidentUniqueDistinguishedAmount(int64_t* aN)
     613             : {
     614             :   // Determine how many entries we need.
     615             :   PSAPI_WORKING_SET_INFORMATION tmp;
     616             :   DWORD tmpSize = sizeof(tmp);
     617             :   memset(&tmp, 0, tmpSize);
     618             : 
     619             :   HANDLE proc = GetCurrentProcess();
     620             :   QueryWorkingSet(proc, &tmp, tmpSize);
     621             : 
     622             :   // Fudge the size in case new entries are added between calls.
     623             :   size_t entries = tmp.NumberOfEntries * 2;
     624             : 
     625             :   if (!entries) {
     626             :     return NS_ERROR_FAILURE;
     627             :   }
     628             : 
     629             :   DWORD infoArraySize = tmpSize + (entries * sizeof(PSAPI_WORKING_SET_BLOCK));
     630             :   UniqueFreePtr<PSAPI_WORKING_SET_INFORMATION> infoArray(
     631             :       static_cast<PSAPI_WORKING_SET_INFORMATION*>(malloc(infoArraySize)));
     632             : 
     633             :   if (!infoArray) {
     634             :     return NS_ERROR_FAILURE;
     635             :   }
     636             : 
     637             :   if (!QueryWorkingSet(proc, infoArray.get(), infoArraySize)) {
     638             :     return NS_ERROR_FAILURE;
     639             :   }
     640             : 
     641             :   entries = static_cast<size_t>(infoArray->NumberOfEntries);
     642             :   size_t privatePages = 0;
     643             :   for (size_t i = 0; i < entries; i++) {
     644             :     // Count shared pages that only one process is using as private.
     645             :     if (!infoArray->WorkingSetInfo[i].Shared ||
     646             :         infoArray->WorkingSetInfo[i].ShareCount <= 1) {
     647             :       privatePages++;
     648             :     }
     649             :   }
     650             : 
     651             :   SYSTEM_INFO si;
     652             :   GetSystemInfo(&si);
     653             : 
     654             :   *aN = privatePages * si.dwPageSize;
     655             :   return NS_OK;
     656             : }
     657             : 
     658             : #define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1
     659             : static MOZ_MUST_USE nsresult
     660             : VsizeMaxContiguousDistinguishedAmount(int64_t* aN)
     661             : {
     662             :   SIZE_T biggestRegion = 0;
     663             :   MEMORY_BASIC_INFORMATION vmemInfo = { 0 };
     664             :   for (size_t currentAddress = 0; ; ) {
     665             :     if (!VirtualQuery((LPCVOID)currentAddress, &vmemInfo, sizeof(vmemInfo))) {
     666             :       // Something went wrong, just return whatever we've got already.
     667             :       break;
     668             :     }
     669             : 
     670             :     if (vmemInfo.State == MEM_FREE) {
     671             :       biggestRegion = std::max(biggestRegion, vmemInfo.RegionSize);
     672             :     }
     673             : 
     674             :     SIZE_T lastAddress = currentAddress;
     675             :     currentAddress += vmemInfo.RegionSize;
     676             : 
     677             :     // If we overflow, we've examined all of the address space.
     678             :     if (currentAddress < lastAddress) {
     679             :       break;
     680             :     }
     681             :   }
     682             : 
     683             :   *aN = biggestRegion;
     684             :   return NS_OK;
     685             : }
     686             : 
     687             : #define HAVE_PRIVATE_REPORTER 1
     688             : static MOZ_MUST_USE nsresult
     689             : PrivateDistinguishedAmount(int64_t* aN)
     690             : {
     691             :   PROCESS_MEMORY_COUNTERS_EX pmcex;
     692             :   pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
     693             : 
     694             :   if (!GetProcessMemoryInfo(GetCurrentProcess(),
     695             :                             (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex))) {
     696             :     return NS_ERROR_FAILURE;
     697             :   }
     698             : 
     699             :   *aN = pmcex.PrivateUsage;
     700             :   return NS_OK;
     701             : }
     702             : 
     703             : #define HAVE_SYSTEM_HEAP_REPORTER 1
     704             : // Windows can have multiple separate heaps. During testing there were multiple
     705             : // heaps present but the non-default ones had sizes no more than a few 10s of
     706             : // KiBs. So we combine their sizes into a single measurement.
     707             : static MOZ_MUST_USE nsresult
     708             : SystemHeapSize(int64_t* aSizeOut)
     709             : {
     710             :   // Get the number of heaps.
     711             :   DWORD nHeaps = GetProcessHeaps(0, nullptr);
     712             :   NS_ENSURE_TRUE(nHeaps != 0, NS_ERROR_FAILURE);
     713             : 
     714             :   // Get handles to all heaps, checking that the number of heaps hasn't
     715             :   // changed in the meantime.
     716             :   UniquePtr<HANDLE[]> heaps(new HANDLE[nHeaps]);
     717             :   DWORD nHeaps2 = GetProcessHeaps(nHeaps, heaps.get());
     718             :   NS_ENSURE_TRUE(nHeaps2 != 0 && nHeaps2 == nHeaps, NS_ERROR_FAILURE);
     719             : 
     720             :   // Lock and iterate over each heap to get its size.
     721             :   int64_t heapsSize = 0;
     722             :   for (DWORD i = 0; i < nHeaps; i++) {
     723             :     HANDLE heap = heaps[i];
     724             : 
     725             :     NS_ENSURE_TRUE(HeapLock(heap), NS_ERROR_FAILURE);
     726             : 
     727             :     int64_t heapSize = 0;
     728             :     PROCESS_HEAP_ENTRY entry;
     729             :     entry.lpData = nullptr;
     730             :     while (HeapWalk(heap, &entry)) {
     731             :       // We don't count entry.cbOverhead, because we just want to measure the
     732             :       // space available to the program.
     733             :       if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) {
     734             :         heapSize += entry.cbData;
     735             :       }
     736             :     }
     737             : 
     738             :     // Check this result only after unlocking the heap, so that we don't leave
     739             :     // the heap locked if there was an error.
     740             :     DWORD lastError = GetLastError();
     741             : 
     742             :     // I have no idea how things would proceed if unlocking this heap failed...
     743             :     NS_ENSURE_TRUE(HeapUnlock(heap), NS_ERROR_FAILURE);
     744             : 
     745             :     NS_ENSURE_TRUE(lastError == ERROR_NO_MORE_ITEMS, NS_ERROR_FAILURE);
     746             : 
     747             :     heapsSize += heapSize;
     748             :   }
     749             : 
     750             :   *aSizeOut = heapsSize;
     751             :   return NS_OK;
     752             : }
     753             : 
     754             : struct SegmentKind
     755             : {
     756             :   DWORD mState;
     757             :   DWORD mType;
     758             :   DWORD mProtect;
     759             :   int mIsStack;
     760             : };
     761             : 
     762             : struct SegmentEntry : public PLDHashEntryHdr
     763             : {
     764             :   static PLDHashNumber HashKey(const void* aKey)
     765             :   {
     766             :     auto kind = static_cast<const SegmentKind*>(aKey);
     767             :     return mozilla::HashGeneric(kind->mState, kind->mType, kind->mProtect,
     768             :                                 kind->mIsStack);
     769             :   }
     770             : 
     771             :   static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
     772             :   {
     773             :     auto kind = static_cast<const SegmentKind*>(aKey);
     774             :     auto entry = static_cast<const SegmentEntry*>(aEntry);
     775             :     return kind->mState == entry->mKind.mState &&
     776             :            kind->mType == entry->mKind.mType &&
     777             :            kind->mProtect == entry->mKind.mProtect &&
     778             :            kind->mIsStack == entry->mKind.mIsStack;
     779             :   }
     780             : 
     781             :   static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
     782             :   {
     783             :     auto kind = static_cast<const SegmentKind*>(aKey);
     784             :     auto entry = static_cast<SegmentEntry*>(aEntry);
     785             :     entry->mKind = *kind;
     786             :     entry->mCount = 0;
     787             :     entry->mSize = 0;
     788             :   }
     789             : 
     790             :   static const PLDHashTableOps Ops;
     791             : 
     792             :   SegmentKind mKind;  // The segment kind.
     793             :   uint32_t mCount;    // The number of segments of this kind.
     794             :   size_t mSize;       // The combined size of segments of this kind.
     795             : };
     796             : 
     797             : /* static */ const PLDHashTableOps SegmentEntry::Ops = {
     798             :   SegmentEntry::HashKey,
     799             :   SegmentEntry::MatchEntry,
     800             :   PLDHashTable::MoveEntryStub,
     801             :   PLDHashTable::ClearEntryStub,
     802             :   SegmentEntry::InitEntry
     803             : };
     804             : 
     805             : class WindowsAddressSpaceReporter final : public nsIMemoryReporter
     806             : {
     807             :   ~WindowsAddressSpaceReporter() {}
     808             : 
     809             : public:
     810             :   NS_DECL_ISUPPORTS
     811             : 
     812             :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     813             :                             nsISupports* aData, bool aAnonymize) override
     814             :   {
     815             :     // First iterate over all the segments and record how many of each kind
     816             :     // there were and their aggregate sizes. We use a hash table for this
     817             :     // because there are a couple of dozen different kinds possible.
     818             : 
     819             :     PLDHashTable table(&SegmentEntry::Ops, sizeof(SegmentEntry));
     820             :     MEMORY_BASIC_INFORMATION info = { 0 };
     821             :     bool isPrevSegStackGuard = false;
     822             :     for (size_t currentAddress = 0; ; ) {
     823             :       if (!VirtualQuery((LPCVOID)currentAddress, &info, sizeof(info))) {
     824             :         // Something went wrong, just return whatever we've got already.
     825             :         break;
     826             :       }
     827             : 
     828             :       size_t size = info.RegionSize;
     829             : 
     830             :       // Note that |type| and |protect| are ignored in some cases.
     831             :       DWORD state = info.State;
     832             :       DWORD type =
     833             :         (state == MEM_RESERVE || state == MEM_COMMIT) ? info.Type : 0;
     834             :       DWORD protect = (state == MEM_COMMIT) ? info.Protect : 0;
     835             :       bool isStack = isPrevSegStackGuard &&
     836             :                      state == MEM_COMMIT &&
     837             :                      type == MEM_PRIVATE &&
     838             :                      protect == PAGE_READWRITE;
     839             : 
     840             :       SegmentKind kind = { state, type, protect, isStack ? 1 : 0 };
     841             :       auto entry =
     842             :         static_cast<SegmentEntry*>(table.Add(&kind, mozilla::fallible));
     843             :       if (entry) {
     844             :         entry->mCount += 1;
     845             :         entry->mSize += size;
     846             :       }
     847             : 
     848             :       isPrevSegStackGuard = info.State == MEM_COMMIT &&
     849             :                             info.Type == MEM_PRIVATE &&
     850             :                             info.Protect == (PAGE_READWRITE|PAGE_GUARD);
     851             : 
     852             :       size_t lastAddress = currentAddress;
     853             :       currentAddress += size;
     854             : 
     855             :       // If we overflow, we've examined all of the address space.
     856             :       if (currentAddress < lastAddress) {
     857             :         break;
     858             :       }
     859             :     }
     860             : 
     861             :     // Then iterate over the hash table and report the details for each segment
     862             :     // kind.
     863             : 
     864             :     for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
     865             :       // For each range of pages, we consider one or more of its State, Type
     866             :       // and Protect values. These are documented at
     867             :       // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366775%28v=vs.85%29.aspx
     868             :       // (for State and Type) and
     869             :       // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786%28v=vs.85%29.aspx
     870             :       // (for Protect).
     871             :       //
     872             :       // Not all State values have accompanying Type and Protection values.
     873             :       bool doType = false;
     874             :       bool doProtect = false;
     875             : 
     876             :       auto entry = static_cast<const SegmentEntry*>(iter.Get());
     877             : 
     878             :       nsCString path("address-space");
     879             : 
     880             :       switch (entry->mKind.mState) {
     881             :         case MEM_FREE:
     882             :           path.AppendLiteral("/free");
     883             :           break;
     884             : 
     885             :         case MEM_RESERVE:
     886             :           path.AppendLiteral("/reserved");
     887             :           doType = true;
     888             :           break;
     889             : 
     890             :         case MEM_COMMIT:
     891             :           path.AppendLiteral("/commit");
     892             :           doType = true;
     893             :           doProtect = true;
     894             :           break;
     895             : 
     896             :         default:
     897             :           // Should be impossible, but handle it just in case.
     898             :           path.AppendLiteral("/???");
     899             :           break;
     900             :       }
     901             : 
     902             :       if (doType) {
     903             :         switch (entry->mKind.mType) {
     904             :           case MEM_IMAGE:
     905             :             path.AppendLiteral("/image");
     906             :             break;
     907             : 
     908             :           case MEM_MAPPED:
     909             :             path.AppendLiteral("/mapped");
     910             :             break;
     911             : 
     912             :           case MEM_PRIVATE:
     913             :             path.AppendLiteral("/private");
     914             :             break;
     915             : 
     916             :           default:
     917             :             // Should be impossible, but handle it just in case.
     918             :             path.AppendLiteral("/???");
     919             :             break;
     920             :         }
     921             :       }
     922             : 
     923             :       if (doProtect) {
     924             :         DWORD protect = entry->mKind.mProtect;
     925             :         // Basic attributes. Exactly one of these should be set.
     926             :         if (protect & PAGE_EXECUTE) {
     927             :           path.AppendLiteral("/execute");
     928             :         }
     929             :         if (protect & PAGE_EXECUTE_READ) {
     930             :           path.AppendLiteral("/execute-read");
     931             :         }
     932             :         if (protect & PAGE_EXECUTE_READWRITE) {
     933             :           path.AppendLiteral("/execute-readwrite");
     934             :         }
     935             :         if (protect & PAGE_EXECUTE_WRITECOPY) {
     936             :           path.AppendLiteral("/execute-writecopy");
     937             :         }
     938             :         if (protect & PAGE_NOACCESS) {
     939             :           path.AppendLiteral("/noaccess");
     940             :         }
     941             :         if (protect & PAGE_READONLY) {
     942             :           path.AppendLiteral("/readonly");
     943             :         }
     944             :         if (protect & PAGE_READWRITE) {
     945             :           path.AppendLiteral("/readwrite");
     946             :         }
     947             :         if (protect & PAGE_WRITECOPY) {
     948             :           path.AppendLiteral("/writecopy");
     949             :         }
     950             : 
     951             :         // Modifiers. At most one of these should be set.
     952             :         if (protect & PAGE_GUARD) {
     953             :           path.AppendLiteral("+guard");
     954             :         }
     955             :         if (protect & PAGE_NOCACHE) {
     956             :           path.AppendLiteral("+nocache");
     957             :         }
     958             :         if (protect & PAGE_WRITECOMBINE) {
     959             :           path.AppendLiteral("+writecombine");
     960             :         }
     961             : 
     962             :         // Annotate likely stack segments, too.
     963             :         if (entry->mKind.mIsStack) {
     964             :           path.AppendLiteral("+stack");
     965             :         }
     966             :       }
     967             : 
     968             :       // Append the segment count.
     969             :       path.AppendPrintf("(segments=%u)", entry->mCount);
     970             : 
     971             :       aHandleReport->Callback(
     972             :         EmptyCString(), path, KIND_OTHER, UNITS_BYTES, entry->mSize,
     973             :         NS_LITERAL_CSTRING("From MEMORY_BASIC_INFORMATION."), aData);
     974             :     }
     975             : 
     976             :     return NS_OK;
     977             :   }
     978             : };
     979             : NS_IMPL_ISUPPORTS(WindowsAddressSpaceReporter, nsIMemoryReporter)
     980             : 
     981             : #endif  // XP_<PLATFORM>
     982             : 
     983             : #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
     984             : class VsizeMaxContiguousReporter final : public nsIMemoryReporter
     985             : {
     986             :   ~VsizeMaxContiguousReporter() {}
     987             : 
     988             : public:
     989             :   NS_DECL_ISUPPORTS
     990             : 
     991             :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     992             :                             nsISupports* aData, bool aAnonymize) override
     993             :   {
     994             :     int64_t amount;
     995             :     if (NS_SUCCEEDED(VsizeMaxContiguousDistinguishedAmount(&amount))) {
     996             :       MOZ_COLLECT_REPORT(
     997             :         "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
     998             :         "Size of the maximum contiguous block of available virtual memory.");
     999             :     }
    1000             :     return NS_OK;
    1001             :   }
    1002             : };
    1003             : NS_IMPL_ISUPPORTS(VsizeMaxContiguousReporter, nsIMemoryReporter)
    1004             : #endif
    1005             : 
    1006             : #ifdef HAVE_PRIVATE_REPORTER
    1007             : class PrivateReporter final : public nsIMemoryReporter
    1008             : {
    1009             :   ~PrivateReporter() {}
    1010             : 
    1011             : public:
    1012             :   NS_DECL_ISUPPORTS
    1013             : 
    1014             :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1015             :                             nsISupports* aData, bool aAnonymize) override
    1016             :   {
    1017             :     int64_t amount;
    1018             :     if (NS_SUCCEEDED(PrivateDistinguishedAmount(&amount))) {
    1019             :       MOZ_COLLECT_REPORT(
    1020             :         "private", KIND_OTHER, UNITS_BYTES, amount,
    1021             : "Memory that cannot be shared with other processes, including memory that is "
    1022             : "committed and marked MEM_PRIVATE, data that is not mapped, and executable "
    1023             : "pages that have been written to.");
    1024             :     }
    1025             :     return NS_OK;
    1026             :   }
    1027             : };
    1028             : NS_IMPL_ISUPPORTS(PrivateReporter, nsIMemoryReporter)
    1029             : #endif
    1030             : 
    1031             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    1032           3 : class VsizeReporter final : public nsIMemoryReporter
    1033             : {
    1034           0 :   ~VsizeReporter() {}
    1035             : 
    1036             : public:
    1037             :   NS_DECL_ISUPPORTS
    1038             : 
    1039           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1040             :                             nsISupports* aData, bool aAnonymize) override
    1041             :   {
    1042             :     int64_t amount;
    1043           0 :     if (NS_SUCCEEDED(VsizeDistinguishedAmount(&amount))) {
    1044           0 :       MOZ_COLLECT_REPORT(
    1045             :         "vsize", KIND_OTHER, UNITS_BYTES, amount,
    1046             : "Memory mapped by the process, including code and data segments, the heap, "
    1047             : "thread stacks, memory explicitly mapped by the process via mmap and similar "
    1048             : "operations, and memory shared with other processes. This is the vsize figure "
    1049             : "as reported by 'top' and 'ps'.  This figure is of limited use on Mac, where "
    1050             : "processes share huge amounts of memory with one another.  But even on other "
    1051             : "operating systems, 'resident' is a much better measure of the memory "
    1052           0 : "resources used by the process.");
    1053             :     }
    1054           0 :     return NS_OK;
    1055             :   }
    1056             : };
    1057          24 : NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
    1058             : 
    1059           3 : class ResidentReporter final : public nsIMemoryReporter
    1060             : {
    1061           0 :   ~ResidentReporter() {}
    1062             : 
    1063             : public:
    1064             :   NS_DECL_ISUPPORTS
    1065             : 
    1066           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1067             :                             nsISupports* aData, bool aAnonymize) override
    1068             :   {
    1069             :     int64_t amount;
    1070           0 :     if (NS_SUCCEEDED(ResidentDistinguishedAmount(&amount))) {
    1071           0 :       MOZ_COLLECT_REPORT(
    1072             :         "resident", KIND_OTHER, UNITS_BYTES, amount,
    1073             : "Memory mapped by the process that is present in physical memory, also known "
    1074             : "as the resident set size (RSS).  This is the best single figure to use when "
    1075             : "considering the memory resources used by the process, but it depends both on "
    1076             : "other processes being run and details of the OS kernel and so is best used "
    1077             : "for comparing the memory usage of a single process at different points in "
    1078           0 : "time.");
    1079             :     }
    1080           0 :     return NS_OK;
    1081             :   }
    1082             : };
    1083          24 : NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter)
    1084             : 
    1085             : #endif  // HAVE_VSIZE_AND_RESIDENT_REPORTERS
    1086             : 
    1087             : #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
    1088           3 : class ResidentUniqueReporter final : public nsIMemoryReporter
    1089             : {
    1090           0 :   ~ResidentUniqueReporter() {}
    1091             : 
    1092             : public:
    1093             :   NS_DECL_ISUPPORTS
    1094             : 
    1095           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1096             :                             nsISupports* aData, bool aAnonymize) override
    1097             :   {
    1098           0 :     int64_t amount = 0;
    1099           0 :     if (NS_SUCCEEDED(ResidentUniqueDistinguishedAmount(&amount))) {
    1100           0 :       MOZ_COLLECT_REPORT(
    1101             :         "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
    1102             : "Memory mapped by the process that is present in physical memory and not "
    1103             : "shared with any other processes.  This is also known as the process's unique "
    1104             : "set size (USS).  This is the amount of RAM we'd expect to be freed if we "
    1105           0 : "closed this process.");
    1106             :     }
    1107           0 :     return NS_OK;
    1108             :   }
    1109             : };
    1110          24 : NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter)
    1111             : 
    1112             : #endif // HAVE_RESIDENT_UNIQUE_REPORTER
    1113             : 
    1114             : #ifdef HAVE_SYSTEM_HEAP_REPORTER
    1115             : 
    1116           3 : class SystemHeapReporter final : public nsIMemoryReporter
    1117             : {
    1118           0 :   ~SystemHeapReporter() {}
    1119             : 
    1120             : public:
    1121             :   NS_DECL_ISUPPORTS
    1122             : 
    1123           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1124             :                             nsISupports* aData, bool aAnonymize) override
    1125             :   {
    1126             :     int64_t amount;
    1127           0 :     if (NS_SUCCEEDED(SystemHeapSize(&amount))) {
    1128           0 :       MOZ_COLLECT_REPORT(
    1129             :         "system-heap-allocated", KIND_OTHER, UNITS_BYTES, amount,
    1130             : "Memory used by the system allocator that is currently allocated to the "
    1131             : "application. This is distinct from the jemalloc heap that Firefox uses for "
    1132             : "most or all of its heap allocations. Ideally this number is zero, but "
    1133           0 : "on some platforms we cannot force every heap allocation through jemalloc.");
    1134             :     }
    1135           0 :     return NS_OK;
    1136             :   }
    1137             : };
    1138          24 : NS_IMPL_ISUPPORTS(SystemHeapReporter, nsIMemoryReporter)
    1139             : 
    1140             : #endif // HAVE_SYSTEM_HEAP_REPORTER
    1141             : 
    1142             : #ifdef XP_UNIX
    1143             : 
    1144             : #include <sys/resource.h>
    1145             : 
    1146             : #define HAVE_RESIDENT_PEAK_REPORTER 1
    1147             : 
    1148             : static MOZ_MUST_USE nsresult
    1149           0 : ResidentPeakDistinguishedAmount(int64_t* aN)
    1150             : {
    1151             :   struct rusage usage;
    1152           0 :   if (0 == getrusage(RUSAGE_SELF, &usage)) {
    1153             :     // The units for ru_maxrrs:
    1154             :     // - Mac: bytes
    1155             :     // - Solaris: pages? But some sources it actually always returns 0, so
    1156             :     //   check for that
    1157             :     // - Linux, {Net/Open/Free}BSD, DragonFly: KiB
    1158             : #ifdef XP_MACOSX
    1159             :     *aN = usage.ru_maxrss;
    1160             : #elif defined(SOLARIS)
    1161             :     *aN = usage.ru_maxrss * getpagesize();
    1162             : #else
    1163           0 :     *aN = usage.ru_maxrss * 1024;
    1164             : #endif
    1165           0 :     if (*aN > 0) {
    1166           0 :       return NS_OK;
    1167             :     }
    1168             :   }
    1169           0 :   return NS_ERROR_FAILURE;
    1170             : }
    1171             : 
    1172           3 : class ResidentPeakReporter final : public nsIMemoryReporter
    1173             : {
    1174           0 :   ~ResidentPeakReporter() {}
    1175             : 
    1176             : public:
    1177             :   NS_DECL_ISUPPORTS
    1178             : 
    1179           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1180             :                             nsISupports* aData, bool aAnonymize) override
    1181             :   {
    1182           0 :     int64_t amount = 0;
    1183           0 :     if (NS_SUCCEEDED(ResidentPeakDistinguishedAmount(&amount))) {
    1184           0 :       MOZ_COLLECT_REPORT(
    1185             :         "resident-peak", KIND_OTHER, UNITS_BYTES, amount,
    1186           0 : "The peak 'resident' value for the lifetime of the process.");
    1187             :     }
    1188           0 :     return NS_OK;
    1189             :   }
    1190             : };
    1191          24 : NS_IMPL_ISUPPORTS(ResidentPeakReporter, nsIMemoryReporter)
    1192             : 
    1193             : #define HAVE_PAGE_FAULT_REPORTERS 1
    1194             : 
    1195           3 : class PageFaultsSoftReporter final : public nsIMemoryReporter
    1196             : {
    1197           0 :   ~PageFaultsSoftReporter() {}
    1198             : 
    1199             : public:
    1200             :   NS_DECL_ISUPPORTS
    1201             : 
    1202           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1203             :                             nsISupports* aData, bool aAnonymize) override
    1204             :   {
    1205             :     struct rusage usage;
    1206           0 :     int err = getrusage(RUSAGE_SELF, &usage);
    1207           0 :     if (err == 0) {
    1208           0 :       int64_t amount = usage.ru_minflt;
    1209           0 :       MOZ_COLLECT_REPORT(
    1210             :         "page-faults-soft", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
    1211             : "The number of soft page faults (also known as 'minor page faults') that "
    1212             : "have occurred since the process started.  A soft page fault occurs when the "
    1213             : "process tries to access a page which is present in physical memory but is "
    1214             : "not mapped into the process's address space.  For instance, a process might "
    1215             : "observe soft page faults when it loads a shared library which is already "
    1216             : "present in physical memory. A process may experience many thousands of soft "
    1217             : "page faults even when the machine has plenty of available physical memory, "
    1218             : "and because the OS services a soft page fault without accessing the disk, "
    1219           0 : "they impact performance much less than hard page faults.");
    1220             :     }
    1221           0 :     return NS_OK;
    1222             :   }
    1223             : };
    1224          24 : NS_IMPL_ISUPPORTS(PageFaultsSoftReporter, nsIMemoryReporter)
    1225             : 
    1226             : static MOZ_MUST_USE nsresult
    1227           0 : PageFaultsHardDistinguishedAmount(int64_t* aAmount)
    1228             : {
    1229             :   struct rusage usage;
    1230           0 :   int err = getrusage(RUSAGE_SELF, &usage);
    1231           0 :   if (err != 0) {
    1232           0 :     return NS_ERROR_FAILURE;
    1233             :   }
    1234           0 :   *aAmount = usage.ru_majflt;
    1235           0 :   return NS_OK;
    1236             : }
    1237             : 
    1238           3 : class PageFaultsHardReporter final : public nsIMemoryReporter
    1239             : {
    1240           0 :   ~PageFaultsHardReporter() {}
    1241             : 
    1242             : public:
    1243             :   NS_DECL_ISUPPORTS
    1244             : 
    1245           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1246             :                             nsISupports* aData, bool aAnonymize) override
    1247             :   {
    1248           0 :     int64_t amount = 0;
    1249           0 :     if (NS_SUCCEEDED(PageFaultsHardDistinguishedAmount(&amount))) {
    1250           0 :       MOZ_COLLECT_REPORT(
    1251             :         "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
    1252             : "The number of hard page faults (also known as 'major page faults') that have "
    1253             : "occurred since the process started.  A hard page fault occurs when a process "
    1254             : "tries to access a page which is not present in physical memory. The "
    1255             : "operating system must access the disk in order to fulfill a hard page fault. "
    1256             : "When memory is plentiful, you should see very few hard page faults. But if "
    1257             : "the process tries to use more memory than your machine has available, you "
    1258             : "may see many thousands of hard page faults. Because accessing the disk is up "
    1259             : "to a million times slower than accessing RAM, the program may run very "
    1260             : "slowly when it is experiencing more than 100 or so hard page faults a "
    1261           0 : "second.");
    1262             :     }
    1263           0 :     return NS_OK;
    1264             :   }
    1265             : };
    1266          24 : NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter)
    1267             : 
    1268             : #endif  // XP_UNIX
    1269             : 
    1270             : /**
    1271             :  ** memory reporter implementation for jemalloc and OSX malloc,
    1272             :  ** to obtain info on total memory in use (that we know about,
    1273             :  ** at least -- on OSX, there are sometimes other zones in use).
    1274             :  **/
    1275             : 
    1276             : #ifdef HAVE_JEMALLOC_STATS
    1277             : 
    1278             : static size_t
    1279           0 : HeapOverhead(jemalloc_stats_t* aStats)
    1280             : {
    1281           0 :   return aStats->waste + aStats->bookkeeping +
    1282           0 :          aStats->page_cache + aStats->bin_unused;
    1283             : }
    1284             : 
    1285             : // This has UNITS_PERCENTAGE, so it is multiplied by 100x *again* on top of the
    1286             : // 100x for the percentage.
    1287             : static int64_t
    1288           0 : HeapOverheadFraction(jemalloc_stats_t* aStats)
    1289             : {
    1290           0 :   size_t heapOverhead = HeapOverhead(aStats);
    1291           0 :   size_t heapCommitted = aStats->allocated + heapOverhead;
    1292           0 :   return int64_t(10000 * (heapOverhead / (double)heapCommitted));
    1293             : }
    1294             : 
    1295           3 : class JemallocHeapReporter final : public nsIMemoryReporter
    1296             : {
    1297           0 :   ~JemallocHeapReporter() {}
    1298             : 
    1299             : public:
    1300             :   NS_DECL_ISUPPORTS
    1301             : 
    1302           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1303             :                             nsISupports* aData, bool aAnonymize) override
    1304             :   {
    1305             :     jemalloc_stats_t stats;
    1306           0 :     jemalloc_stats(&stats);
    1307             : 
    1308           0 :     MOZ_COLLECT_REPORT(
    1309             :       "heap-committed/allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
    1310             : "Memory mapped by the heap allocator that is currently allocated to the "
    1311             : "application.  This may exceed the amount of memory requested by the "
    1312             : "application because the allocator regularly rounds up request sizes. (The "
    1313           0 : "exact amount requested is not recorded.)");
    1314             : 
    1315           0 :     MOZ_COLLECT_REPORT(
    1316             :       "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
    1317           0 : "The same as 'heap-committed/allocated'.");
    1318             : 
    1319             :     // We mark this and the other heap-overhead reporters as KIND_NONHEAP
    1320             :     // because KIND_HEAP memory means "counted in heap-allocated", which
    1321             :     // this is not.
    1322           0 :     MOZ_COLLECT_REPORT(
    1323             :       "explicit/heap-overhead/bin-unused", KIND_NONHEAP, UNITS_BYTES,
    1324             :       stats.bin_unused,
    1325             : "Unused bytes due to fragmentation in the bins used for 'small' (<= 2 KiB) "
    1326           0 : "allocations. These bytes will be used if additional allocations occur.");
    1327             : 
    1328           0 :     if (stats.waste > 0) {
    1329           0 :       MOZ_COLLECT_REPORT(
    1330             :         "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES,
    1331             :         stats.waste,
    1332             : "Committed bytes which do not correspond to an active allocation and which the "
    1333             : "allocator is not intentionally keeping alive (i.e., not "
    1334           0 : "'explicit/heap-overhead/{bookkeeping,page-cache,bin-unused}').");
    1335             :     }
    1336             : 
    1337           0 :     MOZ_COLLECT_REPORT(
    1338             :       "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES,
    1339             :       stats.bookkeeping,
    1340           0 : "Committed bytes which the heap allocator uses for internal data structures.");
    1341             : 
    1342           0 :     MOZ_COLLECT_REPORT(
    1343             :       "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES,
    1344             :       stats.page_cache,
    1345             : "Memory which the allocator could return to the operating system, but hasn't. "
    1346             : "The allocator keeps this memory around as an optimization, so it doesn't "
    1347             : "have to ask the OS the next time it needs to fulfill a request. This value "
    1348           0 : "is typically not larger than a few megabytes.");
    1349             : 
    1350           0 :     MOZ_COLLECT_REPORT(
    1351             :       "heap-committed/overhead", KIND_OTHER, UNITS_BYTES,
    1352             :       HeapOverhead(&stats),
    1353           0 : "The sum of 'explicit/heap-overhead/*'.");
    1354             : 
    1355           0 :     MOZ_COLLECT_REPORT(
    1356             :       "heap-mapped", KIND_OTHER, UNITS_BYTES, stats.mapped,
    1357             : "Amount of memory currently mapped. Includes memory that is uncommitted, i.e. "
    1358           0 : "neither in physical memory nor paged to disk.");
    1359             : 
    1360           0 :     MOZ_COLLECT_REPORT(
    1361             :       "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize,
    1362           0 :       "Size of chunks.");
    1363             : 
    1364           0 :     return NS_OK;
    1365             :   }
    1366             : };
    1367          24 : NS_IMPL_ISUPPORTS(JemallocHeapReporter, nsIMemoryReporter)
    1368             : 
    1369             : #endif  // HAVE_JEMALLOC_STATS
    1370             : 
    1371             : // Why is this here?  At first glance, you'd think it could be defined and
    1372             : // registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
    1373             : // However, the obvious time to register it is when the table is initialized,
    1374             : // and that happens before XPCOM components are initialized, which means the
    1375             : // RegisterStrongMemoryReporter call fails.  So instead we do it here.
    1376           3 : class AtomTablesReporter final : public nsIMemoryReporter
    1377             : {
    1378           0 :   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
    1379             : 
    1380           0 :   ~AtomTablesReporter() {}
    1381             : 
    1382             : public:
    1383             :   NS_DECL_ISUPPORTS
    1384             : 
    1385           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1386             :                             nsISupports* aData, bool aAnonymize) override
    1387             :   {
    1388             :     size_t Main, Static;
    1389           0 :     NS_SizeOfAtomTablesIncludingThis(MallocSizeOf, &Main, &Static);
    1390             : 
    1391           0 :     MOZ_COLLECT_REPORT(
    1392             :       "explicit/atom-tables/main", KIND_HEAP, UNITS_BYTES, Main,
    1393           0 :       "Memory used by the main atoms table.");
    1394             : 
    1395           0 :     MOZ_COLLECT_REPORT(
    1396             :       "explicit/atom-tables/static", KIND_HEAP, UNITS_BYTES, Static,
    1397           0 :       "Memory used by the static atoms table.");
    1398             : 
    1399           0 :     return NS_OK;
    1400             :   }
    1401             : };
    1402          24 : NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
    1403             : 
    1404             : #ifdef DEBUG
    1405             : 
    1406             : // Ideally, this would be implemented in BlockingResourceBase.cpp.
    1407             : // However, this ends up breaking the linking step of various unit tests due
    1408             : // to adding a new dependency to libdmd for a commonly used feature (mutexes)
    1409             : // in  DMD  builds. So instead we do it here.
    1410           3 : class DeadlockDetectorReporter final : public nsIMemoryReporter
    1411             : {
    1412           0 :   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
    1413             : 
    1414           0 :   ~DeadlockDetectorReporter() {}
    1415             : 
    1416             : public:
    1417             :   NS_DECL_ISUPPORTS
    1418             : 
    1419           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1420             :                             nsISupports* aData, bool aAnonymize) override
    1421             :   {
    1422           0 :     MOZ_COLLECT_REPORT(
    1423             :       "explicit/deadlock-detector", KIND_HEAP, UNITS_BYTES,
    1424             :       BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf),
    1425           0 :       "Memory used by the deadlock detector.");
    1426             : 
    1427           0 :     return NS_OK;
    1428             :   }
    1429             : };
    1430          24 : NS_IMPL_ISUPPORTS(DeadlockDetectorReporter, nsIMemoryReporter)
    1431             : 
    1432             : #endif
    1433             : 
    1434             : #ifdef MOZ_DMD
    1435             : 
    1436             : namespace mozilla {
    1437             : namespace dmd {
    1438             : 
    1439             : class DMDReporter final : public nsIMemoryReporter
    1440             : {
    1441             : public:
    1442             :   NS_DECL_ISUPPORTS
    1443             : 
    1444             :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1445             :                             nsISupports* aData, bool aAnonymize) override
    1446             :   {
    1447             :     dmd::Sizes sizes;
    1448             :     dmd::SizeOf(&sizes);
    1449             : 
    1450             :     MOZ_COLLECT_REPORT(
    1451             :       "explicit/dmd/stack-traces/used", KIND_HEAP, UNITS_BYTES,
    1452             :       sizes.mStackTracesUsed,
    1453             :       "Memory used by stack traces which correspond to at least "
    1454             :       "one heap block DMD is tracking.");
    1455             : 
    1456             :     MOZ_COLLECT_REPORT(
    1457             :       "explicit/dmd/stack-traces/unused", KIND_HEAP, UNITS_BYTES,
    1458             :       sizes.mStackTracesUnused,
    1459             :       "Memory used by stack traces which don't correspond to any heap "
    1460             :       "blocks DMD is currently tracking.");
    1461             : 
    1462             :     MOZ_COLLECT_REPORT(
    1463             :       "explicit/dmd/stack-traces/table", KIND_HEAP, UNITS_BYTES,
    1464             :       sizes.mStackTraceTable,
    1465             :       "Memory used by DMD's stack trace table.");
    1466             : 
    1467             :     MOZ_COLLECT_REPORT(
    1468             :       "explicit/dmd/live-block-table", KIND_HEAP, UNITS_BYTES,
    1469             :       sizes.mLiveBlockTable,
    1470             :       "Memory used by DMD's live block table.");
    1471             : 
    1472             :     MOZ_COLLECT_REPORT(
    1473             :       "explicit/dmd/dead-block-list", KIND_HEAP, UNITS_BYTES,
    1474             :       sizes.mDeadBlockTable,
    1475             :       "Memory used by DMD's dead block list.");
    1476             : 
    1477             :     return NS_OK;
    1478             :   }
    1479             : 
    1480             : private:
    1481             :   ~DMDReporter() {}
    1482             : };
    1483             : NS_IMPL_ISUPPORTS(DMDReporter, nsIMemoryReporter)
    1484             : 
    1485             : } // namespace dmd
    1486             : } // namespace mozilla
    1487             : 
    1488             : #endif  // MOZ_DMD
    1489             : 
    1490             : /**
    1491             :  ** nsMemoryReporterManager implementation
    1492             :  **/
    1493             : 
    1494         743 : NS_IMPL_ISUPPORTS(nsMemoryReporterManager, nsIMemoryReporterManager)
    1495             : 
    1496             : NS_IMETHODIMP
    1497           3 : nsMemoryReporterManager::Init()
    1498             : {
    1499           3 :   if (!NS_IsMainThread()) {
    1500           0 :     MOZ_CRASH();
    1501             :   }
    1502             : 
    1503             :   // Under normal circumstances this function is only called once. However,
    1504             :   // we've (infrequently) seen memory report dumps in crash reports that
    1505             :   // suggest that this function is sometimes called multiple times. That in
    1506             :   // turn means that multiple reporters of each kind are registered, which
    1507             :   // leads to duplicated reports of individual measurements such as "resident",
    1508             :   // "vsize", etc.
    1509             :   //
    1510             :   // It's unclear how these multiple calls can occur. The only plausible theory
    1511             :   // so far is badly-written extensions, because this function is callable from
    1512             :   // JS code via nsIMemoryReporter.idl.
    1513             :   //
    1514             :   // Whatever the cause, it's a bad thing. So we protect against it with the
    1515             :   // following check.
    1516             :   static bool isInited = false;
    1517           3 :   if (isInited) {
    1518           0 :     NS_WARNING("nsMemoryReporterManager::Init() has already been called!");
    1519           0 :     return NS_OK;
    1520             :   }
    1521           3 :   isInited = true;
    1522             : 
    1523             : #if defined(HAVE_JEMALLOC_STATS) && defined(MOZ_GLUE_IN_PROGRAM)
    1524           3 :   if (!jemalloc_stats) {
    1525           0 :     return NS_ERROR_FAILURE;
    1526             :   }
    1527             : #endif
    1528             : 
    1529             : #ifdef HAVE_JEMALLOC_STATS
    1530           3 :   RegisterStrongReporter(new JemallocHeapReporter());
    1531             : #endif
    1532             : 
    1533             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    1534           3 :   RegisterStrongReporter(new VsizeReporter());
    1535           3 :   RegisterStrongReporter(new ResidentReporter());
    1536             : #endif
    1537             : 
    1538             : #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
    1539             :   RegisterStrongReporter(new VsizeMaxContiguousReporter());
    1540             : #endif
    1541             : 
    1542             : #ifdef HAVE_RESIDENT_PEAK_REPORTER
    1543           3 :   RegisterStrongReporter(new ResidentPeakReporter());
    1544             : #endif
    1545             : 
    1546             : #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
    1547           3 :   RegisterStrongReporter(new ResidentUniqueReporter());
    1548             : #endif
    1549             : 
    1550             : #ifdef HAVE_PAGE_FAULT_REPORTERS
    1551           3 :   RegisterStrongReporter(new PageFaultsSoftReporter());
    1552           3 :   RegisterStrongReporter(new PageFaultsHardReporter());
    1553             : #endif
    1554             : 
    1555             : #ifdef HAVE_PRIVATE_REPORTER
    1556             :   RegisterStrongReporter(new PrivateReporter());
    1557             : #endif
    1558             : 
    1559             : #ifdef HAVE_SYSTEM_HEAP_REPORTER
    1560           3 :   RegisterStrongReporter(new SystemHeapReporter());
    1561             : #endif
    1562             : 
    1563           3 :   RegisterStrongReporter(new AtomTablesReporter());
    1564             : 
    1565             : #ifdef DEBUG
    1566           3 :   RegisterStrongReporter(new DeadlockDetectorReporter());
    1567             : #endif
    1568             : 
    1569             : #ifdef MOZ_GECKO_PROFILER
    1570             :   // We have to register this here rather than in profiler_init() because
    1571             :   // profiler_init() runs prior to nsMemoryReporterManager's creation.
    1572           3 :   RegisterStrongReporter(new GeckoProfilerReporter());
    1573             : #endif
    1574             : 
    1575             : #ifdef MOZ_DMD
    1576             :   RegisterStrongReporter(new mozilla::dmd::DMDReporter());
    1577             : #endif
    1578             : 
    1579             : #ifdef XP_WIN
    1580             :   RegisterStrongReporter(new WindowsAddressSpaceReporter());
    1581             : #endif
    1582             : 
    1583             : #ifdef XP_UNIX
    1584           3 :   nsMemoryInfoDumper::Initialize();
    1585             : #endif
    1586             : 
    1587           3 :   return NS_OK;
    1588             : }
    1589             : 
    1590           3 : nsMemoryReporterManager::nsMemoryReporterManager()
    1591             :   : mMutex("nsMemoryReporterManager::mMutex")
    1592             :   , mIsRegistrationBlocked(false)
    1593           3 :   , mStrongReporters(new StrongReportersTable())
    1594           3 :   , mWeakReporters(new WeakReportersTable())
    1595             :   , mSavedStrongReporters(nullptr)
    1596             :   , mSavedWeakReporters(nullptr)
    1597             :   , mNextGeneration(1)
    1598             :   , mPendingProcessesState(nullptr)
    1599             :   , mPendingReportersState(nullptr)
    1600             : #ifdef HAVE_JEMALLOC_STATS
    1601           9 :   , mThreadPool(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID))
    1602             : #endif
    1603             : {
    1604           3 : }
    1605             : 
    1606           0 : nsMemoryReporterManager::~nsMemoryReporterManager()
    1607             : {
    1608           0 :   delete mStrongReporters;
    1609           0 :   delete mWeakReporters;
    1610           0 :   NS_ASSERTION(!mSavedStrongReporters, "failed to restore strong reporters");
    1611           0 :   NS_ASSERTION(!mSavedWeakReporters, "failed to restore weak reporters");
    1612           0 : }
    1613             : 
    1614             : #ifdef MOZ_WIDGET_GONK
    1615             : #define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
    1616             : #endif
    1617             : 
    1618             : #ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
    1619             : #define MEMORY_REPORTING_LOG(format, ...) \
    1620             :   printf_stderr("++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
    1621             : #else
    1622             : #define MEMORY_REPORTING_LOG(...)
    1623             : #endif
    1624             : 
    1625             : NS_IMETHODIMP
    1626           0 : nsMemoryReporterManager::GetReports(
    1627             :   nsIHandleReportCallback* aHandleReport,
    1628             :   nsISupports* aHandleReportData,
    1629             :   nsIFinishReportingCallback* aFinishReporting,
    1630             :   nsISupports* aFinishReportingData,
    1631             :   bool aAnonymize)
    1632             : {
    1633           0 :   return GetReportsExtended(aHandleReport, aHandleReportData,
    1634             :                             aFinishReporting, aFinishReportingData,
    1635             :                             aAnonymize,
    1636             :                             /* minimize = */ false,
    1637           0 :                             /* DMDident = */ EmptyString());
    1638             : }
    1639             : 
    1640             : NS_IMETHODIMP
    1641           0 : nsMemoryReporterManager::GetReportsExtended(
    1642             :   nsIHandleReportCallback* aHandleReport,
    1643             :   nsISupports* aHandleReportData,
    1644             :   nsIFinishReportingCallback* aFinishReporting,
    1645             :   nsISupports* aFinishReportingData,
    1646             :   bool aAnonymize,
    1647             :   bool aMinimize,
    1648             :   const nsAString& aDMDDumpIdent)
    1649             : {
    1650             :   nsresult rv;
    1651             : 
    1652             :   // Memory reporters are not necessarily threadsafe, so this function must
    1653             :   // be called from the main thread.
    1654           0 :   if (!NS_IsMainThread()) {
    1655           0 :     MOZ_CRASH();
    1656             :   }
    1657             : 
    1658           0 :   uint32_t generation = mNextGeneration++;
    1659             : 
    1660           0 :   if (mPendingProcessesState) {
    1661             :     // A request is in flight.  Don't start another one.  And don't report
    1662             :     // an error;  just ignore it, and let the in-flight request finish.
    1663             :     MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
    1664             :                          generation, mPendingProcessesState->mGeneration);
    1665           0 :     return NS_OK;
    1666             :   }
    1667             : 
    1668             :   MEMORY_REPORTING_LOG("GetReports (gen=%u)\n", generation);
    1669             : 
    1670           0 :   uint32_t concurrency = Preferences::GetUint("memory.report_concurrency", 1);
    1671           0 :   MOZ_ASSERT(concurrency >= 1);
    1672           0 :   if (concurrency < 1) {
    1673           0 :     concurrency = 1;
    1674             :   }
    1675           0 :   mPendingProcessesState = new PendingProcessesState(generation,
    1676             :                                                      aAnonymize,
    1677             :                                                      aMinimize,
    1678             :                                                      concurrency,
    1679             :                                                      aHandleReport,
    1680             :                                                      aHandleReportData,
    1681             :                                                      aFinishReporting,
    1682             :                                                      aFinishReportingData,
    1683           0 :                                                      aDMDDumpIdent);
    1684             : 
    1685           0 :   if (aMinimize) {
    1686             :     nsCOMPtr<nsIRunnable> callback =
    1687           0 :       NewRunnableMethod("nsMemoryReporterManager::StartGettingReports",
    1688             :                         this,
    1689           0 :                         &nsMemoryReporterManager::StartGettingReports);
    1690           0 :     rv = MinimizeMemoryUsage(callback);
    1691             :   } else {
    1692           0 :     rv = StartGettingReports();
    1693             :   }
    1694           0 :   return rv;
    1695             : }
    1696             : 
    1697             : nsresult
    1698           0 : nsMemoryReporterManager::StartGettingReports()
    1699             : {
    1700           0 :   PendingProcessesState* s = mPendingProcessesState;
    1701             :   nsresult rv;
    1702             : 
    1703             :   // Get reports for this process.
    1704           0 :   FILE* parentDMDFile = nullptr;
    1705             : #ifdef MOZ_DMD
    1706             :   if (!s->mDMDDumpIdent.IsEmpty()) {
    1707             :     rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
    1708             :                                                   &parentDMDFile);
    1709             :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1710             :       // Proceed with the memory report as if DMD were disabled.
    1711             :       parentDMDFile = nullptr;
    1712             :     }
    1713             :   }
    1714             : #endif
    1715             : 
    1716             :   // This is async.
    1717           0 :   GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
    1718           0 :                                    s->mAnonymize, parentDMDFile,
    1719           0 :                                    s->mFinishReporting, s->mFinishReportingData);
    1720             : 
    1721           0 :   nsTArray<dom::ContentParent*> childWeakRefs;
    1722           0 :   dom::ContentParent::GetAll(childWeakRefs);
    1723           0 :   if (!childWeakRefs.IsEmpty()) {
    1724             :     // Request memory reports from child processes.  This happens
    1725             :     // after the parent report so that the parent's main thread will
    1726             :     // be free to process the child reports, instead of causing them
    1727             :     // to be buffered and consume (possibly scarce) memory.
    1728             : 
    1729           0 :     for (size_t i = 0; i < childWeakRefs.Length(); ++i) {
    1730           0 :       s->mChildrenPending.AppendElement(childWeakRefs[i]);
    1731             :     }
    1732             :   }
    1733             : 
    1734           0 :   if (gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get()) {
    1735           0 :     if (RefPtr<MemoryReportingProcess> proc = gpu->GetProcessMemoryReporter()) {
    1736           0 :       s->mChildrenPending.AppendElement(proc.forget());
    1737             :     }
    1738             :   }
    1739             : 
    1740           0 :   if (!s->mChildrenPending.IsEmpty()) {
    1741           0 :     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
    1742             :     // Don't use NS_ENSURE_* here; can't return until the report is finished.
    1743           0 :     if (NS_WARN_IF(!timer)) {
    1744           0 :       FinishReporting();
    1745           0 :       return NS_ERROR_FAILURE;
    1746             :     }
    1747           0 :     rv = timer->InitWithNamedFuncCallback(
    1748             :       TimeoutCallback,
    1749             :       this,
    1750             :       kTimeoutLengthMS,
    1751             :       nsITimer::TYPE_ONE_SHOT,
    1752           0 :       "nsMemoryReporterManager::StartGettingReports");
    1753           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1754           0 :       FinishReporting();
    1755           0 :       return rv;
    1756             :     }
    1757             : 
    1758           0 :     MOZ_ASSERT(!s->mTimer);
    1759           0 :     s->mTimer.swap(timer);
    1760             :   }
    1761             : 
    1762           0 :   return NS_OK;
    1763             : }
    1764             : 
    1765             : void
    1766           0 : nsMemoryReporterManager::DispatchReporter(
    1767             :   nsIMemoryReporter* aReporter, bool aIsAsync,
    1768             :   nsIHandleReportCallback* aHandleReport,
    1769             :   nsISupports* aHandleReportData,
    1770             :   bool aAnonymize)
    1771             : {
    1772           0 :   MOZ_ASSERT(mPendingReportersState);
    1773             : 
    1774             :   // Grab refs to everything used in the lambda function.
    1775           0 :   RefPtr<nsMemoryReporterManager> self = this;
    1776           0 :   nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
    1777           0 :   nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
    1778           0 :   nsCOMPtr<nsISupports> handleReportData = aHandleReportData;
    1779             : 
    1780           0 :   nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
    1781             :     "nsMemoryReporterManager::DispatchReporter",
    1782           0 :     [self, reporter, aIsAsync, handleReport, handleReportData, aAnonymize]() {
    1783           0 :       reporter->CollectReports(handleReport, handleReportData, aAnonymize);
    1784           0 :       if (!aIsAsync) {
    1785           0 :         self->EndReport();
    1786             :       }
    1787           0 :     });
    1788             : 
    1789           0 :   NS_DispatchToMainThread(event);
    1790           0 :   mPendingReportersState->mReportsPending++;
    1791           0 : }
    1792             : 
    1793             : NS_IMETHODIMP
    1794           0 : nsMemoryReporterManager::GetReportsForThisProcessExtended(
    1795             :   nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
    1796             :   bool aAnonymize, FILE* aDMDFile,
    1797             :   nsIFinishReportingCallback* aFinishReporting,
    1798             :   nsISupports* aFinishReportingData)
    1799             : {
    1800             :   // Memory reporters are not necessarily threadsafe, so this function must
    1801             :   // be called from the main thread.
    1802           0 :   if (!NS_IsMainThread()) {
    1803           0 :     MOZ_CRASH();
    1804             :   }
    1805             : 
    1806           0 :   if (NS_WARN_IF(mPendingReportersState)) {
    1807             :     // Report is already in progress.
    1808           0 :     return NS_ERROR_IN_PROGRESS;
    1809             :   }
    1810             : 
    1811             : #ifdef MOZ_DMD
    1812             :   if (aDMDFile) {
    1813             :     // Clear DMD's reportedness state before running the memory
    1814             :     // reporters, to avoid spurious twice-reported warnings.
    1815             :     dmd::ClearReports();
    1816             :   }
    1817             : #else
    1818           0 :   MOZ_ASSERT(!aDMDFile);
    1819             : #endif
    1820             : 
    1821           0 :   mPendingReportersState = new PendingReportersState(
    1822           0 :       aFinishReporting, aFinishReportingData, aDMDFile);
    1823             : 
    1824             :   {
    1825           0 :     mozilla::MutexAutoLock autoLock(mMutex);
    1826             : 
    1827           0 :     for (auto iter = mStrongReporters->Iter(); !iter.Done(); iter.Next()) {
    1828           0 :       DispatchReporter(iter.Key(), iter.Data(),
    1829           0 :                        aHandleReport, aHandleReportData, aAnonymize);
    1830             :     }
    1831             : 
    1832           0 :     for (auto iter = mWeakReporters->Iter(); !iter.Done(); iter.Next()) {
    1833           0 :       nsCOMPtr<nsIMemoryReporter> reporter = iter.Key();
    1834           0 :       DispatchReporter(reporter, iter.Data(),
    1835           0 :                        aHandleReport, aHandleReportData, aAnonymize);
    1836             :     }
    1837             :   }
    1838             : 
    1839           0 :   return NS_OK;
    1840             : }
    1841             : 
    1842             : NS_IMETHODIMP
    1843           0 : nsMemoryReporterManager::EndReport()
    1844             : {
    1845           0 :   if (--mPendingReportersState->mReportsPending == 0) {
    1846             : #ifdef MOZ_DMD
    1847             :     if (mPendingReportersState->mDMDFile) {
    1848             :       nsMemoryInfoDumper::DumpDMDToFile(mPendingReportersState->mDMDFile);
    1849             :     }
    1850             : #endif
    1851           0 :     if (mPendingProcessesState) {
    1852             :       // This is the parent process.
    1853           0 :       EndProcessReport(mPendingProcessesState->mGeneration, true);
    1854             :     } else {
    1855           0 :       mPendingReportersState->mFinishReporting->Callback(
    1856           0 :           mPendingReportersState->mFinishReportingData);
    1857             :     }
    1858             : 
    1859           0 :     delete mPendingReportersState;
    1860           0 :     mPendingReportersState = nullptr;
    1861             :   }
    1862             : 
    1863           0 :   return NS_OK;
    1864             : }
    1865             : 
    1866             : nsMemoryReporterManager::PendingProcessesState*
    1867           0 : nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
    1868             : {
    1869             :   // Memory reporting only happens on the main thread.
    1870           0 :   MOZ_RELEASE_ASSERT(NS_IsMainThread());
    1871             : 
    1872           0 :   PendingProcessesState* s = mPendingProcessesState;
    1873             : 
    1874           0 :   if (!s) {
    1875             :     // If we reach here, then:
    1876             :     //
    1877             :     // - A child process reported back too late, and no subsequent request
    1878             :     //   is in flight.
    1879             :     //
    1880             :     // So there's nothing to be done.  Just ignore it.
    1881             :     MEMORY_REPORTING_LOG(
    1882             :       "HandleChildReports: no request in flight (aGen=%u)\n",
    1883             :       aGeneration);
    1884           0 :     return nullptr;
    1885             :   }
    1886             : 
    1887           0 :   if (aGeneration != s->mGeneration) {
    1888             :     // If we reach here, a child process must have reported back, too late,
    1889             :     // while a subsequent (higher-numbered) request is in flight.  Again,
    1890             :     // ignore it.
    1891           0 :     MOZ_ASSERT(aGeneration < s->mGeneration);
    1892             :     MEMORY_REPORTING_LOG(
    1893             :       "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
    1894             :       aGeneration, s->mGeneration);
    1895           0 :     return nullptr;
    1896             :   }
    1897             : 
    1898           0 :   return s;
    1899             : }
    1900             : 
    1901             : // This function has no return value.  If something goes wrong, there's no
    1902             : // clear place to report the problem to, but that's ok -- we will end up
    1903             : // hitting the timeout and executing TimeoutCallback().
    1904             : void
    1905           0 : nsMemoryReporterManager::HandleChildReport(
    1906             :   uint32_t aGeneration,
    1907             :   const dom::MemoryReport& aChildReport)
    1908             : {
    1909           0 :   PendingProcessesState* s = GetStateForGeneration(aGeneration);
    1910           0 :   if (!s) {
    1911           0 :     return;
    1912             :   }
    1913             : 
    1914             :   // Child reports should have a non-empty process.
    1915           0 :   MOZ_ASSERT(!aChildReport.process().IsEmpty());
    1916             : 
    1917             :   // If the call fails, ignore and continue.
    1918           0 :   s->mHandleReport->Callback(aChildReport.process(),
    1919           0 :                              aChildReport.path(),
    1920           0 :                              aChildReport.kind(),
    1921           0 :                              aChildReport.units(),
    1922           0 :                              aChildReport.amount(),
    1923           0 :                              aChildReport.desc(),
    1924           0 :                              s->mHandleReportData);
    1925             : }
    1926             : 
    1927             : /* static */ bool
    1928           0 : nsMemoryReporterManager::StartChildReport(mozilla::MemoryReportingProcess* aChild,
    1929             :                                           const PendingProcessesState* aState)
    1930             : {
    1931           0 :   if (!aChild->IsAlive()) {
    1932             :     MEMORY_REPORTING_LOG("StartChildReports (gen=%u): child exited before"
    1933             :                          " its report was started\n",
    1934             :                          aState->mGeneration);
    1935           0 :     return false;
    1936             :   }
    1937             : 
    1938           0 :   mozilla::dom::MaybeFileDesc dmdFileDesc = void_t();
    1939             : #ifdef MOZ_DMD
    1940             :   if (!aState->mDMDDumpIdent.IsEmpty()) {
    1941             :     FILE *dmdFile = nullptr;
    1942             :     nsresult rv = nsMemoryInfoDumper::OpenDMDFile(aState->mDMDDumpIdent,
    1943             :                                                   aChild->Pid(), &dmdFile);
    1944             :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1945             :       // Proceed with the memory report as if DMD were disabled.
    1946             :       dmdFile = nullptr;
    1947             :     }
    1948             :     if (dmdFile) {
    1949             :       dmdFileDesc = mozilla::ipc::FILEToFileDescriptor(dmdFile);
    1950             :       fclose(dmdFile);
    1951             :     }
    1952             :   }
    1953             : #endif
    1954           0 :   return aChild->SendRequestMemoryReport(
    1955           0 :     aState->mGeneration, aState->mAnonymize, aState->mMinimize, dmdFileDesc);
    1956             : }
    1957             : 
    1958             : void
    1959           0 : nsMemoryReporterManager::EndProcessReport(uint32_t aGeneration, bool aSuccess)
    1960             : {
    1961           0 :   PendingProcessesState* s = GetStateForGeneration(aGeneration);
    1962           0 :   if (!s) {
    1963           0 :     return;
    1964             :   }
    1965             : 
    1966           0 :   MOZ_ASSERT(s->mNumProcessesRunning > 0);
    1967           0 :   s->mNumProcessesRunning--;
    1968           0 :   s->mNumProcessesCompleted++;
    1969             :   MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): process %u %s"
    1970             :                        " (%u running, %u pending)\n",
    1971             :                        aGeneration, s->mNumProcessesCompleted,
    1972             :                        aSuccess ? "completed" : "exited during report",
    1973             :                        s->mNumProcessesRunning,
    1974             :                        static_cast<unsigned>(s->mChildrenPending.Length()));
    1975             : 
    1976             :   // Start pending children up to the concurrency limit.
    1977           0 :   while (s->mNumProcessesRunning < s->mConcurrencyLimit &&
    1978           0 :          !s->mChildrenPending.IsEmpty()) {
    1979             :     // Pop last element from s->mChildrenPending
    1980           0 :     RefPtr<MemoryReportingProcess> nextChild;
    1981           0 :     nextChild.swap(s->mChildrenPending.LastElement());
    1982           0 :     s->mChildrenPending.TruncateLength(s->mChildrenPending.Length() - 1);
    1983             :     // Start report (if the child is still alive).
    1984           0 :     if (StartChildReport(nextChild, s)) {
    1985           0 :       ++s->mNumProcessesRunning;
    1986             :       MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): started child report"
    1987             :                            " (%u running, %u pending)\n",
    1988             :                            aGeneration, s->mNumProcessesRunning,
    1989             :                            static_cast<unsigned>(s->mChildrenPending.Length()));
    1990             :     }
    1991             :   }
    1992             : 
    1993             :   // If all the child processes (if any) have reported, we can cancel
    1994             :   // the timer (if started) and finish up.  Otherwise, just return.
    1995           0 :   if (s->mNumProcessesRunning == 0) {
    1996           0 :     MOZ_ASSERT(s->mChildrenPending.IsEmpty());
    1997           0 :     if (s->mTimer) {
    1998           0 :       s->mTimer->Cancel();
    1999             :     }
    2000           0 :     FinishReporting();
    2001             :   }
    2002             : }
    2003             : 
    2004             : /* static */ void
    2005           0 : nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
    2006             : {
    2007           0 :   nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
    2008           0 :   PendingProcessesState* s = mgr->mPendingProcessesState;
    2009             : 
    2010             :   // Release assert because: if the pointer is null we're about to
    2011             :   // crash regardless of DEBUG, and this way the compiler doesn't
    2012             :   // complain about unused variables.
    2013           0 :   MOZ_RELEASE_ASSERT(s, "mgr->mPendingProcessesState");
    2014             :   MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u; %u running, %u pending)\n",
    2015             :                        s->mGeneration, s->mNumProcessesRunning,
    2016             :                        static_cast<unsigned>(s->mChildrenPending.Length()));
    2017             : 
    2018             :   // We don't bother sending any kind of cancellation message to the child
    2019             :   // processes that haven't reported back.
    2020           0 :   mgr->FinishReporting();
    2021           0 : }
    2022             : 
    2023             : nsresult
    2024           0 : nsMemoryReporterManager::FinishReporting()
    2025             : {
    2026             :   // Memory reporting only happens on the main thread.
    2027           0 :   if (!NS_IsMainThread()) {
    2028           0 :     MOZ_CRASH();
    2029             :   }
    2030             : 
    2031           0 :   MOZ_ASSERT(mPendingProcessesState);
    2032             :   MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u; %u processes reported)\n",
    2033             :                        mPendingProcessesState->mGeneration,
    2034             :                        mPendingProcessesState->mNumProcessesCompleted);
    2035             : 
    2036             :   // Call this before deleting |mPendingProcessesState|.  That way, if
    2037             :   // |mFinishReportData| calls GetReports(), it will silently abort, as
    2038             :   // required.
    2039           0 :   nsresult rv = mPendingProcessesState->mFinishReporting->Callback(
    2040           0 :     mPendingProcessesState->mFinishReportingData);
    2041             : 
    2042           0 :   delete mPendingProcessesState;
    2043           0 :   mPendingProcessesState = nullptr;
    2044           0 :   return rv;
    2045             : }
    2046             : 
    2047           0 : nsMemoryReporterManager::PendingProcessesState::PendingProcessesState(
    2048             :     uint32_t aGeneration, bool aAnonymize, bool aMinimize,
    2049             :     uint32_t aConcurrencyLimit,
    2050             :     nsIHandleReportCallback* aHandleReport,
    2051             :     nsISupports* aHandleReportData,
    2052             :     nsIFinishReportingCallback* aFinishReporting,
    2053             :     nsISupports* aFinishReportingData,
    2054           0 :     const nsAString& aDMDDumpIdent)
    2055             :   : mGeneration(aGeneration)
    2056             :   , mAnonymize(aAnonymize)
    2057             :   , mMinimize(aMinimize)
    2058             :   , mChildrenPending()
    2059             :   , mNumProcessesRunning(1) // reporting starts with the parent
    2060             :   , mNumProcessesCompleted(0)
    2061             :   , mConcurrencyLimit(aConcurrencyLimit)
    2062             :   , mHandleReport(aHandleReport)
    2063             :   , mHandleReportData(aHandleReportData)
    2064             :   , mFinishReporting(aFinishReporting)
    2065             :   , mFinishReportingData(aFinishReportingData)
    2066           0 :   , mDMDDumpIdent(aDMDDumpIdent)
    2067             : {
    2068           0 : }
    2069             : 
    2070             : static void
    2071         146 : CrashIfRefcountIsZero(nsISupports* aObj)
    2072             : {
    2073             :   // This will probably crash if the object's refcount is 0.
    2074         146 :   uint32_t refcnt = NS_ADDREF(aObj);
    2075         146 :   if (refcnt <= 1) {
    2076           0 :     MOZ_CRASH("CrashIfRefcountIsZero: refcount is zero");
    2077             :   }
    2078         146 :   NS_RELEASE(aObj);
    2079         146 : }
    2080             : 
    2081             : nsresult
    2082         146 : nsMemoryReporterManager::RegisterReporterHelper(
    2083             :   nsIMemoryReporter* aReporter, bool aForce, bool aStrong, bool aIsAsync)
    2084             : {
    2085             :   // This method is thread-safe.
    2086         292 :   mozilla::MutexAutoLock autoLock(mMutex);
    2087             : 
    2088         146 :   if (mIsRegistrationBlocked && !aForce) {
    2089           0 :     return NS_ERROR_FAILURE;
    2090             :   }
    2091             : 
    2092         292 :   if (mStrongReporters->Contains(aReporter) ||
    2093         146 :       mWeakReporters->Contains(aReporter)) {
    2094           0 :     return NS_ERROR_FAILURE;
    2095             :   }
    2096             : 
    2097             :   // If |aStrong| is true, |aReporter| may have a refcnt of 0, so we take
    2098             :   // a kung fu death grip before calling PutEntry.  Otherwise, if PutEntry
    2099             :   // addref'ed and released |aReporter| before finally addref'ing it for
    2100             :   // good, it would free aReporter!  The kung fu death grip could itself be
    2101             :   // problematic if PutEntry didn't addref |aReporter| (because then when the
    2102             :   // death grip goes out of scope, we would delete the reporter).  In debug
    2103             :   // mode, we check that this doesn't happen.
    2104             :   //
    2105             :   // If |aStrong| is false, we require that |aReporter| have a non-zero
    2106             :   // refcnt.
    2107             :   //
    2108         146 :   if (aStrong) {
    2109         176 :     nsCOMPtr<nsIMemoryReporter> kungFuDeathGrip = aReporter;
    2110          88 :     mStrongReporters->Put(aReporter, aIsAsync);
    2111          88 :     CrashIfRefcountIsZero(aReporter);
    2112             :   } else {
    2113          58 :     CrashIfRefcountIsZero(aReporter);
    2114         116 :     nsCOMPtr<nsIXPConnectWrappedJS> jsComponent = do_QueryInterface(aReporter);
    2115          58 :     if (jsComponent) {
    2116             :       // We cannot allow non-native reporters (WrappedJS), since we'll be
    2117             :       // holding onto a raw pointer, which would point to the wrapper,
    2118             :       // and that wrapper is likely to go away as soon as this register
    2119             :       // call finishes.  This would then lead to subsequent crashes in
    2120             :       // CollectReports().
    2121           0 :       return NS_ERROR_XPC_BAD_CONVERT_JS;
    2122             :     }
    2123          58 :     mWeakReporters->Put(aReporter, aIsAsync);
    2124             :   }
    2125             : 
    2126         146 :   return NS_OK;
    2127             : }
    2128             : 
    2129             : NS_IMETHODIMP
    2130          88 : nsMemoryReporterManager::RegisterStrongReporter(nsIMemoryReporter* aReporter)
    2131             : {
    2132             :   return RegisterReporterHelper(aReporter, /* force = */ false,
    2133             :                                 /* strong = */ true,
    2134          88 :                                 /* async = */ false);
    2135             : }
    2136             : 
    2137             : NS_IMETHODIMP
    2138           0 : nsMemoryReporterManager::RegisterStrongAsyncReporter(nsIMemoryReporter* aReporter)
    2139             : {
    2140             :   return RegisterReporterHelper(aReporter, /* force = */ false,
    2141             :                                 /* strong = */ true,
    2142           0 :                                 /* async = */ true);
    2143             : }
    2144             : 
    2145             : NS_IMETHODIMP
    2146          57 : nsMemoryReporterManager::RegisterWeakReporter(nsIMemoryReporter* aReporter)
    2147             : {
    2148             :   return RegisterReporterHelper(aReporter, /* force = */ false,
    2149             :                                 /* strong = */ false,
    2150          57 :                                 /* async = */ false);
    2151             : }
    2152             : 
    2153             : NS_IMETHODIMP
    2154           1 : nsMemoryReporterManager::RegisterWeakAsyncReporter(nsIMemoryReporter* aReporter)
    2155             : {
    2156             :   return RegisterReporterHelper(aReporter, /* force = */ false,
    2157             :                                 /* strong = */ false,
    2158           1 :                                 /* async = */ true);
    2159             : }
    2160             : 
    2161             : NS_IMETHODIMP
    2162           0 : nsMemoryReporterManager::RegisterStrongReporterEvenIfBlocked(
    2163             :   nsIMemoryReporter* aReporter)
    2164             : {
    2165             :   return RegisterReporterHelper(aReporter, /* force = */ true,
    2166             :                                 /* strong = */ true,
    2167           0 :                                 /* async = */ false);
    2168             : }
    2169             : 
    2170             : NS_IMETHODIMP
    2171           0 : nsMemoryReporterManager::UnregisterStrongReporter(nsIMemoryReporter* aReporter)
    2172             : {
    2173             :   // This method is thread-safe.
    2174           0 :   mozilla::MutexAutoLock autoLock(mMutex);
    2175             : 
    2176           0 :   MOZ_ASSERT(!mWeakReporters->Contains(aReporter));
    2177             : 
    2178           0 :   if (mStrongReporters->Contains(aReporter)) {
    2179           0 :     mStrongReporters->Remove(aReporter);
    2180           0 :     return NS_OK;
    2181             :   }
    2182             : 
    2183             :   // We don't register new reporters when the block is in place, but we do
    2184             :   // unregister existing reporters. This is so we don't keep holding strong
    2185             :   // references that these reporters aren't expecting (which can keep them
    2186             :   // alive longer than intended).
    2187           0 :   if (mSavedStrongReporters && mSavedStrongReporters->Contains(aReporter)) {
    2188           0 :     mSavedStrongReporters->Remove(aReporter);
    2189           0 :     return NS_OK;
    2190             :   }
    2191             : 
    2192           0 :   return NS_ERROR_FAILURE;
    2193             : }
    2194             : 
    2195             : NS_IMETHODIMP
    2196           7 : nsMemoryReporterManager::UnregisterWeakReporter(nsIMemoryReporter* aReporter)
    2197             : {
    2198             :   // This method is thread-safe.
    2199          14 :   mozilla::MutexAutoLock autoLock(mMutex);
    2200             : 
    2201           7 :   MOZ_ASSERT(!mStrongReporters->Contains(aReporter));
    2202             : 
    2203           7 :   if (mWeakReporters->Contains(aReporter)) {
    2204           7 :     mWeakReporters->Remove(aReporter);
    2205           7 :     return NS_OK;
    2206             :   }
    2207             : 
    2208             :   // We don't register new reporters when the block is in place, but we do
    2209             :   // unregister existing reporters. This is so we don't keep holding weak
    2210             :   // references that the old reporters aren't expecting (which can end up as
    2211             :   // dangling pointers that lead to use-after-frees).
    2212           0 :   if (mSavedWeakReporters && mSavedWeakReporters->Contains(aReporter)) {
    2213           0 :     mSavedWeakReporters->Remove(aReporter);
    2214           0 :     return NS_OK;
    2215             :   }
    2216             : 
    2217           0 :   return NS_ERROR_FAILURE;
    2218             : }
    2219             : 
    2220             : NS_IMETHODIMP
    2221           0 : nsMemoryReporterManager::BlockRegistrationAndHideExistingReporters()
    2222             : {
    2223             :   // This method is thread-safe.
    2224           0 :   mozilla::MutexAutoLock autoLock(mMutex);
    2225           0 :   if (mIsRegistrationBlocked) {
    2226           0 :     return NS_ERROR_FAILURE;
    2227             :   }
    2228           0 :   mIsRegistrationBlocked = true;
    2229             : 
    2230             :   // Hide the existing reporters, saving them for later restoration.
    2231           0 :   MOZ_ASSERT(!mSavedStrongReporters);
    2232           0 :   MOZ_ASSERT(!mSavedWeakReporters);
    2233           0 :   mSavedStrongReporters = mStrongReporters;
    2234           0 :   mSavedWeakReporters = mWeakReporters;
    2235           0 :   mStrongReporters = new StrongReportersTable();
    2236           0 :   mWeakReporters = new WeakReportersTable();
    2237             : 
    2238           0 :   return NS_OK;
    2239             : }
    2240             : 
    2241             : NS_IMETHODIMP
    2242           0 : nsMemoryReporterManager::UnblockRegistrationAndRestoreOriginalReporters()
    2243             : {
    2244             :   // This method is thread-safe.
    2245           0 :   mozilla::MutexAutoLock autoLock(mMutex);
    2246           0 :   if (!mIsRegistrationBlocked) {
    2247           0 :     return NS_ERROR_FAILURE;
    2248             :   }
    2249             : 
    2250             :   // Banish the current reporters, and restore the hidden ones.
    2251           0 :   delete mStrongReporters;
    2252           0 :   delete mWeakReporters;
    2253           0 :   mStrongReporters = mSavedStrongReporters;
    2254           0 :   mWeakReporters = mSavedWeakReporters;
    2255           0 :   mSavedStrongReporters = nullptr;
    2256           0 :   mSavedWeakReporters = nullptr;
    2257             : 
    2258           0 :   mIsRegistrationBlocked = false;
    2259           0 :   return NS_OK;
    2260             : }
    2261             : 
    2262             : NS_IMETHODIMP
    2263           0 : nsMemoryReporterManager::GetVsize(int64_t* aVsize)
    2264             : {
    2265             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    2266           0 :   return VsizeDistinguishedAmount(aVsize);
    2267             : #else
    2268             :   *aVsize = 0;
    2269             :   return NS_ERROR_NOT_AVAILABLE;
    2270             : #endif
    2271             : }
    2272             : 
    2273             : NS_IMETHODIMP
    2274           0 : nsMemoryReporterManager::GetVsizeMaxContiguous(int64_t* aAmount)
    2275             : {
    2276             : #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
    2277             :   return VsizeMaxContiguousDistinguishedAmount(aAmount);
    2278             : #else
    2279           0 :   *aAmount = 0;
    2280           0 :   return NS_ERROR_NOT_AVAILABLE;
    2281             : #endif
    2282             : }
    2283             : 
    2284             : NS_IMETHODIMP
    2285           0 : nsMemoryReporterManager::GetResident(int64_t* aAmount)
    2286             : {
    2287             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    2288           0 :   return ResidentDistinguishedAmount(aAmount);
    2289             : #else
    2290             :   *aAmount = 0;
    2291             :   return NS_ERROR_NOT_AVAILABLE;
    2292             : #endif
    2293             : }
    2294             : 
    2295             : NS_IMETHODIMP
    2296           0 : nsMemoryReporterManager::GetResidentFast(int64_t* aAmount)
    2297             : {
    2298             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    2299           0 :   return ResidentFastDistinguishedAmount(aAmount);
    2300             : #else
    2301             :   *aAmount = 0;
    2302             :   return NS_ERROR_NOT_AVAILABLE;
    2303             : #endif
    2304             : }
    2305             : 
    2306             : /*static*/ int64_t
    2307           0 : nsMemoryReporterManager::ResidentFast()
    2308             : {
    2309             : #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
    2310             :   int64_t amount;
    2311           0 :   nsresult rv = ResidentFastDistinguishedAmount(&amount);
    2312           0 :   NS_ENSURE_SUCCESS(rv, 0);
    2313           0 :   return amount;
    2314             : #else
    2315             :   return 0;
    2316             : #endif
    2317             : }
    2318             : 
    2319             : NS_IMETHODIMP
    2320           0 : nsMemoryReporterManager::GetResidentPeak(int64_t* aAmount)
    2321             : {
    2322             : #ifdef HAVE_RESIDENT_PEAK_REPORTER
    2323           0 :   return ResidentPeakDistinguishedAmount(aAmount);
    2324             : #else
    2325             :   *aAmount = 0;
    2326             :   return NS_ERROR_NOT_AVAILABLE;
    2327             : #endif
    2328             : }
    2329             : 
    2330             : /*static*/ int64_t
    2331           0 : nsMemoryReporterManager::ResidentPeak()
    2332             : {
    2333             : #ifdef HAVE_RESIDENT_PEAK_REPORTER
    2334           0 :   int64_t amount = 0;
    2335           0 :   nsresult rv = ResidentPeakDistinguishedAmount(&amount);
    2336           0 :   NS_ENSURE_SUCCESS(rv, 0);
    2337           0 :   return amount;
    2338             : #else
    2339             :   return 0;
    2340             : #endif
    2341             : }
    2342             : 
    2343             : NS_IMETHODIMP
    2344           0 : nsMemoryReporterManager::GetResidentUnique(int64_t* aAmount)
    2345             : {
    2346             : #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
    2347           0 :   return ResidentUniqueDistinguishedAmount(aAmount);
    2348             : #else
    2349             :   *aAmount = 0;
    2350             :   return NS_ERROR_NOT_AVAILABLE;
    2351             : #endif
    2352             : }
    2353             : 
    2354             : /*static*/ int64_t
    2355           0 : nsMemoryReporterManager::ResidentUnique()
    2356             : {
    2357             : #ifdef HAVE_RESIDENT_UNIQUE_REPORTER
    2358           0 :   int64_t amount = 0;
    2359           0 :   nsresult rv = ResidentUniqueDistinguishedAmount(&amount);
    2360           0 :   NS_ENSURE_SUCCESS(rv, 0);
    2361           0 :   return amount;
    2362             : #else
    2363             :   return 0;
    2364             : #endif
    2365             : }
    2366             : 
    2367             : NS_IMETHODIMP
    2368           0 : nsMemoryReporterManager::GetHeapAllocated(int64_t* aAmount)
    2369             : {
    2370             : #ifdef HAVE_JEMALLOC_STATS
    2371             :   jemalloc_stats_t stats;
    2372           0 :   jemalloc_stats(&stats);
    2373           0 :   *aAmount = stats.allocated;
    2374           0 :   return NS_OK;
    2375             : #else
    2376             :   *aAmount = 0;
    2377             :   return NS_ERROR_NOT_AVAILABLE;
    2378             : #endif
    2379             : }
    2380             : 
    2381             : NS_IMETHODIMP
    2382           0 : nsMemoryReporterManager::GetHeapAllocatedAsync(nsIHeapAllocatedCallback *aCallback)
    2383             : {
    2384             : #ifdef HAVE_JEMALLOC_STATS
    2385           0 :   if (!mThreadPool) {
    2386           0 :     return NS_ERROR_UNEXPECTED;
    2387             :   }
    2388             : 
    2389           0 :   RefPtr<nsIMemoryReporterManager> self{this};
    2390             :   nsMainThreadPtrHandle<nsIHeapAllocatedCallback> mainThreadCallback(
    2391             :     new nsMainThreadPtrHolder<nsIHeapAllocatedCallback>("HeapAllocatedCallback",
    2392           0 :                                                         aCallback));
    2393             : 
    2394           0 :   nsCOMPtr<nsIRunnable> getHeapAllocatedRunnable = NS_NewRunnableFunction(
    2395             :     "nsMemoryReporterManager::GetHeapAllocatedAsync",
    2396           0 :     [self, mainThreadCallback]() mutable {
    2397           0 :       MOZ_ASSERT(!NS_IsMainThread());
    2398             : 
    2399           0 :       int64_t heapAllocated = 0;
    2400           0 :       nsresult rv = self->GetHeapAllocated(&heapAllocated);
    2401             : 
    2402           0 :       nsCOMPtr<nsIRunnable> resultCallbackRunnable = NS_NewRunnableFunction(
    2403             :         "nsMemoryReporterManager::GetHeapAllocatedAsync",
    2404           0 :         [mainThreadCallback, heapAllocated, rv]() mutable {
    2405           0 :           MOZ_ASSERT(NS_IsMainThread());
    2406             : 
    2407           0 :           if (NS_FAILED(rv)) {
    2408           0 :             mainThreadCallback->Callback(0);
    2409           0 :             return;
    2410             :           }
    2411             : 
    2412           0 :           mainThreadCallback->Callback(heapAllocated);
    2413           0 :         });  // resultCallbackRunnable.
    2414             : 
    2415           0 :       Unused << NS_DispatchToMainThread(resultCallbackRunnable);
    2416           0 :     }); // getHeapAllocatedRunnable.
    2417             : 
    2418           0 :   return mThreadPool->Dispatch(getHeapAllocatedRunnable, NS_DISPATCH_NORMAL);
    2419             : #else
    2420             :   return NS_ERROR_NOT_AVAILABLE;
    2421             : #endif
    2422             : }
    2423             : 
    2424             : // This has UNITS_PERCENTAGE, so it is multiplied by 100x.
    2425             : NS_IMETHODIMP
    2426           0 : nsMemoryReporterManager::GetHeapOverheadFraction(int64_t* aAmount)
    2427             : {
    2428             : #ifdef HAVE_JEMALLOC_STATS
    2429             :   jemalloc_stats_t stats;
    2430           0 :   jemalloc_stats(&stats);
    2431           0 :   *aAmount = HeapOverheadFraction(&stats);
    2432           0 :   return NS_OK;
    2433             : #else
    2434             :   *aAmount = 0;
    2435             :   return NS_ERROR_NOT_AVAILABLE;
    2436             : #endif
    2437             : }
    2438             : 
    2439             : static MOZ_MUST_USE nsresult
    2440           0 : GetInfallibleAmount(InfallibleAmountFn aAmountFn, int64_t* aAmount)
    2441             : {
    2442           0 :   if (aAmountFn) {
    2443           0 :     *aAmount = aAmountFn();
    2444           0 :     return NS_OK;
    2445             :   }
    2446           0 :   *aAmount = 0;
    2447           0 :   return NS_ERROR_NOT_AVAILABLE;
    2448             : }
    2449             : 
    2450             : NS_IMETHODIMP
    2451           0 : nsMemoryReporterManager::GetJSMainRuntimeGCHeap(int64_t* aAmount)
    2452             : {
    2453           0 :   return GetInfallibleAmount(mAmountFns.mJSMainRuntimeGCHeap, aAmount);
    2454             : }
    2455             : 
    2456             : NS_IMETHODIMP
    2457           0 : nsMemoryReporterManager::GetJSMainRuntimeTemporaryPeak(int64_t* aAmount)
    2458             : {
    2459           0 :   return GetInfallibleAmount(mAmountFns.mJSMainRuntimeTemporaryPeak, aAmount);
    2460             : }
    2461             : 
    2462             : NS_IMETHODIMP
    2463           0 : nsMemoryReporterManager::GetJSMainRuntimeCompartmentsSystem(int64_t* aAmount)
    2464             : {
    2465           0 :   return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsSystem,
    2466           0 :                              aAmount);
    2467             : }
    2468             : 
    2469             : NS_IMETHODIMP
    2470           0 : nsMemoryReporterManager::GetJSMainRuntimeCompartmentsUser(int64_t* aAmount)
    2471             : {
    2472           0 :   return GetInfallibleAmount(mAmountFns.mJSMainRuntimeCompartmentsUser,
    2473           0 :                              aAmount);
    2474             : }
    2475             : 
    2476             : NS_IMETHODIMP
    2477           0 : nsMemoryReporterManager::GetImagesContentUsedUncompressed(int64_t* aAmount)
    2478             : {
    2479           0 :   return GetInfallibleAmount(mAmountFns.mImagesContentUsedUncompressed,
    2480           0 :                              aAmount);
    2481             : }
    2482             : 
    2483             : NS_IMETHODIMP
    2484           0 : nsMemoryReporterManager::GetStorageSQLite(int64_t* aAmount)
    2485             : {
    2486           0 :   return GetInfallibleAmount(mAmountFns.mStorageSQLite, aAmount);
    2487             : }
    2488             : 
    2489             : NS_IMETHODIMP
    2490           0 : nsMemoryReporterManager::GetLowMemoryEventsVirtual(int64_t* aAmount)
    2491             : {
    2492           0 :   return GetInfallibleAmount(mAmountFns.mLowMemoryEventsVirtual, aAmount);
    2493             : }
    2494             : 
    2495             : NS_IMETHODIMP
    2496           0 : nsMemoryReporterManager::GetLowMemoryEventsPhysical(int64_t* aAmount)
    2497             : {
    2498           0 :   return GetInfallibleAmount(mAmountFns.mLowMemoryEventsPhysical, aAmount);
    2499             : }
    2500             : 
    2501             : NS_IMETHODIMP
    2502           0 : nsMemoryReporterManager::GetGhostWindows(int64_t* aAmount)
    2503             : {
    2504           0 :   return GetInfallibleAmount(mAmountFns.mGhostWindows, aAmount);
    2505             : }
    2506             : 
    2507             : NS_IMETHODIMP
    2508           0 : nsMemoryReporterManager::GetPageFaultsHard(int64_t* aAmount)
    2509             : {
    2510             : #ifdef HAVE_PAGE_FAULT_REPORTERS
    2511           0 :   return PageFaultsHardDistinguishedAmount(aAmount);
    2512             : #else
    2513             :   *aAmount = 0;
    2514             :   return NS_ERROR_NOT_AVAILABLE;
    2515             : #endif
    2516             : }
    2517             : 
    2518             : NS_IMETHODIMP
    2519           0 : nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
    2520             : {
    2521           0 :   void* p = malloc(16);
    2522           0 :   if (!p) {
    2523           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2524             :   }
    2525           0 :   size_t usable = moz_malloc_usable_size(p);
    2526           0 :   free(p);
    2527           0 :   *aHas = !!(usable > 0);
    2528           0 :   return NS_OK;
    2529             : }
    2530             : 
    2531             : NS_IMETHODIMP
    2532           0 : nsMemoryReporterManager::GetIsDMDEnabled(bool* aIsEnabled)
    2533             : {
    2534             : #ifdef MOZ_DMD
    2535             :   *aIsEnabled = true;
    2536             : #else
    2537           0 :   *aIsEnabled = false;
    2538             : #endif
    2539           0 :   return NS_OK;
    2540             : }
    2541             : 
    2542             : NS_IMETHODIMP
    2543           0 : nsMemoryReporterManager::GetIsDMDRunning(bool* aIsRunning)
    2544             : {
    2545             : #ifdef MOZ_DMD
    2546             :   *aIsRunning = dmd::IsRunning();
    2547             : #else
    2548           0 :   *aIsRunning = false;
    2549             : #endif
    2550           0 :   return NS_OK;
    2551             : }
    2552             : 
    2553             : namespace {
    2554             : 
    2555             : /**
    2556             :  * This runnable lets us implement
    2557             :  * nsIMemoryReporterManager::MinimizeMemoryUsage().  We fire a heap-minimize
    2558             :  * notification, spin the event loop, and repeat this process a few times.
    2559             :  *
    2560             :  * When this sequence finishes, we invoke the callback function passed to the
    2561             :  * runnable's constructor.
    2562             :  */
    2563           0 : class MinimizeMemoryUsageRunnable : public Runnable
    2564             : {
    2565             : public:
    2566           0 :   explicit MinimizeMemoryUsageRunnable(nsIRunnable* aCallback)
    2567           0 :     : mozilla::Runnable("MinimizeMemoryUsageRunnable")
    2568             :     , mCallback(aCallback)
    2569           0 :     , mRemainingIters(sNumIters)
    2570             :   {
    2571           0 :   }
    2572             : 
    2573           0 :   NS_IMETHOD Run() override
    2574             :   {
    2575           0 :     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    2576           0 :     if (!os) {
    2577           0 :       return NS_ERROR_FAILURE;
    2578             :     }
    2579             : 
    2580           0 :     if (mRemainingIters == 0) {
    2581           0 :       os->NotifyObservers(nullptr, "after-minimize-memory-usage",
    2582           0 :                           u"MinimizeMemoryUsageRunnable");
    2583           0 :       if (mCallback) {
    2584           0 :         mCallback->Run();
    2585             :       }
    2586           0 :       return NS_OK;
    2587             :     }
    2588             : 
    2589           0 :     os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
    2590           0 :     mRemainingIters--;
    2591           0 :     NS_DispatchToMainThread(this);
    2592             : 
    2593           0 :     return NS_OK;
    2594             :   }
    2595             : 
    2596             : private:
    2597             :   // Send sNumIters heap-minimize notifications, spinning the event
    2598             :   // loop after each notification (see bug 610166 comment 12 for an
    2599             :   // explanation), because one notification doesn't cut it.
    2600             :   static const uint32_t sNumIters = 3;
    2601             : 
    2602             :   nsCOMPtr<nsIRunnable> mCallback;
    2603             :   uint32_t mRemainingIters;
    2604             : };
    2605             : 
    2606             : } // namespace
    2607             : 
    2608             : NS_IMETHODIMP
    2609           0 : nsMemoryReporterManager::MinimizeMemoryUsage(nsIRunnable* aCallback)
    2610             : {
    2611             :   RefPtr<MinimizeMemoryUsageRunnable> runnable =
    2612           0 :     new MinimizeMemoryUsageRunnable(aCallback);
    2613             : 
    2614           0 :   return NS_DispatchToMainThread(runnable);
    2615             : }
    2616             : 
    2617             : NS_IMETHODIMP
    2618           0 : nsMemoryReporterManager::SizeOfTab(mozIDOMWindowProxy* aTopWindow,
    2619             :                                    int64_t* aJSObjectsSize,
    2620             :                                    int64_t* aJSStringsSize,
    2621             :                                    int64_t* aJSOtherSize,
    2622             :                                    int64_t* aDomSize,
    2623             :                                    int64_t* aStyleSize,
    2624             :                                    int64_t* aOtherSize,
    2625             :                                    int64_t* aTotalSize,
    2626             :                                    double*  aJSMilliseconds,
    2627             :                                    double*  aNonJSMilliseconds)
    2628             : {
    2629           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow);
    2630           0 :   auto* piWindow = nsPIDOMWindowOuter::From(aTopWindow);
    2631           0 :   if (NS_WARN_IF(!global) || NS_WARN_IF(!piWindow)) {
    2632           0 :     return NS_ERROR_FAILURE;
    2633             :   }
    2634             : 
    2635           0 :   TimeStamp t1 = TimeStamp::Now();
    2636             : 
    2637             :   // Measure JS memory consumption (and possibly some non-JS consumption, via
    2638             :   // |jsPrivateSize|).
    2639             :   size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize;
    2640           0 :   nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(),
    2641             :                                   &jsObjectsSize, &jsStringsSize,
    2642           0 :                                   &jsPrivateSize, &jsOtherSize);
    2643           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2644           0 :     return rv;
    2645             :   }
    2646             : 
    2647           0 :   TimeStamp t2 = TimeStamp::Now();
    2648             : 
    2649             :   // Measure non-JS memory consumption.
    2650             :   size_t domSize, styleSize, otherSize;
    2651           0 :   rv = mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize);
    2652           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2653           0 :     return rv;
    2654             :   }
    2655             : 
    2656           0 :   TimeStamp t3 = TimeStamp::Now();
    2657             : 
    2658           0 :   *aTotalSize = 0;
    2659             : #define DO(aN, n) { *aN = (n); *aTotalSize += (n); }
    2660           0 :   DO(aJSObjectsSize, jsObjectsSize);
    2661           0 :   DO(aJSStringsSize, jsStringsSize);
    2662           0 :   DO(aJSOtherSize,   jsOtherSize);
    2663           0 :   DO(aDomSize,       jsPrivateSize + domSize);
    2664           0 :   DO(aStyleSize,     styleSize);
    2665           0 :   DO(aOtherSize,     otherSize);
    2666             : #undef DO
    2667             : 
    2668           0 :   *aJSMilliseconds    = (t2 - t1).ToMilliseconds();
    2669           0 :   *aNonJSMilliseconds = (t3 - t2).ToMilliseconds();
    2670             : 
    2671           0 :   return NS_OK;
    2672             : }
    2673             : 
    2674             : namespace mozilla {
    2675             : 
    2676             : #define GET_MEMORY_REPORTER_MANAGER(mgr)                                      \
    2677             :   RefPtr<nsMemoryReporterManager> mgr =                                       \
    2678             :     nsMemoryReporterManager::GetOrCreate();                                   \
    2679             :   if (!mgr) {                                                                 \
    2680             :     return NS_ERROR_FAILURE;                                                  \
    2681             :   }
    2682             : 
    2683             : nsresult
    2684          60 : RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
    2685             : {
    2686             :   // Hold a strong reference to the argument to make sure it gets released if
    2687             :   // we return early below.
    2688         120 :   nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
    2689         120 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2690          55 :   return mgr->RegisterStrongReporter(reporter);
    2691             : }
    2692             : 
    2693             : nsresult
    2694           0 : RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter)
    2695             : {
    2696             :   // Hold a strong reference to the argument to make sure it gets released if
    2697             :   // we return early below.
    2698           0 :   nsCOMPtr<nsIMemoryReporter> reporter = aReporter;
    2699           0 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2700           0 :   return mgr->RegisterStrongAsyncReporter(reporter);
    2701             : }
    2702             : 
    2703             : nsresult
    2704          57 : RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
    2705             : {
    2706         114 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2707          57 :   return mgr->RegisterWeakReporter(aReporter);
    2708             : }
    2709             : 
    2710             : nsresult
    2711           1 : RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter)
    2712             : {
    2713           2 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2714           1 :   return mgr->RegisterWeakAsyncReporter(aReporter);
    2715             : }
    2716             : 
    2717             : nsresult
    2718           0 : UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter)
    2719             : {
    2720           0 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2721           0 :   return mgr->UnregisterStrongReporter(aReporter);
    2722             : }
    2723             : 
    2724             : nsresult
    2725           7 : UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter)
    2726             : {
    2727          14 :   GET_MEMORY_REPORTER_MANAGER(mgr)
    2728           7 :   return mgr->UnregisterWeakReporter(aReporter);
    2729             : }
    2730             : 
    2731             : // Macro for generating functions that register distinguished amount functions
    2732             : // with the memory reporter manager.
    2733             : #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name)                      \
    2734             :   nsresult                                                                    \
    2735             :   Register##name##DistinguishedAmount(kind##AmountFn aAmountFn)               \
    2736             :   {                                                                           \
    2737             :     GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
    2738             :     mgr->mAmountFns.m##name = aAmountFn;                                      \
    2739             :     return NS_OK;                                                             \
    2740             :   }
    2741             : 
    2742             : // Macro for generating functions that unregister distinguished amount
    2743             : // functions with the memory reporter manager.
    2744             : #define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name)                          \
    2745             :   nsresult                                                                    \
    2746             :   Unregister##name##DistinguishedAmount()                                     \
    2747             :   {                                                                           \
    2748             :     GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
    2749             :     mgr->mAmountFns.m##name = nullptr;                                        \
    2750             :     return NS_OK;                                                             \
    2751             :   }
    2752             : 
    2753           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
    2754           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
    2755           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
    2756           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
    2757             : 
    2758           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
    2759           0 : DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
    2760             : 
    2761           1 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
    2762           0 : DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
    2763             : 
    2764           0 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
    2765           0 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
    2766             : 
    2767           3 : DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
    2768             : 
    2769             : #undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT
    2770             : #undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT
    2771             : 
    2772             : #define DEFINE_REGISTER_SIZE_OF_TAB(name)                                     \
    2773             :   nsresult                                                                    \
    2774             :   Register##name##SizeOfTab(name##SizeOfTabFn aSizeOfTabFn)                   \
    2775             :   {                                                                           \
    2776             :     GET_MEMORY_REPORTER_MANAGER(mgr)                                          \
    2777             :     mgr->mSizeOfTabFns.m##name = aSizeOfTabFn;                                \
    2778             :     return NS_OK;                                                             \
    2779             :   }
    2780             : 
    2781           3 : DEFINE_REGISTER_SIZE_OF_TAB(JS);
    2782           3 : DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
    2783             : 
    2784             : #undef DEFINE_REGISTER_SIZE_OF_TAB
    2785             : 
    2786             : #undef GET_MEMORY_REPORTER_MANAGER
    2787             : 
    2788             : } // namespace mozilla

Generated by: LCOV version 1.13