mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1137569 - Delay stringification of JIT frames until streaming time. (r=djvj)
This commit is contained in:
parent
e7aa0f3877
commit
2a5551df33
@ -8,6 +8,7 @@
|
||||
#define js_ProfilingFrameIterator_h
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -22,6 +23,7 @@ namespace js {
|
||||
namespace jit {
|
||||
class JitActivation;
|
||||
class JitProfilingFrameIterator;
|
||||
class JitcodeGlobalEntry;
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,18 +111,22 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
void* returnAddress;
|
||||
void* activation;
|
||||
const char* label;
|
||||
bool mightHaveTrackedOptimizations;
|
||||
};
|
||||
|
||||
bool isAsmJS() const;
|
||||
bool isJit() const;
|
||||
|
||||
uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
|
||||
|
||||
mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
|
||||
|
||||
private:
|
||||
mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const;
|
||||
|
||||
void iteratorConstruct(const RegisterState& state);
|
||||
void iteratorConstruct();
|
||||
void iteratorDestroy();
|
||||
bool iteratorDone();
|
||||
|
||||
bool isAsmJS() const;
|
||||
bool isJit() const;
|
||||
};
|
||||
|
||||
extern JS_PUBLIC_API(ProfilingFrameIterator::FrameKind)
|
||||
@ -141,6 +147,15 @@ JS_FRIEND_API(void)
|
||||
UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime* runtime, uint32_t generation,
|
||||
uint32_t lapCount);
|
||||
|
||||
struct ForEachProfiledFrameOp
|
||||
{
|
||||
// Called once per frame.
|
||||
virtual void operator()(const char* label, bool mightHaveTrackedOptimizations) = 0;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_ProfilingFrameIterator_h */
|
||||
|
@ -172,7 +172,6 @@ JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
|
||||
uint32_t maxResults) const
|
||||
{
|
||||
MOZ_ASSERT(containsPointer(ptr));
|
||||
MOZ_ASSERT(script_->hasBaselineScript());
|
||||
MOZ_ASSERT(maxResults >= 1);
|
||||
|
||||
results[0] = str();
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::Maybe;
|
||||
using mozilla::PodCopy;
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -1861,70 +1862,86 @@ JS::ProfilingFrameIterator::stackAddress() const
|
||||
return jitIter().stackAddress();
|
||||
}
|
||||
|
||||
Maybe<JS::ProfilingFrameIterator::Frame>
|
||||
JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry* entry) const
|
||||
{
|
||||
void* stackAddr = stackAddress();
|
||||
|
||||
if (isAsmJS()) {
|
||||
Frame frame;
|
||||
frame.kind = Frame_AsmJS;
|
||||
frame.stackAddress = stackAddr;
|
||||
frame.returnAddress = nullptr;
|
||||
frame.activation = activation_;
|
||||
frame.label = nullptr;
|
||||
return mozilla::Some(frame);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(isJit());
|
||||
|
||||
// Look up an entry for the return address.
|
||||
void* returnAddr = jitIter().returnAddressToFp();
|
||||
jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable();
|
||||
if (hasSampleBufferGen())
|
||||
table->lookupForSampler(returnAddr, entry, rt_, sampleBufferGen_);
|
||||
else
|
||||
table->lookupInfallible(returnAddr, entry, rt_);
|
||||
|
||||
MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() || entry->isDummy());
|
||||
|
||||
// Dummy frames produce no stack frames.
|
||||
if (entry->isDummy())
|
||||
return mozilla::Nothing();
|
||||
|
||||
Frame frame;
|
||||
frame.kind = entry->isBaseline() ? Frame_Baseline : Frame_Ion;
|
||||
frame.stackAddress = stackAddr;
|
||||
frame.returnAddress = returnAddr;
|
||||
frame.activation = activation_;
|
||||
frame.label = nullptr;
|
||||
return mozilla::Some(frame);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
JS::ProfilingFrameIterator::extractStack(Frame* frames, uint32_t offset, uint32_t end) const
|
||||
{
|
||||
if (offset >= end)
|
||||
return 0;
|
||||
|
||||
void* stackAddr = stackAddress();
|
||||
jit::JitcodeGlobalEntry entry;
|
||||
Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
|
||||
|
||||
// Dummy frames produce no stack frames.
|
||||
if (physicalFrame.isNothing())
|
||||
return 0;
|
||||
|
||||
if (isAsmJS()) {
|
||||
frames[offset].kind = Frame_AsmJS;
|
||||
frames[offset].stackAddress = stackAddr;
|
||||
frames[offset].returnAddress = nullptr;
|
||||
frames[offset].activation = activation_;
|
||||
frames[offset] = physicalFrame.value();
|
||||
frames[offset].label = asmJSIter().label();
|
||||
frames[offset].mightHaveTrackedOptimizations = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(isJit());
|
||||
void* returnAddr = jitIter().returnAddressToFp();
|
||||
|
||||
// Look up an entry for the return address.
|
||||
jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable();
|
||||
jit::JitcodeGlobalEntry entry;
|
||||
table->lookupInfallible(returnAddr, &entry, rt_);
|
||||
if (hasSampleBufferGen())
|
||||
table->lookupForSampler(returnAddr, &entry, rt_, sampleBufferGen_);
|
||||
else
|
||||
table->lookup(returnAddr, &entry, rt_);
|
||||
|
||||
MOZ_ASSERT(entry.isIon() || entry.isIonCache() || entry.isBaseline() || entry.isDummy());
|
||||
|
||||
// Dummy frames produce no stack frames.
|
||||
if (entry.isDummy())
|
||||
return 0;
|
||||
|
||||
FrameKind kind = entry.isBaseline() ? Frame_Baseline : Frame_Ion;
|
||||
|
||||
// Extract the stack for the entry. Assume maximum inlining depth is <64
|
||||
const char* labels[64];
|
||||
uint32_t depth = entry.callStackAtAddr(rt_, returnAddr, labels, 64);
|
||||
uint32_t depth = entry.callStackAtAddr(rt_, jitIter().returnAddressToFp(), labels, 64);
|
||||
MOZ_ASSERT(depth < 64);
|
||||
for (uint32_t i = 0; i < depth; i++) {
|
||||
if (offset + i >= end)
|
||||
return i;
|
||||
frames[offset + i].kind = kind;
|
||||
frames[offset + i].stackAddress = stackAddr;
|
||||
frames[offset + i].returnAddress = returnAddr;
|
||||
frames[offset + i].activation = activation_;
|
||||
frames[offset + i] = physicalFrame.value();
|
||||
frames[offset + i].label = labels[i];
|
||||
frames[offset + i].mightHaveTrackedOptimizations = false;
|
||||
}
|
||||
|
||||
// A particular return address might have tracked optimizations only if
|
||||
// there are any optimizations at all.
|
||||
//
|
||||
// All inlined Ion frames will have the same optimization information by
|
||||
// virtue of sharing the JitcodeGlobalEntry, but such information is only
|
||||
// interpretable on the youngest frame.
|
||||
frames[offset].mightHaveTrackedOptimizations = entry.hasTrackedOptimizations();
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
Maybe<JS::ProfilingFrameIterator::Frame>
|
||||
JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const
|
||||
{
|
||||
jit::JitcodeGlobalEntry unused;
|
||||
return getPhysicalFrameAndEntry(&unused);
|
||||
}
|
||||
|
||||
bool
|
||||
JS::ProfilingFrameIterator::isAsmJS() const
|
||||
{
|
||||
@ -1937,3 +1954,22 @@ 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());
|
||||
}
|
||||
}
|
||||
|
@ -280,12 +280,47 @@ public:
|
||||
|
||||
void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override {
|
||||
mWriter.BeginObject();
|
||||
{
|
||||
// Stringify the reasons for now; could stream enum values in the future
|
||||
// to save space.
|
||||
mWriter.NameValue("strategy", JS::TrackedStrategyString(strategy));
|
||||
mWriter.NameValue("outcome", JS::TrackedOutcomeString(outcome));
|
||||
}
|
||||
mWriter.EndObject();
|
||||
}
|
||||
};
|
||||
|
||||
class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
|
||||
{
|
||||
JSRuntime* mRuntime;
|
||||
void* mReturnAddress;
|
||||
UniqueJITOptimizations& mUniqueOpts;
|
||||
JSStreamWriter& mWriter;
|
||||
|
||||
public:
|
||||
StreamJSFramesOp(JSRuntime* aRuntime, void* aReturnAddr, UniqueJITOptimizations& aUniqueOpts,
|
||||
JSStreamWriter& aWriter)
|
||||
: mRuntime(aRuntime)
|
||||
, mReturnAddress(aReturnAddr)
|
||||
, mUniqueOpts(aUniqueOpts)
|
||||
, mWriter(aWriter)
|
||||
{ }
|
||||
|
||||
void operator()(const char* label, bool mightHaveTrackedOptimizations) override {
|
||||
mWriter.BeginObject();
|
||||
mWriter.NameValue("location", label);
|
||||
JS::ProfilingFrameIterator::FrameKind frameKind =
|
||||
JS::GetProfilingFrameKindFromNativeAddr(mRuntime, mReturnAddress);
|
||||
MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
|
||||
frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
|
||||
const char* jitLevelString =
|
||||
(frameKind == JS::ProfilingFrameIterator::Frame_Ion) ? "ion"
|
||||
: "baseline";
|
||||
mWriter.NameValue("implementation", jitLevelString);
|
||||
if (mightHaveTrackedOptimizations) {
|
||||
Maybe<unsigned> optsIndex = mUniqueOpts.getIndex(mReturnAddress, mRuntime);
|
||||
if (optsIndex.isSome()) {
|
||||
mWriter.NameValue("optsIndex", optsIndex.value());
|
||||
}
|
||||
}
|
||||
mWriter.EndObject();
|
||||
}
|
||||
};
|
||||
@ -473,6 +508,10 @@ void ProfileBuffer::StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId, JS
|
||||
incBy++;
|
||||
}
|
||||
b.EndObject();
|
||||
} else if (frame.mTagName == 'J') {
|
||||
void* pc = frame.mTagPtr;
|
||||
StreamJSFramesOp framesOp(rt, pc, aUniqueOpts, b);
|
||||
JS::ForEachProfiledFrame(rt, pc, framesOp);
|
||||
}
|
||||
framePos = (framePos + incBy) % mEntrySize;
|
||||
}
|
||||
|
@ -550,11 +550,18 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
registerState,
|
||||
startBufferGen);
|
||||
for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
|
||||
uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
|
||||
MOZ_ASSERT(extracted <= (maxFrames - jsCount));
|
||||
jsCount += extracted;
|
||||
if (jsCount == maxFrames)
|
||||
break;
|
||||
// See note below regarding 'J' entries.
|
||||
if (aSample->isSamplingCurrentThread || jsIter.isAsmJS()) {
|
||||
uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
|
||||
jsCount += extracted;
|
||||
if (jsCount == maxFrames)
|
||||
break;
|
||||
} else {
|
||||
mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
|
||||
jsIter.getPhysicalFrameWithoutLabel();
|
||||
if (frame.isSome())
|
||||
jsFrames[jsCount++] = frame.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -641,15 +648,10 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
if (jsStackAddr > nativeStackAddr) {
|
||||
MOZ_ASSERT(jsIndex >= 0);
|
||||
const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
|
||||
addDynamicTag(aProfile, 'c', jsFrame.label);
|
||||
|
||||
// Stringifying optimization information and the JIT tier is delayed
|
||||
// until streaming time. To re-lookup the entry in the
|
||||
// JitcodeGlobalTable, we need to store the JIT code address in the
|
||||
// circular buffer.
|
||||
//
|
||||
// Frames which may have optimization information are tagged by an 'O'
|
||||
// entry. Otherwise they are tagged by a 'J' entry.
|
||||
// Stringifying non-asm.js JIT frames is delayed until streaming
|
||||
// time. To re-lookup the entry in the JitcodeGlobalTable, we need to
|
||||
// store the JIT code address ('J') in the circular buffer.
|
||||
//
|
||||
// Note that we cannot do this when we are sychronously sampling the
|
||||
// current thread; that is, when called from profiler_get_backtrace. The
|
||||
@ -660,11 +662,13 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
// its JIT code. This means that if we inserted such 'J' entries into
|
||||
// the buffer, nsRefreshDriver would now be holding on to a backtrace
|
||||
// with stale JIT code return addresses.
|
||||
if (!aSample->isSamplingCurrentThread &&
|
||||
(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
|
||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline)) {
|
||||
char entryTag = jsFrame.mightHaveTrackedOptimizations ? 'O' : 'J';
|
||||
aProfile.addTag(ProfileEntry(entryTag, jsFrames[jsIndex].returnAddress));
|
||||
if (aSample->isSamplingCurrentThread ||
|
||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_AsmJS) {
|
||||
addDynamicTag(aProfile, 'c', jsFrame.label);
|
||||
} else {
|
||||
MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
|
||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
|
||||
aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
|
||||
}
|
||||
|
||||
jsIndex--;
|
||||
|
Loading…
Reference in New Issue
Block a user