summaryrefslogtreecommitdiffstats
path: root/js/src/vm/TraceLogging.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/TraceLogging.cpp')
-rw-r--r--js/src/vm/TraceLogging.cpp1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp
new file mode 100644
index 000000000..3d6183b3c
--- /dev/null
+++ b/js/src/vm/TraceLogging.cpp
@@ -0,0 +1,1085 @@
+/* -*- 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 "vm/TraceLogging.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ScopeExit.h"
+
+#include <string.h>
+
+#include "jsapi.h"
+#include "jsprf.h"
+#include "jsscript.h"
+
+#include "jit/BaselineJIT.h"
+#include "jit/CompileWrappers.h"
+#include "threading/LockGuard.h"
+#include "vm/Runtime.h"
+#include "vm/Time.h"
+#include "vm/TraceLoggingGraph.h"
+
+#include "jit/JitFrames-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+using mozilla::DebugOnly;
+using mozilla::NativeEndian;
+
+TraceLoggerThreadState* traceLoggerState = nullptr;
+
+#if defined(MOZ_HAVE_RDTSC)
+
+uint64_t inline rdtsc() {
+ return ReadTimestampCounter();
+}
+
+#elif defined(__powerpc__)
+static __inline__ uint64_t
+rdtsc(void)
+{
+ uint64_t result=0;
+ uint32_t upper, lower,tmp;
+ __asm__ volatile(
+ "0: \n"
+ "\tmftbu %0 \n"
+ "\tmftb %1 \n"
+ "\tmftbu %2 \n"
+ "\tcmpw %2,%0 \n"
+ "\tbne 0b \n"
+ : "=r"(upper),"=r"(lower),"=r"(tmp)
+ );
+ result = upper;
+ result = result<<32;
+ result = result|lower;
+
+ return result;
+
+}
+#elif defined(__arm__)
+
+#include <sys/time.h>
+
+static __inline__ uint64_t
+rdtsc(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ uint64_t ret = tv.tv_sec;
+ ret *= 1000000;
+ ret += tv.tv_usec;
+ return ret;
+}
+
+#else
+
+static __inline__ uint64_t
+rdtsc(void)
+{
+ return 0;
+}
+
+#endif // defined(MOZ_HAVE_RDTSC)
+
+static bool
+EnsureTraceLoggerState()
+{
+ if (MOZ_LIKELY(traceLoggerState))
+ return true;
+
+ traceLoggerState = js_new<TraceLoggerThreadState>();
+ if (!traceLoggerState)
+ return false;
+
+ if (!traceLoggerState->init()) {
+ DestroyTraceLoggerThreadState();
+ return false;
+ }
+
+ return true;
+}
+
+void
+js::DestroyTraceLoggerThreadState()
+{
+ if (traceLoggerState) {
+ js_delete(traceLoggerState);
+ traceLoggerState = nullptr;
+ }
+}
+
+void
+js::DestroyTraceLoggerMainThread(JSRuntime* runtime)
+{
+ if (!EnsureTraceLoggerState())
+ return;
+ traceLoggerState->destroyMainThread(runtime);
+}
+
+bool
+TraceLoggerThread::init()
+{
+ if (!pointerMap.init())
+ return false;
+ if (!textIdPayloads.init())
+ return false;
+ if (!events.init())
+ return false;
+
+ // Minimum amount of capacity needed for operation to allow flushing.
+ // Flushing requires space for the actual event and two spaces to log the
+ // start and stop of flushing.
+ if (!events.ensureSpaceBeforeAdd(3))
+ return false;
+
+ return true;
+}
+
+void
+TraceLoggerThread::initGraph()
+{
+ // Create a graph. I don't like this is called reset, but it locks the
+ // graph into the UniquePtr. So it gets deleted when TraceLoggerThread
+ // is destructed.
+ graph.reset(js_new<TraceLoggerGraph>());
+ if (!graph.get())
+ return;
+
+ MOZ_ASSERT(traceLoggerState);
+ uint64_t start = rdtsc() - traceLoggerState->startupTime;
+ if (!graph->init(start)) {
+ graph = nullptr;
+ return;
+ }
+
+ // Report the textIds to the graph.
+ for (uint32_t i = 0; i < TraceLogger_LastTreeItem; i++) {
+ TraceLoggerTextId id = TraceLoggerTextId(i);
+ graph->addTextId(i, TLTextIdString(id));
+ }
+ graph->addTextId(TraceLogger_LastTreeItem, "TraceLogger internal");
+ for (uint32_t i = TraceLogger_LastTreeItem + 1; i < TraceLogger_Last; i++) {
+ TraceLoggerTextId id = TraceLoggerTextId(i);
+ graph->addTextId(i, TLTextIdString(id));
+ }
+}
+
+TraceLoggerThread::~TraceLoggerThread()
+{
+ if (graph.get()) {
+ if (!failed)
+ graph->log(events);
+ graph = nullptr;
+ }
+
+ if (textIdPayloads.initialized()) {
+ for (TextIdHashMap::Range r = textIdPayloads.all(); !r.empty(); r.popFront())
+ js_delete(r.front().value());
+ }
+}
+
+bool
+TraceLoggerThread::enable()
+{
+ if (enabled_ > 0) {
+ enabled_++;
+ return true;
+ }
+
+ if (failed)
+ return false;
+
+ enabled_ = 1;
+ logTimestamp(TraceLogger_Enable);
+
+ return true;
+}
+
+bool
+TraceLoggerThread::fail(JSContext* cx, const char* error)
+{
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL, error);
+ failed = true;
+ enabled_ = 0;
+
+ return false;
+}
+
+bool
+TraceLoggerThread::enable(JSContext* cx)
+{
+ if (!enable())
+ return fail(cx, "internal error");
+
+ if (enabled_ == 1) {
+ // Get the top Activation to log the top script/pc (No inlined frames).
+ ActivationIterator iter(cx->runtime());
+ Activation* act = iter.activation();
+
+ if (!act)
+ return fail(cx, "internal error");
+
+ JSScript* script = nullptr;
+ int32_t engine = 0;
+
+ if (act->isJit()) {
+ JitFrameIterator it(iter);
+
+ while (!it.isScripted() && !it.done())
+ ++it;
+
+ MOZ_ASSERT(!it.done());
+ MOZ_ASSERT(it.isIonJS() || it.isBaselineJS());
+
+ script = it.script();
+ engine = it.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline;
+ } else if (act->isWasm()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL,
+ "not yet supported in wasm code");
+ return false;
+ } else {
+ MOZ_ASSERT(act->isInterpreter());
+ InterpreterFrame* fp = act->asInterpreter()->current();
+ MOZ_ASSERT(!fp->runningInJit());
+
+ script = fp->script();
+ engine = TraceLogger_Interpreter;
+ }
+ if (script->compartment() != cx->compartment())
+ return fail(cx, "compartment mismatch");
+
+ TraceLoggerEvent event(this, TraceLogger_Scripts, script);
+ startEvent(event);
+ startEvent(engine);
+ }
+
+ return true;
+}
+
+bool
+TraceLoggerThread::disable(bool force, const char* error)
+{
+ if (failed) {
+ MOZ_ASSERT(enabled_ == 0);
+ return false;
+ }
+
+ if (enabled_ == 0)
+ return true;
+
+ if (enabled_ > 1 && !force) {
+ enabled_--;
+ return true;
+ }
+
+ if (force)
+ traceLoggerState->maybeSpewError(error);
+
+ logTimestamp(TraceLogger_Disable);
+ enabled_ = 0;
+
+ return true;
+}
+
+const char*
+TraceLoggerThread::eventText(uint32_t id)
+{
+ if (id < TraceLogger_Last)
+ return TLTextIdString(static_cast<TraceLoggerTextId>(id));
+
+ TextIdHashMap::Ptr p = textIdPayloads.lookup(id);
+ MOZ_ASSERT(p);
+
+ return p->value()->string();
+}
+
+bool
+TraceLoggerThread::textIdIsScriptEvent(uint32_t id)
+{
+ if (id < TraceLogger_Last)
+ return false;
+
+ // Currently this works by checking if text begins with "script".
+ const char* str = eventText(id);
+ return EqualChars(str, "script", 6);
+}
+
+void
+TraceLoggerThread::extractScriptDetails(uint32_t textId, const char** filename, size_t* filename_len,
+ const char** lineno, size_t* lineno_len, const char** colno,
+ size_t* colno_len)
+{
+ MOZ_ASSERT(textIdIsScriptEvent(textId));
+
+ const char* script = eventText(textId);
+
+ // Get the start of filename (remove 'script ' at the start).
+ MOZ_ASSERT(EqualChars(script, "script ", 7));
+ *filename = script + 7;
+
+ // Get the start of lineno and colno.
+ *lineno = script;
+ *colno = script;
+ const char* next = script - 1;
+ while ((next = strchr(next + 1, ':'))) {
+ *lineno = *colno;
+ *colno = next;
+ }
+
+ MOZ_ASSERT(*lineno && *lineno != script);
+ MOZ_ASSERT(*colno && *colno != script);
+
+ // Remove the ':' at the front.
+ *lineno = *lineno + 1;
+ *colno = *colno + 1;
+
+ *filename_len = *lineno - *filename - 1;
+ *lineno_len = *colno - *lineno - 1;
+ *colno_len = strlen(*colno);
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId textId)
+{
+ TextIdHashMap::AddPtr p = textIdPayloads.lookupForAdd(textId);
+ if (p) {
+ MOZ_ASSERT(p->value()->textId() == textId); // Sanity check.
+ return p->value();
+ }
+
+ TraceLoggerEventPayload* payload = js_new<TraceLoggerEventPayload>(textId, (char*)nullptr);
+ if (!payload)
+ return nullptr;
+
+ if (!textIdPayloads.add(p, textId, payload))
+ return nullptr;
+
+ return payload;
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThread::getOrCreateEventPayload(const char* text)
+{
+ PointerHashMap::AddPtr p = pointerMap.lookupForAdd((const void*)text);
+ if (p) {
+ MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
+ return p->value();
+ }
+
+ TraceLoggerEventPayload* payload = nullptr;
+
+ startEvent(TraceLogger_Internal);
+ auto guardInternalStopEvent = mozilla::MakeScopeExit([&] {
+ stopEvent(TraceLogger_Internal);
+ if (payload)
+ payload->release();
+ });
+
+ char* str = js_strdup(text);
+ if (!str)
+ return nullptr;
+
+ uint32_t textId = nextTextId;
+
+ payload = js_new<TraceLoggerEventPayload>(textId, str);
+ if (!payload) {
+ js_free(str);
+ return nullptr;
+ }
+
+ if (!textIdPayloads.putNew(textId, payload)) {
+ js_delete(payload);
+ payload = nullptr;
+ return nullptr;
+ }
+
+ // Temporarily mark the payload as used. To make sure it doesn't get GC'ed.
+ payload->use();
+
+ if (graph.get())
+ graph->addTextId(textId, str);
+
+ nextTextId++;
+
+ if (!pointerMap.add(p, text, payload))
+ return nullptr;
+
+ return payload;
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId type, const char* filename,
+ size_t lineno, size_t colno, const void* ptr)
+{
+ MOZ_ASSERT(type == TraceLogger_Scripts || type == TraceLogger_AnnotateScripts ||
+ type == TraceLogger_InlinedScripts);
+
+ if (!filename)
+ filename = "<unknown>";
+
+ // Only log scripts when enabled otherwise return the global Scripts textId,
+ // which will get filtered out.
+ MOZ_ASSERT(traceLoggerState);
+ if (!traceLoggerState->isTextIdEnabled(type))
+ return getOrCreateEventPayload(type);
+
+ PointerHashMap::AddPtr p;
+ if (ptr) {
+ p = pointerMap.lookupForAdd(ptr);
+ if (p) {
+ MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
+ return p->value();
+ }
+ }
+
+ TraceLoggerEventPayload* payload = nullptr;
+
+ startEvent(TraceLogger_Internal);
+ auto guardInternalStopEvent = mozilla::MakeScopeExit([&] {
+ stopEvent(TraceLogger_Internal);
+ if (payload)
+ payload->release();
+ });
+
+ // Compute the length of the string to create.
+ size_t lenFilename = strlen(filename);
+ size_t lenLineno = 1;
+ for (size_t i = lineno; i /= 10; lenLineno++);
+ size_t lenColno = 1;
+ for (size_t i = colno; i /= 10; lenColno++);
+
+ size_t len = 7 + lenFilename + 1 + lenLineno + 1 + lenColno;
+ char* str = js_pod_malloc<char>(len + 1);
+ if (!str)
+ return nullptr;
+
+ DebugOnly<size_t> ret =
+ snprintf(str, len + 1, "script %s:%" PRIuSIZE ":%" PRIuSIZE, filename, lineno, colno);
+ MOZ_ASSERT(ret == len);
+ MOZ_ASSERT(strlen(str) == len);
+
+ uint32_t textId = nextTextId;
+ payload = js_new<TraceLoggerEventPayload>(textId, str);
+ if (!payload) {
+ js_free(str);
+ return nullptr;
+ }
+
+ if (!textIdPayloads.putNew(textId, payload)) {
+ js_delete(payload);
+ payload = nullptr;
+ return nullptr;
+ }
+
+ // Temporarily mark the payload as used. To make sure it doesn't get GC'ed.
+ payload->use();
+
+ if (graph.get())
+ graph->addTextId(textId, str);
+
+ nextTextId++;
+
+ if (ptr) {
+ if (!pointerMap.add(p, ptr, payload))
+ return nullptr;
+ }
+
+ return payload;
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId type, JSScript* script)
+{
+ return getOrCreateEventPayload(type, script->filename(), script->lineno(), script->column(),
+ nullptr);
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId type,
+ const JS::ReadOnlyCompileOptions& script)
+{
+ return getOrCreateEventPayload(type, script.filename(), script.lineno, script.column, nullptr);
+}
+
+void
+TraceLoggerThread::startEvent(TraceLoggerTextId id) {
+ startEvent(uint32_t(id));
+}
+
+void
+TraceLoggerThread::startEvent(const TraceLoggerEvent& event) {
+ if (!event.hasPayload()) {
+ if (!enabled())
+ return;
+ startEvent(TraceLogger_Error);
+ disable(/* force = */ true, "TraceLogger encountered an empty event. "
+ "Potentially due to OOM during creation of "
+ "this event. Disabling TraceLogger.");
+ return;
+ }
+ startEvent(event.payload()->textId());
+}
+
+void
+TraceLoggerThread::startEvent(uint32_t id)
+{
+ MOZ_ASSERT(TLTextIdIsTreeEvent(id) || id == TraceLogger_Error);
+ MOZ_ASSERT(traceLoggerState);
+ if (!traceLoggerState->isTextIdEnabled(id))
+ return;
+
+#ifdef DEBUG
+ if (enabled_ > 0) {
+ AutoEnterOOMUnsafeRegion oomUnsafe;
+ if (!graphStack.append(id))
+ oomUnsafe.crash("Could not add item to debug stack.");
+ }
+#endif
+
+ log(id);
+}
+
+void
+TraceLoggerThread::stopEvent(TraceLoggerTextId id) {
+ stopEvent(uint32_t(id));
+}
+
+void
+TraceLoggerThread::stopEvent(const TraceLoggerEvent& event) {
+ if (!event.hasPayload()) {
+ stopEvent(TraceLogger_Error);
+ return;
+ }
+ stopEvent(event.payload()->textId());
+}
+
+void
+TraceLoggerThread::stopEvent(uint32_t id)
+{
+ MOZ_ASSERT(TLTextIdIsTreeEvent(id) || id == TraceLogger_Error);
+ MOZ_ASSERT(traceLoggerState);
+ if (!traceLoggerState->isTextIdEnabled(id))
+ return;
+
+#ifdef DEBUG
+ if (enabled_ > 0 && !graphStack.empty()) {
+ uint32_t prev = graphStack.popCopy();
+ if (id == TraceLogger_Error || prev == TraceLogger_Error) {
+ // When encountering an Error id the stack will most likely not be correct anymore.
+ // Ignore this.
+ } else if (id == TraceLogger_Engine) {
+ MOZ_ASSERT(prev == TraceLogger_IonMonkey || prev == TraceLogger_Baseline ||
+ prev == TraceLogger_Interpreter);
+ } else if (id == TraceLogger_Scripts) {
+ MOZ_ASSERT(prev >= TraceLogger_Last);
+ } else if (id >= TraceLogger_Last) {
+ MOZ_ASSERT(prev >= TraceLogger_Last);
+ MOZ_ASSERT_IF(prev != id, strcmp(eventText(id), eventText(prev)) == 0);
+ } else {
+ MOZ_ASSERT(id == prev);
+ }
+ }
+#endif
+
+ log(TraceLogger_Stop);
+}
+
+void
+TraceLoggerThread::logTimestamp(TraceLoggerTextId id)
+{
+ logTimestamp(uint32_t(id));
+}
+
+void
+TraceLoggerThread::logTimestamp(uint32_t id)
+{
+ MOZ_ASSERT(id > TraceLogger_LastTreeItem && id < TraceLogger_Last);
+ log(id);
+}
+
+void
+TraceLoggerThread::log(uint32_t id)
+{
+ if (enabled_ == 0)
+ return;
+
+#ifdef DEBUG
+ if (id == TraceLogger_Disable)
+ graphStack.clear();
+#endif
+
+ MOZ_ASSERT(traceLoggerState);
+
+ // We request for 3 items to add, since if we don't have enough room
+ // we record the time it took to make more space. To log this information
+ // we need 2 extra free entries.
+ if (!events.hasSpaceForAdd(3)) {
+ uint64_t start = rdtsc() - traceLoggerState->startupTime;
+
+ if (!events.ensureSpaceBeforeAdd(3)) {
+ if (graph.get())
+ graph->log(events);
+
+ iteration_++;
+ events.clear();
+
+ // Remove the item in the pointerMap for which the payloads
+ // have no uses anymore
+ for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) {
+ if (e.front().value()->uses() != 0)
+ continue;
+
+ TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId());
+ MOZ_ASSERT(p);
+ textIdPayloads.remove(p);
+
+ e.removeFront();
+ }
+
+ // Free all payloads that have no uses anymore.
+ for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) {
+ if (e.front().value()->uses() == 0) {
+ js_delete(e.front().value());
+ e.removeFront();
+ }
+ }
+ }
+
+ // Log the time it took to flush the events as being from the
+ // Tracelogger.
+ if (graph.get()) {
+ MOZ_ASSERT(events.hasSpaceForAdd(2));
+ EventEntry& entryStart = events.pushUninitialized();
+ entryStart.time = start;
+ entryStart.textId = TraceLogger_Internal;
+
+ EventEntry& entryStop = events.pushUninitialized();
+ entryStop.time = rdtsc() - traceLoggerState->startupTime;
+ entryStop.textId = TraceLogger_Stop;
+ }
+
+ }
+
+ uint64_t time = rdtsc() - traceLoggerState->startupTime;
+
+ EventEntry& entry = events.pushUninitialized();
+ entry.time = time;
+ entry.textId = id;
+}
+
+TraceLoggerThreadState::~TraceLoggerThreadState()
+{
+ while (TraceLoggerMainThread* logger = traceLoggerMainThreadList.popFirst())
+ js_delete(logger);
+
+ if (threadLoggers.initialized()) {
+ for (ThreadLoggerHashMap::Range r = threadLoggers.all(); !r.empty(); r.popFront())
+ js_delete(r.front().value());
+
+ threadLoggers.finish();
+ }
+
+#ifdef DEBUG
+ initialized = false;
+#endif
+}
+
+static bool
+ContainsFlag(const char* str, const char* flag)
+{
+ size_t flaglen = strlen(flag);
+ const char* index = strstr(str, flag);
+ while (index) {
+ if ((index == str || index[-1] == ',') && (index[flaglen] == 0 || index[flaglen] == ','))
+ return true;
+ index = strstr(index + flaglen, flag);
+ }
+ return false;
+}
+
+bool
+TraceLoggerThreadState::init()
+{
+ if (!threadLoggers.init())
+ return false;
+
+ const char* env = getenv("TLLOG");
+ if (!env)
+ env = "";
+
+ if (strstr(env, "help")) {
+ fflush(nullptr);
+ printf(
+ "\n"
+ "usage: TLLOG=option,option,option,... where options can be:\n"
+ "\n"
+ "Collections:\n"
+ " Default Output all default. It includes:\n"
+ " AnnotateScripts, Bailout, Baseline, BaselineCompilation, GC,\n"
+ " GCAllocation, GCSweeping, Interpreter, IonAnalysis, IonCompilation,\n"
+ " IonLinking, IonMonkey, MinorGC, ParserCompileFunction,\n"
+ " ParserCompileScript, ParserCompileLazy, ParserCompileModule,\n"
+ " IrregexpCompile, IrregexpExecute, Scripts, Engine, WasmCompilation\n"
+ "\n"
+ " IonCompiler Output all information about compilation. It includes:\n"
+ " IonCompilation, IonLinking, PruneUnusedBranches, FoldTests,\n"
+ " SplitCriticalEdges, RenumberBlocks, ScalarReplacement, \n"
+ " DominatorTree, PhiAnalysis, MakeLoopsContiguous, ApplyTypes, \n"
+ " EagerSimdUnbox, AliasAnalysis, GVN, LICM, Sincos, RangeAnalysis, \n"
+ " LoopUnrolling, FoldLinearArithConstants, EffectiveAddressAnalysis, \n"
+ " AlignmentMaskAnalysis, EliminateDeadCode, ReorderInstructions, \n"
+ " EdgeCaseAnalysis, EliminateRedundantChecks, \n"
+ " AddKeepAliveInstructions, GenerateLIR, RegisterAllocation, \n"
+ " GenerateCode, Scripts, IonBuilderRestartLoop\n"
+ "\n"
+ " VMSpecific Output the specific name of the VM call"
+ "\n"
+ "Specific log items:\n"
+ );
+ for (uint32_t i = 1; i < TraceLogger_Last; i++) {
+ TraceLoggerTextId id = TraceLoggerTextId(i);
+ if (!TLTextIdIsTogglable(id))
+ continue;
+ printf(" %s\n", TLTextIdString(id));
+ }
+ printf("\n");
+ exit(0);
+ /*NOTREACHED*/
+ }
+
+ for (uint32_t i = 1; i < TraceLogger_Last; i++) {
+ TraceLoggerTextId id = TraceLoggerTextId(i);
+ if (TLTextIdIsTogglable(id))
+ enabledTextIds[i] = ContainsFlag(env, TLTextIdString(id));
+ else
+ enabledTextIds[i] = true;
+ }
+
+ if (ContainsFlag(env, "Default")) {
+ enabledTextIds[TraceLogger_AnnotateScripts] = true;
+ enabledTextIds[TraceLogger_Bailout] = true;
+ enabledTextIds[TraceLogger_Baseline] = true;
+ enabledTextIds[TraceLogger_BaselineCompilation] = true;
+ enabledTextIds[TraceLogger_GC] = true;
+ enabledTextIds[TraceLogger_GCAllocation] = true;
+ enabledTextIds[TraceLogger_GCSweeping] = true;
+ enabledTextIds[TraceLogger_Interpreter] = true;
+ enabledTextIds[TraceLogger_IonAnalysis] = true;
+ enabledTextIds[TraceLogger_IonCompilation] = true;
+ enabledTextIds[TraceLogger_IonLinking] = true;
+ enabledTextIds[TraceLogger_IonMonkey] = true;
+ enabledTextIds[TraceLogger_MinorGC] = true;
+ enabledTextIds[TraceLogger_ParserCompileFunction] = true;
+ enabledTextIds[TraceLogger_ParserCompileLazy] = true;
+ enabledTextIds[TraceLogger_ParserCompileScript] = true;
+ enabledTextIds[TraceLogger_ParserCompileModule] = true;
+ enabledTextIds[TraceLogger_IrregexpCompile] = true;
+ enabledTextIds[TraceLogger_IrregexpExecute] = true;
+ enabledTextIds[TraceLogger_Scripts] = true;
+ enabledTextIds[TraceLogger_Engine] = true;
+ enabledTextIds[TraceLogger_WasmCompilation] = true;
+ }
+
+ if (ContainsFlag(env, "IonCompiler")) {
+ enabledTextIds[TraceLogger_IonCompilation] = true;
+ enabledTextIds[TraceLogger_IonLinking] = true;
+ enabledTextIds[TraceLogger_PruneUnusedBranches] = true;
+ enabledTextIds[TraceLogger_FoldTests] = true;
+ enabledTextIds[TraceLogger_SplitCriticalEdges] = true;
+ enabledTextIds[TraceLogger_RenumberBlocks] = true;
+ enabledTextIds[TraceLogger_ScalarReplacement] = true;
+ enabledTextIds[TraceLogger_DominatorTree] = true;
+ enabledTextIds[TraceLogger_PhiAnalysis] = true;
+ enabledTextIds[TraceLogger_MakeLoopsContiguous] = true;
+ enabledTextIds[TraceLogger_ApplyTypes] = true;
+ enabledTextIds[TraceLogger_EagerSimdUnbox] = true;
+ enabledTextIds[TraceLogger_AliasAnalysis] = true;
+ enabledTextIds[TraceLogger_GVN] = true;
+ enabledTextIds[TraceLogger_LICM] = true;
+ enabledTextIds[TraceLogger_Sincos] = true;
+ enabledTextIds[TraceLogger_RangeAnalysis] = true;
+ enabledTextIds[TraceLogger_LoopUnrolling] = true;
+ enabledTextIds[TraceLogger_FoldLinearArithConstants] = true;
+ enabledTextIds[TraceLogger_EffectiveAddressAnalysis] = true;
+ enabledTextIds[TraceLogger_AlignmentMaskAnalysis] = true;
+ enabledTextIds[TraceLogger_EliminateDeadCode] = true;
+ enabledTextIds[TraceLogger_ReorderInstructions] = true;
+ enabledTextIds[TraceLogger_EdgeCaseAnalysis] = true;
+ enabledTextIds[TraceLogger_EliminateRedundantChecks] = true;
+ enabledTextIds[TraceLogger_AddKeepAliveInstructions] = true;
+ enabledTextIds[TraceLogger_GenerateLIR] = true;
+ enabledTextIds[TraceLogger_RegisterAllocation] = true;
+ enabledTextIds[TraceLogger_GenerateCode] = true;
+ enabledTextIds[TraceLogger_Scripts] = true;
+ enabledTextIds[TraceLogger_IonBuilderRestartLoop] = true;
+ }
+
+ enabledTextIds[TraceLogger_Interpreter] = enabledTextIds[TraceLogger_Engine];
+ enabledTextIds[TraceLogger_Baseline] = enabledTextIds[TraceLogger_Engine];
+ enabledTextIds[TraceLogger_IonMonkey] = enabledTextIds[TraceLogger_Engine];
+
+ enabledTextIds[TraceLogger_Error] = 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"
+ " EnableMainThread Start logging the main thread immediately.\n"
+ " EnableOffThread Start logging helper threads immediately.\n"
+ " EnableGraph Enable spewing the tracelogging graph to a file.\n"
+ " Errors Report errors during tracing to stderr.\n"
+ );
+ printf("\n");
+ exit(0);
+ /*NOTREACHED*/
+ }
+
+ if (strstr(options, "EnableMainThread"))
+ mainThreadEnabled = true;
+ if (strstr(options, "EnableOffThread"))
+ offThreadEnabled = true;
+ if (strstr(options, "EnableGraph"))
+ graphSpewingEnabled = true;
+ if (strstr(options, "Errors"))
+ spewErrors = true;
+ }
+
+ startupTime = rdtsc();
+
+#ifdef DEBUG
+ initialized = true;
+#endif
+
+ return true;
+}
+
+void
+TraceLoggerThreadState::enableTextId(JSContext* cx, uint32_t textId)
+{
+ MOZ_ASSERT(TLTextIdIsTogglable(textId));
+
+ if (enabledTextIds[textId])
+ return;
+
+ ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
+ enabledTextIds[textId] = true;
+ if (textId == TraceLogger_Engine) {
+ enabledTextIds[TraceLogger_IonMonkey] = true;
+ enabledTextIds[TraceLogger_Baseline] = true;
+ enabledTextIds[TraceLogger_Interpreter] = true;
+ }
+
+ if (textId == TraceLogger_Scripts)
+ jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), true);
+ if (textId == TraceLogger_Engine)
+ jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), true);
+
+}
+void
+TraceLoggerThreadState::disableTextId(JSContext* cx, uint32_t textId)
+{
+ MOZ_ASSERT(TLTextIdIsTogglable(textId));
+
+ if (!enabledTextIds[textId])
+ return;
+
+ ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
+
+ enabledTextIds[textId] = false;
+ if (textId == TraceLogger_Engine) {
+ enabledTextIds[TraceLogger_IonMonkey] = false;
+ enabledTextIds[TraceLogger_Baseline] = false;
+ enabledTextIds[TraceLogger_Interpreter] = false;
+ }
+
+ if (textId == TraceLogger_Scripts)
+ jit::ToggleBaselineTraceLoggerScripts(cx->runtime(), false);
+ if (textId == TraceLogger_Engine)
+ jit::ToggleBaselineTraceLoggerEngine(cx->runtime(), false);
+}
+
+
+TraceLoggerThread*
+js::TraceLoggerForMainThread(CompileRuntime* runtime)
+{
+ if (!EnsureTraceLoggerState())
+ return nullptr;
+ return traceLoggerState->forMainThread(runtime);
+}
+
+TraceLoggerThread*
+TraceLoggerThreadState::forMainThread(CompileRuntime* runtime)
+{
+ return forMainThread(runtime->mainThread());
+}
+
+TraceLoggerThread*
+js::TraceLoggerForMainThread(JSRuntime* runtime)
+{
+ if (!EnsureTraceLoggerState())
+ return nullptr;
+ return traceLoggerState->forMainThread(runtime);
+}
+
+TraceLoggerThread*
+TraceLoggerThreadState::forMainThread(JSRuntime* runtime)
+{
+ return forMainThread(&runtime->mainThread);
+}
+
+TraceLoggerThread*
+TraceLoggerThreadState::forMainThread(PerThreadData* mainThread)
+{
+ MOZ_ASSERT(initialized);
+ if (!mainThread->traceLogger) {
+ LockGuard<Mutex> guard(lock);
+
+ TraceLoggerMainThread* logger = js_new<TraceLoggerMainThread>();
+ if (!logger)
+ return nullptr;
+
+ if (!logger->init()) {
+ js_delete(logger);
+ return nullptr;
+ }
+
+ traceLoggerMainThreadList.insertFront(logger);
+ mainThread->traceLogger = logger;
+
+ if (graphSpewingEnabled)
+ logger->initGraph();
+
+ if (mainThreadEnabled)
+ logger->enable();
+ }
+
+ return mainThread->traceLogger;
+}
+
+void
+TraceLoggerThreadState::destroyMainThread(JSRuntime* runtime)
+{
+ MOZ_ASSERT(initialized);
+ PerThreadData* mainThread = &runtime->mainThread;
+ if (mainThread->traceLogger) {
+ LockGuard<Mutex> guard(lock);
+
+ mainThread->traceLogger->remove();
+ js_delete(mainThread->traceLogger);
+ mainThread->traceLogger = nullptr;
+ }
+}
+
+TraceLoggerThread*
+js::TraceLoggerForCurrentThread()
+{
+ if (!EnsureTraceLoggerState())
+ return nullptr;
+ return traceLoggerState->forThread(ThisThread::GetId());
+}
+
+TraceLoggerThread*
+TraceLoggerThreadState::forThread(const Thread::Id& thread)
+{
+ return nullptr;
+}
+
+bool
+js::TraceLogTextIdEnabled(uint32_t textId)
+{
+ if (!EnsureTraceLoggerState())
+ return false;
+ return traceLoggerState->isTextIdEnabled(textId);
+}
+
+void
+js::TraceLogEnableTextId(JSContext* cx, uint32_t textId)
+{
+ if (!EnsureTraceLoggerState())
+ return;
+ traceLoggerState->enableTextId(cx, textId);
+}
+void
+js::TraceLogDisableTextId(JSContext* cx, uint32_t textId)
+{
+ if (!EnsureTraceLoggerState())
+ return;
+ traceLoggerState->disableTextId(cx, textId);
+}
+
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId textId)
+{
+ payload_ = nullptr;
+ if (logger) {
+ payload_ = logger->getOrCreateEventPayload(textId);
+ if (payload_)
+ payload_->use();
+ }
+}
+
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type,
+ JSScript* script)
+{
+ payload_ = nullptr;
+ if (logger) {
+ payload_ = logger->getOrCreateEventPayload(type, script);
+ if (payload_)
+ payload_->use();
+ }
+}
+
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type,
+ const JS::ReadOnlyCompileOptions& compileOptions)
+{
+ payload_ = nullptr;
+ if (logger) {
+ payload_ = logger->getOrCreateEventPayload(type, compileOptions);
+ if (payload_)
+ payload_->use();
+ }
+}
+
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerThread* logger, const char* text)
+{
+ payload_ = nullptr;
+ if (logger) {
+ payload_ = logger->getOrCreateEventPayload(text);
+ if (payload_)
+ payload_->use();
+ }
+}
+
+TraceLoggerEvent::~TraceLoggerEvent()
+{
+ if (payload_)
+ payload_->release();
+}
+
+TraceLoggerEvent&
+TraceLoggerEvent::operator=(const TraceLoggerEvent& other)
+{
+ if (other.hasPayload())
+ other.payload()->use();
+ if (hasPayload())
+ payload()->release();
+
+ payload_ = other.payload_;
+
+ return *this;
+}
+
+TraceLoggerEvent::TraceLoggerEvent(const TraceLoggerEvent& other)
+{
+ payload_ = other.payload_;
+ if (hasPayload())
+ payload()->use();
+}