| /* -*- 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 <string.h> |
| |
| #include "jsapi.h" |
| #include "jsprf.h" |
| #include "jsscript.h" |
| |
| #include "jit/BaselineJIT.h" |
| #include "jit/CompileWrappers.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) |
| |
| #if defined(JS_TRACE_LOGGING) |
| |
| class AutoTraceLoggerThreadStateLock |
| { |
| TraceLoggerThreadState* logging; |
| |
| public: |
| explicit AutoTraceLoggerThreadStateLock(TraceLoggerThreadState* logging MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : logging(logging) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| PR_Lock(logging->lock); |
| } |
| ~AutoTraceLoggerThreadStateLock() { |
| PR_Unlock(logging->lock); |
| } |
| private: |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| 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; |
| } |
| } |
| |
| 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_ReportErrorNumber(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->isAsmJS()) { |
| JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL, |
| "not yet supported in asmjs 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() |
| { |
| if (failed) |
| return false; |
| |
| if (enabled == 0) |
| return true; |
| |
| if (enabled > 1) { |
| enabled--; |
| return true; |
| } |
| |
| 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 (!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(); |
| } |
| |
| size_t len = strlen(text); |
| char* str = js_pod_malloc<char>(len + 1); |
| if (!str) |
| return nullptr; |
| |
| DebugOnly<size_t> ret = JS_snprintf(str, len + 1, "%s", text); |
| 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); |
| return nullptr; |
| } |
| |
| if (!pointerMap.add(p, text, payload)) |
| return nullptr; |
| |
| if (graph.get()) |
| graph->addTextId(textId, str); |
| |
| nextTextId++; |
| |
| 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 = pointerMap.lookupForAdd(ptr); |
| if (p) { |
| MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check. |
| 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 = |
| JS_snprintf(str, len + 1, "script %s:%u:%u", 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); |
| return nullptr; |
| } |
| |
| if (!pointerMap.add(p, ptr, payload)) |
| return nullptr; |
| |
| if (graph.get()) |
| graph->addTextId(textId, str); |
| |
| nextTextId++; |
| |
| return payload; |
| } |
| |
| TraceLoggerEventPayload* |
| TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId type, JSScript* script) |
| { |
| return getOrCreateEventPayload(type, script->filename(), script->lineno(), script->column(), |
| script); |
| } |
| |
| TraceLoggerEventPayload* |
| TraceLoggerThread::getOrCreateEventPayload(TraceLoggerTextId type, |
| const JS::ReadOnlyCompileOptions& script) |
| { |
| return getOrCreateEventPayload(type, script.filename(), script.lineno, script.column, &script); |
| } |
| |
| void |
| TraceLoggerThread::startEvent(TraceLoggerTextId id) { |
| startEvent(uint32_t(id)); |
| } |
| |
| void |
| TraceLoggerThread::startEvent(const TraceLoggerEvent& event) { |
| if (!event.hasPayload()) { |
| startEvent(TraceLogger_Error); |
| 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; |
| |
| 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; |
| |
| 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; |
| |
| MOZ_ASSERT(traceLoggerState); |
| if (!events.ensureSpaceBeforeAdd()) { |
| uint64_t start = rdtsc() - traceLoggerState->startupTime; |
| |
| if (graph.get()) |
| graph->log(events); |
| |
| iteration_++; |
| events.clear(); |
| |
| // Log the time it took to flush the events as being from the |
| // Tracelogger. |
| if (graph.get()) { |
| MOZ_ASSERT(events.capacity() > 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; |
| } |
| |
| // 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(); |
| } |
| } |
| } |
| |
| uint64_t time = rdtsc() - traceLoggerState->startupTime; |
| |
| EventEntry& entry = events.pushUninitialized(); |
| entry.time = time; |
| entry.textId = id; |
| } |
| |
| TraceLoggerThreadState::~TraceLoggerThreadState() |
| { |
| for (size_t i = 0; i < mainThreadLoggers.length(); i++) |
| js_delete(mainThreadLoggers[i]); |
| |
| mainThreadLoggers.clear(); |
| |
| if (threadLoggers.initialized()) { |
| for (ThreadLoggerHashMap::Range r = threadLoggers.all(); !r.empty(); r.popFront()) |
| js_delete(r.front().value()); |
| |
| threadLoggers.finish(); |
| } |
| |
| if (lock) { |
| PR_DestroyLock(lock); |
| lock = nullptr; |
| } |
| |
| #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() |
| { |
| lock = PR_NewLock(); |
| if (!lock) |
| return false; |
| |
| if (!threadLoggers.init()) |
| return false; |
| |
| const char* env = js_sb_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, IonCompilation, IonLinking,\n" |
| " IonMonkey, MinorGC, ParserCompileFunction, ParserCompileScript,\n" |
| " ParserCompileLazy, ParserCompileModule, IrregexpCompile,\n" |
| " IrregexpExecute, Scripts, Engine\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, EffectiveAddressAnalysis, AlignmentMaskAnalysis, \n" |
| " EliminateDeadCode, ReorderInstructions, EdgeCaseAnalysis, \n" |
| " EliminateRedundantChecks, AddKeepAliveInstructions, GenerateLIR, \n" |
| " RegisterAllocation, GenerateCode, Scripts\n" |
| "\n" |
| "Specific log items:\n" |
| ); |
| for (uint32_t i = 1; i < TraceLogger_Last; i++) { |
| TraceLoggerTextId id = TraceLoggerTextId(i); |
| if (!TLTextIdIsToggable(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 (TLTextIdIsToggable(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_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; |
| } |
| |
| 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_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_Interpreter] = enabledTextIds[TraceLogger_Engine]; |
| enabledTextIds[TraceLogger_Baseline] = enabledTextIds[TraceLogger_Engine]; |
| enabledTextIds[TraceLogger_IonMonkey] = enabledTextIds[TraceLogger_Engine]; |
| |
| const char* options = js_sb_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" |
| ); |
| printf("\n"); |
| exit(0); |
| /*NOTREACHED*/ |
| } |
| |
| if (strstr(options, "EnableMainThread")) |
| mainThreadEnabled = true; |
| if (strstr(options, "EnableOffThread")) |
| offThreadEnabled = true; |
| if (strstr(options, "EnableGraph")) |
| graphSpewingEnabled = true; |
| } |
| |
| startupTime = rdtsc(); |
| |
| #ifdef DEBUG |
| initialized = true; |
| #endif |
| |
| return true; |
| } |
| |
| void |
| TraceLoggerThreadState::enableTextId(JSContext* cx, uint32_t textId) |
| { |
| MOZ_ASSERT(TLTextIdIsToggable(textId)); |
| |
| if (enabledTextIds[textId]) |
| return; |
| |
| enabledTextIds[textId] = true; |
| if (textId == TraceLogger_Engine) { |
| enabledTextIds[TraceLogger_IonMonkey] = true; |
| enabledTextIds[TraceLogger_Baseline] = true; |
| enabledTextIds[TraceLogger_Interpreter] = true; |
| } |
| |
| ReleaseAllJITCode(cx->runtime()->defaultFreeOp()); |
| |
| 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(TLTextIdIsToggable(textId)); |
| |
| if (!enabledTextIds[textId]) |
| return; |
| |
| enabledTextIds[textId] = false; |
| if (textId == TraceLogger_Engine) { |
| enabledTextIds[TraceLogger_IonMonkey] = false; |
| enabledTextIds[TraceLogger_Baseline] = false; |
| enabledTextIds[TraceLogger_Interpreter] = false; |
| } |
| |
| ReleaseAllJITCode(cx->runtime()->defaultFreeOp()); |
| |
| 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) { |
| AutoTraceLoggerThreadStateLock lock(this); |
| |
| TraceLoggerThread* logger = create(); |
| if (!logger) |
| return nullptr; |
| |
| if (!mainThreadLoggers.append(logger)) { |
| js_delete(logger); |
| return nullptr; |
| } |
| |
| mainThread->traceLogger = logger; |
| |
| if (graphSpewingEnabled) |
| logger->initGraph(); |
| |
| if (mainThreadEnabled) |
| logger->enable(); |
| } |
| |
| return mainThread->traceLogger; |
| } |
| |
| TraceLoggerThread* |
| js::TraceLoggerForCurrentThread() |
| { |
| PRThread* thread = PR_GetCurrentThread(); |
| if (!EnsureTraceLoggerState()) |
| return nullptr; |
| return traceLoggerState->forThread(thread); |
| } |
| |
| TraceLoggerThread* |
| TraceLoggerThreadState::forThread(PRThread* thread) |
| { |
| MOZ_ASSERT(initialized); |
| |
| AutoTraceLoggerThreadStateLock lock(this); |
| |
| ThreadLoggerHashMap::AddPtr p = threadLoggers.lookupForAdd(thread); |
| if (p) |
| return p->value(); |
| |
| TraceLoggerThread* logger = create(); |
| if (!logger) |
| return nullptr; |
| |
| if (!threadLoggers.add(p, thread, logger)) { |
| js_delete(logger); |
| return nullptr; |
| } |
| |
| if (graphSpewingEnabled) |
| logger->initGraph(); |
| |
| if (offThreadEnabled) |
| logger->enable(); |
| |
| return logger; |
| } |
| |
| TraceLoggerThread* |
| TraceLoggerThreadState::create() |
| { |
| TraceLoggerThread* logger = js_new<TraceLoggerThread>(); |
| if (!logger) |
| return nullptr; |
| |
| if (!logger->init()) { |
| js_delete(logger); |
| return nullptr; |
| } |
| |
| return 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(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 (hasPayload()) |
| payload()->release(); |
| if (other.hasPayload()) |
| other.payload()->use(); |
| |
| payload_ = other.payload_; |
| |
| return *this; |
| } |
| |
| #endif // defined(JS_TRACE_LOGGING) |