mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 987995, part 4 - Add new crash reporter annotations for JS out-of-memory conditions. r=mccr8,r=bsmedberg.
This commit is contained in:
parent
162eb32023
commit
0629a4a901
@ -0,0 +1,21 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_after_js_large_allocation_failure.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
|
||||
Components.utils.getJSTestingFunctions().reportLargeAllocationFailure();
|
||||
Components.utils.forceGC();
|
||||
},
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestingOOMCrash, "Yes");
|
||||
do_check_false("JSOutOfMemory" in extra);
|
||||
do_check_eq(extra.JSLargeAllocationFailure, "Recovered");
|
||||
},
|
||||
true);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_after_js_oom_reporting.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
|
||||
|
||||
function crashWhileReporting() {
|
||||
CrashTestUtils.crash(crashType);
|
||||
}
|
||||
|
||||
var observerService = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
observerService.addObserver(crashWhileReporting, "memory-pressure", false);
|
||||
Components.utils.getJSTestingFunctions().reportLargeAllocationFailure();
|
||||
},
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestingOOMCrash, "Yes");
|
||||
do_check_eq(extra.JSLargeAllocationFailure, "Reporting");
|
||||
},
|
||||
true);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_after_js_oom_recovered.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
|
||||
Components.utils.getJSTestingFunctions().reportOutOfMemory();
|
||||
Components.utils.forceGC();
|
||||
},
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestingOOMCrash, "Yes");
|
||||
do_check_eq(extra.JSOutOfMemory, "Recovered");
|
||||
},
|
||||
true);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_after_js_oom_reported.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
|
||||
|
||||
// GC now to avoid having it happen randomly later, which would make the
|
||||
// test bogusly fail. See comment below.
|
||||
Components.utils.forceGC();
|
||||
|
||||
Components.utils.getJSTestingFunctions().reportOutOfMemory();
|
||||
},
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestingOOMCrash, "Yes");
|
||||
|
||||
// The JSOutOfMemory field is absent if the JS engine never reported OOM,
|
||||
// "Reported" if it did, and "Recovered" if it reported OOM but
|
||||
// subsequently completed a full GC cycle. Since this test calls
|
||||
// reportOutOfMemory() and then crashes, we expect "Reported".
|
||||
//
|
||||
// Theoretically, GC can happen any time, so it is just possible that
|
||||
// this property could be "Recovered" even if the implementation is
|
||||
// correct. More likely, though, that indicates a bug, so only accept
|
||||
// "Reported".
|
||||
do_check_eq(extra.JSOutOfMemory, "Reported");
|
||||
},
|
||||
true);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_after_js_oom_reported_2.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do_crash(
|
||||
function() {
|
||||
crashType = CrashTestUtils.CRASH_MOZ_CRASH;
|
||||
crashReporter.annotateCrashReport("TestingOOMCrash", "Yes");
|
||||
Components.utils.getJSTestingFunctions().reportOutOfMemory();
|
||||
Components.utils.forceGC(); // recover from first OOM
|
||||
Components.utils.getJSTestingFunctions().reportOutOfMemory();
|
||||
},
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestingOOMCrash, "Yes");
|
||||
|
||||
// Technically, GC can happen at any time, but it would be really
|
||||
// peculiar for it to happen again heuristically right after a GC was
|
||||
// forced. If extra.JSOutOfMemory is "Recovered" here, that's most
|
||||
// likely a bug in the error reporting machinery.
|
||||
do_check_eq(extra.JSOutOfMemory, "Reported");
|
||||
},
|
||||
true);
|
||||
}
|
@ -8,6 +8,8 @@ function run_test()
|
||||
function(mdump, extra) {
|
||||
do_check_eq(extra.TestKey, "TestValue");
|
||||
do_check_false("OOMAllocationSize" in extra);
|
||||
do_check_false("JSOutOfMemory" in extra);
|
||||
do_check_false("JSLargeAllocationFailure" in extra);
|
||||
},
|
||||
// process will exit with a zero exit status
|
||||
true);
|
||||
|
@ -1,7 +1,7 @@
|
||||
function run_test()
|
||||
{
|
||||
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
|
||||
dump("INFO | test_crash_purevirtual.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
dump("INFO | test_crash_oom.js | Can't test crashreporter in a non-libxul build.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,11 @@ support-files =
|
||||
[test_crash_moz_crash.js]
|
||||
[test_crash_purevirtual.js]
|
||||
[test_crash_runtimeabort.js]
|
||||
[test_crash_after_js_oom_reported.js]
|
||||
[test_crash_after_js_oom_recovered.js]
|
||||
[test_crash_after_js_oom_reported_2.js]
|
||||
[test_crash_after_js_large_allocation_failure.js]
|
||||
[test_crash_after_js_large_allocation_failure_reporting.js]
|
||||
[test_crash_oom.js]
|
||||
skip-if = os == 'win' && debug
|
||||
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "nsIException.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
@ -467,6 +468,8 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
|
||||
, mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal)
|
||||
, mJSRuntime(nullptr)
|
||||
, mJSHolders(512)
|
||||
, mOutOfMemoryState(OOMState::OK)
|
||||
, mLargeAllocationFailureState(OOMState::OK)
|
||||
{
|
||||
mozilla::dom::InitScriptSettings();
|
||||
|
||||
@ -1176,6 +1179,22 @@ CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::AnnotateAndSetOutOfMemory(OOMState *aStatePtr, OOMState aNewState)
|
||||
{
|
||||
*aStatePtr = aNewState;
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
CrashReporter::AnnotateCrashReport(aStatePtr == &mOutOfMemoryState
|
||||
? NS_LITERAL_CSTRING("JSOutOfMemory")
|
||||
: NS_LITERAL_CSTRING("JSLargeAllocationFailure"),
|
||||
aNewState == OOMState::Reporting
|
||||
? NS_LITERAL_CSTRING("Reporting")
|
||||
: aNewState == OOMState::Reported
|
||||
? NS_LITERAL_CSTRING("Reported")
|
||||
: NS_LITERAL_CSTRING("Recovered"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
||||
{
|
||||
@ -1184,6 +1203,15 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
||||
nsCycleCollector_prepareForGarbageCollection();
|
||||
break;
|
||||
case JSGC_END: {
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (mOutOfMemoryState == OOMState::Reported) {
|
||||
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Recovered);
|
||||
}
|
||||
if (mLargeAllocationFailureState == OOMState::Reported) {
|
||||
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Recovered);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the previous GC created a runnable to finalize objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
@ -1210,11 +1238,15 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
||||
void
|
||||
CycleCollectedJSRuntime::OnOutOfMemory()
|
||||
{
|
||||
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reporting);
|
||||
CustomOutOfMemoryCallback();
|
||||
AnnotateAndSetOutOfMemory(&mOutOfMemoryState, OOMState::Reported);
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::OnLargeAllocationFailure()
|
||||
{
|
||||
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reporting);
|
||||
CustomLargeAllocationFailureCallback();
|
||||
AnnotateAndSetOutOfMemory(&mLargeAllocationFailureState, OOMState::Reported);
|
||||
}
|
||||
|
@ -206,6 +206,41 @@ private:
|
||||
|
||||
void FinalizeDeferredThings(DeferredFinalizeType aType);
|
||||
|
||||
public:
|
||||
// Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
|
||||
// crash reports. Here are the values that can appear in the reports:
|
||||
MOZ_BEGIN_NESTED_ENUM_CLASS(OOMState, uint32_t)
|
||||
// The condition has never happened. No entry appears in the crash report.
|
||||
OK,
|
||||
|
||||
// We are currently reporting the given condition.
|
||||
//
|
||||
// Suppose a crash report contains "JSLargeAllocationFailure:
|
||||
// Reporting". This means we crashed while executing memory-pressure
|
||||
// observers, trying to shake loose some memory. The large allocation in
|
||||
// question did not return null: it is still on the stack. Had we not
|
||||
// crashed, it would have been retried.
|
||||
Reporting,
|
||||
|
||||
// The condition has been reported since the last GC.
|
||||
//
|
||||
// If a crash report contains "JSOutOfMemory: Reported", that means a small
|
||||
// allocation failed, and then we crashed, probably due to buggy
|
||||
// error-handling code that ran after allocation returned null.
|
||||
//
|
||||
// This contrasts with "Reporting" which means that no error-handling code
|
||||
// had executed yet.
|
||||
Reported,
|
||||
|
||||
// The condition has happened, but a GC cycle ended since then.
|
||||
//
|
||||
// GC is taken as a proxy for "we've been banging on the heap a good bit
|
||||
// now and haven't crashed; the OOM was probably handled correctly".
|
||||
Recovered
|
||||
MOZ_END_NESTED_ENUM_CLASS(OOMState)
|
||||
|
||||
private:
|
||||
void AnnotateAndSetOutOfMemory(OOMState *aStatePtr, OOMState aNewState);
|
||||
void OnGC(JSGCStatus aStatus);
|
||||
void OnOutOfMemory();
|
||||
void OnLargeAllocationFailure();
|
||||
@ -269,8 +304,13 @@ private:
|
||||
nsRefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
|
||||
|
||||
nsCOMPtr<nsIException> mPendingException;
|
||||
|
||||
OOMState mOutOfMemoryState;
|
||||
OOMState mLargeAllocationFailureState;
|
||||
};
|
||||
|
||||
MOZ_FINISH_NESTED_ENUM_CLASS(CycleCollectedJSRuntime::OOMState)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_CycleCollectedJSRuntime_h__
|
||||
|
Loading…
Reference in New Issue
Block a user