Bug 1154115 - Rewrite the JSAPI profiling API to use a FrameHandle, as to avoid multiple lookups in JitcodeGlobalTable. (r=djvj)
authorShu-yu Guo <shu@rfrn.org>
Mon, 11 May 2015 14:16:44 -0700
changeset 243411 cb4b66d730b3f517f786af925f36b84d78c3672d
parent 243410 79630cdf61aac76223b47e87e2a6ca419bf3c194
child 243412 ea1f7a05bd3205e41129bf4c1dd3f7e62248943f
push id28738
push usercbook@mozilla.com
push dateTue, 12 May 2015 14:11:31 +0000
treeherdermozilla-central@bedce1b405a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs1154115
milestone40.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 1154115 - Rewrite the JSAPI profiling API to use a FrameHandle, as to avoid multiple lookups in JitcodeGlobalTable. (r=djvj)
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());
-    }
-}