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
--- 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;