Bug 607539 - Allow profiler to be run from interpreter (r=dmandelin,dvander)
☠☠ backed out by 80842d45466c ☠ ☠
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 17 Nov 2010 12:46:44 -0800
changeset 57837 08794e076dedf23252ac6c7f03c2cd1f5a896d0f
parent 57836 7a0bb2b04bbd9979eb8b6fdf1b6747579b711354
child 57838 78a42f77bb90c49b066023e740fc05f7c863ab37
child 57880 80842d45466cceced04ab032c07bd85bffe5f99d
child 58038 30c864b404833da3c08199a7f703c87a7b4dadd7
child 58054 1fa547033cec06f52553db88c7dc69dea31121ee
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersdmandelin, dvander
bugs607539
milestone2.0b8pre
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
@@ -2269,17 +2269,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
@@ -744,16 +744,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;
 
@@ -2332,21 +2336,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) &&            \
@@ -2399,19 +2413,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
 
@@ -2435,16 +2455,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
@@ -2546,21 +2567,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) {
@@ -2625,16 +2655,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;
@@ -2649,29 +2683,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
@@ -2407,16 +2407,17 @@ TraceMonitor::outOfMemory() const
 
 /*
  * This function destroys the recorder after a successful recording, possibly
  * starting a suspended outer recorder.
  */
 AbortableRecordingStatus
 TraceRecorder::finishSuccessfully()
 {
+    JS_ASSERT(!traceMonitor->profile);
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(fragment->lastIns && fragment->code());
 
     AUDIT(traceCompleted);
     mark.commit();
 
     /* Grab local copies of members needed after |delete this|. */
     JSContext* localcx = cx;
@@ -2432,16 +2433,17 @@ TraceRecorder::finishSuccessfully()
     }
     return ARECORD_COMPLETED;
 }
 
 /* This function aborts a recorder and any pending outer recorders. */
 JS_REQUIRES_STACK TraceRecorder::AbortResult
 TraceRecorder::finishAbort(const char* reason)
 {
+    JS_ASSERT(!traceMonitor->profile);
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(!fragment->code());
 
     AUDIT(recorderAborted);
 #ifdef DEBUG
     debug_only_printf(LC_TMMinimal,
                       "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
                       tree->treeFileName,
@@ -2702,16 +2704,17 @@ ValueToNative(const Value &v, JSValueTyp
 #endif
 }
 
 void
 TraceMonitor::flush()
 {
     /* flush should only be called after all recorders have been aborted. */
     JS_ASSERT(!recorder);
+    JS_ASSERT(!profile);
     AUDIT(cacheFlushed);
 
     // recover profiling data from expiring Fragments
     verbose_only(
         for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
             for (TreeFragment *f = vmfragments[i]; f; f = f->next) {
                 JS_ASSERT(f->root == f);
                 for (TreeFragment *p = f; p; p = p->peer)
@@ -4395,16 +4398,20 @@ ResetJITImpl(JSContext* cx)
     if (!cx->traceJitEnabled)
         return;
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     debug_only_print0(LC_TMTracer, "Flushing cache.\n");
     if (tm->recorder) {
         JS_ASSERT_NOT_ON_TRACE(cx);
         AbortRecording(cx, "flush cache");
     }
+#if JS_METHODJIT
+    if (tm->profile)
+        AbortProfiling(cx);
+#endif
     if (ProhibitFlush(cx)) {
         debug_only_print0(LC_TMTracer, "Deferring JIT flush due to deep bail.\n");
         tm->needFlush = JS_TRUE;
         return;
     }
     tm->flush();
 }
 
@@ -5493,16 +5500,17 @@ CheckGlobalObjectShape(JSContext* cx, Tr
 bool JS_REQUIRES_STACK
 TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f,
                              unsigned stackSlots, unsigned ngslots,
                              JSValueType* typeMap, VMSideExit* expectedInnerExit,
                              JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc,
                              bool speculate)
 {
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
+    JS_ASSERT(!tm->profile);
     JS_ASSERT(!tm->needFlush);
     JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
 
     tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap,
                                      expectedInnerExit, outerScript, outerPC, outerArgc,
                                      speculate);
 
     if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(tm)) {
@@ -6387,16 +6395,19 @@ TracerState::~TracerState()
     JS_TRACE_MONITOR(cx).tracecx = NULL;
 }
 
 /* Call |f|, return the exit taken. */
 static JS_ALWAYS_INLINE VMSideExit*
 ExecuteTrace(JSContext* cx, Fragment* f, TracerState& state)
 {
     JS_ASSERT(!cx->bailExit);
+#ifdef JS_METHODJIT
+    JS_ASSERT(!TRACE_PROFILER(cx));
+#endif
     union { NIns *code; GuardRecord* (FASTCALL *func)(TracerState*); } u;
     u.code = f->code();
     GuardRecord* rec;
 #if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
     SIMULATE_FASTCALL(rec, state, NULL, u.func);
 #else
     rec = u.func(&state);
 #endif
@@ -6454,17 +6465,18 @@ static JS_REQUIRES_STACK bool
 ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount,
             VMSideExit** innermostNestedGuardp, VMSideExit **lrp)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_EXECUTE);
 #endif
     JS_ASSERT(f->root == f && f->code());
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-
+    JS_ASSERT(!tm->profile);
+    
     if (!ScopeChainCheck(cx, f) || !cx->stack().ensureEnoughSpaceToEnterTrace() ||
         inlineCallCount + f->maxCallDepth > JS_MAX_INLINE_CALL_COUNT) {
         *lrp = NULL;
         return true;
     }
 
     /* Make sure the global object is sane. */
     JS_ASSERT(f->globalObj->numSlots() <= MAX_GLOBAL_SLOTS);
@@ -6918,24 +6930,26 @@ TraceRecorder::assertInsideLoop()
      * immediately preceeding a loop (the one that jumps to the loop
      * condition).
      */
     JS_ASSERT(pc >= beg - JSOP_GOTO_LENGTH && pc <= end);
 #endif
 }
 
 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);
 
+    JS_ASSERT(!tm->profile);
+
     /* Is the recorder currently active? */
     if (tm->recorder) {
         tm->recorder->assertInsideLoop();
         jsbytecode* pc = cx->regs->pc;
         if (pc == tm->recorder->tree->ip) {
             tm->recorder->closeLoop();
         } else {
             MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount);
@@ -7041,16 +7055,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
@@ -7135,16 +7152,17 @@ RecordLoopEdge(JSContext* cx, uintN& inl
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::monitorRecording(JSOp op)
 {
     TraceMonitor &localtm = JS_TRACE_MONITOR(cx);
     debug_only_stmt( JSContext *localcx = cx; )
     assertInsideLoop();
+    JS_ASSERT(!localtm.profile);
 
     /* Process needFlush requests now. */
     if (localtm.needFlush) {
         ResetJIT(cx, FR_DEEP_BAIL);
         return ARECORD_ABORTED;
     }
     JS_ASSERT(!fragment->lastIns);
 
@@ -7620,16 +7638,17 @@ InitJIT(TraceMonitor *tm)
 #endif
 #endif
 }
 
 void
 FinishJIT(TraceMonitor *tm)
 {
     JS_ASSERT(!tm->recorder);
+    JS_ASSERT(!tm->profile);
 
 #ifdef JS_JIT_SPEW
     if (jitstats.recorderStarted) {
         char sep = ':';
         debug_only_print0(LC_TMStats, "recorder");
 #define RECORDER_JITSTAT(_ident, _name)                             \
         debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep,    \
                           (unsigned long long int)jitstats._ident); \
@@ -16202,16 +16221,17 @@ class AutoRetBlacklist
 JS_REQUIRES_STACK TracePointAction
 RecordTracePoint(JSContext* cx, uintN& inlineCallCount, bool* blacklist, bool execAllowed)
 {
     JSStackFrame* fp = cx->fp();
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     jsbytecode* pc = cx->regs->pc;
 
     JS_ASSERT(!TRACE_RECORDER(cx));
+    JS_ASSERT(!tm->profile);
 
     JSObject* globalObj = cx->fp()->scopeChain().getGlobal();
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
 
     AutoRetBlacklist autoRetBlacklist(pc, blacklist);
 
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
@@ -16303,23 +16323,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),
@@ -16332,30 +16354,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;
@@ -16382,42 +16405,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)
 {
@@ -16448,80 +16471,80 @@ MonitorTracePoint(JSContext *cx, uintN& 
     if (prof->profiled) {
         if (prof->traceOK) {
             return RecordTracePoint(cx, inlineCallCount, blacklist, prof->execOK);
         } else {
             return TPA_Nothing;
         }
     }
 
-    debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
+    debug_only_printf(LC_TMProfiler, "Profiling at line %d from methodjit\n",
                       js_FramePCToLineNumber(cx, cx->fp()));
 
+    prof->entryfp = cx->fp();
     tm->profile = prof;
 
     if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
         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;
     }
@@ -16573,17 +16596,17 @@ 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 (op == JSOP_TABLESWITCH) {
         jsint low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN);
         jsint high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN*2);
         branchMultiplier *= high - low + 1;
     }
 
     if (op == JSOP_LOOKUPSWITCH)
@@ -16607,17 +16630,17 @@ LoopProfile::profileOperation(JSContext*
         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;
@@ -16655,16 +16678,25 @@ LoopProfile::profileOperation(JSContext*
     } else if (op == JSOP_AND) {
         bool b = !!js_ValueToBoolean(cx->regs->sp[-1]);
         StackValue v = stackAt(-1);
         if (b)
             stackPop();
     } else {
         stackClear();
     }
+
+    if (count(OP_RECURSIVE) || count(OP_EVAL)) {
+        debug_only_printf(LC_TMProfiler, "Profiling complete (early exit) at %d (line %u)\n",
+                          (int)(cx->regs->pc - script->code),
+                          js_FramePCToLineNumber(cx, cx->fp()));
+        tm->profile->decide(cx);
+        tm->profile = NULL;
+        return ProfComplete;
+    }
     
     return ProfContinue;
 }
 
 static LoopProfile *
 LookupLoopProfile(JSContext *cx, jsbytecode *pc)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
@@ -16690,17 +16722,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;
     }
@@ -16765,30 +16797,34 @@ LoopProfile::decide(JSContext *cx)
     debug_only_printf(LC_TMProfiler, "FEATURE shortLoop %d\n", shortLoop);
     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;
+    /*
+     * The first two cases are "early exits"; if these change, need
+     * to change profileOperation too.
+    */
     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;
@@ -16833,37 +16869,62 @@ 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 from interpreter\n",
+                      js_FramePCToLineNumber(cx, cx->fp()));
+
+    prof->entryfp = 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;
+    Blacklist(tm->profile->top);
     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,24 @@ 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 {
+        JSStackFrame *entryfp;
         JSScript *script;
         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), script(entryfp->script()), 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 +783,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)
@@ -1555,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