Bug 1434965 - Replace callback-based API ForEachProfiledFrame with an iterator-based API called GetProfiledFrames. r?njn draft
authorMarkus Stange <mstange@themasta.com>
Sat, 10 Feb 2018 20:38:41 -0500
changeset 753741 1d009b09578e00531cefc4cf9c5204c495f347ab
parent 753740 38e9bc431a0cf230cf4675b3068c38d9e7206b92
child 753742 2a5e16ca1c1f3fdf2928c21071dc2976c24d20af
push id98666
push userbmo:mstange@themasta.com
push dateMon, 12 Feb 2018 06:22:14 +0000
reviewersnjn
bugs1434965
milestone60.0a1
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;