Bug 1490390 - Add an option to make oomTest keep failing after the initial simulated failure r=nbp
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 12 Sep 2018 14:58:05 +0100
changeset 436027 e5c0d34d8fdf1152de53a09421679f71a2fb7a0d
parent 436026 b499d1183c996ce044961ecf5cd67b139b095427
child 436028 b28cb41dd456532982ca7a2d579908b95f4ac17c
push id34625
push userdvarga@mozilla.com
push dateThu, 13 Sep 2018 02:31:40 +0000
treeherdermozilla-central@51e9e9660b3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1490390
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1490390 - Add an option to make oomTest keep failing after the initial simulated failure r=nbp
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/gc/oomInRegExp2.js
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1808,25 +1808,26 @@ struct MOZ_STACK_CLASS IterativeFailureT
 {
     explicit IterativeFailureTestParams(JSContext* cx)
       : testFunction(cx)
     {}
 
     RootedFunction testFunction;
     unsigned threadStart = 0;
     unsigned threadEnd = 0;
-    bool expectExceptionOnFailure = false;
+    bool expectExceptionOnFailure = true;
+    bool keepFailing = false;
     bool verbose = false;
 };
 
 struct IterativeFailureSimulator
 {
     virtual void setup(JSContext* cx) {}
     virtual void teardown(JSContext* cx) {}
-    virtual void startSimulating(JSContext* cx, unsigned iteration, unsigned thread) = 0;
+    virtual void startSimulating(JSContext* cx, unsigned iteration, unsigned thread, bool keepFailing) = 0;
     virtual bool stopSimulating() = 0;
     virtual void cleanup(JSContext* cx) {}
 };
 
 bool
 RunIterativeFailureTest(JSContext* cx, const IterativeFailureTestParams& params,
                         IterativeFailureSimulator& simulator)
 {
@@ -1844,33 +1845,35 @@ RunIterativeFailureTest(JSContext* cx, c
     MOZ_ASSERT(!cx->isExceptionPending());
 
 #ifdef JS_GC_ZEAL
     JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
 #endif
 
     size_t compartmentCount = CountCompartments(cx);
 
+    RootedValue exception(cx);
+
     simulator.setup(cx);
 
     for (unsigned thread = params.threadStart; thread < params.threadEnd; thread++) {
         if (params.verbose) {
             fprintf(stderr, "thread %d\n", thread);
         }
 
         unsigned iteration = 1;
         bool failureWasSimulated;
         do {
             if (params.verbose) {
                 fprintf(stderr, "  iteration %d\n", iteration);
             }
 
             MOZ_ASSERT(!cx->isExceptionPending());
 
-            simulator.startSimulating(cx, iteration, thread);
+            simulator.startSimulating(cx, iteration, thread, params.keepFailing);
 
             RootedValue result(cx);
             bool ok = JS_CallFunction(cx, cx->global(), params.testFunction,
                                       HandleValueArray::empty(), &result);
 
             failureWasSimulated = simulator.stopSimulating();
 
             MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
@@ -1885,19 +1888,25 @@ RunIterativeFailureTest(JSContext* cx, c
                            "missing call to js::ReportOutOfMemory()?");
             }
 
             // Note that it is possible that the function throws an exception
             // unconnected to the simulated failure, 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.
 
+            if (!failureWasSimulated && cx->isExceptionPending()) {
+                if (!cx->getPendingException(&exception))
+                    return false;
+            }
             cx->clearPendingException();
             simulator.cleanup(cx);
 
+            gc::FinishGC(cx);
+
             // Some tests create a new compartment or zone on every
             // iteration. Our GC is triggered by GC allocations and not by
             // number of compartments or zones, so these won't normally get
             // cleaned up. The check here stops some tests running out of
             // memory.
             if (CountCompartments(cx) > compartmentCount + 100) {
                 JS_GC(cx);
                 compartmentCount = CountCompartments(cx);
@@ -1914,16 +1923,24 @@ RunIterativeFailureTest(JSContext* cx, c
             }
 #endif
 
             iteration++;
         } while (failureWasSimulated);
 
         if (params.verbose) {
             fprintf(stderr, "  finished after %d iterations\n", iteration - 2);
+            if (!exception.isUndefined()) {
+                RootedString str(cx, JS::ToString(cx, exception));
+                UniqueChars bytes(JS_EncodeStringToLatin1(cx, str));
+                if (!bytes) {
+                    return false;
+                }
+                fprintf(stderr, "  threw %s\n", bytes.get());
+            }
         }
     }
 
     simulator.teardown(cx);
 
     cx->runningOOMTest = false;
     return true;
 }
@@ -1942,23 +1959,43 @@ ParseIterativeFailureTestParams(JSContex
     if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
         JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
         return false;
     }
     params->testFunction = &args[0].toObject().as<JSFunction>();
 
     // There are some places where we do fail without raising an exception, so
     // we can't expose this to the fuzzers by default.
-    params->expectExceptionOnFailure = !fuzzingSafe;
+    if (fuzzingSafe)
+        params->expectExceptionOnFailure = false;
+
     if (args.length() == 2) {
-        if (!args[1].isBoolean()) {
-            JS_ReportErrorASCII(cx, "The optional second argument must be a boolean.");
+        if (args[1].isBoolean()) {
+            params->expectExceptionOnFailure = args[1].toBoolean();
+        } else if (args[1].isObject()) {
+            RootedObject options(cx, &args[1].toObject());
+            RootedValue value(cx);
+
+            if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
+                return false;
+            }
+            if (!value.isUndefined()) {
+                params->expectExceptionOnFailure = ToBoolean(value);
+            }
+
+            if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
+                return false;
+            }
+            if (!value.isUndefined()) {
+                params->keepFailing = ToBoolean(value);
+            }
+        } else {
+            JS_ReportErrorASCII(cx, "The optional second argument must be an object or a boolean.");
             return false;
         }
-        params->expectExceptionOnFailure = args[1].toBoolean();
     }
 
     // Test all threads by default.
     params->threadStart = oom::FirstThreadTypeToTest;
     params->threadEnd = oom::LastThreadTypeToTest;
 
     // Test a single thread type if specified by the OOM_THREAD environment variable.
     int threadOption = 0;
@@ -1978,19 +2015,19 @@ ParseIterativeFailureTestParams(JSContex
 }
 
 struct OOMSimulator : public IterativeFailureSimulator
 {
     void setup(JSContext* cx) override {
         cx->runtime()->hadOutOfMemory = false;
     }
 
-    void startSimulating(JSContext* cx, unsigned i, unsigned thread) override {
+    void startSimulating(JSContext* cx, unsigned i, unsigned thread, bool keepFailing) override {
         MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
-        js::oom::SimulateOOMAfter(i, thread, false);
+        js::oom::SimulateOOMAfter(i, thread, keepFailing);
     }
 
     bool stopSimulating() override {
         bool handledOOM = js::oom::HadSimulatedOOM();
         js::oom::ResetSimulatedOOM();
         return handledOOM;
     }
 
@@ -2015,18 +2052,18 @@ OOMTest(JSContext* cx, unsigned argc, Va
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 struct StackOOMSimulator : public IterativeFailureSimulator
 {
-    void startSimulating(JSContext* cx, unsigned i, unsigned thread) override {
-        js::oom::SimulateStackOOMAfter(i, thread, false);
+    void startSimulating(JSContext* cx, unsigned i, unsigned thread, bool keepFailing) override {
+        js::oom::SimulateStackOOMAfter(i, thread, keepFailing);
     }
 
     bool stopSimulating() override {
         bool handledOOM = js::oom::HadSimulatedStackOOM();
         js::oom::ResetSimulatedStackOOM();
         return handledOOM;
     }
 };
@@ -2063,18 +2100,18 @@ struct FailingIterruptSimulator : public
         prevEnd = cx->interruptCallbacks().end();
         JS_AddInterruptCallback(cx, failingInterruptCallback);
     }
 
     void teardown(JSContext* cx) override {
         cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
     }
 
-    void startSimulating(JSContext* cx, unsigned i, unsigned thread) override {
-        js::oom::SimulateInterruptAfter(i, thread, false);
+    void startSimulating(JSContext* cx, unsigned i, unsigned thread, bool keepFailing) override {
+        js::oom::SimulateInterruptAfter(i, thread, keepFailing);
     }
 
     bool stopSimulating() override {
         bool handledInterrupt = js::oom::HadSimulatedInterrupt();
         js::oom::ResetSimulatedInterrupt();
         return handledInterrupt;
     }
 };
@@ -5565,23 +5602,26 @@ static const JSFunctionSpecWithHelp Test
 "  specified type of helper thread."),
 
     JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
 "resetOOMFailure()",
 "  Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
 "  oomAtAllocation() and return whether any allocation had been caused to fail."),
 
     JS_FN_HELP("oomTest", OOMTest, 0, 0,
-"oomTest(function, [expectExceptionOnFailure = true])",
+"oomTest(function, [expectExceptionOnFailure = true | options])",
 "  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.\n"
 "  By default this tests that an exception is raised if execution fails, but\n"
 "  this can be disabled by passing false as the optional second parameter.\n"
-"  This is also disabled when --fuzzing-safe is specified."),
+"  This is also disabled when --fuzzing-safe is specified.\n"
+"  Alternatively an object can be passed to set the following options:\n"
+"    expectExceptionOnFailure: bool - as described above.\n"
+"    keepFailing: bool - continue to fail after first simulated failure.\n"),
 
     JS_FN_HELP("stackTest", StackTest, 0, 0,
 "stackTest(function, [expectExceptionOnFailure = true])",
 "  This function behaves exactly like oomTest with the difference that\n"
 "  instead of simulating regular OOM conditions, it simulates the engine\n"
 "  running out of stack space (failing recursion check)."),
 
     JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInRegExp2.js
@@ -0,0 +1,6 @@
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3), {keepFailing: true});
+oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false), {keepFailing: true});
+oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true), {keepFailing: true});