Bug 1072906 - TraceLogger: Part 1: Make it possible to toggle text ids dynamically in Baseline and IonMonkey, r=jandem
authorHannes Verschore <hv1989@gmail.com>
Thu, 20 Nov 2014 17:44:02 +0100
changeset 247498 aa41463b912e5a6c33c3cb92ce361f58fd14fc7f
parent 247497 2d0b50d683201ad827260b454aed1a940775f1cf
child 247499 dd75dcb78dc7e79c1e444afd75d76ef1e627b15c
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1072906
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 1072906 - TraceLogger: Part 1: Make it possible to toggle text ids dynamically in Baseline and IonMonkey, r=jandem
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/shared/BaselineCompiler-shared.cpp
js/src/jit/shared/BaselineCompiler-shared.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/TraceLogging.cpp
js/src/vm/TraceLogging.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -170,25 +170,33 @@ BaselineCompiler::compile()
     }
 
     if (pcEntries.oom())
         return Method_Error;
 
     prologueOffset_.fixup(&masm);
     epilogueOffset_.fixup(&masm);
     spsPushToggleOffset_.fixup(&masm);
+#ifdef JS_TRACE_LOGGING
+    traceLoggerEnterToggleOffset_.fixup(&masm);
+    traceLoggerExitToggleOffset_.fixup(&masm);
+    traceLoggerScriptTextIdOffset_.fixup(&masm);
+#endif
     postDebugPrologueOffset_.fixup(&masm);
 
     // Note: There is an extra entry in the bytecode type map for the search hint, see below.
     size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
 
     mozilla::UniquePtr<BaselineScript, JS::DeletePolicy<BaselineScript> > baselineScript(
         BaselineScript::New(script, prologueOffset_.offset(),
                             epilogueOffset_.offset(),
                             spsPushToggleOffset_.offset(),
+                            traceLoggerEnterToggleOffset_.offset(),
+                            traceLoggerExitToggleOffset_.offset(),
+                            traceLoggerScriptTextIdOffset_.offset(),
                             postDebugPrologueOffset_.offset(),
                             icEntries_.length(),
                             pcMappingIndexEntries.length(),
                             pcEntries.length(),
                             bytecodeTypeMapEntries,
                             yieldOffsets_.length()));
     if (!baselineScript)
         return Method_Error;
@@ -234,16 +242,21 @@ BaselineCompiler::compile()
     // All barriers are emitted off-by-default, toggle them on if needed.
     if (cx->zone()->needsIncrementalBarrier())
         baselineScript->toggleBarriers(true);
 
     // All SPS instrumentation is emitted toggled off.  Toggle them on if needed.
     if (cx->runtime()->spsProfiler.enabled())
         baselineScript->toggleSPS(true);
 
+    if (TraceLogTextIdEnabled(TraceLogger_Scripts))
+        baselineScript->toggleTraceLoggerScripts(cx->runtime(), script, true);
+    if (TraceLogTextIdEnabled(TraceLogger_Engine))
+        baselineScript->toggleTraceLoggerEngine(true);
+
     uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
     types::FillBytecodeTypeMap(script, bytecodeMap);
 
     // The last entry in the last index found, and is used to avoid binary
     // searches for the sought entry when queries are in linear order.
     bytecodeMap[script->nTypeSets()] = 0;
 
     baselineScript->copyYieldEntries(script, yieldOffsets_);
@@ -369,25 +382,18 @@ BaselineCompiler::emitPrologue()
     if (frame.nvars() > 0)
         emitInitializeLocals(frame.nvars(), UndefinedValue());
     if (frame.nlexicals() > 0)
         emitInitializeLocals(frame.nlexicals(), MagicValue(JS_UNINITIALIZED_LEXICAL));
 
     if (needsEarlyStackCheck())
         masm.bind(&earlyStackCheckFailed);
 
-#ifdef JS_TRACE_LOGGING
-    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
-    Register loggerReg = RegisterSet::Volatile().takeGeneral();
-    masm.Push(loggerReg);
-    masm.movePtr(ImmPtr(logger), loggerReg);
-    masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script));
-    masm.tracelogStart(loggerReg, TraceLogger_Baseline);
-    masm.Pop(loggerReg);
-#endif
+    if (!emitTraceLoggerEnter())
+        return false;
 
     // Record the offset of the prologue, because Ion can bailout before
     // the scope chain is initialized.
     prologueOffset_ = CodeOffsetLabel(masm.currentOffset());
 
     // Initialize the scope chain before any operation that may
     // call into the VM and trigger a GC.
     if (!initScopeChain())
@@ -420,23 +426,18 @@ BaselineCompiler::emitEpilogue()
 {
     // Record the offset of the epilogue, so we can do early return from
     // Debugger handlers during on-stack recompile.
     epilogueOffset_ = CodeOffsetLabel(masm.currentOffset());
 
     masm.bind(&return_);
 
 #ifdef JS_TRACE_LOGGING
-    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
-    Register loggerReg = RegisterSet::Volatile().takeGeneral();
-    masm.Push(loggerReg);
-    masm.movePtr(ImmPtr(logger), loggerReg);
-    masm.tracelogStop(loggerReg, TraceLogger_Baseline);
-    masm.tracelogStop(loggerReg, TraceLogger_Scripts);
-    masm.Pop(loggerReg);
+    if (!emitTraceLoggerExit())
+        return false;
 #endif
 
     // Pop SPS frame if necessary
     emitSPSPop();
 
     masm.mov(BaselineFrameReg, BaselineStackReg);
     masm.pop(BaselineFrameReg);
 
@@ -767,16 +768,70 @@ BaselineCompiler::emitDebugTrap()
     icEntry.setReturnOffset(CodeOffsetLabel(masm.currentOffset()));
     if (!icEntries_.append(icEntry))
         return false;
 
     return true;
 }
 
 bool
+BaselineCompiler::emitTraceLoggerEnter()
+{
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    RegisterSet regs = RegisterSet::Volatile();
+    Register loggerReg = regs.takeGeneral();
+    Register scriptReg = regs.takeGeneral();
+
+    Label noTraceLogger;
+    traceLoggerEnterToggleOffset_ = masm.toggledJump(&noTraceLogger);
+
+    masm.Push(loggerReg);
+    masm.Push(scriptReg);
+
+    masm.movePtr(ImmPtr(logger), loggerReg);
+
+    // Script start.
+    traceLoggerScriptTextIdOffset_ =
+        masm.movWithPatch(ImmWord(uintptr_t(TraceLogger_Scripts)), scriptReg);
+    masm.tracelogStart(loggerReg, scriptReg);
+
+    // Engine start.
+    masm.tracelogStart(loggerReg, TraceLogger_Baseline, /* force = */ true);
+
+    masm.Pop(scriptReg);
+    masm.Pop(loggerReg);
+
+    masm.bind(&noTraceLogger);
+
+    return true;
+}
+
+bool
+BaselineCompiler::emitTraceLoggerExit()
+{
+    TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
+    Register loggerReg = RegisterSet::Volatile().takeGeneral();
+
+    Label noTraceLogger;
+    traceLoggerExitToggleOffset_ = masm.toggledJump(&noTraceLogger);
+
+    masm.Push(loggerReg);
+    masm.movePtr(ImmPtr(logger), loggerReg);
+
+    masm.tracelogStop(loggerReg, TraceLogger_Baseline, /* force = */ true);
+    masm.tracelogStop(loggerReg, TraceLogger_Scripts, /* force = */ true);
+
+    masm.Pop(loggerReg);
+
+    masm.bind(&noTraceLogger);
+
+    return true;
+}
+
+bool
 BaselineCompiler::emitSPSPush()
 {
     // Enter the IC, guarded by a toggled jump (initially disabled).
     Label noPush;
     CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush);
     MOZ_ASSERT(frame.numUnsyncedSlots() == 0);
     ICProfiler_Fallback::Compiler compiler(cx);
     if (!emitNonOpIC(compiler.getStub(&stubSpace_)))
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -249,16 +249,18 @@ class BaselineCompiler : public Baseline
     }
 
     bool emitStackCheck(bool earlyCheck=false);
     bool emitInterruptCheck();
     bool emitWarmUpCounterIncrement(bool allowOsr=true);
     bool emitArgumentTypeChecks();
     bool emitDebugPrologue();
     bool emitDebugTrap();
+    bool emitTraceLoggerEnter();
+    bool emitTraceLoggerExit();
     bool emitSPSPush();
     void emitSPSPop();
 
     bool initScopeChain();
 
     void storeValue(const StackValue *source, const Address &dest,
                     const ValueOperand &scratch);
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -37,27 +37,37 @@ PCMappingSlotInfo::ToSlotLocation(const 
         MOZ_ASSERT(stackVal->reg() == R1);
         return SlotInR1;
     }
     MOZ_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
-                               uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
+                               uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                               uint32_t traceLoggerExitToggleOffset,
+                               uint32_t traceLoggerScriptTextIdOffset,
+                               uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
     fallbackStubSpace_(),
     dependentAsmJSModules_(nullptr),
     prologueOffset_(prologueOffset),
     epilogueOffset_(epilogueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
+#ifdef DEBUG
+    traceLoggerScriptsEnabled_(false),
+    traceLoggerEngineEnabled_(false),
+#endif
+    traceLoggerEnterToggleOffset_(traceLoggerEnterToggleOffset),
+    traceLoggerExitToggleOffset_(traceLoggerExitToggleOffset),
+    traceLoggerScriptTextIdOffset_(traceLoggerScriptTextIdOffset),
     postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0)
 { }
 
 static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
 static bool
 CheckFrame(InterpreterFrame *fp)
@@ -336,18 +346,20 @@ jit::CanEnterBaselineMethod(JSContext *c
     }
 
     RootedScript script(cx, state.script());
     return CanEnterBaselineJIT(cx, script, /* osrFrame = */ nullptr);
 };
 
 BaselineScript *
 BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset,
-                    uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset,
-                    size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
+                    uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                    uint32_t traceLoggerExitToggleOffset, uint32_t traceLoggerScriptTextIdOffset,
+                    uint32_t postDebugPrologueOffset, size_t icEntries,
+                    size_t pcMappingIndexEntries, size_t pcMappingSize,
                     size_t bytecodeTypeMapEntries, size_t yieldEntries)
 {
     static const unsigned DataAlignment = sizeof(uintptr_t);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
     size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
     size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t);
     size_t yieldEntriesSize = yieldEntries * sizeof(uintptr_t);
@@ -363,17 +375,19 @@ BaselineScript::New(JSScript *jsscript, 
                         paddedPCMappingSize +
                         paddedBytecodeTypesMapSize +
                         paddedYieldEntriesSize;
 
     BaselineScript *script = jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>(allocBytes);
     if (!script)
         return nullptr;
     new (script) BaselineScript(prologueOffset, epilogueOffset,
-                                spsPushToggleOffset, postDebugPrologueOffset);
+                                spsPushToggleOffset, traceLoggerEnterToggleOffset,
+                                traceLoggerExitToggleOffset, traceLoggerScriptTextIdOffset,
+                                postDebugPrologueOffset);
 
     size_t offsetCursor = sizeof(BaselineScript);
     MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment));
 
     script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
     offsetCursor += paddedICEntriesSize;
 
@@ -891,16 +905,83 @@ BaselineScript::toggleSPS(bool enable)
     else
         Assembler::ToggleToJmp(pushToggleLocation);
 #ifdef DEBUG
     spsOn_ = enable;
 #endif
 }
 
 void
+BaselineScript::toggleTraceLoggerScripts(JSRuntime *runtime, JSScript *script, bool enable)
+{
+    bool engineEnabled = TraceLogTextIdEnabled(TraceLogger_Engine);
+
+    MOZ_ASSERT(enable == !traceLoggerScriptsEnabled_);
+    MOZ_ASSERT(engineEnabled == traceLoggerEngineEnabled_);
+
+    // Patch the logging script textId to be correct.
+    // When logging log the specific textId else the global Scripts textId.
+    TraceLoggerThread *logger = TraceLoggerForMainThread(runtime);
+    uint32_t textId = TraceLogCreateTextId(logger, script);
+    CodeLocationLabel patchLocation(method()->raw() + traceLoggerScriptTextIdOffset_);
+    if (enable) {
+        Assembler::PatchDataWithValueCheck(patchLocation,
+                                           PatchedImmPtr((void *)textId),
+                                           PatchedImmPtr((void *)TraceLogger_Scripts));
+    } else {
+        Assembler::PatchDataWithValueCheck(patchLocation,
+                                           PatchedImmPtr((void *)TraceLogger_Scripts),
+                                           PatchedImmPtr((void *)textId));
+    }
+
+    // Enable/Disable the traceLogger prologue and epilogue.
+    CodeLocationLabel enter(method_, CodeOffsetLabel(traceLoggerEnterToggleOffset_));
+    CodeLocationLabel exit(method_, CodeOffsetLabel(traceLoggerExitToggleOffset_));
+    if (!engineEnabled) {
+        if (enable) {
+            Assembler::ToggleToCmp(enter);
+            Assembler::ToggleToCmp(exit);
+        } else {
+            Assembler::ToggleToJmp(enter);
+            Assembler::ToggleToJmp(exit);
+        }
+    }
+
+#if DEBUG
+    traceLoggerScriptsEnabled_ = enable;
+#endif
+}
+
+void
+BaselineScript::toggleTraceLoggerEngine(bool enable)
+{
+    bool scriptsEnabled = TraceLogTextIdEnabled(TraceLogger_Scripts);
+
+    MOZ_ASSERT(enable == !traceLoggerEngineEnabled_);
+    MOZ_ASSERT(scriptsEnabled == traceLoggerScriptsEnabled_);
+
+    // Enable/Disable the traceLogger prologue and epilogue.
+    CodeLocationLabel enter(method_, CodeOffsetLabel(traceLoggerEnterToggleOffset_));
+    CodeLocationLabel exit(method_, CodeOffsetLabel(traceLoggerExitToggleOffset_));
+    if (!scriptsEnabled) {
+        if (enable) {
+            Assembler::ToggleToCmp(enter);
+            Assembler::ToggleToCmp(exit);
+        } else {
+            Assembler::ToggleToJmp(enter);
+            Assembler::ToggleToJmp(exit);
+        }
+    }
+
+#if DEBUG
+    traceLoggerEngineEnabled_ = enable;
+#endif
+}
+
+void
 BaselineScript::purgeOptimizedStubs(Zone *zone)
 {
     JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
 
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry &entry = icEntry(i);
         if (!entry.hasStub())
             continue;
@@ -997,16 +1078,42 @@ jit::ToggleBaselineSPS(JSRuntime *runtim
             JSScript *script = i.get<JSScript>();
             if (!script->hasBaselineScript())
                 continue;
             script->baselineScript()->toggleSPS(enable);
         }
     }
 }
 
+void
+jit::ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable)
+{
+    for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
+        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (!script->hasBaselineScript())
+                continue;
+            script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable);
+        }
+    }
+}
+
+void
+jit::ToggleBaselineTraceLoggerEngine(JSRuntime *runtime, bool enable)
+{
+    for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
+        for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (!script->hasBaselineScript())
+                continue;
+            script->baselineScript()->toggleTraceLoggerEngine(enable);
+        }
+    }
+}
+
 static void
 MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation)
 {
     for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -139,16 +139,26 @@ struct BaselineScript
     uint32_t epilogueOffset_;
 
     // The offsets for the toggledJump instructions for SPS update ICs.
 #ifdef DEBUG
     mozilla::DebugOnly<bool> spsOn_;
 #endif
     uint32_t spsPushToggleOffset_;
 
+#ifdef JS_TRACE_LOGGING
+#ifdef DEBUG
+    bool traceLoggerScriptsEnabled_;
+    bool traceLoggerEngineEnabled_;
+#endif
+    uint32_t traceLoggerEnterToggleOffset_;
+    uint32_t traceLoggerExitToggleOffset_;
+    uint32_t traceLoggerScriptTextIdOffset_;
+#endif
+
     // Native code offsets right after the debug prologue VM call returns, or
     // would have returned. This offset is recorded even when debug mode is
     // off to aid on-stack debug mode recompilation.
     //
     // We don't need one for the debug epilogue because that always happens
     // right before the epilogue, so we just use the epilogue offset.
     uint32_t postDebugPrologueOffset_;
 
@@ -197,21 +207,25 @@ struct BaselineScript
 
     // For generator scripts, we store the native code address for each yield
     // instruction.
     uint32_t yieldEntriesOffset_;
 
   public:
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
     BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
-                   uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset);
+                   uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                   uint32_t traceLoggerExitToggleOffset, uint32_t traceLoggerScriptTextIdOffset,
+                   uint32_t postDebugPrologueOffset);
 
     static BaselineScript *New(JSScript *jsscript, uint32_t prologueOffset,
                                uint32_t epilogueOffset, uint32_t postDebugPrologueOffset,
-                               uint32_t spsPushToggleOffset, size_t icEntries,
+                               uint32_t spsPushToggleOffset, uint32_t traceLoggerEnterToggleOffset,
+                               uint32_t traceLoggerExitToggleOffset,
+                               uint32_t traceLoggerScriptTextIdOffset, size_t icEntries,
                                size_t pcMappingIndexEntries, size_t pcMappingSize,
                                size_t bytecodeTypeMapEntries, size_t yieldEntries);
 
     static void Trace(JSTracer *trc, BaselineScript *script);
     static void Destroy(FreeOp *fop, BaselineScript *script);
 
     void purgeOptimizedStubs(Zone *zone);
 
@@ -381,16 +395,19 @@ struct BaselineScript
   public:
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
 
     void toggleSPS(bool enable);
 
+    void toggleTraceLoggerScripts(JSRuntime *runtime, JSScript *script, bool enable);
+    void toggleTraceLoggerEngine(bool enable);
+
     void noteAccessedGetter(uint32_t pcOffset);
     void noteArrayWriteHole(uint32_t pcOffset);
 
     static size_t offsetOfFlags() {
         return offsetof(BaselineScript, flags_);
     }
     static size_t offsetOfYieldEntriesOffset() {
         return offsetof(BaselineScript, yieldEntriesOffset_);
@@ -433,16 +450,21 @@ FinishDiscardBaselineScript(FreeOp *fop,
 
 void
 AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size_t *data,
                       size_t *fallbackStubs);
 
 void
 ToggleBaselineSPS(JSRuntime *runtime, bool enable);
 
+void
+ToggleBaselineTraceLoggerScripts(JSRuntime *runtime, bool enable);
+void
+ToggleBaselineTraceLoggerEngine(JSRuntime *runtime, bool enable);
+
 struct BaselineBailoutInfo
 {
     // Pointer into the current C stack, where overwriting will start.
     uint8_t *incomingStack;
 
     // The top and bottom heapspace addresses of the reconstructed stack
     // which will be copied to the bottom.
     uint8_t *copyStackTop;
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1664,79 +1664,75 @@ MacroAssembler::printf(const char *outpu
     passABIArg(value);
     callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf1_));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 #ifdef JS_TRACE_LOGGING
 void
-MacroAssembler::tracelogStart(Register logger, uint32_t textId)
+MacroAssembler::tracelogStart(Register logger, uint32_t textId, bool force)
 {
-    if (!TraceLogTextIdEnabled(textId))
+    if (!force && !TraceLogTextIdEnabled(textId))
         return;
 
-    void (&TraceLogFunc)(TraceLoggerThread *, uint32_t) = TraceLogStartEvent;
-
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEvent));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
 MacroAssembler::tracelogStart(Register logger, Register textId)
 {
-    void (&TraceLogFunc)(TraceLoggerThread *, uint32_t) = TraceLogStartEvent;
-
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStartEvent));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogStop(Register logger, uint32_t textId)
+MacroAssembler::tracelogStop(Register logger, uint32_t textId, bool force)
 {
-    if (!TraceLogTextIdEnabled(textId))
+    if (!force && !TraceLogTextIdEnabled(textId))
         return;
 
     PushRegsInMask(RegisterSet::Volatile());
 
     RegisterSet regs = RegisterSet::Volatile();
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
 
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogEvent));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEvent));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
 MacroAssembler::tracelogStop(Register logger, Register textId)
 {
     PushRegsInMask(RegisterSet::Volatile());
@@ -1745,17 +1741,17 @@ MacroAssembler::tracelogStop(Register lo
 
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
     passABIArg(logger);
     passABIArg(textId);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogEvent));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStopEvent));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 #endif
 
 void
 MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scratch, Label *done)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1202,19 +1202,19 @@ class MacroAssembler : public MacroAssem
 
     void finish();
 
     void assumeUnreachable(const char *output);
     void printf(const char *output);
     void printf(const char *output, Register value);
 
 #ifdef JS_TRACE_LOGGING
-    void tracelogStart(Register logger, uint32_t textId);
+    void tracelogStart(Register logger, uint32_t textId, bool force = false);
     void tracelogStart(Register logger, Register textId);
-    void tracelogStop(Register logger, uint32_t textId);
+    void tracelogStop(Register logger, uint32_t textId, bool force = false);
     void tracelogStop(Register logger, Register textId);
 #endif
 
 #define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2)    \
     MOZ_ASSERT(IsFloatingPointType(type));                              \
     if (type == MIRType_Double)                                         \
         method##Double(arg1d, arg2);                                    \
     else                                                                \
--- a/js/src/jit/shared/BaselineCompiler-shared.cpp
+++ b/js/src/jit/shared/BaselineCompiler-shared.cpp
@@ -25,17 +25,20 @@ BaselineCompilerShared::BaselineCompiler
     analysis_(alloc, script),
     frame(script, masm),
     stubSpace_(),
     icEntries_(),
     pcMappingEntries_(),
     icLoadLabels_(),
     pushedBeforeCall_(0),
     inCall_(false),
-    spsPushToggleOffset_()
+    spsPushToggleOffset_(),
+    traceLoggerEnterToggleOffset_(),
+    traceLoggerExitToggleOffset_(),
+    traceLoggerScriptTextIdOffset_()
 { }
 
 bool
 BaselineCompilerShared::callVM(const VMFunction &fun, CallVMPhase phase)
 {
     JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
     if (!code)
         return false;
--- a/js/src/jit/shared/BaselineCompiler-shared.h
+++ b/js/src/jit/shared/BaselineCompiler-shared.h
@@ -63,16 +63,19 @@ class BaselineCompilerShared
         CodeOffsetLabel label;
     };
     js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;
 
     uint32_t pushedBeforeCall_;
     mozilla::DebugOnly<bool> inCall_;
 
     CodeOffsetLabel spsPushToggleOffset_;
+    CodeOffsetLabel traceLoggerEnterToggleOffset_;
+    CodeOffsetLabel traceLoggerExitToggleOffset_;
+    CodeOffsetLabel traceLoggerScriptTextIdOffset_;
 
     BaselineCompilerShared(JSContext *cx, TempAllocator &alloc, JSScript *script);
 
     ICEntry *allocateICEntry(ICStub *stub, ICEntry::Kind kind) {
         if (!stub)
             return nullptr;
 
         // Create the entry and add it to the vector.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -21,16 +21,17 @@
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "js/GCAPI.h"
 #include "js/UbiNodeTraverse.h"
 #include "js/Vector.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/SPSProfiler.h"
+#include "vm/TraceLogging.h"
 #include "vm/WrapperObject.h"
 
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
@@ -3751,16 +3752,36 @@ Debugger::makeGlobalObjectReference(JSCo
     Rooted<GlobalObject *> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
     if (!global)
         return false;
 
     args.rval().setObject(*global);
     return dbg->wrapDebuggeeValue(cx, args.rval());
 }
 
+bool
+Debugger::enableTraceItem(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGGER(cx, argc, vp, "enableTraceItem", args, dbg);
+    if (!args.requireAtLeast(cx, "Debugger.enableTraceItem", 1))
+        return false;
+
+    uint32_t id = args[0].toInt32();
+
+    if (!TLTextIdIsToggable(id)) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    TraceLogEnableTextId(cx, id);
+
+    args.rval().setBoolean(true);
+    return true;
+}
+
 const JSPropertySpec Debugger::properties[] = {
     JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
     JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
             Debugger::setOnDebuggerStatement, 0),
     JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
             Debugger::setOnExceptionUnwind, 0),
     JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
     JS_PSGS("onNewPromise", Debugger::getOnNewPromise, Debugger::setOnNewPromise, 0),
@@ -3780,16 +3801,17 @@ const JSFunctionSpec Debugger::methods[]
     JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
     JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
     JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
     JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 0, 0),
     JS_FN("findScripts", Debugger::findScripts, 1, 0),
     JS_FN("findObjects", Debugger::findObjects, 1, 0),
     JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
     JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),
+    JS_FN("enableTraceItem", Debugger::enableTraceItem, 1, 0),
     JS_FS_END
 };
 
 
 /*** Debugger.Script *****************************************************************************/
 
 static inline JSScript *
 GetScriptReferent(JSObject *obj)
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -400,16 +400,17 @@ class Debugger : private mozilla::Linked
     static bool hasDebuggee(JSContext *cx, unsigned argc, Value *vp);
     static bool getDebuggees(JSContext *cx, unsigned argc, Value *vp);
     static bool getNewestFrame(JSContext *cx, unsigned argc, Value *vp);
     static bool clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp);
     static bool findScripts(JSContext *cx, unsigned argc, Value *vp);
     static bool findObjects(JSContext *cx, unsigned argc, Value *vp);
     static bool findAllGlobals(JSContext *cx, unsigned argc, Value *vp);
     static bool makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp);
+    static bool enableTraceItem(JSContext *cx, unsigned argc, Value *vp);
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
 
     static bool getNewestAbstractFramePtr(JSContext *cx);
     static bool updateExecutionObservabilityOfFrames(JSContext *cx, const ExecutionObservableSet &obs,
                                                      IsObserving observing);
     static bool updateExecutionObservabilityOfScripts(JSContext *cx, const ExecutionObservableSet &obs,
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/DebugOnly.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsprf.h"
 #include "jsscript.h"
 
+#include "jit/BaselineJIT.h"
 #include "jit/CompileWrappers.h"
 #include "vm/Runtime.h"
 #include "vm/TraceLoggingGraph.h"
 
 #include "jit/JitFrames-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -573,16 +574,63 @@ TraceLoggerThreadState::lazyInit()
            graphSpewingEnabled = true;
     }
 
     startupTime = rdtsc();
     enabled = 1;
     return true;
 }
 
+void
+TraceLoggerThreadState::enableTextId(JSContext *cx, uint32_t textId)
+{
+    MOZ_ASSERT(TLTextIdIsToggable(textId));
+
+    if (enabledTextIds[textId])
+        return;
+
+    enabledTextIds[textId] = true;
+    if (textId == TraceLogger_Engine) {
+        enabledTextIds[TraceLogger_IonMonkey] = true;
+        enabledTextIds[TraceLogger_Baseline] = true;
+        enabledTextIds[TraceLogger_Interpreter] = true;
+    }
+
+    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
+    if (textId == TraceLogger_Scripts)
+        jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), true);
+    if (textId == TraceLogger_Engine)
+        jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), true);
+
+}
+void
+TraceLoggerThreadState::disableTextId(JSContext *cx, uint32_t textId)
+{
+    MOZ_ASSERT(TLTextIdIsToggable(textId));
+
+    if (!enabledTextIds[textId])
+        return;
+
+    enabledTextIds[textId] = false;
+    if (textId == TraceLogger_Engine) {
+        enabledTextIds[TraceLogger_IonMonkey] = false;
+        enabledTextIds[TraceLogger_Baseline] = false;
+        enabledTextIds[TraceLogger_Interpreter] = false;
+    }
+
+    ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
+    if (textId == TraceLogger_Scripts)
+        jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), false);
+    if (textId == TraceLogger_Engine)
+        jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), false);
+}
+
+
 TraceLoggerThread *
 js::TraceLoggerForMainThread(CompileRuntime *runtime)
 {
     return traceLoggers.forMainThread(runtime);
 }
 
 TraceLoggerThread *
 TraceLoggerThreadState::forMainThread(CompileRuntime *runtime)
@@ -684,8 +732,19 @@ TraceLoggerThreadState::create()
     return logger;
 }
 
 bool
 js::TraceLogTextIdEnabled(uint32_t textId)
 {
     return traceLoggers.isTextIdEnabled(textId);
 }
+
+void
+js::TraceLogEnableTextId(JSContext *cx, uint32_t textId)
+{
+    traceLoggers.enableTextId(cx, textId);
+}
+void
+js::TraceLogDisableTextId(JSContext *cx, uint32_t textId)
+{
+    traceLoggers.disableTextId(cx, textId);
+}
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -166,16 +166,18 @@ class TraceLoggerThreadState
     TraceLoggerThread *forMainThread(jit::CompileRuntime *runtime);
     TraceLoggerThread *forThread(PRThread *thread);
 
     bool isTextIdEnabled(uint32_t textId) {
         if (textId < TraceLogger_Last)
             return enabledTextIds[textId];
         return true;
     }
+    void enableTextId(JSContext *cx, uint32_t textId);
+    void disableTextId(JSContext *cx, uint32_t textId);
 
   private:
     TraceLoggerThread *forMainThread(PerThreadData *mainThread);
     TraceLoggerThread *create();
     bool lazyInit();
 #endif
 };
 
@@ -237,20 +239,24 @@ inline uint32_t TraceLogCreateTextId(Tra
 #ifdef JS_TRACE_LOGGING
     if (logger)
         return logger->createTextId(text);
 #endif
     return TraceLogger_Error;
 }
 #ifdef JS_TRACE_LOGGING
 bool TraceLogTextIdEnabled(uint32_t textId);
+void TraceLogEnableTextId(JSContext *cx, uint32_t textId);
+void TraceLogDisableTextId(JSContext *cx, uint32_t textId);
 #else
 inline bool TraceLogTextIdEnabled(uint32_t textId) {
     return false;
 }
+inline void TraceLogEnableTextId(JSContext *cx, uint32_t textId) {}
+inline void TraceLogDisableTextId(JSContext *cx, uint32_t textId) {}
 #endif
 inline void TraceLogTimestamp(TraceLoggerThread *logger, uint32_t textId) {
 #ifdef JS_TRACE_LOGGING
     if (logger)
         logger->logTimestamp(textId);
 #endif
 }
 inline void TraceLogStartEvent(TraceLoggerThread *logger, uint32_t textId) {