diff options
Diffstat (limited to 'js/src/vm/TraceLogging.cpp')
-rw-r--r-- | js/src/vm/TraceLogging.cpp | 1085 |
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(); +} |