Bug 1107525 - Fix corner case of in-place debug mode bailout and SPS pseudo frame popping. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Tue, 09 Dec 2014 23:10:38 -0800
changeset 219047 170231ba49501eccdeed0cd5aa5c9874408857ef
parent 219046 263322550d0a37858bc0aeb9561c6a4275a87da4
child 219048 0a6dbb9910a1b2d0533700a676bde4be989d3a01
push id27954
push userryanvm@gmail.com
push dateWed, 10 Dec 2014 21:10:24 +0000
treeherdermozilla-central@0cf461e62ce5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1107525
milestone37.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 1107525 - Fix corner case of in-place debug mode bailout and SPS pseudo frame popping. (r=jandem)
js/src/jit-test/tests/debug/bug1107525.js
js/src/jit/Bailouts.cpp
js/src/jit/Bailouts.h
js/src/jit/JitFrames.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1107525.js
@@ -0,0 +1,9 @@
+// |jit-test| error: InternalError
+enableSPSProfiling();
+var g = newGlobal();
+g.parent = this;
+g.eval("new Debugger(parent).onExceptionUnwind = function () { hits++; };");
+function f() {
+    var x = f();
+}
+f();
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -176,34 +176,33 @@ BailoutFrameInfo::BailoutFrameInfo(const
     const OsiIndex *osiIndex = frame.osiIndex();
     snapshotOffset_ = osiIndex->snapshotOffset();
 }
 
 uint32_t
 jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
                              ResumeFromException *rfe,
                              const ExceptionBailoutInfo &excInfo,
-                             bool *overrecursed)
+                             bool *overrecursed, bool *poppedLastSPSFrameOut)
 {
     // We can be propagating debug mode exceptions without there being an
     // actual exception pending. For instance, when we return false from an
     // operation callback like a timeout handler.
     MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
 
     cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
     gc::AutoSuppressGC suppress(cx);
 
     JitActivationIterator jitActivations(cx->runtime());
     BailoutFrameInfo bailoutData(jitActivations, frame.frame());
     JitFrameIterator iter(jitActivations);
 
     BaselineBailoutInfo *bailoutInfo = nullptr;
-    bool poppedLastSPSFrame = false;
     uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true,
-                                           &bailoutInfo, &excInfo, &poppedLastSPSFrame);
+                                           &bailoutInfo, &excInfo, poppedLastSPSFrameOut);
 
     if (retval == BAILOUT_RETURN_OK) {
         MOZ_ASSERT(bailoutInfo);
 
         // Overwrite the kind so HandleException after the bailout returns
         // false, jumping directly to the exception tail.
         if (excInfo.propagatingIonExceptionForDebugMode())
             bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -204,17 +204,17 @@ class ExceptionBailoutInfo
     }
 };
 
 // Called from the exception handler to enter a catch or finally block.
 // Returns a BAILOUT_* error code.
 uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
                                  ResumeFromException *rfe,
                                  const ExceptionBailoutInfo &excInfo,
-                                 bool *overrecursed);
+                                 bool *overrecursed, bool *poppedLastSPSFrameOut);
 
 uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo);
 
 bool CheckFrequentBailouts(JSContext *cx, JSScript *script);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -407,17 +407,17 @@ CloseLiveIterator(JSContext *cx, const I
     if (cx->isExceptionPending())
         UnwindIteratorForException(cx, obj);
     else
         UnwindIteratorForUncatchableException(cx, obj);
 }
 
 static void
 HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe,
-                   bool *overrecursed)
+                   bool *overrecursed, bool *poppedLastSPSFrameOut)
 {
     RootedScript script(cx, frame.script());
     jsbytecode *pc = frame.pc();
 
     if (cx->compartment()->isDebuggee()) {
         // We need to bail when we are the debuggee of a Debugger with a live
         // onExceptionUnwind hook, or if a Debugger has observed this frame
         // (e.g., for onPop).
@@ -439,17 +439,18 @@ HandleExceptionIon(JSContext *cx, const 
             //      frame.
             //
             // An empty exception info denotes that we're propagating an Ion
             // exception due to debug mode, which BailoutIonToBaseline needs to
             // know. This is because we might not be able to fully reconstruct up
             // to the stack depth at the snapshot, as we could've thrown in the
             // middle of a call.
             ExceptionBailoutInfo propagateInfo;
-            uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed);
+            uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed,
+                                                      poppedLastSPSFrameOut);
             if (retval == BAILOUT_RETURN_OK)
                 return;
         }
     }
 
     if (!script->hasTrynotes())
         return;
 
@@ -481,17 +482,18 @@ HandleExceptionIon(JSContext *cx, const 
                 // Ion can compile try-catch, but bailing out to catch
                 // exceptions is slow. Reset the warm-up counter so that if we
                 // catch many exceptions we won't Ion-compile the script.
                 script->resetWarmUpCounter();
 
                 // Bailout at the start of the catch block.
                 jsbytecode *catchPC = script->main() + tn->start + tn->length;
                 ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
-                uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
+                uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed,
+                                                          poppedLastSPSFrameOut);
                 if (retval == BAILOUT_RETURN_OK)
                     return;
 
                 // Error on bailout clears pending exception.
                 MOZ_ASSERT(!cx->isExceptionPending());
             }
             break;
 
@@ -737,17 +739,18 @@ HandleException(ResumeFromException *rfe
             // them.
             InlineFrameIterator frames(cx, &iter);
 
             // Invalidation state will be the same for all inlined scripts in the frame.
             IonScript *ionScript = nullptr;
             bool invalidated = iter.checkInvalidation(&ionScript);
 
             for (;;) {
-                HandleExceptionIon(cx, frames, rfe, &overrecursed);
+                bool poppedLastSPSFrame = false;
+                HandleExceptionIon(cx, frames, rfe, &overrecursed, &poppedLastSPSFrame);
 
                 if (rfe->kind == ResumeFromException::RESUME_BAILOUT) {
                     if (invalidated)
                         ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
                     return;
                 }
 
                 MOZ_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME);
@@ -759,16 +762,21 @@ HandleException(ResumeFromException *rfe
                 bool popSPSFrame = cx->runtime()->spsProfiler.enabled();
                 if (invalidated)
                     popSPSFrame = ionScript->hasSPSInstrumentation();
 
                 // Don't pop an SPS frame for inlined frames, since they are not instrumented.
                 if (frames.more())
                     popSPSFrame = false;
 
+                // Don't pop the last SPS frame if it's already been popped by
+                // bailing out.
+                if (poppedLastSPSFrame)
+                    popSPSFrame = false;
+
                 // When profiling, each frame popped needs a notification that
                 // the function has exited, so invoke the probe that a function
                 // is exiting.
 
                 JSScript *script = frames.script();
                 probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
                 if (!frames.more()) {
                     TraceLogStopEvent(logger, TraceLogger::IonMonkey);