Bug 1459212 - Save memory reports for use in crash reports when low on memory r=dmajor,mccr8

This moves the code that detects very low memory scenarios and grabs memory
reports from the main thread event-loop to the available memory tracker.
Besides removing the overhead of the check from the event-loop code this
increases the likeliness of the reports being gathered by sampling at a
higher frequency but only when we already detected a low-memory scenario. Last
but not least this add checks for low commit-space detection alongside low
virtual-memory detection.

Differential Revision: https://phabricator.services.mozilla.com/D3669

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gabriele Svelto 2018-08-20 21:53:27 +00:00
parent 940e4e9f2c
commit afec91a1db
7 changed files with 34 additions and 121 deletions

View file

@ -5340,18 +5340,6 @@ ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aSco
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvNotifyLowMemory()
{
MarkAsTroubled();
Telemetry::ScalarAdd(Telemetry::ScalarID::DOM_CONTENTPROCESS_TROUBLED_DUE_TO_MEMORY, 1);
nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
return IPC_OK();
}
/* static */ void
ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
BlobImpl* aBlobImpl,

View file

@ -1222,8 +1222,6 @@ public:
virtual mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
const IPC::Principal& aPrincipal) override;
virtual mozilla::ipc::IPCResult RecvNotifyLowMemory() override;
virtual mozilla::ipc::IPCResult RecvGetFilesRequest(const nsID& aID,
const nsString& aDirectoryPath,
const bool& aRecursiveFlag) override;

View file

@ -1123,13 +1123,6 @@ parent:
async NotifyPushSubscriptionChangeObservers(nsCString scope,
Principal principal);
/**
* Tell the parent process that the child process is low on memory. This
* allows the parent process to save a memory report that can potentially be
* sent with a crash report from the content process.
*/
async NotifyLowMemory();
async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
async DeleteGetFilesRequest(nsID aID);

View file

@ -950,21 +950,6 @@ media:
# The following section contains content process base counters.
dom.contentprocess:
troubled_due_to_memory:
bug_numbers:
- 1305091
description: >
The number of content processes that were marked as troubled because
it was running low on virtual memory.
expires: "58"
kind: uint
notification_emails:
- benjamin@smedbergs.us
- mconley@mozilla.com
release_channel_collection: opt-in
record_in_processes:
- 'main'
buildID_mismatch:
bug_numbers:
- 1366808

View file

@ -8,6 +8,7 @@
#if defined(XP_WIN)
#include "nsExceptionHandler.h"
#include "nsICrashReporter.h"
#include "nsIMemoryReporter.h"
#include "nsMemoryPressure.h"
#endif
@ -46,6 +47,7 @@ public:
NS_DECL_NSIOBSERVER
NS_DECL_NSITIMERCALLBACK
nsAvailableMemoryWatcher();
nsresult Init();
private:
@ -57,8 +59,8 @@ private:
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.
// 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
@ -80,12 +82,15 @@ private:
static bool IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat);
~nsAvailableMemoryWatcher() {};
bool OngoingMemoryPressure() { return mUnderMemoryPressure; }
void AdjustPollingInterval(const bool aLowMemory);
void SendMemoryPressureEvent();
void MaybeSaveMemoryReport();
void Shutdown();
nsCOMPtr<nsITimer> mTimer;
bool mUnderMemoryPressure;
bool mSavedReport;
};
const char* const nsAvailableMemoryWatcher::kObserverTopics[] = {
@ -96,6 +101,13 @@ const char* const nsAvailableMemoryWatcher::kObserverTopics[] = {
NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsIObserver, nsITimerCallback)
nsAvailableMemoryWatcher::nsAvailableMemoryWatcher()
: mTimer(nullptr)
, mUnderMemoryPressure(false)
, mSavedReport(false)
{
}
nsresult
nsAvailableMemoryWatcher::Init()
{
@ -173,11 +185,25 @@ nsAvailableMemoryWatcher::IsPhysicalMemoryLow(const MEMORYSTATUSEX& aStat)
void
nsAvailableMemoryWatcher::SendMemoryPressureEvent()
{
MemoryPressureState state = mUnderMemoryPressure ? MemPressure_Ongoing
: MemPressure_New;
MemoryPressureState state = OngoingMemoryPressure() ? MemPressure_Ongoing
: MemPressure_New;
NS_DispatchEventualMemoryPressure(state);
}
void
nsAvailableMemoryWatcher::MaybeSaveMemoryReport()
{
if (!mSavedReport && OngoingMemoryPressure()) {
nsCOMPtr<nsICrashReporter> cr =
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (cr) {
if (NS_SUCCEEDED(cr->SaveMemoryReport())) {
mSavedReport = true;
}
}
}
}
void
nsAvailableMemoryWatcher::AdjustPollingInterval(const bool aLowMemory)
{
@ -185,7 +211,7 @@ nsAvailableMemoryWatcher::AdjustPollingInterval(const bool aLowMemory)
// We entered a low-memory state, wait for a longer interval before polling
// again as there's no point in rapidly sending further notifications.
mTimer->SetDelay(kLowMemoryNotificationIntervalMS);
} else if (mUnderMemoryPressure) {
} else if (OngoingMemoryPressure()) {
// We were under memory pressure but we're not anymore, resume polling at
// a faster pace.
mTimer->SetDelay(kPollingIntervalMS);
@ -210,6 +236,9 @@ nsAvailableMemoryWatcher::Notify(nsITimer* aTimer)
if (lowMemory) {
SendMemoryPressureEvent();
MaybeSaveMemoryReport();
} else {
mSavedReport = false; // Save a new report if memory gets low again
}
AdjustPollingInterval(lowMemory);

View file

@ -41,15 +41,12 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsICrashReporter.h"
#include "nsThreadSyncDispatch.h"
#include "nsServiceManagerUtils.h"
#include "GeckoProfiler.h"
#include "InputEventStatistics.h"
#include "ThreadEventTarget.h"
#include "mozilla/dom/ContentChild.h"
#ifdef XP_LINUX
#ifdef __GLIBC__
#include <gnu/libc-version.h>
@ -593,71 +590,6 @@ nsThread::InitCommon()
//-----------------------------------------------------------------------------
// Tell the crash reporter to save a memory report if our heuristics determine
// that an OOM failure is likely to occur soon.
// Memory usage will not be checked more than every 30 seconds or saved more
// than every 3 minutes
// If |aShouldSave == kForceReport|, a report will be saved regardless of
// whether the process is low on memory or not. However, it will still not be
// saved if a report was saved less than 3 minutes ago.
bool
nsThread::SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave)
{
// Keep an eye on memory usage (cheap, ~7ms) somewhat frequently,
// but save memory reports (expensive, ~75ms) less frequently.
const size_t kLowMemoryCheckSeconds = 30;
const size_t kLowMemorySaveSeconds = 3 * 60;
static TimeStamp nextCheck = TimeStamp::NowLoRes()
+ TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
static bool recentlySavedReport = false; // Keeps track of whether a report
// was saved last time we checked
// Are we checking again too soon?
TimeStamp now = TimeStamp::NowLoRes();
if ((aShouldSave == ShouldSaveMemoryReport::kMaybeReport ||
recentlySavedReport) && now < nextCheck) {
return false;
}
bool needMemoryReport = (aShouldSave == ShouldSaveMemoryReport::kForceReport);
#ifdef XP_WIN // XXX implement on other platforms as needed
// If the report is forced there is no need to check whether it is necessary
if (aShouldSave != ShouldSaveMemoryReport::kForceReport) {
const size_t LOWMEM_THRESHOLD_VIRTUAL = 200 * 1024 * 1024;
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
if (statex.ullAvailVirtual < LOWMEM_THRESHOLD_VIRTUAL) {
needMemoryReport = true;
}
}
}
#endif
if (needMemoryReport) {
if (XRE_IsContentProcess()) {
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
if (cc) {
cc->SendNotifyLowMemory();
}
} else {
nsCOMPtr<nsICrashReporter> cr =
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (cr) {
cr->SaveMemoryReport();
}
}
recentlySavedReport = true;
nextCheck = now + TimeDuration::FromSeconds(kLowMemorySaveSeconds);
} else {
recentlySavedReport = false;
nextCheck = now + TimeDuration::FromSeconds(kLowMemoryCheckSeconds);
}
return recentlySavedReport;
}
#ifdef MOZ_CANARY
int sCanaryOutputFD = -1;
#endif
@ -1425,10 +1357,6 @@ nsThread::DoMainThreadSpecificProcessing(bool aReallyWait)
}
}
}
if (!ShuttingDown()) {
SaveMemoryReportNearOOM(ShouldSaveMemoryReport::kMaybeReport);
}
}
NS_IMETHODIMP

View file

@ -100,14 +100,6 @@ public:
void WaitForAllAsynchronousShutdowns();
enum class ShouldSaveMemoryReport
{
kMaybeReport,
kForceReport
};
static bool SaveMemoryReportNearOOM(ShouldSaveMemoryReport aShouldSave);
static const uint32_t kRunnableNameBufSize = 1000;
static mozilla::Array<char, kRunnableNameBufSize> sMainThreadRunnableName;