diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 644e4164ec9b..c938963f1fec 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1616,7 +1616,7 @@ EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); - args.rval().setBoolean(TraceLoggerEnable(logger)); + args.rval().setBoolean(TraceLoggerEnable(logger, cx)); return true; } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 87da182b4bca..f8ff31113aea 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -1005,17 +1005,23 @@ CodeGeneratorShared::computeDivisionConstants(int d) { bool CodeGeneratorShared::emitTracelogScript(bool isStart) { + Label done; + RegisterSet regs = RegisterSet::Volatile(); Register logger = regs.takeGeneral(); Register script = regs.takeGeneral(); masm.Push(logger); - masm.Push(script); CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger); if (!patchableTraceLoggers_.append(patchLogger)) return false; + Address enabledAddress(logger, TraceLogger::offsetOfEnabled()); + masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done); + + masm.Push(script); + CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script); if (!patchableTLScripts_.append(patchScript)) return false; @@ -1026,6 +1032,9 @@ CodeGeneratorShared::emitTracelogScript(bool isStart) masm.tracelogStop(logger, script); masm.Pop(script); + + masm.bind(&done); + masm.Pop(logger); return true; } @@ -1036,6 +1045,7 @@ CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) if (!TraceLogTextIdEnabled(textId)) return true; + Label done; RegisterSet regs = RegisterSet::Volatile(); Register logger = regs.takeGeneral(); @@ -1045,6 +1055,9 @@ CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) if (!patchableTraceLoggers_.append(patchLocation)) return false; + Address enabledAddress(logger, TraceLogger::offsetOfEnabled()); + masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done); + if (isStart) { masm.tracelogStart(logger, textId); } else { @@ -1055,6 +1068,8 @@ CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) #endif } + masm.bind(&done); + masm.Pop(logger); return true; } diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index d413074ac674..1b4a0e5bb83e 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -17,7 +17,10 @@ #include "jit/CompileWrappers.h" #include "vm/Runtime.h" +#include "jit/IonFrames-inl.h" + using namespace js; +using namespace js::jit; using mozilla::NativeEndian; #ifndef TRACE_LOG_DIR @@ -85,8 +88,7 @@ static const char* const text[] = }; TraceLogger::TraceLogger() - : enabled(false), - enabledTimes(0), + : enabled(0), failed(false), nextTextId(0), treeOffset(0), @@ -157,82 +159,89 @@ TraceLogger::init(uint32_t loggerId) MOZ_ASSERT(textId == i); } - enabled = true; - enabledTimes = 1; + enabled = 1; return true; } bool TraceLogger::enable() { - if (enabled) { - enabledTimes++; + if (enabled > 0) { + enabled++; return true; } if (failed) return false; - if (!tree.ensureSpaceBeforeAdd(stack.size())) { - if (!flush()) { - fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); + enabled = 1; + return true; +} + +bool +TraceLogger::enable(JSContext *cx) +{ + if (!enable()) + return false; + + if (enabled == 1) { + // Get the top Activation to log the top script/pc (No inlined frames). + Activation *act = cx->mainThread().activation(); + while (act && (act->cx() != cx || (act->isJit() && !act->asJit()->isActive()))) + act = act->prev(); + + if (!act) { failed = true; + enabled = 0; return false; } - if (!tree.ensureSpaceBeforeAdd(stack.size())) { - fprintf(stderr, "TraceLogging: Couldn't reserve enough space.\n"); - failed = true; - return false; - } - } - uint64_t start = rdtsc() - traceLoggers.startupTime; - StackEntry *parent = &stack[0]; - for (uint32_t i = 1; i < stack.size(); i++) { - if (!traceLoggers.isTextIdEnabled(stack[i].textId())) - continue; -#ifdef DEBUG - TreeEntry entry; - if (!getTreeEntry(parent->treeId(), &entry)) - return false; -#endif + JSScript *script = nullptr; + int32_t engine = 0; - if (parent->lastChildId() == 0) { - MOZ_ASSERT(!entry.hasChildren()); - MOZ_ASSERT(parent->treeId() == tree.currentId() + treeOffset); - if (!updateHasChildren(parent->treeId())) { - fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); - failed = true; - return false; + if (act->isJit()) { + JSRuntime *rt = cx->runtime(); + + JitFrameIterator it(rt->mainThread.jitTop, SequentialExecution); + + JS_ASSERT(it.type() == JitFrame_Exit); + ++it; + + if (it.type() == JitFrame_Rectifier || + it.type() == JitFrame_Unwound_Rectifier) + { + ++it; + JS_ASSERT(it.type() == JitFrame_BaselineStub || + it.type() == JitFrame_BaselineJS || + it.type() == JitFrame_IonJS); } + if (it.type() == JitFrame_BaselineStub || + it.type() == JitFrame_Unwound_BaselineStub) + { + ++it; + JS_ASSERT(it.type() == JitFrame_BaselineJS); + } + + script = it.script(); + engine = it.isIonJS() ? IonMonkey : Baseline; } else { - MOZ_ASSERT(entry.hasChildren() == 1); - if (!updateNextId(parent->lastChildId(), tree.nextId() + treeOffset)) { - fprintf(stderr, "TraceLogging: Couldn't update an entry.\n"); + JS_ASSERT(act->isInterpreter()); + InterpreterFrame *fp = act->asInterpreter()->current(); + JS_ASSERT(!fp->runningInJit()); + + script = fp->script(); + engine = Interpreter; + if (script->compartment() != cx->compartment()) { failed = true; + enabled = 0; return false; } } - TreeEntry &treeEntry = tree.pushUninitialized(); - treeEntry.setStart(start); - treeEntry.setStop(0); - treeEntry.setTextId(stack[i].textId()); - treeEntry.setHasChildren(false); - treeEntry.setNextId(0); - - stack[i].setActive(true); - stack[i].setTreeId(tree.currentId() + treeOffset); - stack[i].setLastChildId(0); - - parent->setLastChildId(tree.currentId() + treeOffset); - - parent = &stack[i]; + startEvent(createTextId(script)); + startEvent(engine); } - enabled = true; - enabledTimes = 1; - return true; } @@ -242,32 +251,19 @@ TraceLogger::disable() if (failed) return false; - if (!enabled) + if (enabled == 0) return true; - if (enabledTimes > 1) { - enabledTimes--; + if (enabled > 1) { + enabled--; return true; } - uint64_t stop = rdtsc() - traceLoggers.startupTime; - for (uint32_t i = 1; i < stack.size(); i++) { - if (!stack[i].active()) - continue; + JS_ASSERT(enabled == 1); + while (stack.currentId() > 0) + stopEvent(); - if (!updateStop(stack[i].treeId(), stop)) { - fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); - failed = true; - enabled = false; - return false; - } - - stack[i].setActive(false); - } - - - enabled = false; - enabledTimes = 0; + enabled = 0; return true; } @@ -326,15 +322,15 @@ TraceLogger::~TraceLogger() // Make sure every start entry has a corresponding stop value. // We temporary enable logging for this. Stop doesn't need any extra data, // so is safe to do, even when we encountered OOM. - enabled = true; + enabled = 1; while (stack.currentId() > 0) stopEvent(); - enabled = false; + enabled = 0; } if (!failed && !flush()) { fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); - enabled = false; + enabled = 0; failed = true; } @@ -439,12 +435,12 @@ TraceLogger::createTextId(const JS::ReadOnlyCompileOptions &compileOptions) void TraceLogger::logTimestamp(uint32_t id) { - if (!enabled) + if (enabled == 0) return; if (!events.ensureSpaceBeforeAdd()) { fprintf(stderr, "TraceLogging: Disabled a tracelogger due to OOM.\n"); - enabled = false; + enabled = 0; return; } @@ -569,31 +565,15 @@ TraceLogger::updateStop(uint32_t treeId, uint64_t timestamp) void TraceLogger::startEvent(uint32_t id) { - if (failed) + if (failed || enabled == 0) return; - if (!stack.ensureSpaceBeforeAdd()) { - fprintf(stderr, "TraceLogging: Failed to allocate space to keep track of the stack.\n"); - enabled = false; - failed = true; - return; - } - - if (!enabled) { - StackEntry &stackEntry = stack.pushUninitialized(); - stackEntry.setTreeId(tree.currentId() + treeOffset); - stackEntry.setLastChildId(0); - stackEntry.setTextId(id); - stackEntry.setActive(false); - return; - } - if (!tree.hasSpaceForAdd()){ uint64_t start = rdtsc() - traceLoggers.startupTime; if (!tree.ensureSpaceBeforeAdd()) { if (!flush()) { fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n"); - enabled = false; + enabled = 0; failed = true; return; } @@ -603,7 +583,7 @@ TraceLogger::startEvent(uint32_t id) // Tracelogger. if (!startEvent(TraceLogger::TL, start)) { fprintf(stderr, "TraceLogging: Failed to start an event.\n"); - enabled = false; + enabled = 0; failed = true; return; } @@ -613,7 +593,7 @@ TraceLogger::startEvent(uint32_t id) uint64_t start = rdtsc() - traceLoggers.startupTime; if (!startEvent(id, start)) { fprintf(stderr, "TraceLogging: Failed to start an event.\n"); - enabled = false; + enabled = 0; failed = true; return; } @@ -631,6 +611,9 @@ TraceLogger::getActiveAncestor() bool TraceLogger::startEvent(uint32_t id, uint64_t timestamp) { + if (!stack.ensureSpaceBeforeAdd()) + return false; + // When a textId is disabled, a stack entry still needs to be pushed, // together with an annotation that nothing needs to get done when receiving // the stop event. @@ -688,9 +671,11 @@ void TraceLogger::stopEvent(uint32_t id) { #ifdef DEBUG - TreeEntry entry; - MOZ_ASSERT_IF(stack.current().active(), getTreeEntry(stack.current().treeId(), &entry)); - MOZ_ASSERT_IF(stack.current().active(), entry.textId() == id); + if (stack.currentId() > 0) { + TreeEntry entry; + MOZ_ASSERT_IF(stack.current().active(), getTreeEntry(stack.current().treeId(), &entry)); + MOZ_ASSERT_IF(stack.current().active(), entry.textId() == id); + } #endif stopEvent(); } @@ -698,23 +683,31 @@ TraceLogger::stopEvent(uint32_t id) void TraceLogger::stopEvent() { - if (enabled && stack.current().active()) { + if (enabled > 0 && stack.current().active()) { uint64_t stop = rdtsc() - traceLoggers.startupTime; if (!updateStop(stack.current().treeId(), stop)) { fprintf(stderr, "TraceLogging: Failed to stop an event.\n"); - enabled = false; + enabled = 0; failed = true; return; } } - JS_ASSERT(stack.currentId() > 0); + if (stack.currentId() == 0) { + if (enabled == 0) + return; + + // Forcefully disable logging. We have no stack information anymore. + enabled = 1; + disable(); + return; + } stack.pop(); } TraceLogging::TraceLogging() { initialized = false; - enabled = false; + enabled = 0; mainThreadEnabled = true; offThreadEnabled = true; loggerId = 0; @@ -749,7 +742,7 @@ TraceLogging::~TraceLogging() lock = nullptr; } - enabled = false; + enabled = 0; } static bool @@ -769,7 +762,7 @@ bool TraceLogging::lazyInit() { if (initialized) - return enabled; + return enabled > 0; initialized = true; @@ -877,18 +870,18 @@ TraceLogging::lazyInit() } startupTime = rdtsc(); - enabled = true; + enabled = 1; return true; } TraceLogger * -js::TraceLoggerForMainThread(jit::CompileRuntime *runtime) +js::TraceLoggerForMainThread(CompileRuntime *runtime) { return traceLoggers.forMainThread(runtime); } TraceLogger * -TraceLogging::forMainThread(jit::CompileRuntime *runtime) +TraceLogging::forMainThread(CompileRuntime *runtime) { return forMainThread(runtime->mainThread()); } diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index d53a299e5a2b..bc64654fbb87 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -387,8 +387,7 @@ class TraceLogger FILE *treeFile; FILE *eventFile; - bool enabled; - uint32_t enabledTimes; + uint32_t enabled; bool failed; uint32_t nextTextId; @@ -436,6 +435,7 @@ class TraceLogger bool init(uint32_t loggerId); bool enable(); + bool enable(JSContext *cx); bool disable(); // The createTextId functions map a unique input to a logger ID. @@ -455,6 +455,10 @@ class TraceLogger void stopEvent(uint32_t id); void stopEvent(); + static unsigned offsetOfEnabled() { + return offsetof(TraceLogger, enabled); + } + private: void assertNoQuotes(const char *text) { #ifdef DEBUG @@ -531,6 +535,13 @@ inline bool TraceLoggerEnable(TraceLogger *logger) { #endif return false; } +inline bool TraceLoggerEnable(TraceLogger *logger, JSContext *cx) { +#ifdef JS_TRACE_LOGGING + if (logger) + return logger->enable(cx); +#endif + return false; +} inline bool TraceLoggerDisable(TraceLogger *logger) { #ifdef JS_TRACE_LOGGING if (logger)