Bug 944701 - Tracelogging: Add shell functions to enable/disable tracelogging, r=till
authorHannes Verschore <hv1989@gmail.com>
Thu, 17 Apr 2014 12:26:18 +0200
changeset 197608 9238eede008048d3393afd9e92916125a48e1fd2
parent 197607 e7f563c9c7b8d4d0713e97daeda7fe0d77962864
child 197609 e57c8c5c2ccde5059a71a570e53ed7c070fa875c
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
bugs944701
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 944701 - Tracelogging: Add shell functions to enable/disable tracelogging, r=till
js/src/builtin/TestingFunctions.cpp
js/src/vm/TraceLogging.cpp
js/src/vm/TraceLogging.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -18,16 +18,17 @@
 
 #include "jit/AsmJS.h"
 #include "jit/AsmJSLink.h"
 #include "js/StructuredClone.h"
 #include "vm/ForkJoin.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
+#include "vm/TraceLogging.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
 using mozilla::ArrayLength;
@@ -1483,16 +1484,36 @@ static bool
 TimesAccessed(JSContext *cx, unsigned argc, jsval *vp)
 {
     static int32_t accessed = 0;
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(++accessed);
     return true;
 }
 
+static bool
+EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    args.rval().setBoolean(TraceLoggerEnable(logger));
+
+    return true;
+}
+
+static bool
+DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    args.rval().setBoolean(TraceLoggerDisable(logger));
+
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -1718,16 +1739,25 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("neuter", Neuter, 1, 0,
 "neuter(buffer)",
 "  Neuter the given ArrayBuffer object as if it had been transferred to a WebWorker."),
 
     JS_FN_HELP("workerThreadCount", WorkerThreadCount, 0, 0,
 "workerThreadCount()",
 "  Returns the number of worker threads available for off-main-thread tasks."),
 
+    JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
+"startTraceLogger()",
+"  Start logging the mainThread.\n"
+"  Note: tracelogging starts automatically. Disable it by setting environment variable\n"
+"  TLOPTIONS=disableMainThread"),
+
+    JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
+"startTraceLogger()",
+"  Stop logging the mainThread."),
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -105,16 +105,17 @@ const char* const text[] = {
     "EffectiveAddressAnalysis",
     "EliminateDeadCode",
     "EdgeCaseAnalysis",
     "EliminateRedundantChecks"
 };
 
 TraceLogger::TraceLogger()
  : enabled(false),
+   enabledTimes(0),
    failed(false),
    nextTextId(0),
    treeOffset(0),
    top(nullptr)
 { }
 
 bool
 TraceLogger::init(uint32_t loggerId)
@@ -123,17 +124,17 @@ TraceLogger::init(uint32_t loggerId)
         return false;
     if (!tree.init())
         return false;
     if (!stack.init())
         return false;
     if (!events.init())
         return false;
 
-    JS_ASSERT(loggerId <= 999);
+    MOZ_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"];
@@ -154,45 +155,155 @@ TraceLogger::init(uint32_t loggerId)
         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;
+    treeEntry.setStart(start);
+    treeEntry.setStop(0);
+    treeEntry.setTextId(0);
+    treeEntry.setHasChildren(false);
+    treeEntry.setNextId(0);
 
     StackEntry &stackEntry = stack.pushUninitialized();
-    stackEntry.treeId = 0;
-    stackEntry.lastChildId = 0;
-    stackEntry.active = true;
+    stackEntry.setTreeId(0);
+    stackEntry.setLastChildId(0);
+    stackEntry.setActive(true);
 
     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++) {
         mozilla::DebugOnly<uint32_t> textId = createTextId(text[i]);
-        JS_ASSERT(textId == i);
+        MOZ_ASSERT(textId == i);
     }
 
     enabled = true;
+    enabledTimes = 1;
+    return true;
+}
+
+bool
+TraceLogger::enable()
+{
+    if (enabled) {
+        enabledTimes++;
+        return true;
+    }
+
+    if (failed)
+        return false;
+
+    if (!tree.ensureSpaceBeforeAdd(stack.size())) {
+        if (!flush()) {
+            fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
+            failed = true;
+            return false;
+        }
+        if (!tree.ensureSpaceBeforeAdd(stack.size())) {
+            fprintf(stderr, "TraceLogging: Couldn't reserve enough space.\n");
+            failed = true;
+            return false;
+        }
+    }
+
+    uint64_t start = rdtsc() - traceLoggers.startupTime;
+    StackEntry *parent = &stack[0];
+    for (uint32_t i = 1; i < stack.size(); i++) {
+        if (!traceLoggers.isTextIdEnabled(stack[i].textId()))
+            continue;
+#ifdef DEBUG
+        TreeEntry entry;
+        if (!getTreeEntry(parent->treeId(), &entry))
+            return false;
+#endif
+
+        if (parent->lastChildId() == 0) {
+            MOZ_ASSERT(!entry.hasChildren());
+            MOZ_ASSERT(parent->treeId() == tree.currentId() + treeOffset);
+            if (!updateHasChildren(parent->treeId())) {
+                fprintf(stderr, "TraceLogging: Couldn't update an entry.\n");
+                failed = true;
+                return false;
+            }
+        } else {
+            MOZ_ASSERT(entry.hasChildren() == 1);
+            if (!updateNextId(parent->lastChildId(), tree.nextId() + treeOffset)) {
+                fprintf(stderr, "TraceLogging: Couldn't update an entry.\n");
+                failed = true;
+                return false;
+            }
+        }
+
+        TreeEntry &treeEntry = tree.pushUninitialized();
+        treeEntry.setStart(start);
+        treeEntry.setStop(0);
+        treeEntry.setTextId(stack[i].textId());
+        treeEntry.setHasChildren(false);
+        treeEntry.setNextId(0);
+
+        stack[i].setActive(true);
+        stack[i].setTreeId(tree.currentId() + treeOffset);
+
+        parent->setLastChildId(tree.currentId() + treeOffset);
+
+        parent = &stack[i];
+    }
+
+    enabled = true;
+    enabledTimes = 1;
+
+    return true;
+}
+
+bool
+TraceLogger::disable()
+{
+    if (failed)
+        return false;
+
+    if (!enabled)
+        return true;
+
+    if (enabledTimes > 1) {
+        enabledTimes--;
+        return true;
+    }
+
+    uint64_t stop = rdtsc() - traceLoggers.startupTime;
+    for (uint32_t i = 1; i < stack.size(); i++) {
+        if (!stack[i].active())
+            continue;
+
+        if (!updateStop(stack[i].treeId(), stop)) {
+            fprintf(stderr, "TraceLogging: Failed to stop an event.\n");
+            failed = true;
+            enabled = false;
+            return false;
+        }
+
+        stack[i].setActive(false);
+    }
+
+
+    enabled = false;
+    enabledTimes = 0;
+
     return true;
 }
 
 bool
 TraceLogger::flush()
 {
-    JS_ASSERT(!failed);
+    MOZ_ASSERT(!failed);
 
     if (treeFile) {
         // Format data in big endian.
         for (size_t i = 0; i < tree.size(); i++)
             entryToBigEndian(&tree[i]);
 
         int success = fseek(treeFile, 0, SEEK_END);
         if (success != 0)
@@ -359,33 +470,33 @@ TraceLogger::logTimestamp(uint32_t id)
     EventEntry &entry = events.pushUninitialized();
     entry.time = time;
     entry.textId = id;
 }
 
 void
 TraceLogger::entryToBigEndian(TreeEntry *entry)
 {
-    entry->start = htobe64(entry->start);
-    entry->stop = htobe64(entry->stop);
-    entry->u.value = htobe32((entry->u.s.textId << 1) + entry->u.s.hasChildren);
-    entry->nextId = htobe32(entry->nextId);
+    entry->start_ = htobe64(entry->start_);
+    entry->stop_ = htobe64(entry->stop_);
+    entry->u.value_ = htobe32((entry->u.s.textId_ << 1) + entry->u.s.hasChildren_);
+    entry->nextId_ = htobe32(entry->nextId_);
 }
 
 void
 TraceLogger::entryToSystemEndian(TreeEntry *entry)
 {
-    entry->start = be64toh(entry->start);
-    entry->stop = be64toh(entry->stop);
+    entry->start_ = be64toh(entry->start_);
+    entry->stop_ = be64toh(entry->stop_);
 
-    uint32_t data = be32toh(entry->u.value);
-    entry->u.s.textId = data >> 1;
-    entry->u.s.hasChildren = data & 0x1;
+    uint32_t data = be32toh(entry->u.value_);
+    entry->u.s.textId_ = data >> 1;
+    entry->u.s.hasChildren_ = data & 0x1;
 
-    entry->nextId = be32toh(entry->nextId);
+    entry->nextId_ = be32toh(entry->nextId_);
 }
 
 bool
 TraceLogger::getTreeEntry(uint32_t treeId, TreeEntry *entry)
 {
     // Entry is still in memory
     if (treeId >= treeOffset) {
         *entry = tree[treeId];
@@ -422,73 +533,82 @@ TraceLogger::saveTreeEntry(uint32_t tree
 
 bool
 TraceLogger::updateHasChildren(uint32_t treeId, bool hasChildren)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.u.s.hasChildren = hasChildren;
+        entry.setHasChildren(hasChildren);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].u.s.hasChildren = hasChildren;
+    tree[treeId - treeOffset].setHasChildren(hasChildren);
     return true;
 }
 
 bool
 TraceLogger::updateNextId(uint32_t treeId, uint32_t nextId)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.nextId = nextId;
+        entry.setNextId(nextId);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].nextId = nextId;
+    tree[treeId - treeOffset].setNextId(nextId);
     return true;
 }
 
 bool
 TraceLogger::updateStop(uint32_t treeId, uint64_t timestamp)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.stop = timestamp;
+        entry.setStop(timestamp);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].stop = timestamp;
+    tree[treeId - treeOffset].setStop(timestamp);
     return true;
 }
 
 void
 TraceLogger::startEvent(uint32_t id)
 {
-    if (!enabled)
+    if (failed)
         return;
 
     if (!stack.ensureSpaceBeforeAdd()) {
         fprintf(stderr, "TraceLogging: Failed to allocate space to keep track of the stack.\n");
         enabled = false;
         failed = true;
         return;
     }
 
+    if (!enabled) {
+        StackEntry &stackEntry = stack.pushUninitialized();
+        stackEntry.setTreeId(tree.currentId() + treeOffset);
+        stackEntry.setLastChildId(0);
+        stackEntry.setTextId(id);
+        stackEntry.setActive(false);
+        return;
+    }
+
     if (!tree.ensureSpaceBeforeAdd()) {
         uint64_t start = rdtsc() - traceLoggers.startupTime;
         if (!flush()) {
             fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
             enabled = false;
             failed = true;
             return;
         }
@@ -512,110 +632,109 @@ TraceLogger::startEvent(uint32_t id)
         return;
     }
 }
 
 TraceLogger::StackEntry &
 TraceLogger::getActiveAncestor()
 {
     uint32_t parentId = stack.currentId();
-    while (!stack[parentId].active)
+    while (!stack[parentId].active())
         parentId--;
     return stack[parentId];
 }
 
 bool
 TraceLogger::startEvent(uint32_t id, uint64_t timestamp)
 {
     // When a textId is disabled, a stack entry still needs to be pushed,
     // together with an annotation that nothing needs to get done when receiving
     // the stop event.
     if (!traceLoggers.isTextIdEnabled(id)) {
         StackEntry &stackEntry = stack.pushUninitialized();
-        stackEntry.active = false;
+        stackEntry.setActive(false);
         return true;
     }
 
     // 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 = getActiveAncestor();
 #ifdef DEBUG
     TreeEntry entry;
-    if (!getTreeEntry(parent.treeId, &entry))
+    if (!getTreeEntry(parent.treeId(), &entry))
         return false;
 #endif
 
-    if (parent.lastChildId == 0) {
-        JS_ASSERT(entry.u.s.hasChildren == 0);
-        JS_ASSERT(parent.treeId == tree.currentId() + treeOffset);
+    if (parent.lastChildId() == 0) {
+        MOZ_ASSERT(!entry.hasChildren());
+        MOZ_ASSERT(parent.treeId() == tree.currentId() + treeOffset);
 
-        if (!updateHasChildren(parent.treeId))
+        if (!updateHasChildren(parent.treeId()))
             return false;
     } else {
-        JS_ASSERT(entry.u.s.hasChildren == 1);
+        MOZ_ASSERT(entry.hasChildren());
 
-        if (!updateNextId(parent.lastChildId, tree.nextId() + treeOffset))
+        if (!updateNextId(parent.lastChildId(), tree.nextId() + treeOffset))
             return false;
     }
 
     // Add a new tree entry.
     TreeEntry &treeEntry = tree.pushUninitialized();
-    treeEntry.start = timestamp;
-    treeEntry.stop = 0;
-    treeEntry.u.s.textId = id;
-    treeEntry.u.s.hasChildren = false;
-    treeEntry.nextId = 0;
+    treeEntry.setStart(timestamp);
+    treeEntry.setStop(0);
+    treeEntry.setTextId(id);
+    treeEntry.setHasChildren(false);
+    treeEntry.setNextId(0);
 
     // Add a new stack entry.
     StackEntry &stackEntry = stack.pushUninitialized();
-    stackEntry.treeId = tree.currentId() + treeOffset;
-    stackEntry.lastChildId = 0;
-    stackEntry.active = true;
+    stackEntry.setTreeId(tree.currentId() + treeOffset);
+    stackEntry.setLastChildId(0);
+    stackEntry.setActive(true);
 
     // Set the last child of the parent to this newly added entry.
-    parent.lastChildId = tree.currentId() + treeOffset;
+    parent.setLastChildId(tree.currentId() + treeOffset);
 
     return true;
 }
 
 void
 TraceLogger::stopEvent(uint32_t id)
 {
 #ifdef DEBUG
     TreeEntry entry;
-    JS_ASSERT(getTreeEntry(stack.current().treeId, &entry));
-    JS_ASSERT(entry.u.s.textId == id);
+    MOZ_ASSERT(getTreeEntry(stack.current().treeId(), &entry));
+    MOZ_ASSERT(entry.textId() == id);
 #endif
     stopEvent();
 }
 
 void
 TraceLogger::stopEvent()
 {
-    if (!enabled)
-        return;
-
-    if (stack.current().active) {
+    if (enabled && stack.current().active()) {
         uint64_t stop = rdtsc() - traceLoggers.startupTime;
-        if (!updateStop(stack.current().treeId, stop)) {
+        if (!updateStop(stack.current().treeId(), stop)) {
             fprintf(stderr, "TraceLogging: Failed to stop an event.\n");
             enabled = false;
             failed = true;
             return;
         }
     }
     stack.pop();
 }
 
 TraceLogging::TraceLogging()
 {
     initialized = false;
     enabled = false;
+    mainThreadEnabled = true;
+    offThreadEnabled = true;
     loggerId = 0;
 
 #ifdef JS_THREADSAFE
     lock = PR_NewLock();
     if (!lock)
         MOZ_CRASH();
 #endif // JS_THREADSAFE
 }
@@ -678,17 +797,17 @@ TraceLogging::lazyInit()
 
 #ifdef JS_THREADSAFE
     if (!threadLoggers.init())
         return false;
 #endif // JS_THREADSAFE
 
     const char *env = getenv("TLLOG");
     if (!env)
-        return false;
+        env = "";
 
     if (strstr(env, "help")) {
         fflush(nullptr);
         printf(
             "\n"
             "usage: TLLOG=option,option,option,... where options can be:\n"
             "\n"
             "Collections:\n"
@@ -745,16 +864,38 @@ TraceLogging::lazyInit()
         enabledTextIds[TraceLogger::LICM] = true;
         enabledTextIds[TraceLogger::RangeAnalysis] = true;
         enabledTextIds[TraceLogger::EffectiveAddressAnalysis] = true;
         enabledTextIds[TraceLogger::EliminateDeadCode] = true;
         enabledTextIds[TraceLogger::EdgeCaseAnalysis] = true;
         enabledTextIds[TraceLogger::EliminateRedundantChecks] = true;
     }
 
+    const char *options = getenv("TLOPTIONS");
+    if (options) {
+        if (strstr(options, "help")) {
+            fflush(nullptr);
+            printf(
+                "\n"
+                "usage: TLOPTIONS=option,option,option,... where options can be:\n"
+                "\n"
+                "  DisableMainThread        Don't start logging the mainThread automatically.\n"
+                "  DisableOffThread         Don't start logging the off mainThread automatically.\n"
+            );
+            printf("\n");
+            exit(0);
+            /*NOTREACHED*/
+        }
+
+        if (strstr(options, "DisableMainThread"))
+           mainThreadEnabled = false;
+        if (strstr(options, "DisableOffThread"))
+           offThreadEnabled = false;
+    }
+
     startupTime = rdtsc();
     enabled = true;
     return true;
 }
 
 TraceLogger *
 js::TraceLoggerForMainThread(jit::CompileRuntime *runtime)
 {
@@ -788,16 +929,19 @@ TraceLogging::forMainThread(PerThreadDat
         if (!lazyInit())
             return nullptr;
 
         TraceLogger *logger = create();
         mainThread->traceLogger = logger;
 
         if (!mainThreadLoggers.append(logger))
             return nullptr;
+
+        if (!mainThreadEnabled)
+            logger->disable();
     }
 
     return mainThread->traceLogger;
 }
 
 TraceLogger *
 js::TraceLoggerForCurrentThread()
 {
@@ -826,16 +970,19 @@ TraceLogging::forThread(PRThread *thread
     if (!logger)
         return nullptr;
 
     if (!threadLoggers.add(p, thread, logger)) {
         delete logger;
         return nullptr;
     }
 
+    if (!offThreadEnabled)
+        logger->disable();
+
     return logger;
 }
 #endif // JS_THREADSAFE
 
 TraceLogger *
 TraceLogging::create()
 {
     if (loggerId > 999) {
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -147,29 +147,31 @@ class ContinuousSpace {
         return next_;
     }
 
     T &next() {
         return data()[next_];
     }
 
     uint32_t currentId() {
-        JS_ASSERT(next_ > 0);
+        MOZ_ASSERT(next_ > 0);
         return next_ - 1;
     }
 
     T &current() {
         return data()[currentId()];
     }
 
-    bool ensureSpaceBeforeAdd() {
-        if (next_ < capacity_)
+    bool ensureSpaceBeforeAdd(uint32_t count = 1) {
+        if (next_ + count <= capacity_)
             return true;
 
         uint32_t nCapacity = capacity_ * 2;
+        if (next_ + count > nCapacity)
+            nCapacity = next_ + count;
         T *entries = (T *) js_realloc(data_, nCapacity * sizeof(T));
 
         if (!entries)
             return false;
 
         data_ = entries;
         capacity_ = nCapacity;
 
@@ -182,21 +184,22 @@ class ContinuousSpace {
     }
 
     void push(T &data) {
         MOZ_ASSERT(next_ < capacity_);
         data()[next_++] = data;
     }
 
     T &pushUninitialized() {
+        MOZ_ASSERT(next_ < capacity_);
         return data()[next_++];
     }
 
     void pop() {
-        JS_ASSERT(next_ > 0);
+        MOZ_ASSERT(next_ > 0);
         next_--;
     }
 
     void clear() {
         next_ = 0;
     }
 };
 
@@ -254,50 +257,112 @@ class TraceLogger
     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;
+        uint64_t start_;
+        uint64_t stop_;
         union {
             struct {
-                uint32_t textId: 31;
-                uint32_t hasChildren: 1;
+                uint32_t textId_: 31;
+                uint32_t hasChildren_: 1;
             } s;
-            uint32_t value;
+            uint32_t value_;
         } u;
-        uint32_t nextId;
+        uint32_t nextId_;
 
         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;
+            start_ = start;
+            stop_ = stop;
+            u.s.textId_ = textId;
+            u.s.hasChildren_ = hasChildren;
+            nextId_ = nextId;
         }
         TreeEntry()
         { }
+        uint64_t start() {
+            return start_;
+        }
+        uint64_t stop() {
+            return stop_;
+        }
+        uint32_t textId() {
+            return u.s.textId_;
+        }
+        bool hasChildren() {
+            return u.s.hasChildren_;
+        }
+        uint32_t nextId() {
+            return nextId_;
+        }
+        void setStart(uint64_t start) {
+            start_ = start;
+        }
+        void setStop(uint64_t stop) {
+            stop_ = stop;
+        }
+        void setTextId(uint32_t textId) {
+            MOZ_ASSERT(textId < (1<<31) );
+            u.s.textId_ = textId;
+        }
+        void setHasChildren(bool hasChildren) {
+            u.s.hasChildren_ = hasChildren;
+        }
+        void setNextId(uint32_t nextId) {
+            nextId_ = nextId;
+        }
     };
 
     // Helper structure for keeping track of the current entries in
     // the tree. Pushed by `start(id)`, popped by `stop(id)`. The active flag
     // is used to know if a subtree doesn't need to get logged.
     struct StackEntry {
-        uint32_t treeId;
-        uint32_t lastChildId;
-        bool active;
+        uint32_t treeId_;
+        uint32_t lastChildId_;
+        struct {
+            uint32_t textId_: 31;
+            uint32_t active_: 1;
+        } s;
         StackEntry(uint32_t treeId, uint32_t lastChildId, bool active = true)
-          : treeId(treeId), lastChildId(lastChildId), active(active)
-        { }
+          : treeId_(treeId), lastChildId_(lastChildId)
+        {
+            s.textId_ = 0;
+            s.active_ = active;
+        }
+        uint32_t treeId() {
+            return treeId_;
+        }
+        uint32_t lastChildId() {
+            return lastChildId_;
+        }
+        uint32_t textId() {
+            return s.textId_;
+        }
+        bool active() {
+            return s.active_;
+        }
+        void setTreeId(uint32_t treeId) {
+            treeId_ = treeId;
+        }
+        void setLastChildId(uint32_t lastChildId) {
+            lastChildId_ = lastChildId;
+        }
+        void setTextId(uint32_t textId) {
+            MOZ_ASSERT(textId < (1<<31) );
+            s.textId_ = textId;
+        }
+        void setActive(bool active) {
+            s.active_ = active;
+        }
     };
 
     // 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)
@@ -305,16 +370,17 @@ class TraceLogger
         { }
     };
 
     FILE *dictFile;
     FILE *treeFile;
     FILE *eventFile;
 
     bool enabled;
+    uint32_t enabledTimes;
     bool failed;
     uint32_t nextTextId;
 
     PointerHashMap pointerMap;
 
     ContinuousSpace<TreeEntry> tree;
     ContinuousSpace<StackEntry> stack;
     ContinuousSpace<EventEntry> events;
@@ -351,16 +417,19 @@ class TraceLogger
     bool flush();
 
   public:
     TraceLogger();
     ~TraceLogger();
 
     bool init(uint32_t loggerId);
 
+    bool enable();
+    bool disable();
+
     // 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).
@@ -392,16 +461,18 @@ class TraceLogging
                     PointerHasher<PRThread *, 3>,
                     SystemAllocPolicy> ThreadLoggerHashMap;
 #endif // JS_THREADSAFE
     typedef Vector<TraceLogger *, 1, js::SystemAllocPolicy > MainThreadLoggers;
 
     bool initialized;
     bool enabled;
     bool enabledTextIds[TraceLogger::LAST];
+    bool mainThreadEnabled;
+    bool offThreadEnabled;
 #ifdef JS_THREADSAFE
     ThreadLoggerHashMap threadLoggers;
 #endif // JS_THREADSAFE
     MainThreadLoggers mainThreadLoggers;
     uint32_t loggerId;
     FILE *out;
 
   public:
@@ -443,16 +514,31 @@ inline TraceLogger *TraceLoggerForMainTh
 inline TraceLogger *TraceLoggerForMainThread(jit::CompileRuntime *runtime) {
     return nullptr;
 };
 inline TraceLogger *TraceLoggerForCurrentThread() {
     return nullptr;
 };
 #endif
 
+inline bool TraceLoggerEnable(TraceLogger *logger) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->enable();
+#endif
+    return false;
+}
+inline bool TraceLoggerDisable(TraceLogger *logger) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->disable();
+#endif
+    return false;
+}
+
 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,