Bug 607539 - Allow profiler to be run from interpreter (r=dmandelin,dvander)
authorBill McCloskey <wmccloskey@mozilla.com>
Thu, 11 Nov 2010 17:22:48 -0800
changeset 57797 d5257e1f0c8bbf356b29f00b6d529ba16aa9e66c
parent 57796 6b22d236a218f14c2ceca8181e529eb9f1f9b679
child 57798 d2dd2db256ce2e9ca13575968eb9ce7256a07038
push id17032
push userrsayre@mozilla.com
push dateWed, 17 Nov 2010 21:55:39 +0000
treeherdermozilla-central@78a42f77bb90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin, dvander
bugs607539
milestone2.0b8pre
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 607539 - Allow profiler to be run from interpreter (r=dmandelin,dvander)
js/src/jscntxt.cpp
js/src/jsinterp.cpp
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -2283,17 +2283,17 @@ JSContext::updateJITEnabled()
     methodJitEnabled = (options & JSOPTION_METHODJIT) &&
                        !IsJITBrokenHere()
 # if defined JS_CPU_X86 || defined JS_CPU_X64
                        && JSC::MacroAssemblerX86Common::getSSEState() >=
                           JSC::MacroAssemblerX86Common::HasSSE2
 # endif
                         ;
 #ifdef JS_TRACER
-    profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled;
+    profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled;
 #endif
 #endif
 }
 
 namespace js {
 
 void
 SetPendingException(JSContext *cx, const Value &v)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -743,16 +743,20 @@ Invoke(JSContext *cx, const CallArgs &ar
 }
 
 bool
 InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
 {
 #ifdef JS_TRACER
     if (TRACE_RECORDER(cx))
         AbortRecording(cx, "attempt to reenter VM while recording");
+#ifdef JS_METHODJIT
+    if (TRACE_PROFILER(cx))
+        AbortProfiling(cx);
+#endif
     LeaveTrace(cx);
 #endif
 
     /* Always push arguments, regardless of optimized/normal invoke. */
     StackSpace &stack = cx->stack();
     if (!stack.pushInvokeArgs(cx, argc, &args_))
         return false;
 
@@ -2324,21 +2328,31 @@ Interpret(JSContext *cx, JSStackFrame *e
 # define END_VARLEN_CASE    goto advance_pc;
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
 # define END_EMPTY_CASES    goto advance_pc_by_one;
 
 #endif /* !JS_THREADED_INTERP */
 
     /* Check for too deep of a native thread stack. */
 #ifdef JS_TRACER
+#ifdef JS_METHODJIT
+    JS_CHECK_RECURSION(cx, do {
+            if (TRACE_RECORDER(cx))
+                AbortRecording(cx, "too much recursion");
+            if (TRACE_PROFILER(cx))
+                AbortProfiling(cx);
+            return JS_FALSE;
+        } while (0););
+#else
     JS_CHECK_RECURSION(cx, do {
             if (TRACE_RECORDER(cx))
                 AbortRecording(cx, "too much recursion");
             return JS_FALSE;
         } while (0););
+#endif
 #else
     JS_CHECK_RECURSION(cx, return JS_FALSE);
 #endif
 
 #define LOAD_ATOM(PCOFF, atom)                                                \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(regs.fp->hasImacropc()                                      \
                   ? atoms == COMMON_ATOMS_START(&rt->atomState) &&            \
@@ -2391,19 +2405,25 @@ Interpret(JSContext *cx, JSStackFrame *e
     JS_END_MACRO
 
 #define MONITOR_BRANCH()                                                      \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
             MonitorResult r = MonitorLoopEdge(cx, inlineCallCount);           \
             if (r == MONITOR_RECORDING) {                                     \
                 JS_ASSERT(TRACE_RECORDER(cx));                                \
+                JS_ASSERT(!TRACE_PROFILER(cx));                               \
                 MONITOR_BRANCH_TRACEVIS;                                      \
                 ENABLE_INTERRUPTS();                                          \
                 CLEAR_LEAVE_ON_TRACE_POINT();                                 \
+            } else if (r == MONITOR_PROFILING) {                              \
+                JS_ASSERT(TRACE_PROFILER(cx));                                \
+                JS_ASSERT(!TRACE_RECORDER(cx));                               \
+                ENABLE_INTERRUPTS();                                          \
+                CLEAR_LEAVE_ON_TRACE_POINT();                                 \
             }                                                                 \
             RESTORE_INTERP_VARS();                                            \
             JS_ASSERT_IF(cx->throwing, r == MONITOR_ERROR);                   \
             if (r == MONITOR_ERROR)                                           \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
@@ -2427,16 +2447,17 @@ Interpret(JSContext *cx, JSStackFrame *e
 #define TRACE_RECORDER(cx) (false)
 #define TRACE_PROFILER(cx) (false)
 #endif
 
 #if defined(JS_TRACER) && defined(JS_METHODJIT)
 # define LEAVE_ON_SAFE_POINT()                                                \
     do {                                                                      \
         JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx));                  \
+        JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx));                  \
         if (leaveOnSafePoint && !regs.fp->hasImacropc() &&                    \
             script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
             JS_ASSERT(!TRACE_RECORDER(cx));                                   \
             interpReturnOK = true;                                            \
             goto leave_on_safe_point;                                         \
         }                                                                     \
     } while (0)
 #else
@@ -2538,21 +2559,30 @@ Interpret(JSContext *cx, JSStackFrame *e
 #ifdef JS_TRACER
     /*
      * The method JIT may have already initiated a recording, in which case
      * there should already be a valid recorder. Otherwise...
      * we cannot reenter the interpreter while recording.
      */
     if (interpMode == JSINTERP_RECORD) {
         JS_ASSERT(TRACE_RECORDER(cx));
+        JS_ASSERT(!TRACE_PROFILER(cx));
         ENABLE_INTERRUPTS();
+#ifdef JS_METHODJIT
     } else if (interpMode == JSINTERP_PROFILE) {
+        JS_ASSERT(TRACE_PROFILER(cx));
+        JS_ASSERT(!TRACE_RECORDER(cx));
         ENABLE_INTERRUPTS();
+#endif
     } else if (TRACE_RECORDER(cx)) {
         AbortRecording(cx, "attempt to reenter interpreter while recording");
+#ifdef JS_METHODJIT
+    } else if (TRACE_PROFILER(cx)) {
+        AbortProfiling(cx);
+#endif
     }
 
     if (regs.fp->hasImacropc())
         atoms = COMMON_ATOMS_START(&rt->atomState);
 #endif
 
     /* Don't call the script prologue if executing between Method and Trace JIT. */
     if (interpMode == JSINTERP_NORMAL) {
@@ -2617,16 +2647,20 @@ Interpret(JSContext *cx, JSStackFrame *e
 #endif /* !JS_THREADED_INTERP */
     {
         bool moreInterrupts = false;
         JSInterruptHook hook = cx->debugHooks->interruptHook;
         if (hook) {
 #ifdef JS_TRACER
             if (TRACE_RECORDER(cx))
                 AbortRecording(cx, "interrupt hook");
+#ifdef JS_METHODJIT
+            if (TRACE_PROFILER(cx))
+                AbortProfiling(cx);
+#endif
 #endif
             Value rval;
             switch (hook(cx, script, regs.pc, Jsvalify(&rval),
                          cx->debugHooks->interruptHookData)) {
               case JSTRAP_ERROR:
                 goto error;
               case JSTRAP_CONTINUE:
                 break;
@@ -2641,29 +2675,31 @@ Interpret(JSContext *cx, JSStackFrame *e
               default:;
             }
             moreInterrupts = true;
         }
 
 #ifdef JS_TRACER
 #ifdef JS_METHODJIT
         if (LoopProfile *prof = TRACE_PROFILER(cx)) {
+            JS_ASSERT(!TRACE_RECORDER(cx));
             LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
             switch (act) {
                 case LoopProfile::ProfComplete:
                     leaveOnSafePoint = true;
                     LEAVE_ON_SAFE_POINT();
                     break;
                 default:
                     moreInterrupts = true;
                     break;
             }
         }
 #endif
         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
+            JS_ASSERT(!TRACE_PROFILER(cx));
             AbortableRecordingStatus status = tr->monitorRecording(op);
             JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
 
             if (interpMode != JSINTERP_NORMAL) {
                 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
                 switch (status) {
                   case ARECORD_IMACRO_ABORTED:
                   case ARECORD_ABORTED:
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6907,17 +6907,17 @@ LeaveTree(TraceMonitor *tm, TracerState&
     else
         AUDIT(timeoutIntoInterpreter);
 #endif
 
     state.innermost = innermost;
 }
 
 JS_REQUIRES_STACK MonitorResult
-RecordLoopEdge(JSContext* cx, uintN& inlineCallCount)
+RecordLoopEdge(JSContext* cx, uintN& inlineCallCount, bool execOK)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_MONITOR);
 #endif
 
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     /* Is the recorder currently active? */
@@ -7030,16 +7030,19 @@ RecordLoopEdge(JSContext* cx, uintN& inl
         debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n");
         Blacklist((jsbytecode*) f->root->ip);
 #ifdef MOZ_TRACEVIS
         tvso.r = R_MAX_PEERS;
 #endif
         return MONITOR_NOT_RECORDING;
     }
 
+    if (!execOK)
+        return MONITOR_NOT_RECORDING;
+    
     VMSideExit* lr = NULL;
     VMSideExit* innermostNestedGuard = NULL;
 
     if (!ExecuteTree(cx, match, inlineCallCount, &innermostNestedGuard, &lr))
         return MONITOR_ERROR;
 
     if (!lr) {
 #ifdef MOZ_TRACEVIS
@@ -16269,23 +16272,25 @@ RecordTracePoint(JSContext* cx, uintN& i
     if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD))
         return TPA_Error;
 
     JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
-LoopProfile::LoopProfile(JSScript *script, jsbytecode *top, jsbytecode *bottom)
-    : script(script),
+LoopProfile::LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
+    : script(entryfp->script()),
+      entryfp(entryfp),
       top(top),
       bottom(bottom),
       hits(0),
       profiled(false),
       traceOK(false),
+      execOK(false),
       numAllOps(0),
       numSelfOps(0),
       numSelfOpsMult(0),
       branchMultiplier(1),
       shortLoop(false),
       maybeShortLoop(false),
       numInnerLoops(0),
       loopStackDepth(0),
@@ -16298,30 +16303,31 @@ LoopProfile::LoopProfile(JSScript *scrip
 MonitorResult
 LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
     if (cx->regs->pc == top) {
         debug_only_print0(LC_TMProfiler, "Profiling complete (edge)\n");
         decide(cx);
     } else {
         /* Record an inner loop invocation. */
-        JSScript *script = cx->fp()->script();
+        JSStackFrame *fp = cx->fp();
         jsbytecode *pc = cx->regs->pc;
         bool found = false;
 
         /* We started with the most deeply nested one first, since it gets hit most often.*/
         for (int i = int(numInnerLoops)-1; i >= 0; i--) {
-            if (innerLoops[i].script == script && innerLoops[i].top == pc) {
+            if (innerLoops[i].entryfp == fp && innerLoops[i].top == pc) {
                 innerLoops[i].iters++;
                 found = true;
+                break;
             }
         }
 
         if (!found && numInnerLoops < PROFILE_MAX_INNER_LOOPS)
-            innerLoops[numInnerLoops++] = InnerLoop(script, pc, NULL);
+            innerLoops[numInnerLoops++] = InnerLoop(fp, pc, NULL);
     }
 
     return MONITOR_NOT_RECORDING;
 }
 
 
 static const uintN PROFILE_HOTLOOP = 61;
 static const uintN MAX_PROFILE_OPS = 4096;
@@ -16353,42 +16359,42 @@ LookupOrAddProfile(JSContext *cx, TraceM
      * follows. Every time the trace monitor is flushed,
      * |tm->flushEpoch| is incremented. When storing the profile in
      * the IC, we store the current |tm->flushEpoch| along with it.
      * Before pulling a profile out of the IC, we check that its
      * stored epoch is still up-to-date with |tm->flushEpoch|.
      * This ensures that no flush has happened in between.
      */
 
-#if JS_MONOIC
-    if (*traceData && *traceEpoch == tm->flushEpoch) {
-        prof = (LoopProfile *)*traceData;
-    } else {
+    if (traceData) {
+        if (*traceData && *traceEpoch == tm->flushEpoch) {
+            prof = (LoopProfile *)*traceData;
+        } else {
+            jsbytecode* pc = cx->regs->pc;
+            jsbytecode* bottom = GetLoopBottom(cx);
+            if (!bottom)
+                return NULL;
+            prof = new (*tm->dataAlloc) LoopProfile(cx->fp(), pc, bottom);
+            *traceData = prof;
+            *traceEpoch = tm->flushEpoch;
+            tm->loopProfiles->put(pc, prof);
+        }
+    } else {
+        LoopProfileMap &table = *tm->loopProfiles;
         jsbytecode* pc = cx->regs->pc;
-        jsbytecode* bottom = GetLoopBottom(cx);
-        if (!bottom)
-            return NULL;
-        prof = new (*tm->dataAlloc) LoopProfile(cx->fp()->script(), pc, bottom);
-        *traceData = prof;
-        *traceEpoch = tm->flushEpoch;
-        tm->loopProfiles->put(pc, prof);
-    }
-#else
-    LoopProfileMap &table = *tm->loopProfiles;
-    jsbytecode* pc = cx->regs->pc;
-    if (LoopProfileMap::AddPtr p = table.lookupForAdd(pc)) {
-        prof = p->value;
-    } else {
-        jsbytecode* bottom = GetLoopBottom(cx);
-        if (!bottom)
-            return NULL;
-        prof = new (*tm->dataAlloc) LoopProfile(cx->fp()->script(), pc, bottom);
-        table.add(p, pc, prof);
-    }
-#endif
+        if (LoopProfileMap::AddPtr p = table.lookupForAdd(pc)) {
+            prof = p->value;
+        } else {
+            jsbytecode* bottom = GetLoopBottom(cx);
+            if (!bottom)
+                return NULL;
+            prof = new (*tm->dataAlloc) LoopProfile(cx->fp(), pc, bottom);
+            table.add(p, pc, prof);
+        }
+    }
 
     return prof;
 }
 
 JS_REQUIRES_STACK TracePointAction
 MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
                   void** traceData, uintN *traceEpoch)
 {
@@ -16433,66 +16439,65 @@ MonitorTracePoint(JSContext *cx, uintN& 
         return TPA_Error;
 
     JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
 /*
- * Returns true if pc is within the given loop.
- * If we're in a different script, then we must have come from
- * a call instruction within the loop (since we check if we're within
- * the loop before each instruction) so we're still in the loop.
+ * Returns true if pc is within the given loop. Also returns true if
+ * we are in some function called by the loop.
  */
 template<class T>
 static inline bool
-PCWithinLoop(JSScript *script, jsbytecode *pc, T& loop)
-{
-    return script != loop.script || (pc >= loop.top && pc <= loop.bottom);
+PCWithinLoop(JSStackFrame *fp, jsbytecode *pc, T& loop)
+{
+    return fp > loop.entryfp || (fp == loop.entryfp && pc >= loop.top && pc <= loop.bottom);
 }
 
 LoopProfile::ProfileAction
 LoopProfile::profileOperation(JSContext* cx, JSOp op)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     if (profiled) {
         tm->profile = NULL;
         return ProfComplete;
     }
 
     jsbytecode *pc = cx->regs->pc;
-    JSScript *script = cx->fp()->maybeScript();
-
-    if (!PCWithinLoop(script, pc, *this)) {
+    JSStackFrame *fp = cx->fp();
+    JSScript *script = fp->script();
+
+    if (!PCWithinLoop(fp, pc, *this)) {
         debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at %d (line %u)\n",
-                          (int)(cx->regs->pc - cx->fp()->script()->code),
+                          (int)(cx->regs->pc - script->code),
                           js_FramePCToLineNumber(cx, cx->fp()));
         tm->profile->decide(cx);
         tm->profile = NULL;
         return ProfComplete;
     }
 
-    while (loopStackDepth > 0 && !PCWithinLoop(script, pc, loopStack[loopStackDepth-1])) {
+    while (loopStackDepth > 0 && !PCWithinLoop(fp, pc, loopStack[loopStackDepth-1])) {
         debug_only_print0(LC_TMProfiler, "Profiler: Exiting inner loop\n");
         loopStackDepth--;
     }
 
     if (op == JSOP_TRACE || op == JSOP_NOTRACE) {
         if (pc != top && (loopStackDepth == 0 || pc != loopStack[loopStackDepth-1].top)) {
             if (loopStackDepth == PROFILE_MAX_INNER_LOOPS) {
                 debug_only_print0(LC_TMProfiler, "Profiling complete (maxnest)\n");
                 tm->profile->decide(cx);
                 tm->profile = NULL;
                 return ProfComplete;
             }
 
             debug_only_print0(LC_TMProfiler, "Profiler: Entering inner loop\n");
-            loopStack[loopStackDepth++] = InnerLoop(script, pc, GetLoopBottom(cx));
+            loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx));
         }
     }
 
     numAllOps++;
     if (loopStackDepth == 0) {
         numSelfOps++;
         numSelfOpsMult += branchMultiplier;
     }
@@ -16544,42 +16549,41 @@ LoopProfile::profileOperation(JSContext*
                 js::Native native = fun->u.n.native;
                 if (js_IsMathFunction(JS_JSVALIFY_NATIVE(native)))
                     increment(OP_FLOAT);
             }
         }
     }
 
     if (op == JSOP_CALLPROP && loopStackDepth == 0)
-        branchMultiplier *= mjit::GetCallTargetCount(cx->fp()->script(), cx->regs->pc);
+        branchMultiplier *= mjit::GetCallTargetCount(script, cx->regs->pc);
 
     if (numAllOps >= MAX_PROFILE_OPS) {
         debug_only_print0(LC_TMProfiler, "Profiling complete (maxops)\n");
         tm->profile->decide(cx);
         tm->profile = NULL;
         return ProfComplete;
     }
 
     /* These are the places where the interpreter skips over branches. */
     jsbytecode *testPC = cx->regs->pc;
     if (op == JSOP_EQ || op == JSOP_NE || op == JSOP_LT || op == JSOP_GT
         || op == JSOP_LE || op == JSOP_GE || op == JSOP_IN || op == JSOP_MOREITER)
     {
         const JSCodeSpec *cs = &js_CodeSpec[op];
         ptrdiff_t oplen = cs->length;
-        JSScript *script = cx->fp()->script();
         JS_ASSERT(oplen != -1);
 
         if (cx->regs->pc - script->code + oplen < ptrdiff_t(script->length))
             if (cx->regs->pc[oplen] == JSOP_IFEQ || cx->regs->pc[oplen] == JSOP_IFNE)
                 testPC = cx->regs->pc + oplen;
     }
 
     /* Check if we're exiting the loop being profiled. */
-    JSOp testOp = js_GetOpcode(cx, cx->fp()->script(), testPC);
+    JSOp testOp = js_GetOpcode(cx, script, testPC);
     if (testOp == JSOP_IFEQ || testOp == JSOP_IFNE || testOp == JSOP_GOTO
         || testOp == JSOP_AND || testOp == JSOP_OR)
     {
         ptrdiff_t len = GET_JUMP_OFFSET(testPC);
         if (testPC + len == top && (op == JSOP_LT || op == JSOP_LE)) {
             StackValue v = stackAt(-1);
             if (v.hasValue && v.value < 8)
                 shortLoop = true;
@@ -16652,17 +16656,17 @@ LoopProfile::isCompilationExpensive(JSCo
     if (depth == 0)
         return true;
 
     /* Too many ops to compile? */
     if (numSelfOps == MAX_PROFILE_OPS)
         return true;
 
     /* Is the code too branchy? */
-    if (numSelfOpsMult >= numSelfOps*100000)
+    if (numSelfOpsMult > numSelfOps*100000)
         return true;
 
     /* Ensure that inner loops aren't too expensive. */
     for (uintN i=0; i<numInnerLoops; i++) {
         LoopProfile *prof = LookupLoopProfile(cx, innerLoops[i].top);
         if (prof && prof->isCompilationExpensive(cx, depth-1))
             return true;
     }
@@ -16728,29 +16732,29 @@ LoopProfile::decide(JSContext *cx)
     debug_only_printf(LC_TMProfiler, "FEATURE maybeShortLoop %d\n", maybeShortLoop);
     debug_only_printf(LC_TMProfiler, "FEATURE numAllOps %d\n", numAllOps);
     debug_only_printf(LC_TMProfiler, "FEATURE selfOps %d\n", numSelfOps);
     debug_only_printf(LC_TMProfiler, "FEATURE selfOpsMult %g\n", numSelfOpsMult);
 #endif
 
     traceOK = false;
     if (count(OP_RECURSIVE)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: recursive\n");
     } else if (count(OP_EVAL)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: eval\n");
     } else if (numInnerLoops > 3) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: >3 inner loops\n");
     } else if (shortLoop) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: short\n");
     } else if (maybeShortLoop && numInnerLoops < 2) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: maybe short\n");
     } else if (isCompilationExpensive(cx, 4)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: expensive\n");
     } else if (isCompilationUnprofitable(cx, 4)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: unprofitable\n");
     } else {
         uintN goodOps = 0;
 
         /* The tracer handles these ops well because of type specialization. */
         goodOps += count(OP_FLOAT)*10 + count(OP_BIT)*10 + count(OP_INT)*5;
 
         /* The tracer handles these ops well because of inlining. */
         goodOps += (count(OP_CALL) + count(OP_NEW))*20;
@@ -16776,18 +16780,18 @@ LoopProfile::decide(JSContext *cx)
                  * Note that execOK for the inner loop is left unchanged. So even
                  * if we trace the inner loop, we will never call that trace
                  * on its own. We'll only call it from this trace.
                  */
                 prof->profiled = true;
                 prof->traceOK = true;
                 if (IsBlacklisted(loop.top))
                     debug_only_printf(LC_TMProfiler, "Unblacklisting at %d\n",
-                                      js_PCToLineNumber(cx, loop.script, loop.top));
-                Unblacklist(loop.script, loop.top);
+                                      js_PCToLineNumber(cx, prof->script, loop.top));
+                Unblacklist(prof->script, loop.top);
             }
         }
     }
 
     if (!traceOK) {
         debug_only_printf(LC_TMProfiler, "Blacklisting at %d\n", line);
         Blacklist(top);
     }
@@ -16795,37 +16799,60 @@ LoopProfile::decide(JSContext *cx)
     debug_only_print0(LC_TMProfiler, "\n");
 
     execOK = traceOK;
 }
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
+    if (!cx->profilingEnabled || TRACE_RECORDER(cx))
+        return RecordLoopEdge(cx, inlineCallCount, true);
+
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
+
     if (tm->profile)
         return tm->profile->profileLoopEdge(cx, inlineCallCount);
-    else
-        return RecordLoopEdge(cx, inlineCallCount);
+
+    LoopProfile *prof = LookupOrAddProfile(cx, tm, NULL, NULL);
+    if (!prof)
+        return MONITOR_NOT_RECORDING;
+
+    if (prof->hits++ < PROFILE_HOTLOOP)
+        return MONITOR_NOT_RECORDING;
+
+    if (prof->profiled) {
+        if (prof->traceOK)
+            return RecordLoopEdge(cx, inlineCallCount, prof->execOK);
+        return MONITOR_NOT_RECORDING;
+    }
+
+    debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
+                      js_FramePCToLineNumber(cx, cx->fp()));
+
+    tm->profile = prof;
+
+    return MONITOR_PROFILING;
 }
 
 void
 AbortProfiling(JSContext *cx)
 {
     debug_only_print0(LC_TMProfiler, "Profiling complete (aborted)\n");
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
-    tm->profile->numSelfOps = MAX_PROFILE_OPS;
-    tm->profile->decide(cx);
+    tm->profile->profiled = true;
+    tm->profile->traceOK = false;
+    tm->profile->execOK = false;
     tm->profile = NULL;
 }
 
 #else /* JS_METHODJIT */
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
-    return RecordLoopEdge(cx, inlineCallCount);
+    return RecordLoopEdge(cx, inlineCallCount, true);
 }
 
 #endif /* JS_METHODJIT */
 
 } /* namespace js */
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -635,16 +635,17 @@ inline TreeFragment*
 VMFragment::toTreeFragment()
 {
     JS_ASSERT(root == this);
     return static_cast<TreeFragment*>(this);
 }
 
 enum MonitorResult {
     MONITOR_RECORDING,
+    MONITOR_PROFILING,
     MONITOR_NOT_RECORDING,
     MONITOR_ERROR
 };
 
 const uintN PROFILE_MAX_INNER_LOOPS = 8;
 const uintN PROFILE_MAX_STACK = 6;
 
 /*
@@ -667,16 +668,19 @@ public:
         OP_RECURSIVE, // Recursive calls
         OP_TYPED_ARRAY, // Accesses to typed arrays
         OP_LIMIT
     };
 
     /* The script in which the loop header lives. */
     JSScript *script;
 
+    /* The stack frame where we started profiling. Only valid while profiling! */
+    JSStackFrame *entryfp;
+    
     /* The bytecode locations of the loop header and the back edge. */
     jsbytecode *top, *bottom;
 
     /* Number of times we have seen this loop executed; used to decide when to profile. */
     uintN hits;
 
     /* Whether we have run a complete profile of the loop. */
     bool profiled;
@@ -722,23 +726,23 @@ public:
     /* Set to true if the loop may be short (has few iterations at profiling time). */
     bool maybeShortLoop;
 
     /*
      * When we hit a nested loop while profiling, we record where it occurs
      * and how many iterations we execute it.
      */
     struct InnerLoop {
-        JSScript *script;
+        JSStackFrame *entryfp;
         jsbytecode *top, *bottom;
         uintN iters;
 
         InnerLoop() {}
-        InnerLoop(JSScript *script, jsbytecode *top, jsbytecode *bottom)
-            : script(script), top(top), bottom(bottom), iters(0) {}
+        InnerLoop(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
+            : entryfp(entryfp), top(top), bottom(bottom), iters(0) {}
     };
 
     /* These two variables track all the inner loops seen while profiling (up to a limit). */
     InnerLoop innerLoops[PROFILE_MAX_INNER_LOOPS];
     uintN numInnerLoops;
 
     /*
      * These two variables track the loops that we are currently nested
@@ -778,17 +782,17 @@ public:
     inline StackValue stackAt(int pos) {
         pos += sp;
         if (pos >= 0 && uintN(pos) < PROFILE_MAX_STACK)
             return stack[pos];
         else
             return StackValue(false);
     }
     
-    LoopProfile(JSScript *script, jsbytecode *top, jsbytecode *bottom);
+    LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom);
 
     enum ProfileAction {
         ProfContinue,
         ProfComplete
     };
 
     /* These two functions track the instruction mix. */
     inline void increment(OpKind kind)
@@ -1556,17 +1560,17 @@ class TraceRecorder
     friend class AdjustCallerStackTypesVisitor;
     friend class TypeCompatibilityVisitor;
     friend class ImportFrameSlotsVisitor;
     friend class SlotMap;
     friend class DefaultSlotMap;
     friend class DetermineTypesVisitor;
     friend class RecursiveSlotMap;
     friend class UpRecursiveSlotMap;
-    friend MonitorResult RecordLoopEdge(JSContext*, uintN&);
+    friend MonitorResult RecordLoopEdge(JSContext*, uintN&, bool);
     friend TracePointAction RecordTracePoint(JSContext*, uintN &inlineCallCount,
                                              bool *blacklist);
     friend AbortResult AbortRecording(JSContext*, const char*);
     friend class BoxArg;
     friend void TraceMonitor::sweep();
 
   public:
     static bool JS_REQUIRES_STACK