Bug 1434965 - Replace callback-based API ForEachProfiledFrame with an iterator-based API called GetProfiledFrames. r=njn
authorMarkus Stange <mstange@themasta.com>
Sat, 10 Feb 2018 20:38:41 -0500
changeset 455951 b99f2166a10b9809d789c6da2f11f1c919a8a1ce
parent 455950 55f89e248047b9aadd27e6704656f6f82c69cf14
child 455952 af243ffe2e85e08c25eb8116da9e638abb1aec20
push id8799
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 16:46:23 +0000
treeherdermozilla-beta@15334014dc67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1434965
milestone60.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 1434965 - Replace callback-based API ForEachProfiledFrame with an iterator-based API called GetProfiledFrames. r=njn This also renames ForEachProfiledFrameOp::FrameHandle to ProfiledFrameHandle. MozReview-Commit-ID: 7Jh1x2QWjXe
js/public/ProfilingFrameIterator.h
js/src/jit/JitcodeMap.cpp
js/src/jit/OptimizationTracking.cpp
tools/profiler/core/ProfileBufferEntry.cpp
tools/profiler/core/ProfileBufferEntry.h
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -154,55 +154,97 @@ IsProfilingEnabledForContext(JSContext* 
  * corresponding field on the JSRuntime.
  *
  * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
  * about what this value is used for.
  */
 JS_FRIEND_API(void)
 SetJSContextProfilerSampleBufferRangeStart(JSContext* cx, uint64_t rangeStart);
 
-struct ForEachProfiledFrameOp
+class ProfiledFrameRange;
+
+// A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
+// lookups on JitcodeGlobalTable.
+class MOZ_STACK_CLASS ProfiledFrameHandle
 {
-    // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
-    // lookups on JitcodeGlobalTable.
-    class MOZ_STACK_CLASS FrameHandle
-    {
-        friend JS_PUBLIC_API(void) ForEachProfiledFrame(JSContext* cx, void* addr,
-                                                        ForEachProfiledFrameOp& op);
+    friend class ProfiledFrameRange;
+
+    JSRuntime* rt_;
+    js::jit::JitcodeGlobalEntry& entry_;
+    void* addr_;
+    void* canonicalAddr_;
+    const char* label_;
+    uint32_t depth_;
+    mozilla::Maybe<uint8_t> optsIndex_;
 
-        JSRuntime* rt_;
-        js::jit::JitcodeGlobalEntry& entry_;
-        void* addr_;
-        void* canonicalAddr_;
-        const char* label_;
-        uint32_t depth_;
-        mozilla::Maybe<uint8_t> optsIndex_;
+    ProfiledFrameHandle(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_; }
 
-        FrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr,
-                    const char* label, uint32_t depth);
+    JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) frameKind() const;
+    JS_PUBLIC_API(void) forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
+                                                    JSScript** scriptOut,
+                                                    jsbytecode** pcOut) const;
 
-        void updateHasTrackedOptimizations();
+    JS_PUBLIC_API(void)
+    forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const;
+};
 
-      public:
-        const char* label() const { return label_; }
-        uint32_t depth() const { return depth_; }
-        bool hasTrackedOptimizations() const { return optsIndex_.isSome(); }
-        void* canonicalAddress() const { return canonicalAddr_; }
+class ProfiledFrameRange
+{
+public:
+    class Iter final
+    {
+    public:
+        Iter(const ProfiledFrameRange& range, uint32_t index)
+          : range_(range)
+          , index_(index)
+        {}
 
-        JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) frameKind() const;
-        JS_PUBLIC_API(void) forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
-                                                       JSScript** scriptOut,
-                                                       jsbytecode** pcOut) const;
+        JS_PUBLIC_API(ProfiledFrameHandle) operator*() const;
 
-        JS_PUBLIC_API(void)
-        forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const;
+        // Provide the bare minimum of iterator methods that are needed for
+        // C++ ranged for loops.
+        Iter& operator++() { ++index_; return *this; }
+        bool operator==(const Iter& rhs) { return index_ == rhs.index_; }
+        bool operator!=(const Iter& rhs) { return !(*this == rhs); }
+
+    private:
+        const ProfiledFrameRange& range_;
+        uint32_t index_;
     };
 
-    // Called once per frame.
-    virtual void operator()(const FrameHandle& frame) = 0;
+    Iter begin() const { return Iter(*this, 0); }
+    Iter end() const { return Iter(*this, depth_); }
+
+private:
+    friend JS_PUBLIC_API(ProfiledFrameRange) GetProfiledFrames(JSContext* cx,
+                                                               void* addr);
+
+    ProfiledFrameRange(JSRuntime* rt, void* addr, js::jit::JitcodeGlobalEntry* entry)
+      : rt_(rt)
+      , addr_(addr)
+      , entry_(entry)
+      , depth_(0)
+    {}
+
+    JSRuntime* rt_;
+    void* addr_;
+    js::jit::JitcodeGlobalEntry* entry_;
+    // Assume maximum inlining depth is <64
+    const char* labels_[64];
+    uint32_t depth_;
 };
 
-JS_PUBLIC_API(void)
-ForEachProfiledFrame(JSContext* cx, void* addr, ForEachProfiledFrameOp& op);
+// Returns a range that can be iterated over using C++ ranged for loops.
+JS_PUBLIC_API(ProfiledFrameRange)
+GetProfiledFrames(JSContext* cx, void* addr);
 
 } // namespace JS
 
 #endif  /* js_ProfilingFrameIterator_h */
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/JitcodeMap.h"
 
+#include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Sprintf.h"
 
 #include "jsprf.h"
 
 #include "gc/Marking.h"
 #include "gc/Statistics.h"
@@ -1619,18 +1620,18 @@ JitcodeIonTable::WriteIonTable(CompactBu
     *numRegionsOut = runOffsets.length();
     return true;
 }
 
 
 } // namespace jit
 } // namespace js
 
-JS::ForEachProfiledFrameOp::FrameHandle::FrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
-                                                     void* addr, const char* label, uint32_t depth)
+JS::ProfiledFrameHandle::ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
+                                             void* addr, const char* label, uint32_t depth)
   : rt_(rt),
     entry_(entry),
     addr_(addr),
     canonicalAddr_(nullptr),
     label_(label),
     depth_(depth),
     optsIndex_()
 {
@@ -1640,33 +1641,40 @@ JS::ForEachProfiledFrameOp::FrameHandle:
         // 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_PUBLIC_API(JS::ProfilingFrameIterator::FrameKind)
-JS::ForEachProfiledFrameOp::FrameHandle::frameKind() const
+JS::ProfiledFrameHandle::frameKind() const
 {
     if (entry_.isBaseline())
         return JS::ProfilingFrameIterator::Frame_Baseline;
     return JS::ProfilingFrameIterator::Frame_Ion;
 }
 
-JS_PUBLIC_API(void)
-JS::ForEachProfiledFrame(JSContext* cx, void* addr, ForEachProfiledFrameOp& op)
+JS_PUBLIC_API(JS::ProfiledFrameRange)
+JS::GetProfiledFrames(JSContext* cx, void* addr)
 {
-    js::jit::JitcodeGlobalTable* table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
+    JSRuntime* rt = cx->runtime();
+    js::jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
     js::jit::JitcodeGlobalEntry* entry = table->lookup(addr);
 
-    if (!entry)
-        return;
+    ProfiledFrameRange result(rt, addr, entry);
+
+    if (entry) {
+        result.depth_ = entry->callStackAtAddr(rt, addr, result.labels_,
+                                               MOZ_ARRAY_LENGTH(result.labels_));
+    }
+    return result;
+}
 
-    // Extract the stack for the entry.  Assume maximum inlining depth is <64
-    const char* labels[64];
-    uint32_t depth = entry->callStackAtAddr(cx->runtime(), addr, labels, 64);
-    MOZ_ASSERT(depth < 64);
-    for (uint32_t i = depth; i != 0; i--) {
-        JS::ForEachProfiledFrameOp::FrameHandle handle(cx->runtime(), *entry, addr, labels[i - 1], i - 1);
-        op(handle);
-    }
+JS::ProfiledFrameHandle
+JS::ProfiledFrameRange::Iter::operator*() const
+{
+    // The iterator iterates in high depth to low depth order. index_ goes up,
+    // and the depth we need to pass to ProfiledFrameHandle goes down.
+    uint32_t depth = range_.depth_ - 1 - index_;
+    return ProfiledFrameHandle(range_.rt_, *range_.entry_, range_.addr_,
+                               range_.labels_[depth], depth);
 }
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -1269,17 +1269,17 @@ IonTrackedOptimizationsTypeInfo::ForEach
 
 void
 IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::operator()(JS::TrackedTypeSite site,
                                                               MIRType mirType)
 {
     op_(site, StringFromMIRType(mirType));
 }
 
-typedef JS::ForEachProfiledFrameOp::FrameHandle FrameHandle;
+typedef JS::ProfiledFrameHandle FrameHandle;
 
 void
 FrameHandle::updateHasTrackedOptimizations()
 {
     // 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)
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -246,41 +246,16 @@ public:
     };
 
     AutoArraySchemaWriter writer(mWriter, mUniqueStrings);
     writer.StringElement(STRATEGY, JS::TrackedStrategyString(strategy));
     writer.StringElement(OUTCOME, JS::TrackedOutcomeString(outcome));
   }
 };
 
-class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
-{
-  void* mReturnAddress;
-  UniqueStacks::Stack& mStack;
-  unsigned mDepth;
-
-public:
-  StreamJSFramesOp(void* aReturnAddr, UniqueStacks::Stack& aStack)
-   : mReturnAddress(aReturnAddr)
-   , mStack(aStack)
-   , mDepth(0)
-  { }
-
-  unsigned depth() const {
-    MOZ_ASSERT(mDepth > 0);
-    return mDepth;
-  }
-
-  void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
-    UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
-    mStack.AppendFrame(frameKey);
-    mDepth++;
-  }
-};
-
 uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
 {
   uint32_t index;
   StringKey key(aStr);
 
   auto it = mStringToIndexMap.find(key);
 
   if (it != mStringToIndexMap.end()) {
@@ -469,17 +444,17 @@ void UniqueStacks::StreamFrame(const OnS
     writer.StringElement(LOCATION, aFrame.mLocation.get());
     if (aFrame.mLine.isSome()) {
       writer.IntElement(LINE, *aFrame.mLine);
     }
     if (aFrame.mCategory.isSome()) {
       writer.IntElement(CATEGORY, *aFrame.mCategory);
     }
   } else {
-    const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
+    const JS::ProfiledFrameHandle& jitFrame = *aFrame.mJITFrameHandle;
 
     writer.StringElement(LOCATION, jitFrame.label());
 
     JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
     MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
                frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
     writer.StringElement(IMPLEMENTATION,
                          frameKind == JS::ProfilingFrameIterator::Frame_Ion
@@ -846,20 +821,24 @@ ProfileBuffer::StreamSamplesToJSON(Splic
 
       } else if (e.Get().IsJitReturnAddr()) {
         numFrames++;
 
         // A JIT frame may expand to multiple frames due to inlining.
         void* pc = e.Get().u.mPtr;
         unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
         if (depth == 0) {
-          StreamJSFramesOp framesOp(pc, stack);
           MOZ_RELEASE_ASSERT(aContext);
-          JS::ForEachProfiledFrame(aContext, pc, framesOp);
-          aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
+          for (JS::ProfiledFrameHandle handle : JS::GetProfiledFrames(aContext, pc)) {
+            UniqueStacks::OnStackFrameKey frameKey(pc, depth, handle);
+            stack.AppendFrame(frameKey);
+            depth++;
+          }
+          MOZ_ASSERT(depth > 0);
+          aUniqueStacks.AddJITFrameDepth(pc, depth);
         } else {
           for (unsigned i = 0; i < depth; i++) {
             UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
             stack.AppendFrame(inlineFrameKey);
           }
         }
 
         e.Next();
--- a/tools/profiler/core/ProfileBufferEntry.h
+++ b/tools/profiler/core/ProfileBufferEntry.h
@@ -223,25 +223,25 @@ public:
       , mJITFrameHandle(nullptr)
     { }
 
     OnStackFrameKey(const OnStackFrameKey& aToCopy)
       : FrameKey(aToCopy)
       , mJITFrameHandle(aToCopy.mJITFrameHandle)
     { }
 
-    const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
+    const JS::ProfiledFrameHandle* mJITFrameHandle;
 
     OnStackFrameKey(void* aJITAddress, unsigned aJITDepth)
       : FrameKey(aJITAddress, aJITDepth)
       , mJITFrameHandle(nullptr)
     { }
 
     OnStackFrameKey(void* aJITAddress, unsigned aJITDepth,
-                    const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle)
+                    const JS::ProfiledFrameHandle& aJITFrameHandle)
       : FrameKey(aJITAddress, aJITDepth)
       , mJITFrameHandle(&aJITFrameHandle)
     { }
   };
 
   struct StackKey {
     mozilla::Maybe<uint32_t> mPrefixHash;
     mozilla::Maybe<uint32_t> mPrefix;