Bug 1212469 - Make oomTest() into a shell function r=nbp

This commit is contained in:
Jon Coppeard 2015-10-13 13:37:07 +01:00
parent daef6b660b
commit 0bba2d39d5
19 changed files with 158 additions and 59 deletions

View File

@ -53,6 +53,25 @@ static bool fuzzingSafe = false;
// OOM conditions. // OOM conditions.
static bool disableOOMFunctions = false; static bool disableOOMFunctions = false;
static bool
EnvVarIsDefined(const char* name)
{
const char* value = getenv(name);
return value && *value;
}
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
static bool
EnvVarAsInt(const char* name, int* valueOut)
{
if (!EnvVarIsDefined(name))
return false;
*valueOut = atoi(getenv(name));
return true;
}
#endif
static bool static bool
GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -1072,6 +1091,93 @@ ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
OOM_maxAllocations = UINT32_MAX; OOM_maxAllocations = UINT32_MAX;
return true; return true;
} }
static bool
OOMTest(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1 || !args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
JS_ReportError(cx, "oomTest() takes a single function argument.");
return false;
}
if (disableOOMFunctions) {
args.rval().setUndefined();
return true;
}
RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
bool verbose = EnvVarIsDefined("OOM_VERBOSE");
unsigned threadStart = oom::THREAD_TYPE_MAIN;
unsigned threadEnd = oom::THREAD_TYPE_MAX;
// Test a single thread type if specified by the OOM_THREAD environment variable.
int threadOption = 0;
if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
if (threadOption < oom::THREAD_TYPE_MAIN || threadOption > oom::THREAD_TYPE_MAX) {
JS_ReportError(cx, "OOM_THREAD value out of range.");
return false;
}
threadStart = threadOption;
threadEnd = threadOption + 1;
}
JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
for (unsigned thread = threadStart; thread < threadEnd; thread++) {
if (verbose)
fprintf(stderr, "thread %d\n", thread);
HelperThreadState().waitForAllThreads();
js::oom::targetThread = thread;
unsigned allocation = 1;
bool handledOOM;
do {
if (verbose)
fprintf(stderr, " allocation %d\n", allocation);
MOZ_ASSERT(!cx->isExceptionPending());
MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
OOM_maxAllocations = OOM_counter + allocation;
OOM_failAlways = false;
RootedValue result(cx);
bool ok = JS_CallFunction(cx, cx->global(), function,
HandleValueArray::empty(), &result);
handledOOM = OOM_counter >= OOM_maxAllocations;
OOM_maxAllocations = UINT32_MAX;
MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
MOZ_ASSERT_IF(!ok, cx->isExceptionPending());
// Note that it is possible that the function throws an exception
// unconnected to OOM, in which case we ignore it. More correct
// would be to have the caller pass some kind of exception
// specification and to check the exception against it.
cx->clearPendingException();
cx->runtime()->hadOutOfMemory = false;
allocation++;
} while (handledOOM);
if (verbose) {
fprintf(stderr, " finished after %d allocations\n", allocation - 2);
}
}
js::oom::targetThread = js::oom::THREAD_TYPE_NONE;
args.rval().setUndefined();
return true;
}
#endif #endif
static const js::Class FakePromiseClass = { static const js::Class FakePromiseClass = {
@ -3062,6 +3168,12 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
"resetOOMFailure()", "resetOOMFailure()",
" Remove the allocation failure scheduled by either oomAfterAllocations() or\n" " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
" oomAtAllocation() and return whether any allocation had been caused to fail."), " oomAtAllocation() and return whether any allocation had been caused to fail."),
JS_FN_HELP("oomTest", OOMTest, 0, 0,
"oomTest(function)",
" Test that the passed function behaves correctly under OOM conditions by\n"
" repeatedly executing it and simulating allocation failure at successive\n"
" allocations until the function completes without seeing a failure."),
#endif #endif
JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0, JS_FN_HELP("makeFakePromise", MakeFakePromise, 0, 0,
@ -3464,7 +3576,7 @@ js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
bool disableOOMFunctions_) bool disableOOMFunctions_)
{ {
fuzzingSafe = fuzzingSafe_; fuzzingSafe = fuzzingSafe_;
if (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0') if (EnvVarIsDefined("MOZ_FUZZING_SAFE"))
fuzzingSafe = true; fuzzingSafe = true;
disableOOMFunctions = disableOOMFunctions_; disableOOMFunctions = disableOOMFunctions_;

View File

@ -1,39 +0,0 @@
// Function to test OOM handling by repeatedly calling a function and failing
// successive allocations.
if (!("oomAtAllocation" in this && "resetOOMFailure" in this && "oomThreadTypes" in this))
quit();
if ("gczeal" in this)
gczeal(0);
const verbose = ("os" in this) && os.getenv("OOM_VERBOSE");
// Test out of memory handing by calling a function f() while causing successive
// memory allocations to fail. Repeat until f() finishes without reaching the
// failing allocation.
function oomTest(f) {
for (let thread = 1; thread < oomThreadTypes(); thread++) {
if (verbose)
print("testing thread " + thread);
var i = 1;
var more;
do {
if (verbose)
print("fail at " + i);
try {
oomAtAllocation(i, thread);
f();
more = resetOOMFailure();
} catch (e) {
// Ignore exceptions.
more = resetOOMFailure();
}
i++;
} while(more);
if (verbose)
print("finished after " + (i - 2) + " failures");
}
}

View File

@ -1,5 +1,7 @@
// |jit-test| --no-ion // |jit-test| --no-ion
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
var g = newGlobal(); var g = newGlobal();
oomTest(function() { oomTest(function() {
Debugger(g); Debugger(g);

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest((function(x) { assertEq(x + y + ex, 25); })); oomTest((function(x) { assertEq(x + y + ex, 25); }));

View File

@ -1,6 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this) || helperThreadCount() === 0)
if (helperThreadCount() === 0)
quit(0); quit(0);
var lfGlobal = newGlobal(); var lfGlobal = newGlobal();

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true})); oomTest(() => getBacktrace({args: oomTest[load+1], locals: true, thisprops: true}));

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => parseModule('import v from "mod";')); oomTest(() => parseModule('import v from "mod";'));

View File

@ -1,4 +1,5 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => { oomTest(() => {
try { try {

View File

@ -1,4 +1,5 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
function arrayProtoOutOfRange() { function arrayProtoOutOfRange() {
function f(obj) { function f(obj) {

View File

@ -1,3 +1,5 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
var g = newGlobal(); var g = newGlobal();
oomTest(() => Debugger(g)); oomTest(() => Debugger(g));

View File

@ -1,4 +1,5 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => { oomTest(() => {
let x = 0; let x = 0;

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => getBacktrace({args: true, locals: true, thisprops: true})); oomTest(() => getBacktrace({args: true, locals: true, thisprops: true}));

View File

@ -1,3 +1,5 @@
// |jit-test| allow-oom; allow-unhandlable-oom; --no-threads // |jit-test| allow-oom; allow-unhandlable-oom; --no-threads
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => getBacktrace({thisprops: gc() && delete addDebuggee.enabled})); oomTest(() => getBacktrace({thisprops: gc() && delete addDebuggee.enabled}));

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(newGlobal); oomTest(newGlobal);

View File

@ -1,4 +1,6 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
function parseAsmJS() { function parseAsmJS() {
eval(`function m(stdlib) eval(`function m(stdlib)
{ {

View File

@ -1,2 +1,4 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => eval("function f() {}")); oomTest(() => eval("function f() {}"));

View File

@ -1,3 +1,5 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3)); oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false)); oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));

View File

@ -1,4 +1,6 @@
load(libdir + 'oomTest.js'); if (!('oomTest' in this))
quit();
oomTest(function () { oomTest(function () {
eval(`var wm = new WeakMap(); eval(`var wm = new WeakMap();
wm.set({}, 'FOO').get(false);`); wm.set({}, 'FOO').get(false);`);

View File

@ -1,5 +1,6 @@
// |jit-test| slow; // |jit-test| slow;
load(libdir + "oomTest.js"); if (!('oomTest' in this))
quit();
enableSPSProfiling(); enableSPSProfiling();
var lfGlobal = newGlobal(); var lfGlobal = newGlobal();