Bug 944392 - Tracelogging: Land version 0.2, r=till,bbouvier
authorHannes Verschore <hv1989@gmail.com>
Wed, 09 Apr 2014 14:20:39 +0200
changeset 196327 07c0cf63729079be8b32e1a9361c203e2f435291
parent 196326 a745d6526479ffe7612a6816793bcf909cd3aeec
child 196328 b32aa010888689e203fddc997e555325f6a5dd2d
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, bbouvier
bugs944392
milestone31.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 944392 - Tracelogging: Land version 0.2, r=till,bbouvier
js/src/TraceLogging.cpp
js/src/TraceLogging.h
js/src/frontend/BytecodeCompiler.cpp
js/src/jit/Bailouts.cpp
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-shared.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jsgc.cpp
js/src/jsworkers.cpp
js/src/shell/js.cpp
js/src/vm/Interpreter.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/yarr/YarrJIT.h
--- a/js/src/TraceLogging.cpp
+++ b/js/src/TraceLogging.cpp
@@ -1,25 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TraceLogging.h"
 
-#include <cstdarg>
-#include <cstdio>
-#include <cstdlib>
-#include <stdint.h>
 #include <string.h>
-#include <unistd.h>
 
+#include "jsapi.h"
 #include "jsscript.h"
 
+#include "vm/Runtime.h"
+
 using namespace js;
 
 #ifndef TRACE_LOG_DIR
 # if defined(_WIN32)
 #  define TRACE_LOG_DIR ""
 # else
 #  define TRACE_LOG_DIR "/tmp/"
 # endif
@@ -59,244 +57,470 @@ rdtsc(void)
     result = upper;
     result = result<<32;
     result = result|lower;
 
     return(result);
 }
 #endif
 
-const char* const TraceLogging::typeName[] = {
-    "1,s",  // start script
-    "0,s",  // stop script
-    "1,c",  // start ion compilation
-    "0,c",  // stop ion compilation
-    "1,r",  // start regexp JIT execution
-    "0,r",  // stop regexp JIT execution
-    "1,G",  // start major GC
-    "0,G",  // stop major GC
-    "1,g",  // start minor GC
-    "0,g",  // stop minor GC
-    "1,gS", // start GC sweeping
-    "0,gS", // stop GC sweeping
-    "1,gA", // start GC allocating
-    "0,gA", // stop GC allocating
-    "1,ps", // start script parsing
-    "0,ps", // stop script parsing
-    "1,pl", // start lazy parsing
-    "0,pl", // stop lazy parsing
-    "1,pf", // start Function parsing
-    "0,pf", // stop Function parsing
-    "e,i",  // engine interpreter
-    "e,b",  // engine baseline
-    "e,o"   // engine ionmonkey
+TraceLogging traceLoggers;
+
+// The text that will get logged for eagerly created logged text.
+// When adding/removing something here, you must update the enum
+// Tracelogger::TextId in TraceLogging.h too.
+const char* const text[] = {
+    "TraceLogger failed to process text",
+    "Bailout",
+    "Baseline",
+    "GC",
+    "GCAllocating",
+    "GCSweeping",
+    "Interpreter",
+    "Invalidation",
+    "IonCompile",
+    "IonLink",
+    "IonMonkey",
+    "MinorGC",
+    "ParserCompileFunction",
+    "ParserCompileLazy",
+    "ParserCompileScript",
+    "TraceLogger",
+    "YarrCompile",
+    "YarrInterpret",
+    "YarrJIT"
 };
-TraceLogging* TraceLogging::loggers[] = {nullptr, nullptr, nullptr};
-bool TraceLogging::atexitSet = false;
-uint64_t TraceLogging::startupTime = 0;
+
+TraceLogger::TraceLogger()
+ : enabled(false),
+   nextTextId(0),
+   top(nullptr)
+{ }
+
+bool
+TraceLogger::init(uint32_t loggerId)
+{
+    if (!pointerMap.init())
+        return false;
+    if (!tree.init())
+        return false;
+    if (!stack.init())
+        return false;
+    if (!events.init())
+        return false;
+
+    JS_ASSERT(loggerId <= 999);
+
+    char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.100.json"];
+    sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%d.json", loggerId);
+    dictFile = fopen(dictFilename, "w");
+    if (!dictFile)
+        return false;
+
+    char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.100.tl"];
+    sprintf(treeFilename, TRACE_LOG_DIR "tl-tree.%d.tl", loggerId);
+    treeFile = fopen(treeFilename, "wb");
+    if (!treeFile) {
+        fclose(dictFile);
+        dictFile = nullptr;
+        return false;
+    }
+
+    char eventFilename[sizeof TRACE_LOG_DIR "tl-event.100.tl"];
+    sprintf(eventFilename, TRACE_LOG_DIR "tl-event.%d.tl", loggerId);
+    eventFile = fopen(eventFilename, "wb");
+    if (!eventFile) {
+        fclose(dictFile);
+        fclose(treeFile);
+        dictFile = nullptr;
+        treeFile = nullptr;
+        return false;
+    }
+
+    uint64_t start = rdtsc() - traceLoggers.startupTime;
+
+    TreeEntry &treeEntry = tree.pushUninitialized();
+    treeEntry.start = start;
+    treeEntry.stop = 0;
+    treeEntry.u.s.textId = 0;
+    treeEntry.u.s.hasChildren = false;
+    treeEntry.nextId = 0;
+
+    StackEntry &stackEntry = stack.pushUninitialized();
+    stackEntry.treeId = 0;
+    stackEntry.lastChildId = 0;
+
+    int written = fprintf(dictFile, "[");
+    if (written < 0)
+        fprintf(stderr, "TraceLogging: Error while writing.\n");
+
+    // Eagerly create the default textIds, to match their Tracelogger::TextId.
+    for (uint32_t i = 0; i < LAST; i++) {
+        uint32_t textId = createTextId(text[i]);
+        JS_ASSERT(textId == i);
+    }
+
+    enabled = true;
+    return true;
+}
+
+TraceLogger::~TraceLogger()
+{
+    // Write dictionary to disk
+    if (dictFile) {
+        int written = fprintf(dictFile, "]");
+        if (written < 0)
+            fprintf(stderr, "TraceLogging: Error while writing.\n");
+        fclose(dictFile);
+
+        dictFile = nullptr;
+    }
+
+    // Write tree of logged events to disk.
+    if (treeFile) {
+        // Make sure every start entry has a corresponding stop value.
+        // We temporary enable logging for this. Stop doesn't need any extra data,
+        // so is safe to do, even when we encountered OOM.
+        enabled = true;
+        while (stack.size() > 0)
+            stopEvent();
+        enabled = false;
+
+        // Format data in big endian.
+        for (uint32_t i = 0; i < tree.size(); i++) {
+            tree[i].start = htobe64(tree[i].start);
+            tree[i].stop = htobe64(tree[i].stop);
+            tree[i].u.value = htobe32((tree[i].u.s.textId << 1) + tree[i].u.s.hasChildren);
+            tree[i].nextId = htobe32(tree[i].nextId);
+        }
+
+        size_t bytesWritten = fwrite(tree.data(), sizeof(TreeEntry), tree.size(), treeFile);
+        if (bytesWritten < tree.size())
+            fprintf(stderr, "TraceLogging: Couldn't write the full tree to disk.\n");
+        tree.clear();
+        fclose(treeFile);
+
+        treeFile = nullptr;
+    }
+
+    // Write details for all log entries to disk.
+    if (eventFile) {
+        // Format data in big endian
+        for (uint32_t i = 0; i < events.size(); i++) {
+            events[i].time = htobe64(events[i].time);
+            events[i].textId = htobe64(events[i].textId);
+        }
+
+        size_t bytesWritten = fwrite(events.data(), sizeof(EventEntry), events.size(), eventFile);
+        if (bytesWritten < events.size())
+            fprintf(stderr, "TraceLogging: Couldn't write all event entries to disk.\n");
+        events.clear();
+        fclose(eventFile);
+
+        eventFile = nullptr;
+    }
+}
+
+uint32_t
+TraceLogger::createTextId(const char *text)
+{
+    assertNoQuotes(text);
+
+    PointerHashMap::AddPtr p = pointerMap.lookupForAdd((const void *)text);
+    if (p)
+        return p->value();
+
+    uint32_t textId = nextTextId++;
+    if (!pointerMap.add(p, text, textId))
+        return TraceLogger::TL_Error;
+
+    int written;
+    if (textId > 0)
+        written = fprintf(dictFile, ",\n\"%s\"", text);
+    else
+        written = fprintf(dictFile, "\"%s\"", text);
+
+    if (written < 0)
+        return TraceLogger::TL_Error;
 
-TraceLogging::TraceLogging(Logger id)
-  : nextTextId(1),
-    entries(nullptr),
-    curEntry(0),
-    numEntries(1000000),
-    fileno(0),
-    out(nullptr),
-    id(id)
+    return textId;
+}
+
+uint32_t
+TraceLogger::createTextId(JSScript *script)
+{
+    assertNoQuotes(script->filename());
+
+    PointerHashMap::AddPtr p = pointerMap.lookupForAdd(script);
+    if (p)
+        return p->value();
+
+    uint32_t textId = nextTextId++;
+    if (!pointerMap.add(p, script, textId))
+        return TraceLogger::TL_Error;
+
+    int written;
+    if (textId > 0) {
+        written = fprintf(dictFile, ",\n\"script %s:%d:%d\"", script->filename(),
+                          script->lineno(), script->column());
+    } else {
+        written = fprintf(dictFile, "\"script %s:%d:%d\"", script->filename(),
+                          script->lineno(), script->column());
+    }
+
+    if (written < 0)
+        return TraceLogger::TL_Error;
+
+    return textId;
+}
+
+uint32_t
+TraceLogger::createTextId(const JS::ReadOnlyCompileOptions &compileOptions)
+{
+    assertNoQuotes(compileOptions.filename());
+
+    PointerHashMap::AddPtr p = pointerMap.lookupForAdd(&compileOptions);
+    if (p)
+        return p->value();
+
+    uint32_t textId = nextTextId++;
+    if (!pointerMap.add(p, &compileOptions, textId))
+        return TraceLogger::TL_Error;
+
+    int written;
+    if (textId > 0) {
+        written = fprintf(dictFile, ",\n\"script %s:%d:%d\"", compileOptions.filename(),
+                          compileOptions.lineno, compileOptions.column);
+    } else {
+        written = fprintf(dictFile, "\"script %s:%d:%d\"", compileOptions.filename(),
+                          compileOptions.lineno, compileOptions.column);
+    }
+
+    if (written < 0)
+        return TraceLogger::TL_Error;
+
+    return textId;
+}
+
+void
+TraceLogger::logTimestamp(uint32_t id)
 {
-    textMap.init();
+    if (!enabled)
+        return;
+
+    if (!events.ensureSpaceBeforeAdd()) {
+        fprintf(stderr, "TraceLogging: Disabled a tracelogger due to OOM.\n");
+        enabled = false;
+        return;
+    }
+
+    uint64_t time = rdtsc() - traceLoggers.startupTime;
+
+    EventEntry &entry = events.pushUninitialized();
+    entry.time = time;
+    entry.textId = id;
+}
+
+void
+TraceLogger::startEvent(uint32_t id)
+{
+    if (!enabled)
+        return;
+
+    if (!tree.ensureSpaceBeforeAdd() || !stack.ensureSpaceBeforeAdd()) {
+        fprintf(stderr, "TraceLogging: Disabled a tracelogger due to OOM.\n");
+        enabled = false;
+        return;
+    }
+
+    uint64_t start = rdtsc() - traceLoggers.startupTime;
+
+    // Patch up the tree to be correct. There are two scenarios:
+    // 1) Parent has no children yet. So update parent to include children.
+    // 2) Parent has already children. Update last child to link to the new
+    //    child.
+    StackEntry &parent = stack.current();
+    if (parent.lastChildId == 0) {
+        JS_ASSERT(tree[parent.treeId].u.s.hasChildren == 0);
+        JS_ASSERT(parent.treeId == tree.currentId());
+
+        tree[parent.treeId].u.s.hasChildren = 1;
+    } else {
+        JS_ASSERT(tree[parent.treeId].u.s.hasChildren == 1);
+
+        tree[parent.lastChildId].nextId = tree.nextId();
+    }
+
+    // Add a new tree entry.
+    TreeEntry &treeEntry = tree.pushUninitialized();
+    treeEntry.start = start;
+    treeEntry.stop = 0;
+    treeEntry.u.s.textId = id;
+    treeEntry.u.s.hasChildren = false;
+    treeEntry.nextId = 0;
+
+    // Add a new stack entry.
+    StackEntry &stackEntry = stack.pushUninitialized();
+    stackEntry.treeId = tree.currentId();
+    stackEntry.lastChildId = 0;
+
+    // Set the last child of the parent to this newly added entry.
+    parent.lastChildId = tree.currentId();
+}
+
+void
+TraceLogger::stopEvent(uint32_t id)
+{
+    MOZ_ASSERT_IF(enabled, tree[stack.current().treeId].u.s.textId == id);
+    stopEvent();
+}
+
+void
+TraceLogger::stopEvent()
+{
+    if (!enabled)
+        return;
+
+    uint64_t stop = rdtsc() - traceLoggers.startupTime;
+    tree[stack.current().treeId].stop = stop;
+    stack.pop();
+}
+
+TraceLogging::TraceLogging()
+{
+    initialized = false;
+    enabled = false;
+    loggerId = 0;
+
+#ifdef JS_THREADSAFE
+    lock = PR_NewLock();
+    if (!lock)
+        MOZ_CRASH();
+#endif
 }
 
 TraceLogging::~TraceLogging()
 {
-    if (entries) {
-        flush();
-        js_free(entries);
-        entries = nullptr;
-    }
-
     if (out) {
+        fprintf(out, "]");
         fclose(out);
         out = nullptr;
     }
-}
 
-void
-TraceLogging::grow()
-{
-    Entry* nentries = (Entry*) js_realloc(entries, numEntries*2*sizeof(Entry));
+    if (threadLoggers.initialized()) {
+        for (ThreadLoggerHashMap::Range r = threadLoggers.all(); !r.empty(); r.popFront()) {
+            delete r.front().value();
+        }
 
-    // Allocating a bigger array failed.
-    // Keep using the current storage, but remove all entries by flushing them.
-    if (!nentries) {
-        flush();
-        return;
+        threadLoggers.finish();
     }
 
-    entries = nentries;
-    numEntries *= 2;
-}
-
-void
-TraceLogging::log(Type type, const char* text /* = nullptr */, unsigned int number /* = 0 */)
-{
-    uint64_t now = rdtsc() - startupTime;
-
-    // Create array containing the entries if not existing.
-    if (!entries) {
-        entries = (Entry*) js_malloc(numEntries*sizeof(Entry));
-        if (!entries)
-            return;
+    if (lock) {
+        PR_DestroyLock(lock);
+        lock = nullptr;
     }
 
-    uint32_t textId = 0;
-    char *text_ = nullptr;
+    enabled = false;
+}
+
+bool
+TraceLogging::lazyInit()
+{
+    if (initialized)
+        return enabled;
+
+    initialized = true;
+
+    out = fopen(TRACE_LOG_DIR "tl-data.json", "w");
+    if (!out)
+        return false;
+    fprintf(out, "[");
+
+    if (!threadLoggers.init())
+        return false;
 
-    if (text) {
-        TextHashMap::AddPtr p = textMap.lookupForAdd(text);
-        if (!p) {
-            // Copy the text, because original could already be freed before writing the log file.
-            text_ = strdup(text);
-            if (!text_)
-                return;
-            textId = nextTextId++;
-            if (!textMap.add(p, text, textId))
-                return;
-        } else {
-            textId = p->value();
-        }
+    startupTime = rdtsc();
+    enabled = true;
+    return true;
+}
+
+TraceLogger *
+js::TraceLoggerForMainThread(JSRuntime *runtime)
+{
+    return traceLoggers.forMainThread(runtime);
+}
+
+TraceLogger *
+TraceLogging::forMainThread(JSRuntime *runtime)
+{
+    if (!runtime->mainThread.traceLogger) {
+        AutoTraceLoggingLock lock(this);
+
+        if (!lazyInit())
+            return nullptr;
+
+        runtime->mainThread.traceLogger = create();
     }
 
-    entries[curEntry++] = Entry(now, text_, textId, number, type);
-
-    // Increase length when not enough place in the array
-    if (curEntry >= numEntries)
-        grow();
-}
-
-void
-TraceLogging::log(Type type, const JS::ReadOnlyCompileOptions &options)
-{
-    this->log(type, options.filename(), options.lineno);
-}
-
-void
-TraceLogging::log(Type type, JSScript* script)
-{
-    this->log(type, script->filename(), script->lineno());
-}
-
-void
-TraceLogging::log(const char* log)
-{
-    this->log(INFO, log, 0);
+    return runtime->mainThread.traceLogger;
 }
 
-void
-TraceLogging::flush()
+TraceLogger *
+js::TraceLoggerForThread(PRThread *thread)
+{
+    return traceLoggers.forThread(thread);
+}
+
+TraceLogger *
+TraceLogging::forThread(PRThread *thread)
 {
-    // Open the logging file, when not opened yet.
-    if (!out) {
-        switch(id) {
-          case DEFAULT:
-            out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
-            break;
-          case ION_BACKGROUND_COMPILER:
-            out = fopen(TRACE_LOG_DIR "tracelogging-compile.log", "w");
-            break;
-          case GC_BACKGROUND:
-            out = fopen(TRACE_LOG_DIR "tracelogging-gc.log", "w");
-            break;
-          default:
-            MOZ_ASSUME_UNREACHABLE("Bad trigger");
-            return;
-        }
+    AutoTraceLoggingLock lock(this);
+
+    if (!lazyInit())
+        return nullptr;
+
+    ThreadLoggerHashMap::AddPtr p = threadLoggers.lookupForAdd(thread);
+    if (p)
+        return p->value();
+
+    TraceLogger *logger = create();
+    if (!logger)
+        return nullptr;
+
+    if (!threadLoggers.add(p, thread, logger)) {
+        delete logger;
+        return nullptr;
     }
 
-    // Print all log entries into the file
-    for (unsigned int i = 0; i < curEntry; i++) {
-        Entry entry = entries[i];
-        int written;
-        if (entry.type() == INFO) {
-            written = fprintf(out, "I,%s\n", entry.text());
-        } else {
-            if (entry.textId() > 0) {
-                if (entry.text()) {
-                    written = fprintf(out, "%llu,%s,%s,%d\n",
-                                      (unsigned long long)entry.tick(),
-                                      typeName[entry.type()],
-                                      entry.text(),
-                                      entry.lineno());
-                } else {
-                    written = fprintf(out, "%llu,%s,%d,%d\n",
-                                      (unsigned long long)entry.tick(),
-                                      typeName[entry.type()],
-                                      entry.textId(),
-                                      entry.lineno());
-                }
-            } else {
-                written = fprintf(out, "%llu,%s\n",
-                                  (unsigned long long)entry.tick(),
-                                  typeName[entry.type()]);
-            }
-        }
-
-        // A logging file can only be 2GB of length (fwrite limit).
-        if (written < 0) {
-            fprintf(stderr, "Writing tracelog to disk failed,");
-            fprintf(stderr, "probably because the file would've exceeded the maximum size of 2GB");
-            fclose(out);
-            exit(-1);
-        }
-
-        if (entries[i].text() != nullptr) {
-            js_free(entries[i].text());
-            entries[i].text_ = nullptr;
-        }
-    }
-    curEntry = 0;
+    return logger;
 }
 
-TraceLogging*
-TraceLogging::getLogger(Logger id)
+TraceLogger *
+TraceLogging::create()
 {
-    if (!loggers[id]) {
-        loggers[id] = new TraceLogging(id);
-        if (!atexitSet) {
-            startupTime = rdtsc();
-            atexit (releaseLoggers);
-            atexitSet = true;
-        }
+    if (loggerId > 999) {
+        fprintf(stderr, "TraceLogging: Can't create more than 999 different loggers.");
+        return nullptr;
+    }
+
+    if (loggerId > 0) {
+        int written = fprintf(out, ",\n");
+        if (written < 0)
+            fprintf(stderr, "TraceLogging: Error while writing.\n");
     }
 
-    return loggers[id];
-}
 
-void
-TraceLogging::releaseLoggers()
-{
-    for (size_t i = 0; i < LAST_LOGGER; i++) {
-        if (!loggers[i])
-            continue;
+    fprintf(out, "{\"tree\":\"tl-tree.%d.tl\", \"events\":\"tl-event.%d.tl\", \"dict\":\"tl-dict.%d.json\", \"treeFormat\":\"64,64,31,1,32\"}",
+            loggerId, loggerId, loggerId);
 
-        delete loggers[i];
-        loggers[i] = nullptr;
-    }
-}
+    loggerId++;
 
-/* Helper functions for asm calls */
-void
-js::TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script)
-{
-    logger->log(type, script);
-}
+    TraceLogger *logger = new TraceLogger();
+    if (!logger)
+        return nullptr;
 
-void
-js::TraceLog(TraceLogging* logger, const char* log)
-{
-    logger->log(log);
-}
+    if (!logger->init(loggerId)) {
+        delete logger;
+        return nullptr;
+    }
 
-void
-js::TraceLog(TraceLogging* logger, TraceLogging::Type type)
-{
-    logger->log(type);
+    return logger;
 }
--- a/js/src/TraceLogging.h
+++ b/js/src/TraceLogging.h
@@ -2,162 +2,508 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TraceLogging_h
 #define TraceLogging_h
 
-#include <stdint.h>
-#include <stdio.h>
+#include "jsalloc.h"
+#ifdef JS_THREADSAFE
+# include "jslock.h"
+#endif
 
-#include "jsalloc.h"
+#include "mozilla/GuardObjects.h"
 
 #include "js/HashTable.h"
 #include "js/TypeDecls.h"
 
+class PRThread;
+struct JSRuntime;
+
 namespace JS {
-class ReadOnlyCompileOptions;
+    class ReadOnlyCompileOptions;
 }
 
 namespace js {
 
-class TraceLogging
+/*
+ * Tracelogging overview.
+ *
+ * Tracelogging makes it possible to trace the timestamp of a single event and/or
+ * the duration of an event. This is implemented to give an as low overhead as
+ * possible so it doesn't interfere with running.
+ *
+ * The output of a tracelogging session is saved in /tmp/tl-data.json.
+ * The format of that file is a JS array per tracelogger (=thread), with a map
+ * containing:
+ *  - dict:   Name of the file containing a json table with the log text.
+ *            All other files only contain a index to this table when logging.
+ *  - events: Name of the file containing a flat list of log events saved
+ *            in binary format.
+ *            (64bit: Time Stamp Counter, 32bit index to dict)
+ *  - tree:   Name of the file containing the events with duration. The content
+ *            is already in a tree data structure. This is also saved in a
+ *            binary file.
+ *  - treeFormat: The format used to encode the tree. By default "64,64,31,1,32".
+ *                There are currently no other formats to save the tree.
+ *     - 64,64,31,1,31 signifies how many bytes are used for the different
+ *       parts of the tree.
+ *       => 64 bits: Time Stamp Counter of start of event.
+ *       => 64 bits: Time Stamp Counter of end of event.
+ *       => 31 bits: Index to dict file containing the log text.
+ *       =>  1 bit:  Boolean signifying if this entry has children.
+ *                   When true, the child can be found just behind this entry.
+ *       => 32 bits: Containing the ID of the next event on the same depth
+ *                   or 0 if there isn't an event on the same depth anymore.
+ *
+ *        /-> The position in the file. Id is this divided by size of entry.
+ *        |   So in this case this would be 1 (192bits per entry).
+ *        |                              /-> Indicates there are children. The
+ *        |                              |   first child is located at current
+ *        |                              |   ID + 1. So 1 + 1 in this case: 2.
+ *        |                              |   Or 0x00180 in the tree file.
+ *        |                              | /-> Next event on the same depth is
+ *        |                              | |    located at 4. So 0x00300 in the
+ *        |                              | |    tree file.
+ *       0x0000C0: [start, end, dictId, 1, 4]
+ *
+ *
+ *       Example:
+ *                          0x0: [start, end, dictId, 1, 0]
+ *                                        |
+ *                      /----------------------------------\
+ *                      |                                  |
+ *       0xC0: [start, end, dictId, 0, 2]      0x180 [start, end, dictId, 1, 0]
+ *                                                      |
+ *                                  /----------------------------------\
+ *                                  |                                  |
+ *         0x240: [start, end, dictId, 0, 4]    0x300 [start, end, dictId, 0, 0]
+ *
+ *
+ * Logging something is done in 3 stages.
+ * 1) Get the tracelogger of the current thread.
+ *     - TraceLoggerForMainThread(JSRuntime *)
+ *     - TraceLoggerForThread(PR_GetCurrentThread());
+ * 2) Optionally create a textId for the text that needs to get logged. This
+ *    step takes some time, so try to do this beforehand, outside the hot
+ *    path and don't do unnecessary repetitions, since it will criple
+ *    performance.
+ *     - TraceLogCreateTextId(logger, ...);
+ *
+ *    There are also some text IDs created beforehand. They are located in
+ *    Tracelogger::TextId.
+ * 3) Log the timestamp of an event:
+ *    - TraceLogTimestamp(logger, textId);
+ *
+ *    or the duration:
+ *    - TraceLogStartEvent(logger, textId);
+ *    - TraceLogStopEvent(logger, textId);
+ *
+ *    or the duration with a RAII class:
+ *    - AutoTraceLog logger(logger, textId);
+ */
+
+class AutoTraceLog;
+
+template <class T>
+class ContinuousSpace {
+    T *data_;
+    uint32_t next_;
+    uint32_t capacity_;
+
+  public:
+    ContinuousSpace ()
+     : data_(nullptr)
+    { }
+
+    bool init() {
+        capacity_ = 64;
+        next_ = 0;
+        data_ = (T *) js_malloc(capacity_ * sizeof(T));
+        if (!data_)
+            return false;
+
+        return true;
+    }
+
+    T *data() {
+        return data_;
+    }
+
+    uint32_t capacity() {
+        return capacity_;
+    }
+
+    uint32_t size() {
+        return next_;
+    }
+
+    uint32_t nextId() {
+        return next_;
+    }
+
+    T &next() {
+        return data()[next_];
+    }
+
+    uint32_t currentId() {
+        JS_ASSERT(next_ > 0);
+        return next_ - 1;
+    }
+
+    T &current() {
+        return data()[currentId()];
+    }
+
+    bool ensureSpaceBeforeAdd() {
+        if (next_ < capacity_)
+            return true;
+
+        uint32_t nCapacity = capacity_ * 2;
+        T *entries = (T *) js_realloc(data_, nCapacity * sizeof(T));
+
+        if (!entries)
+            return false;
+
+        data_ = entries;
+        capacity_ = nCapacity;
+
+        return true;
+    }
+
+    T &operator[](size_t i) {
+        MOZ_ASSERT(i < next_);
+        return data()[i];
+    }
+
+    void push(T &data) {
+        MOZ_ASSERT(next_ < capacity_);
+        data()[next_++] = data;
+    }
+
+    T &pushUninitialized() {
+        return data()[next_++];
+    }
+
+    void pop() {
+        JS_ASSERT(next_ > 0);
+        next_--;
+    }
+
+    void clear() {
+        next_ = 0;
+    }
+};
+
+class TraceLogger
 {
   public:
-    enum Type {
-        SCRIPT_START,
-        SCRIPT_STOP,
-        ION_COMPILE_START,
-        ION_COMPILE_STOP,
-        YARR_JIT_START,
-        YARR_JIT_STOP,
-        GC_START,
-        GC_STOP,
-        MINOR_GC_START,
-        MINOR_GC_STOP,
-        GC_SWEEPING_START,
-        GC_SWEEPING_STOP,
-        GC_ALLOCATING_START,
-        GC_ALLOCATING_STOP,
-        PARSER_COMPILE_SCRIPT_START,
-        PARSER_COMPILE_SCRIPT_STOP,
-        PARSER_COMPILE_LAZY_START,
-        PARSER_COMPILE_LAZY_STOP,
-        PARSER_COMPILE_FUNCTION_START,
-        PARSER_COMPILE_FUNCTION_STOP,
-        INFO_ENGINE_INTERPRETER,
-        INFO_ENGINE_BASELINE,
-        INFO_ENGINE_IONMONKEY,
-        INFO
-    };
-    enum Logger {
-        DEFAULT,
-        ION_BACKGROUND_COMPILER,
-        GC_BACKGROUND,
+    // Predefined IDs for common operations. These IDs can be used
+    // without using TraceLogCreateTextId, because there are already created.
+    // When changing the enum here, you must update the array containing the
+    // actual logged text in TraceLogging.cpp.
+    enum TextId {
+      TL_Error,
+      Bailout,
+      Baseline,
+      GC,
+      GCAllocating,
+      GCSweeping,
+      Interpreter,
+      Invalidation,
+      IonCompile,
+      IonLink,
+      IonMonkey,
+      MinorGC,
+      ParserCompileFunction,
+      ParserCompileLazy,
+      ParserCompileScript,
+      TL,
+      YarrCompile,
+      YarrInterpret,
+      YarrJIT,
 
-        LAST_LOGGER
-    };
-
-  private:
-    struct Entry {
-        uint64_t tick_;
-        char* text_;
-        uint32_t textId_;
-        uint32_t lineno_;
-        uint8_t type_;
-
-        Entry(uint64_t tick, char* text, uint32_t textId, uint32_t lineno, Type type)
-            : tick_(tick),
-              text_(text),
-              textId_(textId),
-              lineno_(lineno),
-              type_((uint8_t)type) {}
-
-        uint64_t tick() const { return tick_; }
-        char *text() const { return text_; }
-        uint32_t textId() const { return textId_; }
-        uint32_t lineno() const { return lineno_; }
-        Type type() const { return (Type) type_; }
+      LAST
     };
 
-    typedef HashMap<const char *,
-                        uint32_t,
-                        PointerHasher<const char *, 3>,
-                        SystemAllocPolicy> TextHashMap;
+#ifdef JS_TRACE_LOGGING
+  private:
+    typedef HashMap<const void *,
+                    uint32_t,
+                    PointerHasher<const void *, 3>,
+                    SystemAllocPolicy> PointerHashMap;
+
+    // The layout of the tree in memory and in the log file. Readable by JS
+    // using TypedArrays.
+    struct TreeEntry {
+        uint64_t start;
+        uint64_t stop;
+        union {
+            struct {
+                uint32_t textId: 31;
+                uint32_t hasChildren: 1;
+            } s;
+            uint32_t value;
+        } u;
+        uint32_t nextId;
 
-    TextHashMap textMap;
+        TreeEntry(uint64_t start, uint64_t stop, uint32_t textId, bool hasChildren,
+                  uint32_t nextId)
+        {
+            this->start = start;
+            this->stop = stop;
+            this->u.s.textId = textId;
+            this->u.s.hasChildren = hasChildren;
+            this->nextId = nextId;
+        }
+    };
+
+    // Helper structure for keeping track of the currently active entries in
+    // the tree. Pushed by `start(id)`, popped by `stop(id)`.
+    struct StackEntry {
+        uint32_t treeId;
+        uint32_t lastChildId;
+        StackEntry(uint32_t treeId, uint32_t lastChildId)
+          : treeId(treeId), lastChildId(lastChildId)
+        { }
+    };
+
+    // The layout of the event log in memory and in the log file.
+    // Readable by JS using TypedArrays.
+    struct EventEntry {
+        uint64_t time;
+        uint32_t textId;
+        EventEntry(uint64_t time, uint32_t textId)
+          : time(time), textId(textId)
+        { }
+    };
+
+    FILE *dictFile;
+    FILE *treeFile;
+    FILE *eventFile;
+
+    bool enabled;
     uint32_t nextTextId;
-    Entry *entries;
-    unsigned int curEntry;
-    unsigned int numEntries;
-    int fileno;
-    FILE *out;
-    Logger id;
+
+    PointerHashMap pointerMap;
+
+    ContinuousSpace<TreeEntry> tree;
+    ContinuousSpace<StackEntry> stack;
+    ContinuousSpace<EventEntry> events;
 
-    static bool atexitSet;
-    static const char * const typeName[];
-    static TraceLogging* loggers[];
-    static uint64_t startupTime;
+    uint32_t treeOffset;
+
   public:
-    TraceLogging(Logger id);
-    ~TraceLogging();
+    AutoTraceLog *top;
 
-    void log(Type type, const char* text = nullptr, unsigned int number = 0);
-    void log(Type type, const JS::ReadOnlyCompileOptions &options);
-    void log(Type type, JSScript* script);
-    void log(const char* log);
+  private:
+    void updateHasChildren(uint32_t treeId, bool hasChildren = false);
+    void updateNextId(uint32_t treeId, bool nextId);
+    void updateStop(uint32_t treeId, uint64_t timestamp);
     void flush();
 
-    static TraceLogging* getLogger(Logger id);
-    static TraceLogging* defaultLogger() {
-        return getLogger(DEFAULT);
-    }
-    static void releaseLoggers();
+  public:
+    TraceLogger();
+    ~TraceLogger();
+
+    bool init(uint32_t loggerId);
+
+    // The createTextId functions map a unique input to a logger ID.
+    // This ID can be used to log something. Calls to these functions should be
+    // limited if possible, because of the overhead.
+    uint32_t createTextId(const char *text);
+    uint32_t createTextId(JSScript *script);
+    uint32_t createTextId(const JS::ReadOnlyCompileOptions &script);
+
+    // Log an event (no start/stop, only the timestamp is recorded).
+    void logTimestamp(uint32_t id);
+
+    // Record timestamps for start and stop of an event.
+    // In the stop method, the ID is only used in debug builds to test
+    // correctness.
+    void startEvent(uint32_t id);
+    void stopEvent(uint32_t id);
+    void stopEvent();
 
   private:
-    void grow();
+    void assertNoQuotes(const char *text) {
+#ifdef DEBUG
+        const char *quote = strchr(text, '"');
+        MOZ_ASSERT(!quote);
+#endif
+    }
+#endif
 };
 
-/* Helpers functions for asm calls */
-void TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script);
-void TraceLog(TraceLogging* logger, const char* log);
-void TraceLog(TraceLogging* logger, TraceLogging::Type type);
+class TraceLogging
+{
+#ifdef JS_TRACE_LOGGING
+    typedef HashMap<PRThread *,
+                    TraceLogger *,
+                    PointerHasher<PRThread *, 3>,
+                    SystemAllocPolicy> ThreadLoggerHashMap;
 
-/* Automatic logging at the start and end of function call */
-class AutoTraceLog {
-    TraceLogging* logger;
-    TraceLogging::Type stop;
+    bool initialized;
+    bool enabled;
+    ThreadLoggerHashMap threadLoggers;
+    uint32_t loggerId;
+    FILE *out;
 
   public:
-    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop,
-                 const JS::ReadOnlyCompileOptions &options)
-      : logger(logger),
-        stop(stop)
-    {
-        logger->log(start, options);
-    }
+    uint64_t startupTime;
+#ifdef JS_THREADSAFE
+    PRLock *lock;
+#endif
+
+    TraceLogging();
+    ~TraceLogging();
+
+    TraceLogger *forMainThread(JSRuntime *runtime);
+    TraceLogger *forThread(PRThread *thread);
+
+  private:
+    TraceLogger *create();
+    bool lazyInit();
+#endif
+};
+
+#ifdef JS_TRACE_LOGGING
+TraceLogger *TraceLoggerForMainThread(JSRuntime *runtime);
+TraceLogger *TraceLoggerForThread(PRThread *thread);
+#else
+inline TraceLogger *TraceLoggerForMainThread(JSRuntime *runtime) {
+    return nullptr;
+};
+inline TraceLogger *TraceLoggerForThread(PRThread *thread) {
+    return nullptr;
+};
+#endif
 
-    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop,
-                 JSScript* script)
+inline uint32_t TraceLogCreateTextId(TraceLogger *logger, JSScript *script) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->createTextId(script);
+#endif
+    return TraceLogger::TL_Error;
+}
+inline uint32_t TraceLogCreateTextId(TraceLogger *logger,
+                                     const JS::ReadOnlyCompileOptions &compileOptions)
+{
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->createTextId(compileOptions);
+#endif
+    return TraceLogger::TL_Error;
+}
+inline uint32_t TraceLogCreateTextId(TraceLogger *logger, const char *text) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->createTextId(text);
+#endif
+    return TraceLogger::TL_Error;
+}
+inline void TraceLogTimestamp(TraceLogger *logger, uint32_t textId) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        logger->logTimestamp(textId);
+#endif
+}
+inline void TraceLogStartEvent(TraceLogger *logger, uint32_t textId) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        logger->startEvent(textId);
+#endif
+}
+inline void TraceLogStopEvent(TraceLogger *logger, uint32_t textId) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        logger->stopEvent(textId);
+#endif
+}
+inline void TraceLogStopEvent(TraceLogger *logger) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        logger->stopEvent();
+#endif
+}
+
+// Automatic logging at the start and end of function call.
+class AutoTraceLog {
+#ifdef JS_TRACE_LOGGING
+    TraceLogger *logger;
+    uint32_t textId;
+    bool executed;
+    AutoTraceLog *prev;
+
+  public:
+    AutoTraceLog(TraceLogger *logger, uint32_t textId MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : logger(logger),
-        stop(stop)
+        textId(textId),
+        executed(false)
     {
-        logger->log(start, script);
-    }
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (logger) {
+            TraceLogStartEvent(logger, textId);
 
-    AutoTraceLog(TraceLogging* logger, TraceLogging::Type start, TraceLogging::Type stop)
-      : logger(logger),
-        stop(stop)
-    {
-        logger->log(start);
+            prev = logger->top;
+            logger->top = this;
+        }
     }
 
     ~AutoTraceLog()
     {
-        logger->log(stop);
+        if (logger) {
+            while (this != logger->top)
+                logger->top->stop();
+            stop();
+        }
     }
+  private:
+    void stop() {
+        if (!executed) {
+            executed = true;
+            TraceLogStopEvent(logger, textId);
+        }
+
+        if (logger->top == this)
+            logger->top = prev;
+    }
+#else
+  public:
+    AutoTraceLog(TraceLogger *logger, uint32_t textId MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+#endif
+
+  private:
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-}  /* namespace js */
+#ifdef JS_TRACE_LOGGING
+class AutoTraceLoggingLock
+{
+  TraceLogging *logging;
+
+  public:
+    AutoTraceLoggingLock(TraceLogging *logging MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : logging(logging)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+#ifdef JS_THREADSAFE
+        PR_Lock(logging->lock);
+#endif
+    }
+    ~AutoTraceLoggingLock() {
+#ifdef JS_THREADSAFE
+        PR_Unlock(logging->lock);
+#endif
+    }
+  private:
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+#endif
+
+}  /* namedata js */
 
 #endif /* TraceLogging_h */
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "frontend/BytecodeCompiler.h"
 
 #include "jscntxt.h"
 #include "jsscript.h"
+#include "TraceLogging.h"
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/Parser.h"
 #include "jit/AsmJSLink.h"
 #include "vm/GlobalObject.h"
 
@@ -184,22 +185,24 @@ frontend::CompileScript(ExclusiveContext
                         const ReadOnlyCompileOptions &options,
                         const jschar *chars, size_t length,
                         JSString *source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask *extraSct /* = nullptr */)
 {
     RootedString source(cx, source_);
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::PARSER_COMPILE_SCRIPT_START,
-                                js::TraceLogging::PARSER_COMPILE_SCRIPT_STOP,
-                                options);
-#endif
+    js::TraceLogger *logger = nullptr;
+    if (cx->isJSContext())
+        logger = TraceLoggerForMainThread(cx->asJSContext()->runtime());
+    else
+        logger = TraceLoggerForThread(PR_GetCurrentThread());
+    uint32_t logId = js::TraceLogCreateTextId(logger, options);
+    js::AutoTraceLog scriptLogger(logger, logId);
+    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript);
 
     if (cx->isJSContext())
         MaybeCallSourceHandler(cx->asJSContext(), options, chars, length);
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
@@ -432,22 +435,20 @@ frontend::CompileLazyFunction(JSContext 
     CompileOptions options(cx, lazy->version());
     options.setOriginPrincipals(lazy->originPrincipals())
            .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::PARSER_COMPILE_LAZY_START,
-                                js::TraceLogging::PARSER_COMPILE_LAZY_STOP,
-                                options);
-#endif
+    js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
+    uint32_t logId = js::TraceLogCreateTextId(logger, options);
+    js::AutoTraceLog scriptLogger(logger, logId);
+    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileLazy);
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, nullptr, lazy);
 
     uint32_t staticLevel = lazy->staticLevel(cx);
 
     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     JS_ASSERT(!lazy->isLegacyGenerator());
@@ -492,22 +493,20 @@ frontend::CompileLazyFunction(JSContext 
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 static bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
                     const AutoNameVector &formals, const jschar *chars, size_t length,
                     GeneratorKind generatorKind)
 {
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::PARSER_COMPILE_FUNCTION_START,
-                                js::TraceLogging::PARSER_COMPILE_FUNCTION_STOP,
-                                options);
-#endif
+    js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
+    uint32_t logId = js::TraceLogCreateTextId(logger, options);
+    js::AutoTraceLog scriptLogger(logger, logId);
+    js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction);
 
     // FIXME: make Function pass in two strings and parse them as arguments and
     // ProgramElements respectively.
 
     MaybeCallSourceHandler(cx, options, chars, length);
 
     if (!CheckLength(cx, length))
         return false;
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Bailouts.h"
 
 #include "jscntxt.h"
+#include "TraceLogging.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/Snapshots.h"
 
 #include "jit/IonFrameIterator-inl.h"
@@ -73,16 +74,19 @@ jit::Bailout(BailoutStack *sp, BaselineB
     JS_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
     cx->mainThread().ionTop = nullptr;
     JitActivationIterator jitActivations(cx->runtime());
     IonBailoutIterator iter(jitActivations, sp);
     JitActivation *activation = jitActivations.activation()->asJit();
 
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogTimestamp(logger, TraceLogger::Bailout);
+
     IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     JS_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = nullptr;
     uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
     JS_ASSERT(retval == BAILOUT_RETURN_OK ||
               retval == BAILOUT_RETURN_FATAL_ERROR ||
@@ -104,16 +108,19 @@ jit::InvalidationBailout(InvalidationBai
     JSContext *cx = GetJSContextFromJitCode();
 
     // We don't have an exit frame.
     cx->mainThread().ionTop = nullptr;
     JitActivationIterator jitActivations(cx->runtime());
     IonBailoutIterator iter(jitActivations, sp);
     JitActivation *activation = jitActivations.activation()->asJit();
 
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogTimestamp(logger, TraceLogger::Invalidation);
+
     IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.topFrameSize();
 
     JS_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = nullptr;
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1256,19 +1256,19 @@ InitFromBailout(JSContext *cx, HandleScr
 uint32_t
 jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
                           bool invalidate, BaselineBailoutInfo **bailoutInfo,
                           const ExceptionBailoutInfo *excInfo)
 {
     JS_ASSERT(bailoutInfo != nullptr);
     JS_ASSERT(*bailoutInfo == nullptr);
 
-#if JS_TRACE_LOGGING
-    TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_BASELINE);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogStopEvent(logger, TraceLogger::IonMonkey);
+    TraceLogStartEvent(logger, TraceLogger::Baseline);
 
     // The caller of the top frame must be one of the following:
     //      IonJS - Ion calling into Ion.
     //      BaselineStub - Baseline calling into Ion.
     //      Entry - Interpreter or other calling into Ion.
     //      Rectifier - Arguments rectifier calling into Ion.
     JS_ASSERT(iter.isIonJS());
     FrameType prevFrameType = iter.prevType();
@@ -1346,22 +1346,21 @@ jit::BailoutIonToBaseline(JSContext *cx,
     AutoValueVector startFrameFormals(cx);
 
     RootedScript topCaller(cx);
     jsbytecode *topCallerPC = nullptr;
 
     while (true) {
         MOZ_ASSERT(snapIter.instruction()->isResumePoint());
 
-#if JS_TRACE_LOGGING
         if (frameNo > 0) {
-            TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_START, scr);
-            TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_BASELINE);
+            TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
+            TraceLogStartEvent(logger, TraceLogger::Baseline);
         }
-#endif
+
         IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
 
         // If we are bailing out to a catch or finally block in this frame,
         // pass excInfo to InitFromBailout and don't unpack any other frames.
         bool handleException = (excInfo && excInfo->frameNo == frameNo);
 
         jsbytecode *callPC = nullptr;
         RootedFunction nextCallee(cx, nullptr);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -340,18 +340,23 @@ BaselineCompiler::emitPrologue()
                              Imm32(LOOP_UNROLL_FACTOR), R1.scratchReg(), &pushLoop);
         }
     }
 
     if (needsEarlyStackCheck())
         masm.bind(&earlyStackCheckFailed);
 
 #if JS_TRACE_LOGGING
-    masm.tracelogStart(script.get());
-    masm.tracelogLog(TraceLogging::INFO_ENGINE_BASELINE);
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    Register loggerReg = RegisterSet::Volatile().takeGeneral();
+    masm.Push(loggerReg);
+    masm.movePtr(ImmPtr(logger), loggerReg);
+    masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script.get()));
+    masm.tracelogStart(loggerReg, TraceLogger::Baseline);
+    masm.Pop(loggerReg);
 #endif
 
     // Record the offset of the prologue, because Ion can bailout before
     // the scope chain is initialized.
     prologueOffset_ = masm.currentOffset();
 
     // Initialize the scope chain before any operation that may
     // call into the VM and trigger a GC.
@@ -377,17 +382,25 @@ BaselineCompiler::emitPrologue()
 }
 
 bool
 BaselineCompiler::emitEpilogue()
 {
     masm.bind(&return_);
 
 #if JS_TRACE_LOGGING
-    masm.tracelogStop();
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    Register loggerReg = RegisterSet::Volatile().takeGeneral();
+    masm.Push(loggerReg);
+    masm.movePtr(ImmPtr(logger), loggerReg);
+    masm.tracelogStop(loggerReg, TraceLogger::Baseline);
+    // Stop the script. Using a stop without checking the textId, since we
+    // we didn't save the textId for the script.
+    masm.tracelogStop(loggerReg);
+    masm.Pop(loggerReg);
 #endif
 
     // Pop SPS frame if necessary
     emitSPSPop();
 
     masm.mov(BaselineFrameReg, BaselineStackReg);
     masm.pop(BaselineFrameReg);
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -195,19 +195,19 @@ jit::EnterBaselineAtBranch(JSContext *cx
 
         // For eval function frames, set the callee token to the enclosing function.
         if (fp->isFunctionFrame())
             data.calleeToken = CalleeToToken(&fp->callee());
         else
             data.calleeToken = CalleeToToken(fp->script());
     }
 
-#if JS_TRACE_LOGGING
-    TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_BASELINE);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogStopEvent(logger, TraceLogger::Interpreter);
+    TraceLogStartEvent(logger, TraceLogger::Baseline);
 
     IonExecStatus status = EnterBaseline(cx, data);
     if (status != IonExec_Ok)
         return status;
 
     fp->setReturnValue(data.result);
     return IonExec_Ok;
 }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1283,17 +1283,22 @@ CodeGenerator::visitReturn(LReturn *lir)
 bool
 CodeGenerator::visitOsrEntry(LOsrEntry *lir)
 {
     // Remember the OSR entry offset into the code buffer.
     masm.flushBuffer();
     setOsrEntryOffset(masm.size());
 
 #if JS_TRACE_LOGGING
-    masm.tracelogLog(TraceLogging::INFO_ENGINE_IONMONKEY);
+    if (gen->info().executionMode() == SequentialExecution) {
+        if (!emitTracelogStopEvent(TraceLogger::Baseline))
+            return false;
+        if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
+            return false;
+    }
 #endif
 
     // Allocate the full frame for this function.
     uint32_t size = frameSize();
     if (size != 0)
         masm.subPtr(Imm32(size), StackPointer);
     return true;
 }
@@ -6244,18 +6249,22 @@ CodeGenerator::generate()
 
     if (!snapshots_.init())
         return false;
 
     if (!safepoints_.init(gen->alloc(), graph.totalSlotCount()))
         return false;
 
 #if JS_TRACE_LOGGING
-    masm.tracelogStart(gen->info().script());
-    masm.tracelogLog(TraceLogging::INFO_ENGINE_IONMONKEY);
+    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
+        if (!emitTracelogScriptStart())
+            return false;
+        if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
+            return false;
+    }
 #endif
 
     // Before generating any code, we generate type checks for all parameters.
     // This comes before deoptTable_, because we can't use deopt tables without
     // creating the actual frame.
     if (!generateArgumentsChecks())
         return false;
 
@@ -6270,18 +6279,22 @@ CodeGenerator::generate()
     masm.jump(&skip);
 #endif
 
     // Remember the entry offset to skip the argument check.
     masm.flushBuffer();
     setSkipArgCheckEntryOffset(masm.size());
 
 #if JS_TRACE_LOGGING
-    masm.tracelogStart(gen->info().script());
-    masm.tracelogLog(TraceLogging::INFO_ENGINE_IONMONKEY);
+    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
+        if (!emitTracelogScriptStart())
+            return false;
+        if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
+            return false;
+    }
     masm.bind(&skip);
 #endif
 
 #ifdef DEBUG
     // Assert that the argument types are correct.
     if (!generateArgumentsChecks(/* bailout = */ false))
         return false;
 #endif
@@ -6464,16 +6477,33 @@ CodeGenerator::link(JSContext *cx, types
         ionScript->copyRecovers(&recovers_);
     if (graph.numConstants())
         ionScript->copyConstants(graph.constantPool());
     if (callTargets.length() > 0)
         ionScript->copyCallTargetEntries(callTargets.begin());
     if (patchableBackedges_.length() > 0)
         ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin());
 
+#if JS_TRACE_LOGGING
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
+        patchableTraceLoggers_[i].fixup(&masm);
+        Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
+                                           ImmPtr(logger),
+                                           ImmPtr(nullptr));
+    }
+    uint32_t scriptId = TraceLogCreateTextId(logger, script);
+    for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
+        patchableTLScripts_[i].fixup(&masm);
+        Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
+                                           ImmPtr((void *)scriptId),
+                                           ImmPtr((void *)0));
+    }
+#endif
+
     switch (executionMode) {
       case SequentialExecution:
         // The correct state for prebarriers is unknown until the end of compilation,
         // since a GC can occur during code generation. All barriers are emitted
         // off-by-default, and are toggled on here if necessary.
         if (cx->zone()->needsBarrier())
             ionScript->toggleBarriers(true);
         break;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -6,19 +6,17 @@
 
 #include "jit/Ion.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsworkers.h"
-#if JS_TRACE_LOGGING
 #include "TraceLogging.h"
-#endif
 
 #include "jsprf.h"
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
@@ -1630,16 +1628,18 @@ AttachFinishedCompilations(JSContext *cx
     if (!ion)
         return;
 
     types::AutoEnterAnalysis enterTypes(cx);
     AutoLockWorkerThreadState lock;
 
     GlobalWorkerThreadState::IonBuilderVector &finished = WorkerThreadState().ionFinishedList();
 
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+
     // Incorporate any off thread compilations for the compartment which have
     // finished, failed or have been cancelled.
     while (true) {
         IonBuilder *builder = nullptr;
 
         // Find a finished builder for the compartment.
         for (size_t i = 0; i < finished.length(); i++) {
             IonBuilder *testBuilder = finished[i];
@@ -1650,16 +1650,18 @@ AttachFinishedCompilations(JSContext *cx
             }
         }
         if (!builder)
             break;
 
         if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, &builder->alloc());
+            AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
+            AutoTraceLog logLink(logger, TraceLogger::IonLink);
 
             // Root the assembler until the builder is finished below. As it
             // was constructed off thread, the assembler has not been rooted
             // previously, though any GC activity would discard the builder.
             codegen->masm.constructRoot(cx);
 
             bool success;
             {
@@ -1743,22 +1745,20 @@ TrackPropertiesForSingletonScopes(JSCont
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
            ExecutionMode executionMode, bool recompile,
            OptimizationLevel optimizationLevel)
 {
-#if JS_TRACE_LOGGING
-    AutoTraceLog logger(TraceLogging::defaultLogger(),
-                        TraceLogging::ION_COMPILE_START,
-                        TraceLogging::ION_COMPILE_STOP,
-                        script);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
+    AutoTraceLog logCompile(logger, TraceLogger::IonCompile);
+
     JS_ASSERT(optimizationLevel > Optimization_DontCompile);
 
     // Make sure the script's canonical function isn't lazy. We can't de-lazify
     // it in a worker thread.
     script->ensureNonLazyCanonicalFunction(cx);
 
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
 
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1133,76 +1133,126 @@ MacroAssembler::printf(const char *outpu
     passABIArg(value);
     callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf1_));
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 #if JS_TRACE_LOGGING
 void
-MacroAssembler::tracelogStart(JSScript *script)
+MacroAssembler::tracelogStart(Register logger, uint32_t textId)
 {
-    void (&TraceLogStart)(TraceLogging*, TraceLogging::Type, JSScript*) = TraceLog;
+    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStartEvent;
+
+    PushRegsInMask(RegisterSet::Volatile());
+
     RegisterSet regs = RegisterSet::Volatile();
-    PushRegsInMask(regs);
+    regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
-    Register type = regs.takeGeneral();
-    Register rscript = regs.takeGeneral();
 
-    setupUnalignedABICall(3, temp);
-    movePtr(ImmPtr(TraceLogging::defaultLogger()), temp);
+    setupUnalignedABICall(2, temp);
+    passABIArg(logger);
+    move32(Imm32(textId), temp);
     passABIArg(temp);
-    move32(Imm32(TraceLogging::SCRIPT_START), type);
-    passABIArg(type);
-    movePtr(ImmGCPtr(script), rscript);
-    passABIArg(rscript);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStart));
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+
+    PopRegsInMask(RegisterSet::Volatile());
+}
+
+void
+MacroAssembler::tracelogStart(Register logger, Register textId)
+{
+    void (&TraceLogFunc)(TraceLogger*, 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));
+
+    regs.add(temp);
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogStop()
+MacroAssembler::tracelogStop(Register logger, uint32_t textId)
 {
-    void (&TraceLogStop)(TraceLogging*, TraceLogging::Type) = TraceLog;
+    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent;
+
+    PushRegsInMask(RegisterSet::Volatile());
+
     RegisterSet regs = RegisterSet::Volatile();
-    PushRegsInMask(regs);
+    regs.takeUnchecked(logger);
 
     Register temp = regs.takeGeneral();
-    Register logger = regs.takeGeneral();
-    Register type = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
-    movePtr(ImmPtr(TraceLogging::defaultLogger()), logger);
     passABIArg(logger);
-    move32(Imm32(TraceLogging::SCRIPT_STOP), type);
-    passABIArg(type);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStop));
+    move32(Imm32(textId), temp);
+    passABIArg(temp);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+
+    regs.add(temp);
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 
 void
-MacroAssembler::tracelogLog(TraceLogging::Type type)
+MacroAssembler::tracelogStop(Register logger, Register textId)
 {
-    void (&TraceLogStop)(TraceLogging*, TraceLogging::Type) = TraceLog;
+#ifdef DEBUG
+    void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent;
+
+    PushRegsInMask(RegisterSet::Volatile());
+
     RegisterSet regs = RegisterSet::Volatile();
-    PushRegsInMask(regs);
+    regs.takeUnchecked(logger);
+    regs.takeUnchecked(textId);
 
     Register temp = regs.takeGeneral();
-    Register logger = regs.takeGeneral();
-    Register rtype = regs.takeGeneral();
 
     setupUnalignedABICall(2, temp);
-    movePtr(ImmPtr(TraceLogging::defaultLogger()), logger);
     passABIArg(logger);
-    move32(Imm32(type), rtype);
-    passABIArg(rtype);
-    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogStop));
+    passABIArg(textId);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+
+    regs.add(temp);
+
+    PopRegsInMask(RegisterSet::Volatile());
+#else
+    tracelogStop(logger);
+#endif
+}
+
+void
+MacroAssembler::tracelogStop(Register logger)
+{
+    void (&TraceLogFunc)(TraceLogger*) = TraceLogStopEvent;
+
+    PushRegsInMask(RegisterSet::Volatile());
+
+    RegisterSet regs = RegisterSet::Volatile();
+    regs.takeUnchecked(logger);
+
+    Register temp = regs.takeGeneral();
+
+    setupUnalignedABICall(1, temp);
+    passABIArg(logger);
+    callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc));
+
+    regs.add(temp);
 
     PopRegsInMask(RegisterSet::Volatile());
 }
 #endif
 
 void
 MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scratch, Label *done)
 {
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_IonMacroAssembler_h
 #define jit_IonMacroAssembler_h
 
 #ifdef JS_ION
 
 #include "jscompartment.h"
+#include "TraceLogging.h"
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/MacroAssembler-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/MacroAssembler-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/MacroAssembler-arm.h"
 #endif
@@ -1118,19 +1119,21 @@ class MacroAssembler : public MacroAssem
 
     void finish();
 
     void assumeUnreachable(const char *output);
     void printf(const char *output);
     void printf(const char *output, Register value);
 
 #if JS_TRACE_LOGGING
-    void tracelogStart(JSScript *script);
-    void tracelogStop();
-    void tracelogLog(TraceLogging::Type type);
+    void tracelogStart(Register logger, uint32_t textId);
+    void tracelogStart(Register logger, Register textId);
+    void tracelogStop(Register logger, uint32_t textId);
+    void tracelogStop(Register logger, Register textId);
+    void tracelogStop(Register logger);
 #endif
 
 #define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2)    \
     JS_ASSERT(IsFloatingPointType(type));                               \
     if (type == MIRType_Double)                                         \
         method##Double(arg1d, arg2);                                    \
     else                                                                \
         method##Float32(arg1f, arg2);                                   \
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -51,19 +51,26 @@ CodeGeneratorARM::generatePrologue()
 
     return true;
 }
 
 bool
 CodeGeneratorARM::generateEpilogue()
 {
     masm.bind(&returnLabel_);
+
 #if JS_TRACE_LOGGING
-    masm.tracelogStop();
+    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
+        if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
+            return false;
+        if (!emitTracelogScriptStop())
+            return false;
+    }
 #endif
+
     if (gen->compilingAsmJS()) {
         // Pop the stack we allocated at the start of the function.
         masm.freeStack(frameDepth_);
         masm.Pop(pc);
         JS_ASSERT(masm.framePushed() == 0);
         //masm.as_bkpt();
     } else {
         // Pop the stack we allocated at the start of the function.
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -990,10 +990,80 @@ CodeGeneratorShared::addCacheLocations(c
         new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script);
         numLocations++;
     }
     JS_ASSERT(numLocations != 0);
     *numLocs = numLocations;
     return firstIndex;
 }
 
+#if JS_TRACE_LOGGING
+
+bool
+CodeGeneratorShared::emitTracelogScript(bool isStart)
+{
+    RegisterSet regs = RegisterSet::Volatile();
+    Register logger = regs.takeGeneral();
+    Register script = regs.takeGeneral();
+
+    masm.Push(logger);
+    masm.Push(script);
+
+    CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
+    if (!patchableTraceLoggers_.append(patchLogger))
+        return false;
+
+    CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script);
+    if (!patchableTLScripts_.append(patchScript))
+        return false;
+
+    if (isStart)
+        masm.tracelogStart(logger, script);
+    else
+        masm.tracelogStop(logger, script);
+
+    masm.Pop(script);
+    masm.Pop(logger);
+    return true;
+}
+
+bool
+CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
+{
+    RegisterSet regs = RegisterSet::Volatile();
+    Register logger = regs.takeGeneral();
+
+    masm.Push(logger);
+
+    CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
+    if (!patchableTraceLoggers_.append(patchLocation))
+        return false;
+
+    if (isStart)
+        masm.tracelogStart(logger, textId);
+    else
+        masm.tracelogStop(logger, textId);
+
+    masm.Pop(logger);
+    return true;
+}
+
+bool
+CodeGeneratorShared::emitTracelogStopEvent()
+{
+    RegisterSet regs = RegisterSet::Volatile();
+    Register logger = regs.takeGeneral();
+
+    masm.Push(logger);
+
+    CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
+    if (!patchableTraceLoggers_.append(patchLocation))
+        return false;
+
+    masm.tracelogStop(logger);
+
+    masm.Pop(logger);
+    return true;
+}
+#endif
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -84,16 +84,21 @@ class CodeGeneratorShared : public LInst
     js::Vector<uint32_t, 0, SystemAllocPolicy> cacheList_;
 
     // List of stack slots that have been pushed as arguments to an MCall.
     js::Vector<uint32_t, 0, SystemAllocPolicy> pushedArgumentSlots_;
 
     // Patchable backedges generated for loops.
     Vector<PatchableBackedgeInfo, 0, SystemAllocPolicy> patchableBackedges_;
 
+#if JS_TRACE_LOGGING
+    js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTraceLoggers_;
+    js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTLScripts_;
+#endif
+
     // When profiling is enabled, this is the instrumentation manager which
     // maintains state of what script is currently being generated (for inline
     // scripts) and when instrumentation needs to be emitted or skipped.
     IonInstrumentation sps_;
 
   protected:
     // The offset of the first instruction of the OSR entry block from the
     // beginning of the code buffer.
@@ -438,16 +443,41 @@ class CodeGeneratorShared : public LInst
     //    only used for error reporting, so that we can provide feedback
     //    to the user about which instruction aborted and (perhaps) why.
     OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
                                    jsbytecode *bytecode);
     OutOfLineAbortPar *oolAbortPar(ParallelBailoutCause cause, LInstruction *lir);
     OutOfLinePropagateAbortPar *oolPropagateAbortPar(LInstruction *lir);
     virtual bool visitOutOfLineAbortPar(OutOfLineAbortPar *ool) = 0;
     virtual bool visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool) = 0;
+
+#if JS_TRACE_LOGGING
+  protected:
+    bool emitTracelogScript(bool isStart);
+    bool emitTracelogTree(bool isStart, uint32_t textId);
+
+  public:
+    bool emitTracelogScriptStart() {
+        return emitTracelogScript(/* isStart =*/ true);
+    }
+    bool emitTracelogScriptStop() {
+        return emitTracelogScript(/* isStart =*/ false);
+    }
+    bool emitTracelogStartEvent(uint32_t textId) {
+        return emitTracelogTree(/* isStart =*/ true, textId);
+    }
+    bool emitTracelogStopEvent(uint32_t textId) {
+#ifdef DEBUG
+        return emitTracelogTree(/* isStart =*/ false, textId);
+#else
+        return emitTracelogScript(/* isStart =*/ false);
+#endif
+    }
+    bool emitTracelogStopEvent();
+#endif
 };
 
 // An out-of-line path is generated at the end of the function.
 class OutOfLineCode : public TempObject
 {
     Label entry_;
     Label rejoin_;
     uint32_t framePushed_;
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -44,17 +44,22 @@ CodeGeneratorX86Shared::generatePrologue
 }
 
 bool
 CodeGeneratorX86Shared::generateEpilogue()
 {
     masm.bind(&returnLabel_);
 
 #if JS_TRACE_LOGGING
-    masm.tracelogStop();
+    if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
+        if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
+            return false;
+        if (!emitTracelogScriptStop())
+            return false;
+    }
 #endif
 
     // Pop the stack we allocated at the start of the function.
     masm.freeStack(frameSize());
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.ret();
     return true;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -192,19 +192,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "prmjtime.h"
-#if JS_TRACE_LOGGING
 #include "TraceLogging.h"
-#endif
 
 #include "gc/FindSCCs.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #ifdef JS_ION
 # include "jit/BaselineJIT.h"
 #endif
@@ -2553,71 +2551,57 @@ GCHelperThread::wait(PRCondVar *which)
 #endif
 }
 
 void
 GCHelperThread::threadLoop()
 {
     AutoLockGC lock(rt);
 
-#if JS_TRACE_LOGGING
-    TraceLogging *logger = TraceLogging::getLogger(TraceLogging::GC_BACKGROUND);
-#endif
+    TraceLogger *logger = TraceLoggerForThread(PR_GetCurrentThread());
 
     /*
      * Even on the first iteration the state can be SHUTDOWN or SWEEPING if
      * the stop request or the GC and the corresponding startBackgroundSweep call
      * happen before this thread has a chance to run.
      */
     for (;;) {
         switch (state) {
           case SHUTDOWN:
             return;
           case IDLE:
             wait(wakeup);
             break;
-          case SWEEPING:
-#if JS_TRACE_LOGGING
-            logger->log(TraceLogging::GC_SWEEPING_START);
-#endif
+          case SWEEPING: {
+            AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
             doSweep();
             if (state == SWEEPING)
                 state = IDLE;
             PR_NotifyAllCondVar(done);
-#if JS_TRACE_LOGGING
-            logger->log(TraceLogging::GC_SWEEPING_STOP);
-#endif
             break;
-          case ALLOCATING:
-#if JS_TRACE_LOGGING
-            logger->log(TraceLogging::GC_ALLOCATING_START);
-#endif
+          }
+          case ALLOCATING: {
+            AutoTraceLog logAllocating(logger, TraceLogger::GCAllocating);
             do {
                 Chunk *chunk;
                 {
                     AutoUnlockGC unlock(rt);
                     chunk = Chunk::allocate(rt);
                 }
 
                 /* OOM stops the background allocation. */
-                if (!chunk) {
-#if JS_TRACE_LOGGING
-                    logger->log(TraceLogging::GC_ALLOCATING_STOP);
-#endif
+                if (!chunk)
                     break;
-                }
                 JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
                 rt->gcChunkPool.put(chunk);
             } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt));
             if (state == ALLOCATING)
                 state = IDLE;
-#if JS_TRACE_LOGGING
-            logger->log(TraceLogging::GC_ALLOCATING_STOP);
-#endif
             break;
+          }
           case CANCEL_ALLOCATION:
             state = IDLE;
             PR_NotifyAllCondVar(done);
             break;
         }
     }
 }
 #endif /* JS_THREADSAFE */
@@ -4903,21 +4887,18 @@ Collect(JSRuntime *rt, bool incremental,
     JS_AbortIfWrongThread(rt);
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
     JS_ASSERT(!rt->isHeapBusy());
 
     if (rt->mainThread.suppressGC)
         return;
 
-#if JS_TRACE_LOGGING
-    AutoTraceLog logger(TraceLogging::defaultLogger(),
-                        TraceLogging::GC_START,
-                        TraceLogging::GC_STOP);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(rt);
+    AutoTraceLog logGC(logger, TraceLogger::GC);
 
 #ifdef JS_GC_ZEAL
     if (rt->gcDeterministicOnly && !IsDeterministicGCReason(reason))
         return;
 #endif
 
     JS_ASSERT_IF(!incremental || budget != SliceBudget::Unlimited, JSGC_INCREMENTAL);
 
@@ -5066,37 +5047,32 @@ JS::ShrinkGCBuffers(JSRuntime *rt)
     else
         rt->gcHelperThread.startBackgroundShrink();
 }
 
 void
 js::MinorGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
 #ifdef JSGC_GENERATIONAL
-#if JS_TRACE_LOGGING
-    AutoTraceLog logger(TraceLogging::defaultLogger(),
-                        TraceLogging::MINOR_GC_START,
-                        TraceLogging::MINOR_GC_STOP);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(rt);
+    AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
     rt->gcNursery.collect(rt, reason, nullptr);
     JS_ASSERT_IF(!rt->mainThread.suppressGC, rt->gcNursery.isEmpty());
 #endif
 }
 
 void
 js::MinorGC(JSContext *cx, JS::gcreason::Reason reason)
 {
     // Alternate to the runtime-taking form above which allows marking type
     // objects as needing pretenuring.
 #ifdef JSGC_GENERATIONAL
-#if JS_TRACE_LOGGING
-    AutoTraceLog logger(TraceLogging::defaultLogger(),
-                        TraceLogging::MINOR_GC_START,
-                        TraceLogging::MINOR_GC_STOP);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
+
     Nursery::TypeObjectList pretenureTypes;
     JSRuntime *rt = cx->runtime();
     rt->gcNursery.collect(cx->runtime(), reason, &pretenureTypes);
     for (size_t i = 0; i < pretenureTypes.length(); i++) {
         if (pretenureTypes[i]->canPreTenure())
             pretenureTypes[i]->setShouldPreTenure(cx);
     }
     JS_ASSERT_IF(!rt->mainThread.suppressGC, rt->gcNursery.isEmpty());
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -7,16 +7,17 @@
 #include "jsworkers.h"
 
 #ifdef JS_THREADSAFE
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnativestack.h"
 #include "prmjtime.h"
+#include "TraceLogging.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "jit/IonBuilder.h"
 #include "vm/Debugger.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
@@ -774,22 +775,19 @@ WorkerThread::handleIonWorkload()
 {
 #ifdef JS_ION
     JS_ASSERT(WorkerThreadState().isLocked());
     JS_ASSERT(WorkerThreadState().canStartIonCompile());
     JS_ASSERT(idle());
 
     ionBuilder = WorkerThreadState().ionWorklist().popCopy();
 
-#if JS_TRACE_LOGGING
-    AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
-                        TraceLogging::ION_COMPILE_START,
-                        TraceLogging::ION_COMPILE_STOP,
-                        ionBuilder->script());
-#endif
+    TraceLogger *logger = TraceLoggerForThread(thread);
+    AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, ionBuilder->script()));
+    AutoTraceLog logCompile(logger, TraceLogger::IonCompile);
 
     JSRuntime *rt = ionBuilder->script()->compartment()->runtimeFromAnyThread();
 
     {
         AutoUnlockWorkerThreadState unlock;
         PerThreadData::AutoEnterRuntime enter(threadData.addr(),
                                               ionBuilder->script()->runtimeFromAnyThread());
         jit::IonContext ictx(jit::CompileRuntime::get(rt),
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -49,19 +49,16 @@
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jsworkers.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
-#if JS_TRACE_LOGGING
-#include "TraceLogging.h"
-#endif
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Ion.h"
 #include "js/OldDebugAPI.h"
 #include "js/StructuredClone.h"
 #include "perf/jsperf.h"
@@ -1537,19 +1534,16 @@ PrintInternal(JSContext *cx, const CallA
     for (unsigned i = 0; i < args.length(); i++) {
         JSString *str = JS::ToString(cx, args[i]);
         if (!str)
             return false;
         char *bytes = JSStringToUTF8(cx, str);
         if (!bytes)
             return false;
         fprintf(file, "%s%s", i ? " " : "", bytes);
-#if JS_TRACE_LOGGING
-        TraceLog(TraceLogging::defaultLogger(), bytes);
-#endif
         JS_free(cx, bytes);
     }
 
     fputc('\n', file);
     fflush(file);
 
     args.rval().setUndefined();
     return true;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -24,19 +24,17 @@
 #include "jsiter.h"
 #include "jslibmath.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
-#if JS_TRACE_LOGGING
 #include "TraceLogging.h"
-#endif
 
 #include "builtin/Eval.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "js/OldDebugAPI.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
@@ -1461,20 +1459,20 @@ Interpret(JSContext *cx, RunState &state
         return false;
 
     InterpreterActivation activation(state, cx, entryFrame);
 
     /* The script is used frequently, so keep a local copy. */
     RootedScript script(cx);
     SET_SCRIPT(REGS.fp()->script());
 
-#if JS_TRACE_LOGGING
-    TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_START, script);
-    TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_INTERPRETER);
-#endif
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    uint32_t scriptLogId = TraceLogCreateTextId(logger, script);
+    TraceLogStartEvent(logger, scriptLogId);
+    TraceLogStartEvent(logger, TraceLogger::Interpreter);
 
     /*
      * Pool of rooters for use in this interpreter frame. References to these
      * are used for local variables within interpreter cases. This avoids
      * creating new rooters each time an interpreter case is entered, and also
      * correctness pitfalls due to incorrect compilation of destructor calls
      * around computed gotos.
      */
@@ -1796,19 +1794,21 @@ CASE(JSOP_RETRVAL)
     CHECK_BRANCH();
 
   successful_return_continuation:
     interpReturnOK = true;
   return_continuation:
     if (activation.entryFrame() != REGS.fp())
   inline_return:
     {
-#if JS_TRACE_LOGGING
-        TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_STOP);
-#endif
+        // Stop the engine. (No details about which engine exactly, could be
+        // interpreter, Baseline or IonMonkey.)
+        TraceLogStopEvent(logger);
+        // Stop the script. (Again no details about which script exactly.)
+        TraceLogStopEvent(logger);
 
         if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
             interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
 
         if (!REGS.fp()->isYielding())
             REGS.fp()->epilogue(cx);
         else
             probes::ExitScript(cx, script, script->functionNonDelazifying(),
@@ -2672,20 +2672,19 @@ CASE(JSOP_FUNCALL)
     if (!activation.pushInlineFrame(args, funScript, initial))
         goto error;
 
     if (newType)
         REGS.fp()->setUseNewType();
 
     SET_SCRIPT(REGS.fp()->script());
 
-#if JS_TRACE_LOGGING
-    TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_START, script);
-    TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_INTERPRETER);
-#endif
+    uint32_t scriptLogId = TraceLogCreateTextId(logger, script);
+    TraceLogStartEvent(logger, scriptLogId);
+    TraceLogStartEvent(logger, TraceLogger::Interpreter);
 
     if (!REGS.fp()->prologue(cx))
         goto error;
     if (MOZ_UNLIKELY(cx->compartment()->debugMode())) {
         switch (ScriptDebugPrologue(cx, REGS.fp(), REGS.pc)) {
           case JSTRAP_CONTINUE:
             break;
           case JSTRAP_RETURN:
@@ -3506,19 +3505,18 @@ DEFAULT()
     if (!REGS.fp()->isYielding())
         REGS.fp()->epilogue(cx);
     else
         probes::ExitScript(cx, script, script->functionNonDelazifying(),
                            REGS.fp()->hasPushedSPSFrame());
 
     gc::MaybeVerifyBarriers(cx, true);
 
-#if JS_TRACE_LOGGING
-        TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_STOP);
-#endif
+    TraceLogStopEvent(logger);
+    TraceLogStopEvent(logger, scriptLogId);
 
 #ifdef JS_ION
     /*
      * This path is used when it's guaranteed the method can be finished
      * inside the JIT.
      */
   leave_on_safe_point:
 #endif
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -3,16 +3,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/RegExpObject.h"
 
 #include "mozilla/MemoryReporting.h"
 
+#include "TraceLogging.h"
+
 #include "frontend/TokenStream.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
 #include "vm/Xdr.h"
 #include "yarr/YarrSyntaxChecker.h"
 
 #include "jsobjinlines.h"
@@ -515,19 +517,24 @@ RegExpShared::compileMatchOnlyIfNecessar
         return true;
     return compile(cx, true);
 }
 
 RegExpRunStatus
 RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length,
                       size_t *lastIndex, MatchPairs &matches)
 {
-    /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx))
-        return RegExpRunStatus_Error;
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+
+    {
+        /* Compile the code at point-of-use. */
+        AutoTraceLog logCompile(logger, TraceLogger::YarrCompile);
+        if (!compileIfNecessary(cx))
+            return RegExpRunStatus_Error;
+    }
 
     /* Ensure sufficient memory for output vector. */
     if (!matches.initArray(pairCount()))
         return RegExpRunStatus_Error;
 
     /*
      * |displacement| emulates sticky mode by matching from this offset
      * into the char buffer and subtracting the delta off at the end.
@@ -542,22 +549,28 @@ RegExpShared::execute(JSContext *cx, con
         length -= displacement;
         start = 0;
     }
 
     unsigned *outputBuf = matches.rawBuf();
     unsigned result;
 
 #if ENABLE_YARR_JIT
-    if (codeBlock.isFallBack())
+    if (codeBlock.isFallBack()) {
+        AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret);
         result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
-    else
+    } else {
+        AutoTraceLog logJIT(logger, TraceLogger::YarrJIT);
         result = codeBlock.execute(chars, start, length, (int *)outputBuf).start;
+    }
 #else
-    result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
+    {
+        AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret);
+        result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
+    }
 #endif
 
     if (result == JSC::Yarr::offsetError) {
         reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError);
         return RegExpRunStatus_Error;
     }
 
     if (result == JSC::Yarr::offsetNoMatch)
@@ -568,35 +581,41 @@ RegExpShared::execute(JSContext *cx, con
     *lastIndex = matches[0].limit;
     return RegExpRunStatus_Success;
 }
 
 RegExpRunStatus
 RegExpShared::executeMatchOnly(JSContext *cx, const jschar *chars, size_t length,
                                size_t *lastIndex, MatchPair &match)
 {
-    /* Compile the code at point-of-use. */
-    if (!compileMatchOnlyIfNecessary(cx))
-        return RegExpRunStatus_Error;
+    TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
+
+    {
+        /* Compile the code at point-of-use. */
+        AutoTraceLog logCompile(logger, TraceLogger::YarrCompile);
+        if (!compileMatchOnlyIfNecessary(cx))
+            return RegExpRunStatus_Error;
+    }
 
 #ifdef DEBUG
     const size_t origLength = length;
 #endif
     size_t start = *lastIndex;
     size_t displacement = 0;
 
     if (sticky()) {
         displacement = start;
         chars += displacement;
         length -= displacement;
         start = 0;
     }
 
 #if ENABLE_YARR_JIT
     if (!codeBlock.isFallBack()) {
+        AutoTraceLog logJIT(logger, TraceLogger::YarrJIT);
         MatchResult result = codeBlock.execute(chars, start, length);
         if (!result)
             return RegExpRunStatus_Success_NotFound;
 
         match = MatchPair(result.start, result.end);
         match.displace(displacement);
         *lastIndex = match.limit;
         return RegExpRunStatus_Success;
@@ -608,18 +627,21 @@ RegExpShared::executeMatchOnly(JSContext
      * Unfortunately, the interpreter does not have a MatchOnly mode, so a
      * temporary output vector must be provided.
      */
     JS_ASSERT(hasBytecode());
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     if (!matches.initArray(pairCount()))
         return RegExpRunStatus_Error;
 
-    unsigned result =
-        JSC::Yarr::interpret(cx, bytecode, chars, length, start, matches.rawBuf());
+    unsigned result;
+    {
+        AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret);
+        result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, matches.rawBuf());
+    }
 
     if (result == JSC::Yarr::offsetError) {
         reportYarrError(cx, nullptr, JSC::Yarr::RuntimeError);
         return RegExpRunStatus_Error;
     }
 
     if (result == JSC::Yarr::offsetNoMatch)
         return RegExpRunStatus_Success_NotFound;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -67,16 +67,19 @@ using JS::DoubleNaNValue;
 const JSSecurityCallbacks js::NullSecurityCallbacks = { };
 
 PerThreadData::PerThreadData(JSRuntime *runtime)
   : PerThreadDataFriendFields(),
     runtime_(runtime),
     ionTop(nullptr),
     jitJSContext(nullptr),
     jitStackLimit(0),
+#if JS_TRACE_LOGGING
+    traceLogger(nullptr),
+#endif
     activation_(nullptr),
     asmJSActivationStack_(nullptr),
 #ifdef JS_ARM_SIMULATOR
     simulator_(nullptr),
     simulatorStackLimit_(0),
 #endif
     dtoaState(nullptr),
     suppressGC(0),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -51,16 +51,19 @@
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 namespace js {
 
 class PerThreadData;
 class ThreadSafeContext;
 class AutoKeepAtoms;
+#if JS_TRACE_LOGGING
+class TraceLogger;
+#endif
 
 /* Thread Local Storage slot for storing the runtime for a thread. */
 extern mozilla::ThreadLocal<PerThreadData*> TlsPerThreadData;
 
 } // namespace js
 
 struct DtoaState;
 
@@ -533,16 +536,20 @@ class PerThreadData : public PerThreadDa
     /*
      * The stack limit checked by JIT code. This stack limit may be temporarily
      * set to null to force JIT code to exit (e.g., for the operation callback).
      */
     uintptr_t            jitStackLimit;
 
     inline void setJitStackLimit(uintptr_t limit);
 
+#if JS_TRACE_LOGGING
+    TraceLogger         *traceLogger;
+#endif
+
     /*
      * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This
      * stack is used by JSRuntime::requestInterrupt to stop long-running asm.js
      * without requiring dynamic polling operations in the generated
      * code. Since requestInterrupt may run on a separate thread than the
      * JSRuntime's owner thread all reads/writes must be synchronized (by
      * rt->interruptLock).
      */
--- a/js/src/yarr/YarrJIT.h
+++ b/js/src/yarr/YarrJIT.h
@@ -38,20 +38,16 @@
 #include "yarr/Yarr.h"
 
 #if WTF_CPU_X86 && !WTF_COMPILER_MSVC && !WTF_COMPILER_SUNCC
 #define YARR_CALL __attribute__ ((regparm (3)))
 #else
 #define YARR_CALL
 #endif
 
-#if JS_TRACE_LOGGING
-#include "TraceLogging.h"
-#endif
-
 #include "jit/JitCommon.h"
 
 namespace JSC {
 
 class JSGlobalData;
 class ExecutablePool;
 
 namespace Yarr {
@@ -94,63 +90,39 @@ public:
     bool has16BitCodeMatchOnly() const { return m_matchOnly16.allocSize(); }
     void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; }
 
 #if YARR_8BIT_CHAR_SUPPORT
     MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output)
     {
         ASSERT(has8BitCode());
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::YARR_JIT_START,
-                                js::TraceLogging::YARR_JIT_STOP);
-#endif
-
         return MatchResult(reinterpret_cast<YarrJITCode8>(m_ref8.code().executableAddress())(input, start, length, output));
     }
 
     MatchResult execute(const LChar* input, unsigned start, unsigned length)
     {
         ASSERT(has8BitCodeMatchOnly());
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::YARR_JIT_START,
-                                js::TraceLogging::YARR_JIT_STOP);
-#endif
-
         return MatchResult(reinterpret_cast<YarrJITCodeMatchOnly8>(m_matchOnly8.code().executableAddress())(input, start, length));
     }
 #endif
 
     MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output)
     {
         ASSERT(has16BitCode());
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::YARR_JIT_START,
-                                js::TraceLogging::YARR_JIT_STOP);
-#endif
-
         YarrJITCode16 fn = JS_FUNC_TO_DATA_PTR(YarrJITCode16, m_ref16.code().executableAddress());
         return MatchResult(CALL_GENERATED_YARR_CODE4(fn, input, start, length, output));
     }
 
     MatchResult execute(const UChar* input, unsigned start, unsigned length)
     {
         ASSERT(has16BitCodeMatchOnly());
 
-#if JS_TRACE_LOGGING
-        js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
-                                js::TraceLogging::YARR_JIT_START,
-                                js::TraceLogging::YARR_JIT_STOP);
-#endif
-
         YarrJITCodeMatchOnly16 fn = JS_FUNC_TO_DATA_PTR(YarrJITCodeMatchOnly16, m_matchOnly16.code().executableAddress());
         return MatchResult(CALL_GENERATED_YARR_CODE3(fn, input, start, length));
     }
 
 #if ENABLE_REGEXP_TRACING
     void *getAddr() { return m_ref.code().executableAddress(); }
 #endif