summaryrefslogtreecommitdiff
path: root/src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp')
-rw-r--r--src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp b/src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp
new file mode 100644
index 00000000000..99a6d1d64d4
--- /dev/null
+++ b/src/third_party/mozjs-60/extract/js/src/vm/TraceLogging.cpp
@@ -0,0 +1,1094 @@
+/* -*- 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/EndianUtils.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ScopeExit.h"
+
+#include <string.h>
+
+#include "jsapi.h"
+
+#include "jit/BaselineJIT.h"
+#include "jit/CompileWrappers.h"
+#include "threading/LockGuard.h"
+#include "vm/JSScript.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;
+
+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__) || defined(__aarch64__)
+
+#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;
+}
+
+size_t
+js::SizeOfTraceLogState(mozilla::MallocSizeOf mallocSizeOf)
+{
+ return traceLoggerState ? traceLoggerState->sizeOfIncludingThis(mallocSizeOf) : 0;
+}
+
+void
+js::DestroyTraceLoggerThreadState()
+{
+ if (traceLoggerState) {
+ js_delete(traceLoggerState);
+ traceLoggerState = nullptr;
+ }
+}
+
+#ifdef DEBUG
+bool
+js::CurrentThreadOwnsTraceLoggerThreadStateLock()
+{
+ return traceLoggerState && traceLoggerState->lock.ownedByCurrentThread();
+}
+#endif
+
+void
+js::DestroyTraceLogger(TraceLoggerThread* logger)
+{
+ if (!EnsureTraceLoggerState())
+ return;
+ traceLoggerState->destroyLogger(logger);
+}
+
+bool
+TraceLoggerThread::init()
+{
+ 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;
+ }
+}
+
+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;
+}
+
+void
+TraceLoggerThread::silentFail(const char* error)
+{
+ traceLoggerState->maybeSpewError(error);
+ failed = true;
+ enabled_ = 0;
+}
+
+size_t
+TraceLoggerThread::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+ size_t size = 0;
+#ifdef DEBUG
+ size += graphStack.sizeOfExcludingThis(mallocSizeOf);
+#endif
+ size += events.sizeOfExcludingThis(mallocSizeOf);
+ if (graph.get())
+ size += graph->sizeOfIncludingThis(mallocSizeOf);
+ return size;
+}
+
+size_t
+TraceLoggerThread::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+ return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+}
+
+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);
+ Activation* act = iter.activation();
+
+ if (!act)
+ return fail(cx, "internal error");
+
+ JSScript* script = nullptr;
+ int32_t engine = 0;
+
+ if (act->isJit()) {
+ JitFrameIter frame(iter->asJit());
+
+ while (!frame.done()) {
+ if (frame.isWasm()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TRACELOGGER_ENABLE_FAIL,
+ "not yet supported in wasm code");
+ return false;
+ }
+ if (frame.asJSJit().isScripted())
+ break;
+ ++frame;
+ }
+
+ MOZ_ASSERT(!frame.done());
+
+ const JSJitFrameIter& jitFrame = frame.asJSJit();
+ MOZ_ASSERT(jitFrame.isIonJS() || jitFrame.isBaselineJS());
+
+ script = jitFrame.script();
+ engine = jitFrame.isIonJS() ? TraceLogger_IonMonkey : TraceLogger_Baseline;
+ } 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(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::maybeEventText(uint32_t id)
+{
+ if (id < TraceLogger_Last)
+ return TLTextIdString(static_cast<TraceLoggerTextId>(id));
+ return traceLoggerState->maybeEventText(id);
+}
+
+const char*
+TraceLoggerThreadState::maybeEventText(uint32_t id)
+{
+ LockGuard<Mutex> guard(lock);
+
+ TextIdHashMap::Ptr p = textIdPayloads.lookup(id);
+ if (!p)
+ return nullptr;
+
+ return p->value()->string();
+}
+
+size_t
+TraceLoggerThreadState::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+ LockGuard<Mutex> guard(lock);
+
+ // Do not count threadLoggers since they are counted by JSContext::traceLogger.
+
+ size_t size = 0;
+ size += pointerMap.sizeOfExcludingThis(mallocSizeOf);
+ if (textIdPayloads.initialized()) {
+ size += textIdPayloads.sizeOfExcludingThis(mallocSizeOf);
+ for (TextIdHashMap::Range r = textIdPayloads.all(); !r.empty(); r.popFront())
+ r.front().value()->sizeOfIncludingThis(mallocSizeOf);
+ }
+ return size;
+}
+
+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*
+TraceLoggerThreadState::getOrCreateEventPayload(const char* text)
+{
+ LockGuard<Mutex> guard(lock);
+
+ PointerHashMap::AddPtr p = pointerMap.lookupForAdd((const void*)text);
+ if (p) {
+ MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
+ p->value()->use();
+ return p->value();
+ }
+
+ char* str = js_strdup(text);
+ if (!str)
+ return nullptr;
+
+ uint32_t textId = nextTextId;
+
+ TraceLoggerEventPayload* 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;
+ }
+
+ payload->use();
+
+ nextTextId++;
+
+ if (!pointerMap.add(p, text, payload))
+ return nullptr;
+
+ payload->incPointerCount();
+
+ return payload;
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThreadState::getOrCreateEventPayload(const char* filename,
+ size_t lineno, size_t colno, const void* ptr)
+{
+ if (!filename)
+ filename = "<unknown>";
+
+ LockGuard<Mutex> guard(lock);
+
+ PointerHashMap::AddPtr p;
+ if (ptr) {
+ p = pointerMap.lookupForAdd(ptr);
+ if (p) {
+ MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
+ p->value()->use();
+ return p->value();
+ }
+ }
+
+ // 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:%zu:%zu", filename, lineno, colno);
+ MOZ_ASSERT(ret == len);
+ MOZ_ASSERT(strlen(str) == len);
+
+ uint32_t textId = nextTextId;
+ TraceLoggerEventPayload* 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;
+ }
+
+ payload->use();
+
+ nextTextId++;
+
+ if (ptr) {
+ if (!pointerMap.add(p, ptr, payload))
+ return nullptr;
+
+ payload->incPointerCount();
+ }
+
+ return payload;
+}
+
+TraceLoggerEventPayload*
+TraceLoggerThreadState::getOrCreateEventPayload(JSScript* script)
+{
+ return getOrCreateEventPayload(script->filename(), script->lineno(), script->column(), nullptr);
+}
+
+void
+TraceLoggerThreadState::purgeUnusedPayloads()
+{
+ // Care needs to be taken to maintain a coherent state in this function,
+ // as payloads can have their use count change at any time from non-zero to
+ // zero (but not the other way around; see TraceLoggerEventPayload::use()).
+ LockGuard<Mutex> guard(lock);
+
+ // Remove all the pointers to payloads that have no uses anymore
+ // and decrease the pointer count of that payload.
+ for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) {
+ if (e.front().value()->uses() == 0) {
+ e.front().value()->decPointerCount();
+ e.removeFront();
+ }
+ }
+
+ // Free all other payloads that have no uses anymore.
+ for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) {
+ if (e.front().value()->uses() == 0 && e.front().value()->pointerCount() == 0) {
+ js_delete(e.front().value());
+ e.removeFront();
+ }
+ }
+}
+
+void
+TraceLoggerThread::startEvent(TraceLoggerTextId id) {
+ startEvent(uint32_t(id));
+}
+
+void
+TraceLoggerThread::startEvent(const TraceLoggerEvent& event) {
+ if (!event.hasTextId()) {
+ 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.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
+
+ if (graph.get()) {
+ for (uint32_t otherId = graph->nextTextId(); otherId <= id; otherId++)
+ graph->addTextId(otherId, maybeEventText(id));
+ }
+
+ log(id);
+}
+
+void
+TraceLoggerThread::stopEvent(TraceLoggerTextId id) {
+ stopEvent(uint32_t(id));
+}
+
+void
+TraceLoggerThread::stopEvent(const TraceLoggerEvent& event) {
+ if (!event.hasTextId()) {
+ stopEvent(TraceLogger_Error);
+ return;
+ }
+ stopEvent(event.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);
+ if (prev != id) {
+ // Ignore if the text has been flushed already.
+ MOZ_ASSERT_IF(maybeEventText(prev), 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();
+
+ // Periodically remove unused payloads from the global logger state.
+ traceLoggerState->purgeUnusedPayloads();
+ }
+
+ // 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 (TraceLoggerThread* logger = threadLoggers.popFirst())
+ js_delete(logger);
+
+ threadLoggers.clear();
+
+ if (textIdPayloads.initialized()) {
+ for (TextIdHashMap::Range r = textIdPayloads.all(); !r.empty(); r.popFront())
+ js_delete(r.front().value());
+ }
+
+#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()
+{
+ 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, Frontend, ParsingFull,\n"
+ " ParsingSyntax, BytecodeEmission, IrregexpCompile, IrregexpExecute,\n"
+ " 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"
+ "\n"
+ " Frontend Output all information about frontend compilation. It includes:\n"
+ " Frontend, ParsingFull, ParsingSyntax, Tokenizing,\n"
+ " BytecodeEmission, BytecodeFoldConstants, BytecodeNameFunctions\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_Frontend] = true;
+ enabledTextIds[TraceLogger_ParsingFull] = true;
+ enabledTextIds[TraceLogger_ParsingSyntax] = true;
+ enabledTextIds[TraceLogger_BytecodeEmission] = 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;
+ }
+
+ if (ContainsFlag(env, "Frontend")) {
+ enabledTextIds[TraceLogger_Frontend] = true;
+ enabledTextIds[TraceLogger_ParsingFull] = true;
+ enabledTextIds[TraceLogger_ParsingSyntax] = true;
+ enabledTextIds[TraceLogger_BytecodeEmission] = true;
+ enabledTextIds[TraceLogger_BytecodeFoldConstants] = true;
+ enabledTextIds[TraceLogger_BytecodeNameFunctions] = 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"
+ " EnableActiveThread Start logging cooperating threads 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, "EnableActiveThread"))
+ cooperatingThreadEnabled = true;
+ if (strstr(options, "EnableOffThread"))
+ helperThreadEnabled = true;
+ if (strstr(options, "EnableGraph"))
+ graphSpewingEnabled = true;
+ if (strstr(options, "Errors"))
+ spewErrors = true;
+ }
+
+ if (!pointerMap.init())
+ return false;
+ if (!textIdPayloads.init())
+ return false;
+
+ 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::TraceLoggerForCurrentThread(JSContext* maybecx)
+{
+ if (!EnsureTraceLoggerState())
+ return nullptr;
+ return traceLoggerState->forCurrentThread(maybecx);
+}
+
+TraceLoggerThread*
+TraceLoggerThreadState::forCurrentThread(JSContext* maybecx)
+{
+ MOZ_ASSERT(initialized);
+ MOZ_ASSERT_IF(maybecx, maybecx == TlsContext.get());
+
+ JSContext* cx = maybecx ? maybecx : TlsContext.get();
+ if (!cx)
+ return nullptr;
+
+ if (!cx->traceLogger) {
+ LockGuard<Mutex> guard(lock);
+
+ TraceLoggerThread* logger = js_new<TraceLoggerThread>();
+ if (!logger)
+ return nullptr;
+
+ if (!logger->init()) {
+ js_delete(logger);
+ return nullptr;
+ }
+
+ threadLoggers.insertFront(logger);
+ cx->traceLogger = logger;
+
+ if (graphSpewingEnabled)
+ logger->initGraph();
+
+ if (CurrentHelperThread() ? helperThreadEnabled : cooperatingThreadEnabled)
+ logger->enable();
+ }
+
+ return cx->traceLogger;
+}
+
+void
+TraceLoggerThreadState::destroyLogger(TraceLoggerThread* logger)
+{
+ MOZ_ASSERT(initialized);
+ MOZ_ASSERT(logger);
+ LockGuard<Mutex> guard(lock);
+
+ logger->remove();
+ js_delete(logger);
+}
+
+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(TraceLoggerTextId type, JSScript* script)
+ : TraceLoggerEvent(type, script->filename(), script->lineno(), script->column())
+{ }
+
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerTextId type, const char* filename, size_t line,
+ size_t column)
+ : payload_()
+{
+ MOZ_ASSERT(type == TraceLogger_Scripts || type == TraceLogger_AnnotateScripts ||
+ type == TraceLogger_InlinedScripts || type == TraceLogger_Frontend);
+
+ if (!traceLoggerState)
+ return;
+
+ // Only log scripts when enabled, otherwise use the more generic type
+ // (which will get filtered out).
+ if (!traceLoggerState->isTextIdEnabled(type)) {
+ payload_.setTextId(type);
+ return;
+ }
+
+ payload_.setEventPayload(
+ traceLoggerState->getOrCreateEventPayload(filename, line, column, nullptr));
+}
+
+TraceLoggerEvent::TraceLoggerEvent(const char* text)
+ : payload_()
+{
+ if (traceLoggerState)
+ payload_.setEventPayload(traceLoggerState->getOrCreateEventPayload(text));
+}
+
+TraceLoggerEvent::~TraceLoggerEvent()
+{
+ if (hasExtPayload())
+ extPayload()->release();
+}
+
+uint32_t
+TraceLoggerEvent::textId() const
+{
+ MOZ_ASSERT(hasTextId());
+ if (hasExtPayload())
+ return extPayload()->textId();
+ return payload_.textId();
+}
+
+TraceLoggerEvent&
+TraceLoggerEvent::operator=(const TraceLoggerEvent& other)
+{
+ if (other.hasExtPayload())
+ other.extPayload()->use();
+ if (hasExtPayload())
+ extPayload()->release();
+
+ payload_ = other.payload_;
+
+ return *this;
+}
+
+TraceLoggerEvent::TraceLoggerEvent(const TraceLoggerEvent& other)
+ : payload_(other.payload_)
+{
+ if (hasExtPayload())
+ extPayload()->use();
+}