Bug 1040390 - Add JS::ProfilingFrameIterator::stackAddress (r=dougc)
authorLuke Wagner <luke@mozilla.com>
Tue, 29 Jul 2014 09:56:21 -0500
changeset 196620 dc93528851130f32496724bce49b335fa41c488e
parent 196619 42ee804e962942da4d90d6ce51cd710ae07e65c6
child 196621 7797ecb20e4b4122a98ebe5fddb22a91757280b3
push id27220
push userkwierso@gmail.com
push dateWed, 30 Jul 2014 00:01:50 +0000
treeherdermozilla-central@f61a27b00e05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougc
bugs1040390
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 1040390 - Add JS::ProfilingFrameIterator::stackAddress (r=dougc)
js/public/ProfilingFrameIterator.h
js/src/jit/AsmJSFrameIterator.cpp
js/src/jit/AsmJSFrameIterator.h
js/src/shell/js.cpp
js/src/vm/Stack.cpp
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -22,17 +22,17 @@ 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_;
 
-    static const unsigned StorageSpace = 5 * sizeof(void*);
+    static const unsigned StorageSpace = 6 * sizeof(void*);
     mozilla::AlignedStorage<StorageSpace> storage_;
     js::AsmJSProfilingFrameIterator &iter() {
         JS_ASSERT(!done());
         return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
     const js::AsmJSProfilingFrameIterator &iter() const {
         JS_ASSERT(!done());
         return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
@@ -50,16 +50,23 @@ class JS_PUBLIC_API(ProfilingFrameIterat
 #endif
     };
 
     ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state);
     ~ProfilingFrameIterator();
     void operator++();
     bool done() const { return !activation_; }
 
+    // Assuming the stack grows down (we do), the return value:
+    //  - always points into the stack
+    //  - 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;
+
     enum Kind {
         Function,
         AsmJSTrampoline,
         CppFunction
     };
     Kind kind() const;
 
     // Methods available if kind() == Function:
--- a/js/src/jit/AsmJSFrameIterator.cpp
+++ b/js/src/jit/AsmJSFrameIterator.cpp
@@ -364,16 +364,17 @@ js::GenerateAsmJSExitEpilogue(MacroAssem
 
 /*****************************************************************************/
 // AsmJSProfilingFrameIterator
 
 AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation &activation)
   : module_(&activation.module()),
     callerFP_(nullptr),
     callerPC_(nullptr),
+    stackAddress_(nullptr),
     exitReason_(AsmJSExit::None),
     codeRange_(nullptr)
 {
     initFromFP(activation);
 }
 
 static inline void
 AssertMatchesCallSite(const AsmJSModule &module, const AsmJSModule::CodeRange *calleeCodeRange,
@@ -422,16 +423,17 @@ AsmJSProfilingFrameIterator::initFromFP(
     // they properly accumulate self-time) and for this we use the exitReason.
 
     exitReason_ = activation.exitReason();
 
     void *pc = ReturnAddressFromFP(fp);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(pc);
     JS_ASSERT(codeRange);
     codeRange_ = codeRange;
+    stackAddress_ = fp;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
         fp = CallerFPFromFP(fp);
@@ -545,16 +547,17 @@ AsmJSProfilingFrameIterator::AsmJSProfil
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         break;
       }
     }
 
     codeRange_ = codeRange;
+    stackAddress_ = state.sp;
     JS_ASSERT(!done());
 }
 
 void
 AsmJSProfilingFrameIterator::operator++()
 {
     if (exitReason_ != AsmJSExit::None) {
         JS_ASSERT(codeRange_);
@@ -572,24 +575,26 @@ AsmJSProfilingFrameIterator::operator++(
 
     JS_ASSERT(callerPC_);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(callerPC_);
     JS_ASSERT(codeRange);
     codeRange_ = codeRange;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
+        JS_ASSERT(callerFP_ == nullptr);
+        JS_ASSERT(callerPC_ != nullptr);
         callerPC_ = nullptr;
-        callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
       case AsmJSModule::CodeRange::FFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
+        stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
         break;
     }
 
     JS_ASSERT(!done());
 }
--- a/js/src/jit/AsmJSFrameIterator.h
+++ b/js/src/jit/AsmJSFrameIterator.h
@@ -110,32 +110,35 @@ namespace AsmJSExit
 // Iterates over the frames of a single AsmJSActivation, given an
 // asynchrously-interrupted thread's state. If the activation's
 // module is not in profiling mode, the activation is skipped.
 class AsmJSProfilingFrameIterator
 {
     const AsmJSModule *module_;
     uint8_t *callerFP_;
     void *callerPC_;
+    void *stackAddress_;
     AsmJSExit::Reason exitReason_;
 
     // Really, a const AsmJSModule::CodeRange*, but no forward declarations of
     // nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
     const void *codeRange_;
 
     void initFromFP(const AsmJSActivation &activation);
 
   public:
     AsmJSProfilingFrameIterator() : codeRange_(nullptr) {}
     AsmJSProfilingFrameIterator(const AsmJSActivation &activation);
     AsmJSProfilingFrameIterator(const AsmJSActivation &activation,
                                 const JS::ProfilingFrameIterator::RegisterState &state);
     void operator++();
     bool done() const { return !codeRange_; }
 
+    void *stackAddress() const { JS_ASSERT(!done()); return stackAddress_; }
+
     typedef JS::ProfilingFrameIterator::Kind Kind;
     Kind kind() const;
 
     JSAtom *functionDisplayAtom() const;
     const char *functionFilename() const;
     unsigned functionLine() const;
 
     const char *nonFunctionDescription() const;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4262,18 +4262,22 @@ SingleStepCallback(void *arg, jit::Simul
 {
     JSRuntime *rt = reinterpret_cast<JSRuntime*>(arg);
 
     JS::ProfilingFrameIterator::RegisterState state;
     state.pc = pc;
     state.sp = (void*)sim->get_register(jit::Simulator::sp);
     state.lr = (void*)sim->get_register(jit::Simulator::lr);
 
+    DebugOnly<void*> lastStackAddress = nullptr;
     StackChars stack;
     for (JS::ProfilingFrameIterator i(rt, state); !i.done(); ++i) {
+        JS_ASSERT(i.stackAddress() != nullptr);
+        JS_ASSERT(lastStackAddress <= i.stackAddress());
+        lastStackAddress = i.stackAddress();
         switch (i.kind()) {
           case JS::ProfilingFrameIterator::Function: {
             JS::AutoCheckCannotGC nogc;
             JSAtom *atom = i.functionDisplayAtom();
             if (atom->hasLatin1Chars())
                 stack.append(atom->latin1Chars(nogc), atom->length());
             else
                 stack.append(atom->twoByteChars(nogc), atom->length());
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1827,16 +1827,26 @@ JS::ProfilingFrameIterator::settle()
             return;
         new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_);
     }
 #else
     MOZ_CRASH("Shouldn't have any frames");
 #endif
 }
 
+void *
+JS::ProfilingFrameIterator::stackAddress() const
+{
+#ifdef JS_ION
+    return iter().stackAddress();
+#else
+    MOZ_CRASH("Shouldn't have any frames");
+#endif
+}
+
 JS::ProfilingFrameIterator::Kind
 JS::ProfilingFrameIterator::kind() const
 {
 #ifdef JS_ION
     return iter().kind();
 #else
     MOZ_CRASH("Shouldn't have any frames");
 #endif