diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h index 2da4ba0dbfb7..0cb573402cfc 100644 --- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -15,7 +15,11 @@ class JSAtom; struct JSRuntime; -namespace js { class AsmJSActivation; class AsmJSProfilingFrameIterator; } + +namespace js { + class Activation; + class AsmJSProfilingFrameIterator; +} namespace JS { @@ -25,15 +29,15 @@ namespace JS { // unwound. class JS_PUBLIC_API(ProfilingFrameIterator) { - js::AsmJSActivation *activation_; + js::Activation *activation_; static const unsigned StorageSpace = 6 * sizeof(void*); mozilla::AlignedStorage storage_; - js::AsmJSProfilingFrameIterator &iter() { + js::AsmJSProfilingFrameIterator &asmJSIter() { JS_ASSERT(!done()); return *reinterpret_cast(storage_.addr()); } - const js::AsmJSProfilingFrameIterator &iter() const { + const js::AsmJSProfilingFrameIterator &asmJSIter() const { JS_ASSERT(!done()); return *reinterpret_cast(storage_.addr()); } @@ -64,6 +68,12 @@ class JS_PUBLIC_API(ProfilingFrameIterator) // Return a label suitable for regexp-matching as performed by // browser/devtools/profiler/cleopatra/js/parserWorker.js const char *label() const; + + private: + void iteratorConstruct(const RegisterState &state); + void iteratorConstruct(); + void iteratorDestroy(); + bool iteratorDone(); }; } // namespace JS diff --git a/js/src/vm/ForkJoin.h b/js/src/vm/ForkJoin.h index d2eab5725b4b..b2bdb1bec710 100644 --- a/js/src/vm/ForkJoin.h +++ b/js/src/vm/ForkJoin.h @@ -267,6 +267,10 @@ class ForkJoinActivation : public Activation public: explicit ForkJoinActivation(JSContext *cx); ~ForkJoinActivation(); + + bool isProfiling() const { + return false; + } }; class ForkJoinContext; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 071b32ceb0be..9371afaa7a1e 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -78,6 +78,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime) traceLogger(nullptr), #endif activation_(nullptr), + profilingActivation_(nullptr), asmJSActivationStack_(nullptr), autoFlushICache_(nullptr), #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index b99be4e50e5c..060ea33690f8 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -567,6 +567,12 @@ class PerThreadData : public PerThreadDataFriendFields */ js::Activation *activation_; + /* + * Points to the most recent profiling activation running on the + * thread. Protected by rt->interruptLock. + */ + js::Activation * volatile profilingActivation_; + /* See AsmJSActivation comment. Protected by rt->interruptLock. */ js::AsmJSActivation * volatile asmJSActivationStack_; @@ -589,6 +595,10 @@ class PerThreadData : public PerThreadDataFriendFields return offsetof(PerThreadData, activation_); } + js::Activation *profilingActivation() const { + return profilingActivation_; + } + js::AsmJSActivation *asmJSActivationStack() const { return asmJSActivationStack_; } diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 4af82bb93a0d..2ea3aa6c3cee 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -738,11 +738,16 @@ Activation::Activation(ThreadSafeContext *cx, Kind kind) : cx_(cx), compartment_(cx->compartment_), prev_(cx->perThreadData->activation_), + prevProfiling_(prev_ ? prev_->mostRecentProfiling() : nullptr), savedFrameChain_(0), hideScriptedCallerCount_(0), kind_(kind) { cx->perThreadData->activation_ = this; + + // Link the activation into the list of profiling activations if needed. + if (isProfiling()) + registerProfiling(); } Activation::~Activation() @@ -750,6 +755,33 @@ Activation::~Activation() JS_ASSERT(cx_->perThreadData->activation_ == this); JS_ASSERT(hideScriptedCallerCount_ == 0); cx_->perThreadData->activation_ = prev_; + + if (isProfiling()) + unregisterProfiling(); +} + +bool +Activation::isProfiling() const +{ + if (isInterpreter()) + return asInterpreter()->isProfiling(); + + if (isJit()) + return asJit()->isProfiling(); + + if (isForkJoin()) + return asForkJoin()->isProfiling(); + + JS_ASSERT(isAsmJS()); + return asAsmJS()->isProfiling(); +} + +Activation * +Activation::mostRecentProfiling() +{ + if (isProfiling()) + return this; + return prevProfiling_; } InterpreterActivation::InterpreterActivation(RunState &state, JSContext *cx, diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 70e2354088af..eb2cd5cfca44 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1618,6 +1618,23 @@ InterpreterFrameIterator::operator++() return *this; } +void +Activation::registerProfiling() +{ + JS_ASSERT(isProfiling()); + JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime()); + cx_->perThreadData->profilingActivation_ = this; +} + +void +Activation::unregisterProfiling() +{ + JS_ASSERT(isProfiling()); + JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime()); + JS_ASSERT(cx_->perThreadData->profilingActivation_ == this); + cx_->perThreadData->profilingActivation_ = prevProfiling_; +} + ActivationIterator::ActivationIterator(JSRuntime *rt) : jitTop_(rt->mainThread.jitTop), activation_(rt->mainThread.activation_) @@ -1653,50 +1670,99 @@ ActivationIterator::settle() } JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state) - : activation_(rt->mainThread.asmJSActivationStack()) + : activation_(rt->mainThread.profilingActivation()) { if (!activation_) return; + JS_ASSERT(activation_->isProfiling()); + static_assert(sizeof(AsmJSProfilingFrameIterator) <= StorageSpace, "Need to increase storage"); - new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_, state); + + iteratorConstruct(state); settle(); } JS::ProfilingFrameIterator::~ProfilingFrameIterator() { - if (!done()) - iter().~AsmJSProfilingFrameIterator(); + if (!done()) { + JS_ASSERT(activation_->isProfiling()); + iteratorDestroy(); + } } void JS::ProfilingFrameIterator::operator++() { JS_ASSERT(!done()); - ++iter(); + + JS_ASSERT(activation_->isAsmJS()); + ++asmJSIter(); settle(); } void JS::ProfilingFrameIterator::settle() { - while (iter().done()) { - iter().~AsmJSProfilingFrameIterator(); - activation_ = activation_->prevAsmJS(); + while (iteratorDone()) { + iteratorDestroy(); + activation_ = activation_->prevProfiling(); if (!activation_) return; - new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_); + iteratorConstruct(); } } +void +JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState &state) +{ + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS(), state); +} + +void +JS::ProfilingFrameIterator::iteratorConstruct() +{ + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS()); +} + +void +JS::ProfilingFrameIterator::iteratorDestroy() +{ + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + asmJSIter().~AsmJSProfilingFrameIterator(); +} + +bool +JS::ProfilingFrameIterator::iteratorDone() +{ + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + return asmJSIter().done(); +} + void * JS::ProfilingFrameIterator::stackAddress() const { - return iter().stackAddress(); + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + return asmJSIter().stackAddress(); } const char * JS::ProfilingFrameIterator::label() const { - return iter().label(); + JS_ASSERT(!done()); + + JS_ASSERT(activation_->isAsmJS()); + return asmJSIter().label(); } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 04a8e6d6f55a..d1bfab36d587 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1103,6 +1103,7 @@ class Activation ThreadSafeContext *cx_; JSCompartment *compartment_; Activation *prev_; + Activation *prevProfiling_; // Counter incremented by JS_SaveFrameChain on the top-most activation and // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop @@ -1133,6 +1134,8 @@ class Activation Activation *prev() const { return prev_; } + Activation *prevProfiling() const { return prevProfiling_; } + inline Activation *mostRecentProfiling(); bool isInterpreter() const { return kind_ == Interpreter; @@ -1147,6 +1150,10 @@ class Activation return kind_ == AsmJS; } + inline bool isProfiling() const; + void registerProfiling(); + void unregisterProfiling(); + InterpreterActivation *asInterpreter() const { JS_ASSERT(isInterpreter()); return (InterpreterActivation *)this; @@ -1238,6 +1245,10 @@ class InterpreterActivation : public Activation return opMask_; } + bool isProfiling() const { + return false; + } + // If this js::Interpret frame is running |script|, enable interrupts. void enableInterruptsIfRunning(JSScript *script) { if (regs_.fp()->script() == script) @@ -1323,6 +1334,10 @@ class JitActivation : public Activation } void setActive(JSContext *cx, bool active = true); + bool isProfiling() const { + return false; + } + uint8_t *prevJitTop() const { return prevJitTop_; } @@ -1481,6 +1496,10 @@ class AsmJSActivation : public Activation AsmJSModule &module() const { return module_; } AsmJSActivation *prevAsmJS() const { return prevAsmJS_; } + bool isProfiling() const { + return true; + } + // Returns a pointer to the base of the innermost stack frame of asm.js code // in this activation. uint8_t *fp() const { return fp_; }