# HG changeset patch # User Gabriele Svelto # Date 1560545801 0 # Node ID 82daf65cbe5e55ddefa27e293dfcbfe27af23062 # Parent 208e98fcf3111a03ef2d8a37d6c3d39ff87c3819 Bug 1558554 - Properly detect low-commit space scenarios on Windows r=dmajor This uses GetPerformanceInformation() to measure how much commit space is available globally. Previously we relied on GlobalMemoryStatusEx() but in spite of the function name the commit-space values it returns are *per process* and thus unreliable. Available physical memory measurement has also been removed since it was never used and we don't plan on using it anytime soon. Differential Revision: https://phabricator.services.mozilla.com/D35018 diff --git a/toolkit/crashreporter/CrashAnnotations.yaml b/toolkit/crashreporter/CrashAnnotations.yaml --- a/toolkit/crashreporter/CrashAnnotations.yaml +++ b/toolkit/crashreporter/CrashAnnotations.yaml @@ -94,19 +94,19 @@ AsyncShutdownTimeout: This annotation is present if a shutdown blocker was not released in time and the browser was crashed instead of waiting for shutdown to finish. The condition that caused the hang is contained in the annotation. type: string ping: true AvailablePageFile: description: > - Windows-only, maximum amount of memory that can be committed. This - annotation is populated with the contents of the MEMORYSTATUSEX's structure - ullAvailPageFile field. + Windows-only, available commit-space in bytes. This annotation is computed + from the PERFORMANCE_INFORMATION structure by substracting the CommitTotal + field from the CommitLimit field. type: string ping: true AvailablePhysicalMemory: description: > Windows-only, amount of free physical memory in bytes. This annotation is populated with the contents of the MEMORYSTATUSEX's structure ullAvailPhys field. @@ -666,19 +666,19 @@ TextureUsage: ThreadIdNameMapping: description: > List of thread names with their corresponding thread IDs. type: string TotalPageFile: description: > - Windows-only, current committed memory limit. This annotation is - populated with the contents of the MEMORYSTATUSEX's structure - ullTotalPageFile field. + Windows-only, maximum amount of memory that can be committed. This + annotation is populated with the contents of the PERFORMANCE_INFORMATION's + structure CommitLimit field. type: string ping: true TotalPhysicalMemory: description: > Windows-only, amount of physical memory in bytes. This annotation is populated with the contents of the MEMORYSTATUSEX's structure ullTotalPhys field. diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -42,16 +42,17 @@ # include "breakpad-client/windows/crash_generation/crash_generation_server.h" # include "breakpad-client/windows/handler/exception_handler.h" # include # include # include "nsDirectoryServiceUtils.h" # include "nsWindowsDllInterceptor.h" # include "mozilla/WindowsVersion.h" +# include "psapi.h" // For PERFORMANCE_INFORAMTION #elif defined(XP_MACOSX) # include "breakpad-client/mac/crash_generation/client_info.h" # include "breakpad-client/mac/crash_generation/crash_generation_server.h" # include "breakpad-client/mac/handler/exception_handler.h" # include # include # include # include @@ -655,38 +656,45 @@ static void OpenAPIData(PlatformWriter& #ifdef XP_WIN static void WriteMemoryAnnotation(PlatformWriter& aWriter, Annotation aAnnotation, uint64_t aValue) { char buffer[128]; if (!_ui64toa_s(aValue, buffer, sizeof(buffer), 10)) { WriteAnnotation(aWriter, aAnnotation, buffer); } } - -static void WriteGlobalMemoryStatus(PlatformWriter& aWriter) { +#endif // XP_WIN + +static void WriteMemoryStatus(PlatformWriter& aWriter) { +#ifdef XP_WIN MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); if (GlobalMemoryStatusEx(&statex)) { WriteMemoryAnnotation(aWriter, Annotation::SystemMemoryUsePercentage, statex.dwMemoryLoad); WriteMemoryAnnotation(aWriter, Annotation::TotalVirtualMemory, statex.ullTotalVirtual); WriteMemoryAnnotation(aWriter, Annotation::AvailableVirtualMemory, statex.ullAvailVirtual); - WriteMemoryAnnotation(aWriter, Annotation::TotalPageFile, - statex.ullTotalPageFile); - WriteMemoryAnnotation(aWriter, Annotation::AvailablePageFile, - statex.ullAvailPageFile); WriteMemoryAnnotation(aWriter, Annotation::TotalPhysicalMemory, statex.ullTotalPhys); WriteMemoryAnnotation(aWriter, Annotation::AvailablePhysicalMemory, statex.ullAvailPhys); } + + PERFORMANCE_INFORMATION info; + if (K32GetPerformanceInfo(&info, sizeof(info))) { + WriteMemoryAnnotation(aWriter, Annotation::TotalPageFile, + info.CommitLimit * info.PageSize); + WriteMemoryAnnotation( + aWriter, Annotation::AvailablePageFile, + (info.CommitLimit - info.CommitTotal) * info.PageSize); + } +#endif // XP_WIN } -#endif // XP_WIN #if !defined(MOZ_WIDGET_ANDROID) /** * Launches the program specified in aProgramPath with aMinidumpPath as its * sole argument. * * @param aProgramPath The path of the program to be launched @@ -874,19 +882,19 @@ static void WriteAnnotationsForMainProce WriteAnnotation(pw, Annotation::BreakpadReserveAddress, buffer); _ui64toa(kReserveSize, buffer, 10); WriteAnnotation(pw, Annotation::BreakpadReserveSize, buffer); } # ifdef HAS_DLL_BLOCKLIST DllBlocklist_WriteNotes(pw.Handle()); # endif - WriteGlobalMemoryStatus(pw); #endif // XP_WIN + WriteMemoryStatus(pw); WriteEscapedMozCrashReason(pw); char oomAllocationSizeBuffer[32] = ""; if (gOOMAllocationSize) { XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer); WriteAnnotation(pw, Annotation::OOMAllocationSize, oomAllocationSizeBuffer); } @@ -1162,19 +1170,17 @@ static void PrepareChildExceptionTimeAnn #else f = GetAnnotationTimeCrashFd(); #endif PlatformWriter apiData; apiData.OpenHandle(f); // ...and write out any annotations. These must be escaped if necessary // (but don't call EscapeAnnotation here, because it touches the heap). -#ifdef XP_WIN - WriteGlobalMemoryStatus(apiData); -#endif + WriteMemoryStatus(apiData); char oomAllocationSizeBuffer[32] = ""; if (gOOMAllocationSize) { XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer); WriteAnnotation(apiData, Annotation::OOMAllocationSize, oomAllocationSizeBuffer); } diff --git a/xpcom/base/AvailableMemoryTracker.cpp b/xpcom/base/AvailableMemoryTracker.cpp --- a/xpcom/base/AvailableMemoryTracker.cpp +++ b/xpcom/base/AvailableMemoryTracker.cpp @@ -6,16 +6,17 @@ #include "mozilla/AvailableMemoryTracker.h" #if defined(XP_WIN) #include "nsExceptionHandler.h" #include "nsICrashReporter.h" #include "nsIMemoryReporter.h" #include "nsMemoryPressure.h" +#include "psapi.h" #endif #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsIRunnable.h" #include "nsISupports.h" #include "nsITimer.h" #include "nsThreadUtils.h" @@ -32,17 +33,16 @@ using namespace mozilla; namespace { #if defined(XP_WIN) Atomic sNumLowVirtualMemEvents; Atomic sNumLowCommitSpaceEvents; -Atomic sNumLowPhysicalMemEvents; class nsAvailableMemoryWatcher final : public nsIObserver, public nsITimerCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSITIMERCALLBACK @@ -58,33 +58,27 @@ private: #else static const size_t kLowVirtualMemoryThreshold = 256 * 1024 * 1024; #endif // Fire a low-memory notification if we have less than this many bytes of // commit space (physical memory plus page file) left. static const size_t kLowCommitSpaceThreshold = 256 * 1024 * 1024; - // Fire a low-memory notification if we have less than this many bytes of - // physical memory available on the whole machine. - static const size_t kLowPhysicalMemoryThreshold = 0; - - // Don't fire a low-memory notification because of low available physical - // memory or low commit space more often than this interval. + // Don't fire a low-memory notification more often than this interval. static const uint32_t kLowMemoryNotificationIntervalMS = 10000; // Poll the amount of free memory at this rate. static const uint32_t kPollingIntervalMS = 1000; // Observer topics we subscribe to, see below. static const char* const kObserverTopics[]; - static bool IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat); - static bool IsCommitSpaceLow(const MEMORYSTATUSEX& aStat); - static bool IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat); + static bool IsVirtualMemoryLow(); + static bool IsCommitSpaceLow(); ~nsAvailableMemoryWatcher() {}; bool OngoingMemoryPressure() { return mUnderMemoryPressure; } void AdjustPollingInterval(const bool aLowMemory); void SendMemoryPressureEvent(); void MaybeSaveMemoryReport(); void Shutdown(); @@ -139,49 +133,50 @@ nsAvailableMemoryWatcher::Shutdown() if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } } /* static */ bool -nsAvailableMemoryWatcher::IsVirtualMemoryLow(const MEMORYSTATUSEX& aStat) +nsAvailableMemoryWatcher::IsVirtualMemoryLow() { - if ((kLowVirtualMemoryThreshold != 0) && - (aStat.ullAvailVirtual < kLowVirtualMemoryThreshold)) { - sNumLowVirtualMemEvents++; - return true; + if (kLowVirtualMemoryThreshold != 0) { + MEMORYSTATUSEX stat; + stat.dwLength = sizeof(stat); + bool success = GlobalMemoryStatusEx(&stat); + + if (success && (stat.ullAvailVirtual < kLowVirtualMemoryThreshold)) { + sNumLowVirtualMemEvents++; + return true; + } } return false; } /* static */ bool -nsAvailableMemoryWatcher::IsCommitSpaceLow(const MEMORYSTATUSEX& aStat) +nsAvailableMemoryWatcher::IsCommitSpaceLow() { - if ((kLowCommitSpaceThreshold != 0) && - (aStat.ullAvailPageFile < kLowCommitSpaceThreshold)) { - sNumLowCommitSpaceEvents++; - CrashReporter::AnnotateCrashReport( - CrashReporter::Annotation::LowCommitSpaceEvents, - uint32_t(sNumLowCommitSpaceEvents)); - return true; - } + if (kLowCommitSpaceThreshold != 0) { + PERFORMANCE_INFORMATION info; + bool success = K32GetPerformanceInfo(&info, sizeof(info)); + + if (success) { + size_t commitFree = (info.CommitLimit - info.CommitTotal) * info.PageSize; - return false; -} - -/* static */ bool -nsAvailableMemoryWatcher::IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat) -{ - if ((kLowPhysicalMemoryThreshold != 0) && - (aStat.ullAvailPhys < kLowPhysicalMemoryThreshold)) { - sNumLowPhysicalMemEvents++; - return true; + if (commitFree < kLowCommitSpaceThreshold) { + sNumLowCommitSpaceEvents++; + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::LowCommitSpaceEvents, + uint32_t(sNumLowCommitSpaceEvents)); + return true; + } + } } return false; } void nsAvailableMemoryWatcher::SendMemoryPressureEvent() { @@ -219,36 +214,27 @@ nsAvailableMemoryWatcher::AdjustPollingI } // Timer callback, polls memory stats to detect low-memory conditions. This // will send memory-pressure events if memory is running low and adjust the // polling interval accordingly. NS_IMETHODIMP nsAvailableMemoryWatcher::Notify(nsITimer* aTimer) { - MEMORYSTATUSEX stat; - stat.dwLength = sizeof(stat); - bool success = GlobalMemoryStatusEx(&stat); - - if (success) { - bool lowMemory = - IsVirtualMemoryLow(stat) || - IsCommitSpaceLow(stat) || - IsPhysicalMemoryLow(stat); + bool lowMemory = IsVirtualMemoryLow() || IsCommitSpaceLow(); - if (lowMemory) { - SendMemoryPressureEvent(); - MaybeSaveMemoryReport(); - } else { - mSavedReport = false; // Save a new report if memory gets low again - } + if (lowMemory) { + SendMemoryPressureEvent(); + MaybeSaveMemoryReport(); + } else { + mSavedReport = false; // Save a new report if memory gets low again + } - AdjustPollingInterval(lowMemory); - mUnderMemoryPressure = lowMemory; - } + AdjustPollingInterval(lowMemory); + mUnderMemoryPressure = lowMemory; return NS_OK; } // Observer service callback, used to stop the polling timer when the user // stops interacting with Firefox and resuming it when they interact again. // Also used to shut down the service if the application is quitting. NS_IMETHODIMP @@ -276,58 +262,43 @@ LowMemoryEventsVirtualDistinguishedAmoun } static int64_t LowMemoryEventsCommitSpaceDistinguishedAmount() { return sNumLowCommitSpaceEvents; } -static int64_t -LowMemoryEventsPhysicalDistinguishedAmount() -{ - return sNumLowPhysicalMemEvents; -} - class LowEventsReporter final : public nsIMemoryReporter { ~LowEventsReporter() {} public: NS_DECL_ISUPPORTS NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) override { MOZ_COLLECT_REPORT( "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE, LowMemoryEventsVirtualDistinguishedAmount(), "Number of low-virtual-memory events fired since startup. We fire such an " -"event if we notice there is less than memory.low_virtual_mem_threshold_mb of " -"virtual address space available (if zero, this behavior is disabled). The " -"process will probably crash if it runs out of virtual address space, so " -"this event is dire."); +"event if we notice there is less than predefined amount of virtual address " +"space available (if zero, this behavior is disabled, see " +"xpcom/base/AvailableMemoryTracker.cpp). The process will probably crash if " +"it runs out of virtual address space, so this event is dire."); MOZ_COLLECT_REPORT( "low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE, LowMemoryEventsCommitSpaceDistinguishedAmount(), "Number of low-commit-space events fired since startup. We fire such an " -"event if we notice there is less than memory.low_commit_space_threshold_mb of " -"commit space available (if zero, this behavior is disabled). Windows will " -"likely kill the process if it runs out of commit space, so this event is " -"dire."); - - MOZ_COLLECT_REPORT( - "low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE, - LowMemoryEventsPhysicalDistinguishedAmount(), -"Number of low-physical-memory events fired since startup. We fire such an " -"event if we notice there is less than memory.low_physical_memory_threshold_mb " -"of physical memory available (if zero, this behavior is disabled). The " -"machine will start to page if it runs out of physical memory. This may " -"cause it to run slowly, but it shouldn't cause it to crash."); +"event if we notice there is less than a predefined amount of commit space " +"available (if zero, this behavior is disabled, see " +"xpcom/base/AvailableMemoryTracker.cpp). Windows will likely kill the process " +"if it runs out of commit space, so this event is dire."); return NS_OK; } }; NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter) #endif // defined(XP_WIN) @@ -423,18 +394,16 @@ Init() watcher->Init(); #if defined(XP_WIN) RegisterStrongMemoryReporter(new LowEventsReporter()); RegisterLowMemoryEventsVirtualDistinguishedAmount( LowMemoryEventsVirtualDistinguishedAmount); RegisterLowMemoryEventsCommitSpaceDistinguishedAmount( LowMemoryEventsCommitSpaceDistinguishedAmount); - RegisterLowMemoryEventsPhysicalDistinguishedAmount( - LowMemoryEventsPhysicalDistinguishedAmount); if (XRE_IsParentProcess()) { RefPtr poller = new nsAvailableMemoryWatcher(); if (NS_FAILED(poller->Init())) { NS_WARNING("Could not start the available memory watcher"); } }