michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "jit/CompileWrappers.h" michael@0: #include "vm/Runtime.h" michael@0: michael@0: using namespace js; michael@0: michael@0: #ifndef TRACE_LOG_DIR michael@0: # if defined(_WIN32) michael@0: # define TRACE_LOG_DIR "" michael@0: # else michael@0: # define TRACE_LOG_DIR "/tmp/" michael@0: # endif michael@0: #endif michael@0: michael@0: #if defined(__i386__) michael@0: static __inline__ uint64_t michael@0: rdtsc(void) michael@0: { michael@0: uint64_t x; michael@0: __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); michael@0: return x; michael@0: } michael@0: #elif defined(__x86_64__) michael@0: static __inline__ uint64_t michael@0: rdtsc(void) michael@0: { michael@0: unsigned hi, lo; michael@0: __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); michael@0: return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 ); michael@0: } michael@0: #elif defined(__powerpc__) michael@0: static __inline__ uint64_t michael@0: rdtsc(void) michael@0: { michael@0: uint64_t result=0; michael@0: uint32_t upper, lower,tmp; michael@0: __asm__ volatile( michael@0: "0: \n" michael@0: "\tmftbu %0 \n" michael@0: "\tmftb %1 \n" michael@0: "\tmftbu %2 \n" michael@0: "\tcmpw %2,%0 \n" michael@0: "\tbne 0b \n" michael@0: : "=r"(upper),"=r"(lower),"=r"(tmp) michael@0: ); michael@0: result = upper; michael@0: result = result<<32; michael@0: result = result|lower; michael@0: michael@0: return result; michael@0: } michael@0: #endif michael@0: michael@0: TraceLogging traceLoggers; michael@0: michael@0: static const char* const text[] = michael@0: { michael@0: "TraceLogger failed to process text", michael@0: #define NAME(x) #x, michael@0: TRACELOGGER_TEXT_ID_LIST(NAME) michael@0: #undef NAME michael@0: }; michael@0: michael@0: TraceLogger::TraceLogger() michael@0: : enabled(false), michael@0: enabledTimes(0), michael@0: failed(false), michael@0: nextTextId(0), michael@0: treeOffset(0), michael@0: top(nullptr) michael@0: { } michael@0: michael@0: bool michael@0: TraceLogger::init(uint32_t loggerId) michael@0: { michael@0: if (!pointerMap.init()) michael@0: return false; michael@0: if (!tree.init()) michael@0: return false; michael@0: if (!stack.init()) michael@0: return false; michael@0: if (!events.init()) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(loggerId <= 999); michael@0: michael@0: char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.100.json"]; michael@0: sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%d.json", loggerId); michael@0: dictFile = fopen(dictFilename, "w"); michael@0: if (!dictFile) michael@0: return false; michael@0: michael@0: char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.100.tl"]; michael@0: sprintf(treeFilename, TRACE_LOG_DIR "tl-tree.%d.tl", loggerId); michael@0: treeFile = fopen(treeFilename, "wb"); michael@0: if (!treeFile) { michael@0: fclose(dictFile); michael@0: dictFile = nullptr; michael@0: return false; michael@0: } michael@0: michael@0: char eventFilename[sizeof TRACE_LOG_DIR "tl-event.100.tl"]; michael@0: sprintf(eventFilename, TRACE_LOG_DIR "tl-event.%d.tl", loggerId); michael@0: eventFile = fopen(eventFilename, "wb"); michael@0: if (!eventFile) { michael@0: fclose(dictFile); michael@0: fclose(treeFile); michael@0: dictFile = nullptr; michael@0: treeFile = nullptr; michael@0: return false; michael@0: } michael@0: michael@0: uint64_t start = rdtsc() - traceLoggers.startupTime; michael@0: michael@0: TreeEntry &treeEntry = tree.pushUninitialized(); michael@0: treeEntry.setStart(start); michael@0: treeEntry.setStop(0); michael@0: treeEntry.setTextId(0); michael@0: treeEntry.setHasChildren(false); michael@0: treeEntry.setNextId(0); michael@0: michael@0: StackEntry &stackEntry = stack.pushUninitialized(); michael@0: stackEntry.setTreeId(0); michael@0: stackEntry.setLastChildId(0); michael@0: stackEntry.setActive(true); michael@0: michael@0: int written = fprintf(dictFile, "["); michael@0: if (written < 0) michael@0: fprintf(stderr, "TraceLogging: Error while writing.\n"); michael@0: michael@0: // Eagerly create the default textIds, to match their Tracelogger::TextId. michael@0: for (uint32_t i = 0; i < LAST; i++) { michael@0: mozilla::DebugOnly textId = createTextId(text[i]); michael@0: MOZ_ASSERT(textId == i); michael@0: } michael@0: michael@0: enabled = true; michael@0: enabledTimes = 1; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::enable() michael@0: { michael@0: if (enabled) { michael@0: enabledTimes++; michael@0: return true; michael@0: } michael@0: michael@0: if (failed) michael@0: return false; michael@0: michael@0: if (!tree.ensureSpaceBeforeAdd(stack.size())) { michael@0: if (!flush()) { michael@0: fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); michael@0: failed = true; michael@0: return false; michael@0: } michael@0: if (!tree.ensureSpaceBeforeAdd(stack.size())) { michael@0: fprintf(stderr, "TraceLogging: Couldn't reserve enough space.\n"); michael@0: failed = true; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: uint64_t start = rdtsc() - traceLoggers.startupTime; michael@0: StackEntry *parent = &stack[0]; michael@0: for (uint32_t i = 1; i < stack.size(); i++) { michael@0: if (!traceLoggers.isTextIdEnabled(stack[i].textId())) michael@0: continue; michael@0: #ifdef DEBUG michael@0: TreeEntry entry; michael@0: if (!getTreeEntry(parent->treeId(), &entry)) michael@0: return false; michael@0: #endif michael@0: michael@0: if (parent->lastChildId() == 0) { michael@0: MOZ_ASSERT(!entry.hasChildren()); michael@0: MOZ_ASSERT(parent->treeId() == tree.currentId() + treeOffset); michael@0: if (!updateHasChildren(parent->treeId())) { michael@0: fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); michael@0: failed = true; michael@0: return false; michael@0: } michael@0: } else { michael@0: MOZ_ASSERT(entry.hasChildren() == 1); michael@0: if (!updateNextId(parent->lastChildId(), tree.nextId() + treeOffset)) { michael@0: fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); michael@0: failed = true; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: TreeEntry &treeEntry = tree.pushUninitialized(); michael@0: treeEntry.setStart(start); michael@0: treeEntry.setStop(0); michael@0: treeEntry.setTextId(stack[i].textId()); michael@0: treeEntry.setHasChildren(false); michael@0: treeEntry.setNextId(0); michael@0: michael@0: stack[i].setActive(true); michael@0: stack[i].setTreeId(tree.currentId() + treeOffset); michael@0: michael@0: parent->setLastChildId(tree.currentId() + treeOffset); michael@0: michael@0: parent = &stack[i]; michael@0: } michael@0: michael@0: enabled = true; michael@0: enabledTimes = 1; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::disable() michael@0: { michael@0: if (failed) michael@0: return false; michael@0: michael@0: if (!enabled) michael@0: return true; michael@0: michael@0: if (enabledTimes > 1) { michael@0: enabledTimes--; michael@0: return true; michael@0: } michael@0: michael@0: uint64_t stop = rdtsc() - traceLoggers.startupTime; michael@0: for (uint32_t i = 1; i < stack.size(); i++) { michael@0: if (!stack[i].active()) michael@0: continue; michael@0: michael@0: if (!updateStop(stack[i].treeId(), stop)) { michael@0: fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); michael@0: failed = true; michael@0: enabled = false; michael@0: return false; michael@0: } michael@0: michael@0: stack[i].setActive(false); michael@0: } michael@0: michael@0: michael@0: enabled = false; michael@0: enabledTimes = 0; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::flush() michael@0: { michael@0: MOZ_ASSERT(!failed); michael@0: michael@0: if (treeFile) { michael@0: // Format data in big endian. michael@0: for (size_t i = 0; i < tree.size(); i++) michael@0: entryToBigEndian(&tree[i]); michael@0: michael@0: int success = fseek(treeFile, 0, SEEK_END); michael@0: if (success != 0) michael@0: return false; michael@0: michael@0: size_t bytesWritten = fwrite(tree.data(), sizeof(TreeEntry), tree.size(), treeFile); michael@0: if (bytesWritten < tree.size()) michael@0: return false; michael@0: michael@0: treeOffset += tree.currentId(); michael@0: tree.clear(); michael@0: } michael@0: michael@0: if (eventFile) { michael@0: // Format data in big endian michael@0: for (size_t i = 0; i < events.size(); i++) { michael@0: events[i].time = htobe64(events[i].time); michael@0: events[i].textId = htobe64(events[i].textId); michael@0: } michael@0: michael@0: size_t bytesWritten = fwrite(events.data(), sizeof(EventEntry), events.size(), eventFile); michael@0: if (bytesWritten < events.size()) michael@0: return false; michael@0: events.clear(); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: TraceLogger::~TraceLogger() michael@0: { michael@0: // Write dictionary to disk michael@0: if (dictFile) { michael@0: int written = fprintf(dictFile, "]"); michael@0: if (written < 0) michael@0: fprintf(stderr, "TraceLogging: Error while writing.\n"); michael@0: fclose(dictFile); michael@0: michael@0: dictFile = nullptr; michael@0: } michael@0: michael@0: if (!failed && treeFile) { michael@0: // Make sure every start entry has a corresponding stop value. michael@0: // We temporary enable logging for this. Stop doesn't need any extra data, michael@0: // so is safe to do, even when we encountered OOM. michael@0: enabled = true; michael@0: while (stack.size() > 0) michael@0: stopEvent(); michael@0: enabled = false; michael@0: } michael@0: michael@0: if (!failed && !flush()) { michael@0: fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: } michael@0: michael@0: if (treeFile) { michael@0: fclose(treeFile); michael@0: treeFile = nullptr; michael@0: } michael@0: michael@0: if (eventFile) { michael@0: fclose(eventFile); michael@0: eventFile = nullptr; michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: TraceLogger::createTextId(const char *text) michael@0: { michael@0: assertNoQuotes(text); michael@0: michael@0: PointerHashMap::AddPtr p = pointerMap.lookupForAdd((const void *)text); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: uint32_t textId = nextTextId++; michael@0: if (!pointerMap.add(p, text, textId)) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: int written; michael@0: if (textId > 0) michael@0: written = fprintf(dictFile, ",\n\"%s\"", text); michael@0: else michael@0: written = fprintf(dictFile, "\"%s\"", text); michael@0: michael@0: if (written < 0) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: return textId; michael@0: } michael@0: michael@0: uint32_t michael@0: TraceLogger::createTextId(JSScript *script) michael@0: { michael@0: assertNoQuotes(script->filename()); michael@0: michael@0: PointerHashMap::AddPtr p = pointerMap.lookupForAdd(script); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: uint32_t textId = nextTextId++; michael@0: if (!pointerMap.add(p, script, textId)) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: int written; michael@0: if (textId > 0) { michael@0: written = fprintf(dictFile, ",\n\"script %s:%d:%d\"", script->filename(), michael@0: script->lineno(), script->column()); michael@0: } else { michael@0: written = fprintf(dictFile, "\"script %s:%d:%d\"", script->filename(), michael@0: script->lineno(), script->column()); michael@0: } michael@0: michael@0: if (written < 0) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: return textId; michael@0: } michael@0: michael@0: uint32_t michael@0: TraceLogger::createTextId(const JS::ReadOnlyCompileOptions &compileOptions) michael@0: { michael@0: assertNoQuotes(compileOptions.filename()); michael@0: michael@0: PointerHashMap::AddPtr p = pointerMap.lookupForAdd(&compileOptions); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: uint32_t textId = nextTextId++; michael@0: if (!pointerMap.add(p, &compileOptions, textId)) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: int written; michael@0: if (textId > 0) { michael@0: written = fprintf(dictFile, ",\n\"script %s:%d:%d\"", compileOptions.filename(), michael@0: compileOptions.lineno, compileOptions.column); michael@0: } else { michael@0: written = fprintf(dictFile, "\"script %s:%d:%d\"", compileOptions.filename(), michael@0: compileOptions.lineno, compileOptions.column); michael@0: } michael@0: michael@0: if (written < 0) michael@0: return TraceLogger::TL_Error; michael@0: michael@0: return textId; michael@0: } michael@0: michael@0: void michael@0: TraceLogger::logTimestamp(uint32_t id) michael@0: { michael@0: if (!enabled) michael@0: return; michael@0: michael@0: if (!events.ensureSpaceBeforeAdd()) { michael@0: fprintf(stderr, "TraceLogging: Disabled a tracelogger due to OOM.\n"); michael@0: enabled = false; michael@0: return; michael@0: } michael@0: michael@0: uint64_t time = rdtsc() - traceLoggers.startupTime; michael@0: michael@0: EventEntry &entry = events.pushUninitialized(); michael@0: entry.time = time; michael@0: entry.textId = id; michael@0: } michael@0: michael@0: void michael@0: TraceLogger::entryToBigEndian(TreeEntry *entry) michael@0: { michael@0: entry->start_ = htobe64(entry->start_); michael@0: entry->stop_ = htobe64(entry->stop_); michael@0: entry->u.value_ = htobe32((entry->u.s.textId_ << 1) + entry->u.s.hasChildren_); michael@0: entry->nextId_ = htobe32(entry->nextId_); michael@0: } michael@0: michael@0: void michael@0: TraceLogger::entryToSystemEndian(TreeEntry *entry) michael@0: { michael@0: entry->start_ = be64toh(entry->start_); michael@0: entry->stop_ = be64toh(entry->stop_); michael@0: michael@0: uint32_t data = be32toh(entry->u.value_); michael@0: entry->u.s.textId_ = data >> 1; michael@0: entry->u.s.hasChildren_ = data & 0x1; michael@0: michael@0: entry->nextId_ = be32toh(entry->nextId_); michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::getTreeEntry(uint32_t treeId, TreeEntry *entry) michael@0: { michael@0: // Entry is still in memory michael@0: if (treeId >= treeOffset) { michael@0: *entry = tree[treeId]; michael@0: return true; michael@0: } michael@0: michael@0: int success = fseek(treeFile, treeId * sizeof(TreeEntry), SEEK_SET); michael@0: if (success != 0) michael@0: return false; michael@0: michael@0: size_t itemsRead = fread((void *)entry, sizeof(TreeEntry), 1, treeFile); michael@0: if (itemsRead < 1) michael@0: return false; michael@0: michael@0: entryToSystemEndian(entry); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::saveTreeEntry(uint32_t treeId, TreeEntry *entry) michael@0: { michael@0: int success = fseek(treeFile, treeId * sizeof(TreeEntry), SEEK_SET); michael@0: if (success != 0) michael@0: return false; michael@0: michael@0: entryToBigEndian(entry); michael@0: michael@0: size_t itemsWritten = fwrite(entry, sizeof(TreeEntry), 1, treeFile); michael@0: if (itemsWritten < 1) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::updateHasChildren(uint32_t treeId, bool hasChildren) michael@0: { michael@0: if (treeId < treeOffset) { michael@0: TreeEntry entry; michael@0: if (!getTreeEntry(treeId, &entry)) michael@0: return false; michael@0: entry.setHasChildren(hasChildren); michael@0: if (!saveTreeEntry(treeId, &entry)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: tree[treeId - treeOffset].setHasChildren(hasChildren); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::updateNextId(uint32_t treeId, uint32_t nextId) michael@0: { michael@0: if (treeId < treeOffset) { michael@0: TreeEntry entry; michael@0: if (!getTreeEntry(treeId, &entry)) michael@0: return false; michael@0: entry.setNextId(nextId); michael@0: if (!saveTreeEntry(treeId, &entry)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: tree[treeId - treeOffset].setNextId(nextId); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::updateStop(uint32_t treeId, uint64_t timestamp) michael@0: { michael@0: if (treeId < treeOffset) { michael@0: TreeEntry entry; michael@0: if (!getTreeEntry(treeId, &entry)) michael@0: return false; michael@0: entry.setStop(timestamp); michael@0: if (!saveTreeEntry(treeId, &entry)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: tree[treeId - treeOffset].setStop(timestamp); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TraceLogger::startEvent(uint32_t id) michael@0: { michael@0: if (failed) michael@0: return; michael@0: michael@0: if (!stack.ensureSpaceBeforeAdd()) { michael@0: fprintf(stderr, "TraceLogging: Failed to allocate space to keep track of the stack.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: return; michael@0: } michael@0: michael@0: if (!enabled) { michael@0: StackEntry &stackEntry = stack.pushUninitialized(); michael@0: stackEntry.setTreeId(tree.currentId() + treeOffset); michael@0: stackEntry.setLastChildId(0); michael@0: stackEntry.setTextId(id); michael@0: stackEntry.setActive(false); michael@0: return; michael@0: } michael@0: michael@0: if (!tree.ensureSpaceBeforeAdd()) { michael@0: uint64_t start = rdtsc() - traceLoggers.startupTime; michael@0: if (!flush()) { michael@0: fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: return; michael@0: } michael@0: michael@0: // Log the time it took to flush the events as being from the michael@0: // Tracelogger. michael@0: if (!startEvent(TraceLogger::TL, start)) { michael@0: fprintf(stderr, "TraceLogging: Failed to start an event.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: return; michael@0: } michael@0: stopEvent(); michael@0: } michael@0: michael@0: uint64_t start = rdtsc() - traceLoggers.startupTime; michael@0: if (!startEvent(id, start)) { michael@0: fprintf(stderr, "TraceLogging: Failed to start an event.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: TraceLogger::StackEntry & michael@0: TraceLogger::getActiveAncestor() michael@0: { michael@0: uint32_t parentId = stack.currentId(); michael@0: while (!stack[parentId].active()) michael@0: parentId--; michael@0: return stack[parentId]; michael@0: } michael@0: michael@0: bool michael@0: TraceLogger::startEvent(uint32_t id, uint64_t timestamp) michael@0: { michael@0: // When a textId is disabled, a stack entry still needs to be pushed, michael@0: // together with an annotation that nothing needs to get done when receiving michael@0: // the stop event. michael@0: if (!traceLoggers.isTextIdEnabled(id)) { michael@0: StackEntry &stackEntry = stack.pushUninitialized(); michael@0: stackEntry.setActive(false); michael@0: return true; michael@0: } michael@0: michael@0: // Patch up the tree to be correct. There are two scenarios: michael@0: // 1) Parent has no children yet. So update parent to include children. michael@0: // 2) Parent has already children. Update last child to link to the new michael@0: // child. michael@0: StackEntry &parent = getActiveAncestor(); michael@0: #ifdef DEBUG michael@0: TreeEntry entry; michael@0: if (!getTreeEntry(parent.treeId(), &entry)) michael@0: return false; michael@0: #endif michael@0: michael@0: if (parent.lastChildId() == 0) { michael@0: MOZ_ASSERT(!entry.hasChildren()); michael@0: MOZ_ASSERT(parent.treeId() == tree.currentId() + treeOffset); michael@0: michael@0: if (!updateHasChildren(parent.treeId())) michael@0: return false; michael@0: } else { michael@0: MOZ_ASSERT(entry.hasChildren()); michael@0: michael@0: if (!updateNextId(parent.lastChildId(), tree.nextId() + treeOffset)) michael@0: return false; michael@0: } michael@0: michael@0: // Add a new tree entry. michael@0: TreeEntry &treeEntry = tree.pushUninitialized(); michael@0: treeEntry.setStart(timestamp); michael@0: treeEntry.setStop(0); michael@0: treeEntry.setTextId(id); michael@0: treeEntry.setHasChildren(false); michael@0: treeEntry.setNextId(0); michael@0: michael@0: // Add a new stack entry. michael@0: StackEntry &stackEntry = stack.pushUninitialized(); michael@0: stackEntry.setTreeId(tree.currentId() + treeOffset); michael@0: stackEntry.setLastChildId(0); michael@0: stackEntry.setActive(true); michael@0: michael@0: // Set the last child of the parent to this newly added entry. michael@0: parent.setLastChildId(tree.currentId() + treeOffset); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TraceLogger::stopEvent(uint32_t id) michael@0: { michael@0: #ifdef DEBUG michael@0: TreeEntry entry; michael@0: MOZ_ASSERT_IF(stack.current().active(), getTreeEntry(stack.current().treeId(), &entry)); michael@0: MOZ_ASSERT_IF(stack.current().active(), entry.textId() == id); michael@0: #endif michael@0: stopEvent(); michael@0: } michael@0: michael@0: void michael@0: TraceLogger::stopEvent() michael@0: { michael@0: if (enabled && stack.current().active()) { michael@0: uint64_t stop = rdtsc() - traceLoggers.startupTime; michael@0: if (!updateStop(stack.current().treeId(), stop)) { michael@0: fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); michael@0: enabled = false; michael@0: failed = true; michael@0: return; michael@0: } michael@0: } michael@0: stack.pop(); michael@0: } michael@0: michael@0: TraceLogging::TraceLogging() michael@0: { michael@0: initialized = false; michael@0: enabled = false; michael@0: mainThreadEnabled = true; michael@0: offThreadEnabled = true; michael@0: loggerId = 0; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: lock = PR_NewLock(); michael@0: if (!lock) michael@0: MOZ_CRASH(); michael@0: #endif // JS_THREADSAFE michael@0: } michael@0: michael@0: TraceLogging::~TraceLogging() michael@0: { michael@0: if (out) { michael@0: fprintf(out, "]"); michael@0: fclose(out); michael@0: out = nullptr; michael@0: } michael@0: michael@0: for (size_t i = 0; i < mainThreadLoggers.length(); i++) michael@0: delete mainThreadLoggers[i]; michael@0: michael@0: mainThreadLoggers.clear(); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: if (threadLoggers.initialized()) { michael@0: for (ThreadLoggerHashMap::Range r = threadLoggers.all(); !r.empty(); r.popFront()) michael@0: delete r.front().value(); michael@0: michael@0: threadLoggers.finish(); michael@0: } michael@0: michael@0: if (lock) { michael@0: PR_DestroyLock(lock); michael@0: lock = nullptr; michael@0: } michael@0: #endif // JS_THREADSAFE michael@0: michael@0: enabled = false; michael@0: } michael@0: michael@0: static bool michael@0: ContainsFlag(const char *str, const char *flag) michael@0: { michael@0: size_t flaglen = strlen(flag); michael@0: const char *index = strstr(str, flag); michael@0: while (index) { michael@0: if ((index == str || index[-1] == ',') && (index[flaglen] == 0 || index[flaglen] == ',')) michael@0: return true; michael@0: index = strstr(index + flaglen, flag); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: TraceLogging::lazyInit() michael@0: { michael@0: if (initialized) michael@0: return enabled; michael@0: michael@0: initialized = true; michael@0: michael@0: out = fopen(TRACE_LOG_DIR "tl-data.json", "w"); michael@0: if (!out) michael@0: return false; michael@0: fprintf(out, "["); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: if (!threadLoggers.init()) michael@0: return false; michael@0: #endif // JS_THREADSAFE michael@0: michael@0: const char *env = getenv("TLLOG"); michael@0: if (!env) michael@0: env = ""; michael@0: michael@0: if (strstr(env, "help")) { michael@0: fflush(nullptr); michael@0: printf( michael@0: "\n" michael@0: "usage: TLLOG=option,option,option,... where options can be:\n" michael@0: "\n" michael@0: "Collections:\n" michael@0: " Default Output all default\n" michael@0: " IonCompiler Output all information about compilation\n" michael@0: "\n" michael@0: "Specific log items:\n" michael@0: ); michael@0: for (uint32_t i = 1; i < TraceLogger::LAST; i++) { michael@0: printf(" %s\n", text[i]); michael@0: } michael@0: printf("\n"); michael@0: exit(0); michael@0: /*NOTREACHED*/ michael@0: } michael@0: michael@0: for (uint32_t i = 1; i < TraceLogger::LAST; i++) michael@0: enabledTextIds[i] = ContainsFlag(env, text[i]); michael@0: michael@0: enabledTextIds[TraceLogger::TL_Error] = true; michael@0: enabledTextIds[TraceLogger::TL] = true; michael@0: michael@0: if (ContainsFlag(env, "Default") || strlen(env) == 0) { michael@0: enabledTextIds[TraceLogger::Bailout] = true; michael@0: enabledTextIds[TraceLogger::Baseline] = true; michael@0: enabledTextIds[TraceLogger::BaselineCompilation] = true; michael@0: enabledTextIds[TraceLogger::GC] = true; michael@0: enabledTextIds[TraceLogger::GCAllocation] = true; michael@0: enabledTextIds[TraceLogger::GCSweeping] = true; michael@0: enabledTextIds[TraceLogger::Interpreter] = true; michael@0: enabledTextIds[TraceLogger::IonCompilation] = true; michael@0: enabledTextIds[TraceLogger::IonLinking] = true; michael@0: enabledTextIds[TraceLogger::IonMonkey] = true; michael@0: enabledTextIds[TraceLogger::MinorGC] = true; michael@0: enabledTextIds[TraceLogger::ParserCompileFunction] = true; michael@0: enabledTextIds[TraceLogger::ParserCompileLazy] = true; michael@0: enabledTextIds[TraceLogger::ParserCompileScript] = true; michael@0: enabledTextIds[TraceLogger::YarrCompile] = true; michael@0: enabledTextIds[TraceLogger::YarrInterpret] = true; michael@0: enabledTextIds[TraceLogger::YarrJIT] = true; michael@0: } michael@0: michael@0: if (ContainsFlag(env, "IonCompiler") || strlen(env) == 0) { michael@0: enabledTextIds[TraceLogger::IonCompilation] = true; michael@0: enabledTextIds[TraceLogger::IonLinking] = true; michael@0: enabledTextIds[TraceLogger::SplitCriticalEdges] = true; michael@0: enabledTextIds[TraceLogger::RenumberBlocks] = true; michael@0: enabledTextIds[TraceLogger::DominatorTree] = true; michael@0: enabledTextIds[TraceLogger::PhiAnalysis] = true; michael@0: enabledTextIds[TraceLogger::ApplyTypes] = true; michael@0: enabledTextIds[TraceLogger::ParallelSafetyAnalysis] = true; michael@0: enabledTextIds[TraceLogger::AliasAnalysis] = true; michael@0: enabledTextIds[TraceLogger::GVN] = true; michael@0: enabledTextIds[TraceLogger::UCE] = true; michael@0: enabledTextIds[TraceLogger::LICM] = true; michael@0: enabledTextIds[TraceLogger::RangeAnalysis] = true; michael@0: enabledTextIds[TraceLogger::EffectiveAddressAnalysis] = true; michael@0: enabledTextIds[TraceLogger::EliminateDeadCode] = true; michael@0: enabledTextIds[TraceLogger::EdgeCaseAnalysis] = true; michael@0: enabledTextIds[TraceLogger::EliminateRedundantChecks] = true; michael@0: } michael@0: michael@0: const char *options = getenv("TLOPTIONS"); michael@0: if (options) { michael@0: if (strstr(options, "help")) { michael@0: fflush(nullptr); michael@0: printf( michael@0: "\n" michael@0: "usage: TLOPTIONS=option,option,option,... where options can be:\n" michael@0: "\n" michael@0: " DisableMainThread Don't start logging the mainThread automatically.\n" michael@0: " DisableOffThread Don't start logging the off mainThread automatically.\n" michael@0: ); michael@0: printf("\n"); michael@0: exit(0); michael@0: /*NOTREACHED*/ michael@0: } michael@0: michael@0: if (strstr(options, "DisableMainThread")) michael@0: mainThreadEnabled = false; michael@0: if (strstr(options, "DisableOffThread")) michael@0: offThreadEnabled = false; michael@0: } michael@0: michael@0: startupTime = rdtsc(); michael@0: enabled = true; michael@0: return true; michael@0: } michael@0: michael@0: TraceLogger * michael@0: js::TraceLoggerForMainThread(jit::CompileRuntime *runtime) michael@0: { michael@0: return traceLoggers.forMainThread(runtime); michael@0: } michael@0: michael@0: TraceLogger * michael@0: TraceLogging::forMainThread(jit::CompileRuntime *runtime) michael@0: { michael@0: return forMainThread(runtime->mainThread()); michael@0: } michael@0: michael@0: TraceLogger * michael@0: js::TraceLoggerForMainThread(JSRuntime *runtime) michael@0: { michael@0: return traceLoggers.forMainThread(runtime); michael@0: } michael@0: michael@0: TraceLogger * michael@0: TraceLogging::forMainThread(JSRuntime *runtime) michael@0: { michael@0: return forMainThread(&runtime->mainThread); michael@0: } michael@0: michael@0: TraceLogger * michael@0: TraceLogging::forMainThread(PerThreadData *mainThread) michael@0: { michael@0: if (!mainThread->traceLogger) { michael@0: AutoTraceLoggingLock lock(this); michael@0: michael@0: if (!lazyInit()) michael@0: return nullptr; michael@0: michael@0: TraceLogger *logger = create(); michael@0: mainThread->traceLogger = logger; michael@0: michael@0: if (!mainThreadLoggers.append(logger)) michael@0: return nullptr; michael@0: michael@0: if (!mainThreadEnabled) michael@0: logger->disable(); michael@0: } michael@0: michael@0: return mainThread->traceLogger; michael@0: } michael@0: michael@0: TraceLogger * michael@0: js::TraceLoggerForCurrentThread() michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: PRThread *thread = PR_GetCurrentThread(); michael@0: return traceLoggers.forThread(thread); michael@0: #else michael@0: MOZ_ASSUME_UNREACHABLE("No threads supported. Use TraceLoggerForMainThread for the main thread."); michael@0: #endif // JS_THREADSAFE michael@0: } michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: TraceLogger * michael@0: TraceLogging::forThread(PRThread *thread) michael@0: { michael@0: AutoTraceLoggingLock lock(this); michael@0: michael@0: if (!lazyInit()) michael@0: return nullptr; michael@0: michael@0: ThreadLoggerHashMap::AddPtr p = threadLoggers.lookupForAdd(thread); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: TraceLogger *logger = create(); michael@0: if (!logger) michael@0: return nullptr; michael@0: michael@0: if (!threadLoggers.add(p, thread, logger)) { michael@0: delete logger; michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!offThreadEnabled) michael@0: logger->disable(); michael@0: michael@0: return logger; michael@0: } michael@0: #endif // JS_THREADSAFE michael@0: michael@0: TraceLogger * michael@0: TraceLogging::create() michael@0: { michael@0: if (loggerId > 999) { michael@0: fprintf(stderr, "TraceLogging: Can't create more than 999 different loggers."); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (loggerId > 0) { michael@0: int written = fprintf(out, ",\n"); michael@0: if (written < 0) michael@0: fprintf(stderr, "TraceLogging: Error while writing.\n"); michael@0: } michael@0: michael@0: loggerId++; michael@0: michael@0: int written = fprintf(out, "{\"tree\":\"tl-tree.%d.tl\", \"events\":\"tl-event.%d.tl\", \"dict\":\"tl-dict.%d.json\", \"treeFormat\":\"64,64,31,1,32\"}", michael@0: loggerId, loggerId, loggerId); michael@0: if (written < 0) michael@0: fprintf(stderr, "TraceLogging: Error while writing.\n"); michael@0: michael@0: michael@0: TraceLogger *logger = new TraceLogger(); michael@0: if (!logger) michael@0: return nullptr; michael@0: michael@0: if (!logger->init(loggerId)) { michael@0: delete logger; michael@0: return nullptr; michael@0: } michael@0: michael@0: return logger; michael@0: } michael@0: michael@0: bool michael@0: js::TraceLogTextIdEnabled(uint32_t textId) michael@0: { michael@0: return traceLoggers.isTextIdEnabled(textId); michael@0: }