LCOV - code coverage report
Current view: top level - xpcom/base - AvailableMemoryTracker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 10 25 40.0 %
Date: 2018-08-07 16:35:00 Functions: 0 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 "mozilla/AvailableMemoryTracker.h"
       8             : 
       9             : #if defined(XP_WIN)
      10             : #include "prinrval.h"
      11             : #include "prenv.h"
      12             : #include "nsExceptionHandler.h"
      13             : #include "nsIMemoryReporter.h"
      14             : #include "nsMemoryPressure.h"
      15             : #include "nsPrintfCString.h"
      16             : #endif
      17             : 
      18             : #include "nsIObserver.h"
      19             : #include "nsIObserverService.h"
      20             : #include "nsIRunnable.h"
      21             : #include "nsISupports.h"
      22             : #include "nsITimer.h"
      23             : #include "nsThreadUtils.h"
      24             : #include "nsXULAppAPI.h"
      25             : 
      26             : #include "mozilla/ResultExtensions.h"
      27             : #include "mozilla/Services.h"
      28             : #include "mozilla/Unused.h"
      29             : 
      30             : #if defined(XP_WIN)
      31             : #   include "nsWindowsDllInterceptor.h"
      32             : #   include <windows.h>
      33             : #endif
      34             : 
      35             : #if defined(MOZ_MEMORY)
      36             : #   include "mozmemory.h"
      37             : #endif  // MOZ_MEMORY
      38             : 
      39             : using namespace mozilla;
      40             : 
      41             : namespace {
      42             : 
      43             : #if defined(XP_WIN)
      44             : 
      45             : // Fire a low-memory notification if we have less than this many bytes of
      46             : // virtual address space available.
      47             : #if defined(HAVE_64BIT_BUILD)
      48             : static const size_t kLowVirtualMemoryThreshold = 0;
      49             : #else
      50             : static const size_t kLowVirtualMemoryThreshold = 256 * 1024 * 1024;
      51             : #endif
      52             : 
      53             : // Fire a low-memory notification if we have less than this many bytes of commit
      54             : // space (physical memory plus page file) left.
      55             : static const size_t kLowCommitSpaceThreshold = 256 * 1024 * 1024;
      56             : 
      57             : // Fire a low-memory notification if we have less than this many bytes of
      58             : // physical memory available on the whole machine.
      59             : static const size_t kLowPhysicalMemoryThreshold = 0;
      60             : 
      61             : // Don't fire a low-memory notification because of low available physical
      62             : // memory or low commit space more often than this interval.
      63             : static const uint32_t kLowMemoryNotificationIntervalMS = 10000;
      64             : 
      65             : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowVirtualMemEvents;
      66             : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowCommitSpaceEvents;
      67             : Atomic<uint32_t, MemoryOrdering::Relaxed> sNumLowPhysicalMemEvents;
      68             : 
      69             : #if !defined(HAVE_64BIT_BUILD)
      70             : 
      71             : WindowsDllInterceptor sKernel32Intercept;
      72             : WindowsDllInterceptor sGdi32Intercept;
      73             : 
      74             : // Has Init() been called?
      75             : bool sInitialized = false;
      76             : 
      77             : // Has Activate() been called?  The hooks don't do anything until this happens.
      78             : bool sHooksActive = false;
      79             : 
      80             : // Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
      81             : // a lock!
      82             : volatile bool sUnderMemoryPressure = false;
      83             : volatile PRIntervalTime sLastLowMemoryNotificationTime;
      84             : 
      85             : // These are function pointers to the functions we wrap in Init().
      86             : 
      87             : void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
      88             :                                   DWORD aAllocationType, DWORD aProtect);
      89             : 
      90             : void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
      91             :                                    DWORD aDesiredAccess, DWORD aFileOffsetHigh,
      92             :                                    DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
      93             : 
      94             : HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
      95             :                                        UINT aUsage, VOID** aBits,
      96             :                                        HANDLE aSection, DWORD aOffset);
      97             : 
      98             : /**
      99             :  * Fire a memory pressure event if we were not under memory pressure yet, or
     100             :  * fire an ongoing one if it's been long enough since the last one we
     101             :  * fired.
     102             :  */
     103             : bool
     104             : MaybeScheduleMemoryPressureEvent()
     105             : {
     106             :   MemoryPressureState state = MemPressure_New;
     107             :   PRIntervalTime now = PR_IntervalNow();
     108             : 
     109             :   // If this interval rolls over, we may fire an extra memory pressure
     110             :   // event, but that's not a big deal.
     111             :   PRIntervalTime interval = now - sLastLowMemoryNotificationTime;
     112             :   if (sUnderMemoryPressure) {
     113             :     if (PR_IntervalToMilliseconds(interval) <
     114             :         kLowMemoryNotificationIntervalMS) {
     115             :       return false;
     116             :     }
     117             : 
     118             :     state = MemPressure_Ongoing;
     119             :   }
     120             : 
     121             :   // There's a bit of a race condition here, since an interval may be a
     122             :   // 64-bit number, and 64-bit writes aren't atomic on x86-32.  But let's
     123             :   // not worry about it -- the races only happen when we're already
     124             :   // experiencing memory pressure and firing notifications, so the worst
     125             :   // thing that can happen is that we fire two notifications when we
     126             :   // should have fired only one.
     127             :   sUnderMemoryPressure = true;
     128             :   sLastLowMemoryNotificationTime = now;
     129             : 
     130             :   NS_DispatchEventualMemoryPressure(state);
     131             :   return true;
     132             : }
     133             : 
     134             : static bool
     135             : CheckLowMemory(DWORDLONG available, size_t threshold,
     136             :                Atomic<uint32_t, MemoryOrdering::Relaxed>& counter)
     137             : {
     138             :   if (available < threshold) {
     139             :     if (MaybeScheduleMemoryPressureEvent()) {
     140             :       counter++;
     141             :     }
     142             : 
     143             :     return true;
     144             :   }
     145             : 
     146             :   return false;
     147             : }
     148             : 
     149             : void
     150             : CheckMemAvailable()
     151             : {
     152             :   if (!sHooksActive) {
     153             :     return;
     154             :   }
     155             : 
     156             :   MEMORYSTATUSEX stat;
     157             :   stat.dwLength = sizeof(stat);
     158             :   bool success = GlobalMemoryStatusEx(&stat);
     159             : 
     160             :   if (success) {
     161             :     bool lowMemory = CheckLowMemory(stat.ullAvailVirtual,
     162             :                                     kLowVirtualMemoryThreshold,
     163             :                                     sNumLowVirtualMemEvents);
     164             :     lowMemory |= CheckLowMemory(stat.ullAvailPageFile, kLowCommitSpaceThreshold,
     165             :                                 sNumLowCommitSpaceEvents);
     166             :     lowMemory |= CheckLowMemory(stat.ullAvailPhys, kLowPhysicalMemoryThreshold,
     167             :                                 sNumLowPhysicalMemEvents);
     168             : 
     169             :     sUnderMemoryPressure = lowMemory;
     170             :   }
     171             : }
     172             : 
     173             : LPVOID WINAPI
     174             : VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
     175             :                  DWORD aAllocationType,
     176             :                  DWORD aProtect)
     177             : {
     178             :   // It's tempting to see whether we have enough free virtual address space for
     179             :   // this allocation and, if we don't, synchronously fire a low-memory
     180             :   // notification to free some before we allocate.
     181             :   //
     182             :   // Unfortunately that doesn't work, principally because code doesn't expect a
     183             :   // call to malloc could trigger a GC (or call into the other routines which
     184             :   // are triggered by a low-memory notification).
     185             :   //
     186             :   // I think the best we can do here is try to allocate the memory and check
     187             :   // afterwards how much free virtual address space we have.  If we're running
     188             :   // low, we schedule a low-memory notification to run as soon as possible.
     189             : 
     190             :   LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
     191             : 
     192             :   // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
     193             :   // virtual memory.  Similarly, don't call CheckMemAvailable for MEM_COMMIT if
     194             :   // we're not tracking low physical memory.
     195             :   if ((kLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
     196             :       ((kLowCommitSpaceThreshold != 0 || kLowPhysicalMemoryThreshold != 0) &&
     197             :        aAllocationType & MEM_COMMIT)) {
     198             :     CheckMemAvailable();
     199             :   }
     200             : 
     201             :   return result;
     202             : }
     203             : 
     204             : LPVOID WINAPI
     205             : MapViewOfFileHook(HANDLE aFileMappingObject,
     206             :                   DWORD aDesiredAccess,
     207             :                   DWORD aFileOffsetHigh,
     208             :                   DWORD aFileOffsetLow,
     209             :                   SIZE_T aNumBytesToMap)
     210             : {
     211             :   LPVOID result = sMapViewOfFileOrig(aFileMappingObject, aDesiredAccess,
     212             :                                      aFileOffsetHigh, aFileOffsetLow,
     213             :                                      aNumBytesToMap);
     214             :   CheckMemAvailable();
     215             :   return result;
     216             : }
     217             : 
     218             : HBITMAP WINAPI
     219             : CreateDIBSectionHook(HDC aDC,
     220             :                      const BITMAPINFO* aBitmapInfo,
     221             :                      UINT aUsage,
     222             :                      VOID** aBits,
     223             :                      HANDLE aSection,
     224             :                      DWORD aOffset)
     225             : {
     226             :   // There are a lot of calls to CreateDIBSection, so we make some effort not
     227             :   // to CheckMemAvailable() for calls to CreateDIBSection which allocate only
     228             :   // a small amount of memory.
     229             : 
     230             :   // If aSection is non-null, CreateDIBSection won't allocate any new memory.
     231             :   bool doCheck = false;
     232             :   if (sHooksActive && !aSection && aBitmapInfo) {
     233             :     uint16_t bitCount = aBitmapInfo->bmiHeader.biBitCount;
     234             :     if (bitCount == 0) {
     235             :       // MSDN says bitCount == 0 means that it figures out how many bits each
     236             :       // pixel gets by examining the corresponding JPEG or PNG data.  We'll just
     237             :       // assume the worst.
     238             :       bitCount = 32;
     239             :     }
     240             : 
     241             :     // |size| contains the expected allocation size in *bits*.  Height may be
     242             :     // negative (indicating the direction the DIB is drawn in), so we take the
     243             :     // absolute value.
     244             :     int64_t size = bitCount * aBitmapInfo->bmiHeader.biWidth *
     245             :                               aBitmapInfo->bmiHeader.biHeight;
     246             :     if (size < 0) {
     247             :       size *= -1;
     248             :     }
     249             : 
     250             :     // If we're allocating more than 1MB, check how much memory is left after
     251             :     // the allocation.
     252             :     if (size > 1024 * 1024 * 8) {
     253             :       doCheck = true;
     254             :     }
     255             :   }
     256             : 
     257             :   HBITMAP result = sCreateDIBSectionOrig(aDC, aBitmapInfo, aUsage, aBits,
     258             :                                          aSection, aOffset);
     259             : 
     260             :   if (doCheck) {
     261             :     CheckMemAvailable();
     262             :   }
     263             : 
     264             :   return result;
     265             : }
     266             : 
     267             : #else
     268             : 
     269             : class nsAvailableMemoryWatcher final : public nsIObserver,
     270             :                                        public nsITimerCallback
     271             : {
     272             : public:
     273             :   NS_DECL_ISUPPORTS
     274             :   NS_DECL_NSIOBSERVER
     275             :   NS_DECL_NSITIMERCALLBACK
     276             : 
     277             :   nsresult Init();
     278             : 
     279             : private:
     280             :   // Poll the amount of free memory at this rate.
     281             :   static const uint32_t kPollingIntervalMS = 1000;
     282             : 
     283             :   // Observer topics we subscribe to
     284             :   static const char* const kObserverTopics[];
     285             : 
     286             :   static bool IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat);
     287             :   static bool IsCommitSpaceLow(const MEMORYSTATUSEX& aStat);
     288             :   static bool IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat);
     289             : 
     290             :   ~nsAvailableMemoryWatcher() {};
     291             :   void AdjustPollingInterval(const bool aLowMemory);
     292             :   void SendMemoryPressureEvent();
     293             :   void Shutdown();
     294             : 
     295             :   nsCOMPtr<nsITimer> mTimer;
     296             :   bool mUnderMemoryPressure;
     297             : };
     298             : 
     299             : const char* const nsAvailableMemoryWatcher::kObserverTopics[] = {
     300             :   "quit-application",
     301             :   "user-interaction-active",
     302             :   "user-interaction-inactive",
     303             : };
     304             : 
     305             : NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsIObserver, nsITimerCallback)
     306             : 
     307             : nsresult
     308             : nsAvailableMemoryWatcher::Init()
     309             : {
     310             :   mTimer = NS_NewTimer();
     311             : 
     312             :   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
     313             :   MOZ_ASSERT(observerService);
     314             : 
     315             :   for (auto topic : kObserverTopics) {
     316             :     nsresult rv = observerService->AddObserver(this, topic,
     317             :                                                /* ownsWeak */ false);
     318             :     NS_ENSURE_SUCCESS(rv, rv);
     319             :   }
     320             : 
     321             :   MOZ_TRY(mTimer->InitWithCallback(this, kPollingIntervalMS,
     322             :                                    nsITimer::TYPE_REPEATING_SLACK));
     323             :   return NS_OK;
     324             : }
     325             : 
     326             : void
     327             : nsAvailableMemoryWatcher::Shutdown()
     328             : {
     329             :   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
     330             :   MOZ_ASSERT(observerService);
     331             : 
     332             :   for (auto topic : kObserverTopics) {
     333             :     Unused << observerService->RemoveObserver(this, topic);
     334             :   }
     335             : 
     336             :   if (mTimer) {
     337             :     mTimer->Cancel();
     338             :     mTimer = nullptr;
     339             :   }
     340             : }
     341             : 
     342             : /* static */ bool
     343             : nsAvailableMemoryWatcher::IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat)
     344             : {
     345             :   if ((kLowVirtualMemoryThreshold != 0) &&
     346             :       (aStat.ullAvailVirtual < kLowVirtualMemoryThreshold)) {
     347             :     sNumLowVirtualMemEvents++;
     348             :     return true;
     349             :   }
     350             : 
     351             :   return false;
     352             : }
     353             : 
     354             : /* static */ bool
     355             : nsAvailableMemoryWatcher::IsCommitSpaceLow(const MEMORYSTATUSEX& aStat)
     356             : {
     357             :   if ((kLowCommitSpaceThreshold != 0) &&
     358             :       (aStat.ullAvailPageFile < kLowCommitSpaceThreshold)) {
     359             :     sNumLowCommitSpaceEvents++;
     360             :     CrashReporter::AnnotateCrashReport(
     361             :       NS_LITERAL_CSTRING("LowCommitSpaceEvents"),
     362             :       nsPrintfCString("%" PRIu32, uint32_t(sNumLowCommitSpaceEvents)));
     363             :     return true;
     364             :   }
     365             : 
     366             :   return false;
     367             : }
     368             : 
     369             : /* static */ bool
     370             : nsAvailableMemoryWatcher::IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat)
     371             : {
     372             :   if ((kLowPhysicalMemoryThreshold != 0) &&
     373             :       (aStat.ullAvailPhys < kLowPhysicalMemoryThreshold)) {
     374             :     sNumLowPhysicalMemEvents++;
     375             :     return true;
     376             :   }
     377             : 
     378             :   return false;
     379             : }
     380             : 
     381             : void
     382             : nsAvailableMemoryWatcher::SendMemoryPressureEvent()
     383             : {
     384             :     MemoryPressureState state = mUnderMemoryPressure ? MemPressure_Ongoing
     385             :                                                      : MemPressure_New;
     386             :     NS_DispatchEventualMemoryPressure(state);
     387             : }
     388             : 
     389             : void
     390             : nsAvailableMemoryWatcher::AdjustPollingInterval(const bool aLowMemory)
     391             : {
     392             :   if (aLowMemory) {
     393             :     // We entered a low-memory state, wait for a longer interval before polling
     394             :     // again as there's no point in rapidly sending further notifications.
     395             :     mTimer->SetDelay(kLowMemoryNotificationIntervalMS);
     396             :   } else if (mUnderMemoryPressure) {
     397             :     // We were under memory pressure but we're not anymore, resume polling at
     398             :     // a faster pace.
     399             :     mTimer->SetDelay(kPollingIntervalMS);
     400             :   }
     401             : }
     402             : 
     403             : // Timer callback, polls memory stats to detect low-memory conditions. This
     404             : // will send memory-pressure events if memory is running low and adjust the
     405             : // polling interval accordingly.
     406             : NS_IMETHODIMP
     407             : nsAvailableMemoryWatcher::Notify(nsITimer* aTimer)
     408             : {
     409             :   MEMORYSTATUSEX stat;
     410             :   stat.dwLength = sizeof(stat);
     411             :   bool success = GlobalMemoryStatusEx(&stat);
     412             : 
     413             :   if (success) {
     414             :     bool lowMemory =
     415             :       IsVirtualMemoryLow(stat) ||
     416             :       IsCommitSpaceLow(stat) ||
     417             :       IsPhysicalMemoryLow(stat);
     418             : 
     419             :     if (lowMemory) {
     420             :       SendMemoryPressureEvent();
     421             :     }
     422             : 
     423             :     AdjustPollingInterval(lowMemory);
     424             :     mUnderMemoryPressure = lowMemory;
     425             :   }
     426             : 
     427             :   return NS_OK;
     428             : }
     429             : 
     430             : // Observer service callback, used to stop the polling timer when the user
     431             : // stops interacting with Firefox and resuming it when they interact again.
     432             : // Also used to shut down the service if the application is quitting.
     433             : NS_IMETHODIMP
     434             : nsAvailableMemoryWatcher::Observe(nsISupports* aSubject, const char* aTopic,
     435             :                                   const char16_t* aData)
     436             : {
     437             :   if (strcmp(aTopic, "quit-application") == 0) {
     438             :     Shutdown();
     439             :   } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
     440             :     mTimer->Cancel();
     441             :   } else if (strcmp(aTopic, "user-interaction-active") == 0) {
     442             :     mTimer->InitWithCallback(this, kPollingIntervalMS,
     443             :                              nsITimer::TYPE_REPEATING_SLACK);
     444             :   } else {
     445             :     MOZ_ASSERT_UNREACHABLE("Unknown topic");
     446             :   }
     447             : 
     448             :   return NS_OK;
     449             : }
     450             : 
     451             : #endif // !defined(HAVE_64BIT_BUILD)
     452             : 
     453             : static int64_t
     454             : LowMemoryEventsVirtualDistinguishedAmount()
     455             : {
     456             :   return sNumLowVirtualMemEvents;
     457             : }
     458             : 
     459             : static int64_t
     460             : LowMemoryEventsCommitSpaceDistinguishedAmount()
     461             : {
     462             :   return sNumLowCommitSpaceEvents;
     463             : }
     464             : 
     465             : static int64_t
     466             : LowMemoryEventsPhysicalDistinguishedAmount()
     467             : {
     468             :   return sNumLowPhysicalMemEvents;
     469             : }
     470             : 
     471             : class LowEventsReporter final : public nsIMemoryReporter
     472             : {
     473             :   ~LowEventsReporter() {}
     474             : 
     475             : public:
     476             :   NS_DECL_ISUPPORTS
     477             : 
     478             :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     479             :                             nsISupports* aData, bool aAnonymize) override
     480             :   {
     481             :     MOZ_COLLECT_REPORT(
     482             :       "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
     483             :       LowMemoryEventsVirtualDistinguishedAmount(),
     484             : "Number of low-virtual-memory events fired since startup. We fire such an "
     485             : "event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
     486             : "virtual address space available (if zero, this behavior is disabled). The "
     487             : "process will probably crash if it runs out of virtual address space, so "
     488             : "this event is dire.");
     489             : 
     490             :     MOZ_COLLECT_REPORT(
     491             :       "low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
     492             :       LowMemoryEventsCommitSpaceDistinguishedAmount(),
     493             : "Number of low-commit-space events fired since startup. We fire such an "
     494             : "event if we notice there is less than memory.low_commit_space_threshold_mb of "
     495             : "commit space available (if zero, this behavior is disabled). Windows will "
     496             : "likely kill the process if it runs out of commit space, so this event is "
     497             : "dire.");
     498             : 
     499             :     MOZ_COLLECT_REPORT(
     500             :       "low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
     501             :       LowMemoryEventsPhysicalDistinguishedAmount(),
     502             : "Number of low-physical-memory events fired since startup. We fire such an "
     503             : "event if we notice there is less than memory.low_physical_memory_threshold_mb "
     504             : "of physical memory available (if zero, this behavior is disabled).  The "
     505             : "machine will start to page if it runs out of physical memory.  This may "
     506             : "cause it to run slowly, but it shouldn't cause it to crash.");
     507             : 
     508             :     return NS_OK;
     509             :   }
     510             : };
     511             : NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter)
     512             : 
     513             : #endif // defined(XP_WIN)
     514             : 
     515             : /**
     516             :  * This runnable is executed in response to a memory-pressure event; we spin
     517             :  * the event-loop when receiving the memory-pressure event in the hope that
     518             :  * other observers will synchronously free some memory that we'll be able to
     519             :  * purge here.
     520             :  */
     521           0 : class nsJemallocFreeDirtyPagesRunnable final : public nsIRunnable
     522             : {
     523           0 :   ~nsJemallocFreeDirtyPagesRunnable() {}
     524             : 
     525             : public:
     526             :   NS_DECL_ISUPPORTS
     527             :   NS_DECL_NSIRUNNABLE
     528             : };
     529             : 
     530           0 : NS_IMPL_ISUPPORTS(nsJemallocFreeDirtyPagesRunnable, nsIRunnable)
     531             : 
     532             : NS_IMETHODIMP
     533           0 : nsJemallocFreeDirtyPagesRunnable::Run()
     534             : {
     535           0 :   MOZ_ASSERT(NS_IsMainThread());
     536             : 
     537             : #if defined(MOZ_MEMORY)
     538           0 :   jemalloc_free_dirty_pages();
     539             : #endif
     540             : 
     541           0 :   return NS_OK;
     542             : }
     543             : 
     544             : /**
     545             :  * The memory pressure watcher is used for listening to memory-pressure events
     546             :  * and reacting upon them. We use one instance per process currently only for
     547             :  * cleaning up dirty unused pages held by jemalloc.
     548             :  */
     549           3 : class nsMemoryPressureWatcher final : public nsIObserver
     550             : {
     551           0 :   ~nsMemoryPressureWatcher() {}
     552             : 
     553             : public:
     554             :   NS_DECL_ISUPPORTS
     555             :   NS_DECL_NSIOBSERVER
     556             : 
     557             :   void Init();
     558             : };
     559             : 
     560          12 : NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
     561             : 
     562             : /**
     563             :  * Initialize and subscribe to the memory-pressure events. We subscribe to the
     564             :  * observer service in this method and not in the constructor because we need
     565             :  * to hold a strong reference to 'this' before calling the observer service.
     566             :  */
     567             : void
     568           0 : nsMemoryPressureWatcher::Init()
     569             : {
     570           0 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     571             : 
     572           1 :   if (os) {
     573           1 :     os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
     574             :   }
     575           1 : }
     576             : 
     577             : /**
     578             :  * Reacts to all types of memory-pressure events, launches a runnable to
     579             :  * free dirty pages held by jemalloc.
     580             :  */
     581             : NS_IMETHODIMP
     582           0 : nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
     583             :                                  const char16_t* aData)
     584             : {
     585           0 :   MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
     586             : 
     587           0 :   nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
     588             : 
     589           0 :   NS_DispatchToMainThread(runnable);
     590             : 
     591           0 :   return NS_OK;
     592             : }
     593             : 
     594             : } // namespace
     595             : 
     596             : namespace mozilla {
     597             : namespace AvailableMemoryTracker {
     598             : 
     599             : void
     600           1 : Activate()
     601             : {
     602             : #if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
     603             :   MOZ_ASSERT(sInitialized);
     604             :   MOZ_ASSERT(!sHooksActive);
     605             : 
     606             :   RegisterStrongMemoryReporter(new LowEventsReporter());
     607             :   RegisterLowMemoryEventsVirtualDistinguishedAmount(
     608             :     LowMemoryEventsVirtualDistinguishedAmount);
     609             :   RegisterLowMemoryEventsPhysicalDistinguishedAmount(
     610             :     LowMemoryEventsPhysicalDistinguishedAmount);
     611             :   sHooksActive = true;
     612             : #endif // defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
     613             : 
     614             :   // The watchers are held alive by the observer service.
     615           2 :   RefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
     616           1 :   watcher->Init();
     617             : 
     618             : #if defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
     619             :   if (XRE_IsParentProcess()) {
     620             :     RefPtr<nsAvailableMemoryWatcher> poller = new nsAvailableMemoryWatcher();
     621             : 
     622             :     if (NS_FAILED(poller->Init())) {
     623             :       NS_WARNING("Could not start the available memory watcher");
     624             :     }
     625             :   }
     626             : #endif // defined(XP_WIN) && defined(HAVE_64BIT_BUILD)
     627           1 : }
     628             : 
     629             : void
     630           1 : Init()
     631             : {
     632             :   // Do nothing on x86-64, because nsWindowsDllInterceptor is not thread-safe
     633             :   // on 64-bit.  (On 32-bit, it's probably thread-safe.)  Even if we run Init()
     634             :   // before any other of our threads are running, another process may have
     635             :   // started a remote thread which could call VirtualAlloc!
     636             :   //
     637             :   // Moreover, the benefit of this code is less clear when we're a 64-bit
     638             :   // process, because we aren't going to run out of virtual memory, and the
     639             :   // system is likely to have a fair bit of physical memory.
     640             : 
     641             : #if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
     642             :   // Don't register the hooks if we're a build instrumented for PGO: If we're
     643             :   // an instrumented build, the compiler adds function calls all over the place
     644             :   // which may call VirtualAlloc; this makes it hard to prevent
     645             :   // VirtualAllocHook from reentering itself.
     646             :   if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
     647             :     sKernel32Intercept.Init("Kernel32.dll");
     648             :     sKernel32Intercept.AddHook("VirtualAlloc",
     649             :                                reinterpret_cast<intptr_t>(VirtualAllocHook),
     650             :                                reinterpret_cast<void**>(&sVirtualAllocOrig));
     651             :     sKernel32Intercept.AddHook("MapViewOfFile",
     652             :                                reinterpret_cast<intptr_t>(MapViewOfFileHook),
     653             :                                reinterpret_cast<void**>(&sMapViewOfFileOrig));
     654             : 
     655             :     sGdi32Intercept.Init("Gdi32.dll");
     656             :     sGdi32Intercept.AddHook("CreateDIBSection",
     657             :                             reinterpret_cast<intptr_t>(CreateDIBSectionHook),
     658             :                             reinterpret_cast<void**>(&sCreateDIBSectionOrig));
     659             :   }
     660             : 
     661             :   sInitialized = true;
     662             : #endif // defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
     663             : }
     664             : 
     665             : } // namespace AvailableMemoryTracker
     666             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952