Bug 1154115 - Rewrite the JSAPI profiling API to use a FrameHandle, as to avoid multiple lookups in JitcodeGlobalTable. (r=djvj),a=sledru
authorShu-yu Guo <shu@rfrn.org>
Mon, 11 May 2015 14:16:44 -0700
changeset 274814 1d73cc5907f1dfb2a777c857b85aa08d61af3f84
parent 274813 6989af9a929f3bc5578489d186f1c6db26484b33
child 274815 abc21b1b28adaf480fca7aa7a9d9339c54056443
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj, sledru
bugs1154115
milestone40.0a2
Bug 1154115 - Rewrite the JSAPI profiling API to use a FrameHandle, as to avoid multiple lookups in JitcodeGlobalTable. (r=djvj),a=sledru
js/public/ProfilingFrameIterator.h
js/public/TrackedOptimizationInfo.h
js/src/jit/JitcodeMap.cpp
js/src/jit/JitcodeMap.h
js/src/jit/OptimizationTracking.cpp
js/src/vm/Stack.cpp
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -5,34 +5,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ProfilingFrameIterator_h
 #define js_ProfilingFrameIterator_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Maybe.h"
 
-#include <stdint.h>
-
+#include "jsbytecode.h"
 #include "js/Utility.h"
 
 struct JSRuntime;
+class JSScript;
 
 namespace js {
     class Activation;
     class AsmJSProfilingFrameIterator;
     namespace jit {
         class JitActivation;
         class JitProfilingFrameIterator;
         class JitcodeGlobalEntry;
     }
 }
 
 namespace JS {
 
+struct ForEachTrackedOptimizationAttemptOp;
+struct ForEachTrackedOptimizationTypeInfoOp;
+
 // 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)
 {
     JSRuntime* rt_;
     uint32_t sampleBufferGen_;
@@ -123,19 +126,16 @@ class JS_PUBLIC_API(ProfilingFrameIterat
     mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const;
 
     void iteratorConstruct(const RegisterState& state);
     void iteratorConstruct();
     void iteratorDestroy();
     bool iteratorDone();
 };
 
-extern JS_PUBLIC_API(ProfilingFrameIterator::FrameKind)
-GetProfilingFrameKindFromNativeAddr(JSRuntime* runtime, void* pc);
-
 JS_FRIEND_API(bool)
 IsProfilingEnabledForRuntime(JSRuntime* runtime);
 
 /**
  * After each sample run, this method should be called with the latest sample
  * buffer generation, and the lapCount.  It will update corresponding fields on
  * JSRuntime.
  *
@@ -143,18 +143,50 @@ IsProfilingEnabledForRuntime(JSRuntime* 
  * JSRuntime for documentation about what these values are used for.
  */
 JS_FRIEND_API(void)
 UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime* runtime, uint32_t generation,
                                        uint32_t lapCount);
 
 struct ForEachProfiledFrameOp
 {
+    // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
+    // lookups on JitcodeGlobalTable.
+    class MOZ_STACK_CLASS FrameHandle
+    {
+        friend JS_PUBLIC_API(void) JS::ForEachProfiledFrame(JSRuntime* rt, void* addr,
+                                                            ForEachProfiledFrameOp& op);
+
+        JSRuntime* rt_;
+        js::jit::JitcodeGlobalEntry& entry_;
+        void* addr_;
+        void* canonicalAddr_;
+        const char* label_;
+        uint32_t depth_;
+        mozilla::Maybe<uint8_t> optsIndex_;
+
+        FrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr,
+                    const char* label, uint32_t depth);
+
+        void updateHasTrackedOptimizations();
+
+      public:
+        const char* label() const { return label_; }
+        uint32_t depth() const { return depth_; }
+        bool hasTrackedOptimizations() const { return optsIndex_.isSome(); }
+        void* canonicalAddress() const { return canonicalAddr_; }
+
+        ProfilingFrameIterator::FrameKind frameKind() const;
+        void forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
+                                        JSScript** scriptOut, jsbytecode** pcOut) const;
+        void forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const;
+    };
+
     // Called once per frame.
-    virtual void operator()(const char* label, bool mightHaveTrackedOptimizations) = 0;
+    virtual void operator()(const FrameHandle& frame) = 0;
 };
 
 JS_PUBLIC_API(void)
 ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op);
 
 } // namespace JS
 
 #endif  /* js_ProfilingFrameIterator_h */
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -177,21 +177,16 @@ TrackedOutcomeString(TrackedOutcome outc
 JS_PUBLIC_API(const char*)
 TrackedTypeSiteString(TrackedTypeSite site);
 
 struct ForEachTrackedOptimizationAttemptOp
 {
     virtual void operator()(TrackedStrategy strategy, TrackedOutcome outcome) = 0;
 };
 
-JS_PUBLIC_API(void)
-ForEachTrackedOptimizationAttempt(JSRuntime* rt, void* addr, uint8_t index,
-                                  ForEachTrackedOptimizationAttemptOp& op,
-                                  JSScript** scriptOut, jsbytecode** pcOut);
-
 struct ForEachTrackedOptimizationTypeInfoOp
 {
     // Called 0+ times per entry, once for each type in the type set that Ion
     // saw during MIR construction. readType is always called _before_
     // operator() on the same entry.
     //
     // The keyedBy parameter describes how the type is keyed:
     //   - "primitive"   for primitive types
@@ -223,18 +218,11 @@ struct ForEachTrackedOptimizationTypeInf
     // quoted.
     virtual void readType(const char* keyedBy, const char* name,
                           const char* location, mozilla::Maybe<unsigned> lineno) = 0;
 
     // Called once per entry.
     virtual void operator()(TrackedTypeSite site, const char* mirType) = 0;
 };
 
-JS_PUBLIC_API(void)
-ForEachTrackedOptimizationTypeInfo(JSRuntime* rt, void* addr, uint8_t index,
-                                   ForEachTrackedOptimizationTypeInfoOp& op);
-
-JS_PUBLIC_API(mozilla::Maybe<uint8_t>)
-TrackedOptimizationIndexAtAddr(JSRuntime* rt, void* addr, void** entryAddr);
-
 } // namespace JS
 
 #endif // js_TrackedOptimizationInfo_h
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -33,16 +33,24 @@ RegionAtAddr(const JitcodeGlobalEntry::I
                  reinterpret_cast<uint8_t*>(entry.nativeStartAddr());
 
     uint32_t regionIdx = entry.regionTable()->findRegionEntry(*ptrOffset);
     MOZ_ASSERT(regionIdx < entry.regionTable()->numRegions());
 
     return entry.regionTable()->regionEntry(regionIdx);
 }
 
+void*
+JitcodeGlobalEntry::IonEntry::canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const
+{
+    uint32_t ptrOffset;
+    JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
+    return (void*)(((uint8_t*) nativeStartAddr()) + region.nativeOffset());
+}
+
 bool
 JitcodeGlobalEntry::IonEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
                                               BytecodeLocationVector& results,
                                               uint32_t* depth) const
 {
     uint32_t ptrOffset;
     JitcodeRegionEntry region = RegionAtAddr(*this, ptr, &ptrOffset);
     *depth = region.scriptDepth();
@@ -143,16 +151,24 @@ JitcodeGlobalEntry::IonEntry::destroy()
     }
     optsRegionTable_ = nullptr;
     optsTypesTable_ = nullptr;
     optsAttemptsTable_ = nullptr;
     js_delete(optsAllTypes_);
     optsAllTypes_ = nullptr;
 }
 
+void*
+JitcodeGlobalEntry::BaselineEntry::canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const
+{
+    // TODO: We can't yet normalize Baseline addresses until we unify
+    // BaselineScript's PCMappingEntries with JitcodeGlobalMap.
+    return ptr;
+}
+
 bool
 JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
                                                    BytecodeLocationVector& results,
                                                    uint32_t* depth) const
 {
     MOZ_ASSERT(containsPointer(ptr));
     MOZ_ASSERT(script_->hasBaselineScript());
 
@@ -204,16 +220,24 @@ RejoinEntry(JSRuntime* rt, const Jitcode
     MOZ_ASSERT(cache.containsPointer(ptr));
 
     // There must exist an entry for the rejoin addr if this entry exists.
     JitRuntime* jitrt = rt->jitRuntime();
     jitrt->getJitcodeGlobalTable()->lookupInfallible(cache.rejoinAddr(), entry, rt);
     MOZ_ASSERT(entry->isIon());
 }
 
+void*
+JitcodeGlobalEntry::IonCacheEntry::canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const
+{
+    JitcodeGlobalEntry entry;
+    RejoinEntry(rt, *this, ptr, &entry);
+    return entry.canonicalNativeAddrFor(rt, rejoinAddr());
+}
+
 bool
 JitcodeGlobalEntry::IonCacheEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
                                                    BytecodeLocationVector& results,
                                                    uint32_t* depth) const
 {
     JitcodeGlobalEntry entry;
     RejoinEntry(rt, *this, ptr, &entry);
     return entry.callStackAtAddr(rt, rejoinAddr(), results, depth);
@@ -1523,22 +1547,51 @@ JitcodeIonTable::WriteIonTable(CompactBu
     *numRegionsOut = runOffsets.length();
     return true;
 }
 
 
 } // namespace jit
 } // namespace js
 
+JS::ForEachProfiledFrameOp::FrameHandle::FrameHandle(JSRuntime* rt, JitcodeGlobalEntry& entry,
+                                                     void* addr, const char* label, uint32_t depth)
+  : rt_(rt),
+    entry_(entry),
+    addr_(addr),
+    canonicalAddr_(nullptr),
+    label_(label),
+    depth_(depth)
+{
+    updateHasTrackedOptimizations();
 
-JS_PUBLIC_API(JS::ProfilingFrameIterator::FrameKind)
-JS::GetProfilingFrameKindFromNativeAddr(JSRuntime* rt, void* addr)
+    if (!canonicalAddr_) {
+        // If the entry has tracked optimizations, updateHasTrackedOptimizations
+        // would have updated the canonical address.
+        MOZ_ASSERT_IF(entry_.isIon(), !hasTrackedOptimizations());
+        canonicalAddr_ = entry_.canonicalNativeAddrFor(rt_, addr_);
+    }
+}
+
+JS::ProfilingFrameIterator::FrameKind
+JS::ForEachProfiledFrameOp::FrameHandle::frameKind() const
+{
+    if (entry_.isBaseline())
+        return JS::ProfilingFrameIterator::Frame_Baseline;
+    return JS::ProfilingFrameIterator::Frame_Ion;
+}
+
+JS_PUBLIC_API(void)
+JS::ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op)
 {
     js::jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
     js::jit::JitcodeGlobalEntry entry;
     table->lookupInfallible(addr, &entry, rt);
-    MOZ_ASSERT(entry.isIon() || entry.isIonCache() || entry.isBaseline());
 
-    if (entry.isBaseline())
-        return JS::ProfilingFrameIterator::Frame_Baseline;
-
-    return JS::ProfilingFrameIterator::Frame_Ion;
+    // Extract the stack for the entry.  Assume maximum inlining depth is <64
+    const char* labels[64];
+    uint32_t depth = entry.callStackAtAddr(rt, addr, labels, 64);
+    MOZ_ASSERT(depth < 64);
+    for (uint32_t i = depth; i != 0; i--) {
+        JS::ForEachProfiledFrameOp::FrameHandle handle(rt, entry, addr, labels[i - 1], i - 1);
+        op(handle);
+    }
 }
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -313,16 +313,18 @@ class JitcodeGlobalEntry
             unsigned count = numScripts();
             for (unsigned i = 0; i < count; i++) {
                 if (getScript(i) == script)
                     return i;
             }
             return -1;
         }
 
+        void* canonicalNativeAddrFor(JSRuntime*rt, void* ptr) const;
+
         bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
                              uint32_t* depth) const;
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
@@ -400,16 +402,18 @@ class JitcodeGlobalEntry
 
         bool hadIonAbort() const {
             MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_);
             return ionAbortPc_ != nullptr;
         }
 
         void destroy();
 
+        void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;
+
         bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
                              uint32_t* depth) const;
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
@@ -432,16 +436,18 @@ class JitcodeGlobalEntry
         }
 
         void* rejoinAddr() const {
             return rejoinAddr_;
         }
 
         void destroy() {}
 
+        void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;
+
         bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
                              uint32_t* depth) const;
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
@@ -457,16 +463,20 @@ class JitcodeGlobalEntry
     struct DummyEntry : public BaseEntry
     {
         void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) {
             BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr);
         }
 
         void destroy() {}
 
+        void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const {
+            return nullptr;
+        }
+
         bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
                              uint32_t* depth) const
         {
             return true;
         }
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const
@@ -700,16 +710,32 @@ class JitcodeGlobalEntry
         MOZ_ASSERT(isDummy());
         return dummy_;
     }
     const QueryEntry& queryEntry() const {
         MOZ_ASSERT(isQuery());
         return query_;
     }
 
+    void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const {
+        switch (kind()) {
+          case Ion:
+            return ionEntry().canonicalNativeAddrFor(rt, ptr);
+          case Baseline:
+            return baselineEntry().canonicalNativeAddrFor(rt, ptr);
+          case IonCache:
+            return ionCacheEntry().canonicalNativeAddrFor(rt, ptr);
+          case Dummy:
+            return dummyEntry().canonicalNativeAddrFor(rt, ptr);
+          default:
+            MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
+        }
+        return nullptr;
+    }
+
     // Read the inline call stack at a given point in the native code and append into
     // the given vector.  Innermost (script,pc) pair will be appended first, and
     // outermost appended last.
     //
     // Returns false on memory failure.
     bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results,
                          uint32_t* depth) const
     {
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -1127,28 +1127,16 @@ IonBuilder::trackOptimizationSuccessUnch
 
 void
 IonBuilder::trackInlineSuccessUnchecked(InliningStatus status)
 {
     if (status == InliningStatus_Inlined)
         trackOptimizationOutcome(TrackedOutcome::Inlined);
 }
 
-JS_PUBLIC_API(void)
-JS::ForEachTrackedOptimizationAttempt(JSRuntime* rt, void* addr, uint8_t index,
-                                      ForEachTrackedOptimizationAttemptOp& op,
-                                      JSScript** scriptOut, jsbytecode** pcOut)
-{
-    JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
-    JitcodeGlobalEntry entry;
-    table->lookupInfallible(addr, &entry, rt);
-    entry.youngestFrameLocationAtAddr(rt, addr, scriptOut, pcOut);
-    entry.trackedOptimizationAttempts(index).forEach(op);
-}
-
 static void
 InterpretedFunctionFilenameAndLineNumber(JSFunction* fun, const char** filename,
                                          Maybe<unsigned>* lineno)
 {
     if (fun->hasScript()) {
         *filename = fun->nonLazyScript()->maybeForwardedScriptSource()->filename();
         *lineno = Some((unsigned) fun->nonLazyScript()->lineno());
     } else if (fun->lazyScriptOrNull()) {
@@ -1256,33 +1244,40 @@ IonTrackedOptimizationsTypeInfo::ForEach
 
 void
 IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::operator()(JS::TrackedTypeSite site,
                                                               MIRType mirType)
 {
     op_(site, StringFromMIRType(mirType));
 }
 
-JS_PUBLIC_API(void)
-JS::ForEachTrackedOptimizationTypeInfo(JSRuntime* rt, void* addr, uint8_t index,
-                                       ForEachTrackedOptimizationTypeInfoOp& op)
+typedef JS::ForEachProfiledFrameOp::FrameHandle FrameHandle;
+
+void
+FrameHandle::updateHasTrackedOptimizations()
 {
-    JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
-    JitcodeGlobalEntry entry;
-    table->lookupInfallible(addr, &entry, rt);
-    IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(op);
-    entry.trackedOptimizationTypeInfo(index).forEach(adapter, entry.allTrackedTypes());
+    // All inlined frames will have the same optimization information by
+    // virtue of sharing the JitcodeGlobalEntry, but such information is
+    // only interpretable on the youngest frame.
+    if (depth() != 0)
+        return;
+    if (!entry_.hasTrackedOptimizations())
+        return;
+    uint32_t entryOffset;
+    optsIndex_ = entry_.trackedOptimizationIndexAtAddr(addr_, &entryOffset);
+    if (optsIndex_.isSome())
+        canonicalAddr_ = (void*)(((uint8_t*) entry_.nativeStartAddr()) + entryOffset);
 }
 
-JS_PUBLIC_API(Maybe<uint8_t>)
-JS::TrackedOptimizationIndexAtAddr(JSRuntime* rt, void* addr, void** entryAddr)
+void
+FrameHandle::forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
+                                        JSScript** scriptOut, jsbytecode** pcOut) const
 {
-    JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
-    JitcodeGlobalEntry entry;
-    table->lookupInfallible(addr, &entry, rt);
-    if (!entry.hasTrackedOptimizations())
-        return Nothing();
-    uint32_t entryOffset = 0;
-    Maybe<uint8_t> index = entry.trackedOptimizationIndexAtAddr(addr, &entryOffset);
-    if (index.isSome())
-        *entryAddr = (void*)(((uint8_t*) entry.nativeStartAddr()) + entryOffset);
-    return index;
+    entry_.trackedOptimizationAttempts(*optsIndex_).forEach(op);
+    entry_.youngestFrameLocationAtAddr(rt_, addr_, scriptOut, pcOut);
 }
+
+void
+FrameHandle::forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const
+{
+    IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(op);
+    entry_.trackedOptimizationTypeInfo(*optsIndex_).forEach(adapter, entry_.allTrackedTypes());
+}
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1949,27 +1949,8 @@ JS::ProfilingFrameIterator::isAsmJS() co
     return activation_->isAsmJS();
 }
 
 bool
 JS::ProfilingFrameIterator::isJit() const
 {
     return activation_->isJit();
 }
-
-JS_PUBLIC_API(void)
-JS::ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op)
-{
-    jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
-    jit::JitcodeGlobalEntry entry;
-    table->lookupInfallible(addr, &entry, rt);
-
-    // Extract the stack for the entry.  Assume maximum inlining depth is <64
-    const char* labels[64];
-    uint32_t depth = entry.callStackAtAddr(rt, addr, labels, 64);
-    MOZ_ASSERT(depth < 64);
-    for (uint32_t i = depth; i != 0; i--) {
-        // All inlined frames will have the same optimization information by
-        // virtue of sharing the JitcodeGlobalEntry, but such information is
-        // only interpretable on the youngest frame.
-        op(labels[i - 1], i == 1 && entry.hasTrackedOptimizations());
-    }
-}