Bug 1057082 - Part 1 - Add profilingActivation linked list and refactor ProfilingFrameIterator. r=luke
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 26 Aug 2014 14:03:04 -0400
changeset 201608 24ea345218767d7e62283f664a98ee5300c86a5b
parent 201607 58a8748ae6ffe89c6dc6b84893ae0883080d4c25
child 201609 efd173e4cc23b316c60e0df7b6df3291dc59ece2
push id48230
push userkvijayan@mozilla.com
push dateTue, 26 Aug 2014 18:03:12 +0000
treeherdermozilla-inbound@24ea34521876 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1057082
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1057082 - Part 1 - Add profilingActivation linked list and refactor ProfilingFrameIterator. r=luke
js/public/ProfilingFrameIterator.h
js/src/vm/ForkJoin.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -10,35 +10,39 @@
 #include "mozilla/Alignment.h"
 
 #include <stdint.h>
 
 #include "js/Utility.h"
 
 class JSAtom;
 struct JSRuntime;
-namespace js { class AsmJSActivation; class AsmJSProfilingFrameIterator; }
+
+namespace js {
+    class Activation;
+    class AsmJSProfilingFrameIterator;
+}
 
 namespace JS {
 
 // This iterator can be used to walk the stack of a thread suspended at an
 // arbitrary pc. To provide acurate results, profiling must have been enabled
 // (via EnableRuntimeProfilingStack) before executing the callstack being
 // unwound.
 class JS_PUBLIC_API(ProfilingFrameIterator)
 {
-    js::AsmJSActivation *activation_;
+    js::Activation *activation_;
 
     static const unsigned StorageSpace = 6 * sizeof(void*);
     mozilla::AlignedStorage<StorageSpace> storage_;
-    js::AsmJSProfilingFrameIterator &iter() {
+    js::AsmJSProfilingFrameIterator &asmJSIter() {
         JS_ASSERT(!done());
         return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
-    const js::AsmJSProfilingFrameIterator &iter() const {
+    const js::AsmJSProfilingFrameIterator &asmJSIter() const {
         JS_ASSERT(!done());
         return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
 
     void settle();
 
   public:
     struct RegisterState
@@ -59,13 +63,19 @@ class JS_PUBLIC_API(ProfilingFrameIterat
     //  - is weakly monotonically increasing (may be equal for successive frames)
     //  - will compare greater than newer native and psuedo-stack frame addresses
     //    and less than older native and psuedo-stack frame addresses
     void *stackAddress() const;
 
     // 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
 
 #endif  /* js_ProfilingFrameIterator_h */
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -262,16 +262,20 @@ class ForkJoinActivation : public Activa
     // join section, but the runtime/zone might still be marked as needing
     // barriers due to being in the middle of verifying barriers. Pause
     // verification during the fork join section.
     gc::AutoStopVerifyingBarriers av_;
 
   public:
     explicit ForkJoinActivation(JSContext *cx);
     ~ForkJoinActivation();
+
+    bool isProfiling() const {
+        return false;
+    }
 };
 
 class ForkJoinContext;
 
 bool ForkJoin(JSContext *cx, CallArgs &args);
 
 ///////////////////////////////////////////////////////////////////////////
 // Bailout tracking
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -73,16 +73,17 @@ PerThreadData::PerThreadData(JSRuntime *
     runtime_(runtime),
     jitTop(nullptr),
     jitJSContext(nullptr),
     jitStackLimit(0),
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     activation_(nullptr),
+    profilingActivation_(nullptr),
     asmJSActivationStack_(nullptr),
     autoFlushICache_(nullptr),
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulator_(nullptr),
     simulatorStackLimit_(0),
 #endif
     dtoaState(nullptr),
     suppressGC(0),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -562,16 +562,22 @@ class PerThreadData : public PerThreadDa
 #endif
 
     /*
      * Points to the most recent activation running on the thread.
      * See Activation comment in vm/Stack.h.
      */
     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_;
 
     /* Pointer to the current AutoFlushICache. */
     js::jit::AutoFlushICache *autoFlushICache_;
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     js::jit::Simulator *simulator_;
@@ -584,16 +590,20 @@ class PerThreadData : public PerThreadDa
     }
     static unsigned offsetOfAsmJSActivationStackReadOnly() {
         return offsetof(PerThreadData, asmJSActivationStack_);
     }
     static unsigned offsetOfActivation() {
         return offsetof(PerThreadData, activation_);
     }
 
+    js::Activation *profilingActivation() const {
+        return profilingActivation_;
+    }
+
     js::AsmJSActivation *asmJSActivationStack() const {
         return asmJSActivationStack_;
     }
     static js::AsmJSActivation *innermostAsmJSActivation() {
         PerThreadData *ptd = TlsPerThreadData.get();
         return ptd ? ptd->asmJSActivationStack_ : nullptr;
     }
 
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -733,28 +733,60 @@ AbstractFramePtr::popWith(JSContext *cx)
     }
     asBaselineFrame()->popWith(cx);
 }
 
 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()
 {
     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,
                                              InterpreterFrame *entryFrame)
   : Activation(cx, Interpreter),
     state_(state),
     entryFrame_(entryFrame),
     opMask_(0)
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1613,16 +1613,33 @@ InterpreterFrameIterator::operator++()
     } else {
         pc_ = nullptr;
         sp_ = nullptr;
         fp_ = nullptr;
     }
     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_)
 {
     settle();
 }
 
 ActivationIterator::ActivationIterator(PerThreadData *perThreadData)
@@ -1648,55 +1665,104 @@ ActivationIterator::settle()
 {
     // Stop at the next active activation. No need to update jitTop_, since
     // we don't iterate over an active jit activation.
     while (!done() && activation_->isJit() && !activation_->asJit()->isActive())
         activation_ = activation_->prev();
 }
 
 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();
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1098,16 +1098,17 @@ namespace jit {
 };
 
 class Activation
 {
   protected:
     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
     // iterating when it reaches this activation (if GO_THROUGH_SAVED is not
     // set).
     size_t savedFrameChain_;
 
     // Counter incremented by JS::HideScriptedCaller and decremented by
@@ -1128,30 +1129,36 @@ class Activation
         return cx_;
     }
     JSCompartment *compartment() const {
         return compartment_;
     }
     Activation *prev() const {
         return prev_;
     }
+    Activation *prevProfiling() const { return prevProfiling_; }
+    inline Activation *mostRecentProfiling();
 
     bool isInterpreter() const {
         return kind_ == Interpreter;
     }
     bool isJit() const {
         return kind_ == Jit;
     }
     bool isForkJoin() const {
         return kind_ == ForkJoin;
     }
     bool isAsmJS() const {
         return kind_ == AsmJS;
     }
 
+    inline bool isProfiling() const;
+    void registerProfiling();
+    void unregisterProfiling();
+
     InterpreterActivation *asInterpreter() const {
         JS_ASSERT(isInterpreter());
         return (InterpreterActivation *)this;
     }
     jit::JitActivation *asJit() const {
         JS_ASSERT(isJit());
         return (jit::JitActivation *)this;
     }
@@ -1233,16 +1240,20 @@ class InterpreterActivation : public Act
     }
     InterpreterFrame *entryFrame() const {
         return entryFrame_;
     }
     size_t opMask() const {
         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)
             enableInterruptsUnconditionally();
     }
     void enableInterruptsUnconditionally() {
         opMask_ = EnableInterruptsPseudoOpcode;
     }
@@ -1318,16 +1329,20 @@ class JitActivation : public Activation
     explicit JitActivation(ForkJoinContext *cx);
     ~JitActivation();
 
     bool isActive() const {
         return active_;
     }
     void setActive(JSContext *cx, bool active = true);
 
+    bool isProfiling() const {
+        return false;
+    }
+
     uint8_t *prevJitTop() const {
         return prevJitTop_;
     }
     bool firstFrameIsConstructing() const {
         return firstFrameIsConstructing_;
     }
     static size_t offsetOfPrevJitTop() {
         return offsetof(JitActivation, prevJitTop_);
@@ -1476,16 +1491,20 @@ class AsmJSActivation : public Activatio
   public:
     AsmJSActivation(JSContext *cx, AsmJSModule &module);
     ~AsmJSActivation();
 
     inline JSContext *cx();
     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_; }
 
     // Returns the reason why asm.js code called out of asm.js code.
     AsmJSExit::Reason exitReason() const { return exitReason_; }
 
     // Read by JIT code: