Bug 1429904 - When a JSContext for a thread is about to go away, collect enough information about any JIT entries in the buffer so that the entire buffer can be streamed to JSON. r=njn

This changeset changes behavior.
If the profile is streamed before any JSContext has gone away, we now iterate
over the entire buffer twice (per thread): First, to collect information about
JIT frames, and then again when we build the JSON for the samples. The first
traversal stores small pieces of JSON for JIT fromes in individual strings, and
the second iteration splices those strings into the thread JSON's frame table.

When the JSContext for a thread goes away, we no longer build JSON for samples,
and we don't reset the profiler buffer. We now only build the JSON for JIT
frames. Once the complete profile is requested and we build samples for it, we
iterate over the entire buffer, and look up the cached JIT frame information for
JitReturnAddr entries from the correct range. Different parts of the buffer may
correspond to the life time of different JSContexts: For each JSContext we will
have one range in the JITFrameInfo, and we can look up the correct range based
on the buffer position of the JitReturnAddr entry that we're processing.

This new way of doing things has multiple advantages:
 - We no longer reset the buffer, so we no longer lose information about other
   threads.
 - All threads from a given process now always have sample data for the same
   time range. Before this change, the "partial profile" from a thread that
   lost its JSContext could extend further into the past than the other threads'
   profiles.
 - Requesting profiles multiple times now has more consistent results. Before
   this change, the first requested profile would include the partial profile,
   but then the partial profile was discarded. And the second requested profile
   would not contain any data for the time before the JSContext went away.
 - We now do less work when a thread's JSContext goes away. This should
   decrease the interruption time.

MozReview-Commit-ID: 3KhnPtBijna

--HG--
extra : rebase_source : 2ef5ac933e4db1c98526a2b36147ff031893de9e
extra : intermediate-source : d63b04327077d1ef55f509b365cd2693905b0733
extra : source : f2d8c993aa2c16bc491c55179d545f5a2c727391
This commit is contained in:
Markus Stange 2018-02-28 00:17:16 -05:00
Родитель 2f171b74fa
Коммит 28d8be3628
6 изменённых файлов: 131 добавлений и 244 удалений

Просмотреть файл

@ -57,9 +57,18 @@ public:
int aThreadId, JSContext* aContext,
JITFrameInfo& aJITFrameInfo) const;
// Stream JSON for samples in the buffer to aWriter, using the supplied
// UniqueStacks object.
// Only streams samples for the given thread ID and which were taken at or
// after aSinceTime.
// aUniqueStacks needs to contain information about any JIT frames that we
// might encounter in the buffer, before this method is called. In other
// words, you need to have called AddJITInfoForRange for every range that
// might contain JIT frame information before calling this method.
bool StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
double aSinceTime, JSContext* cx,
UniqueStacks& aUniqueStacks) const;
bool StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
const mozilla::TimeStamp& aProcessStartTime,
double aSinceTime,

Просмотреть файл

@ -338,15 +338,6 @@ JITFrameInfo::JITFrameInfo(const JITFrameInfo& aOther)
}
}
uint32_t
UniqueStacks::JITAddress::Hash() const
{
uint32_t hash = 0;
hash = AddToHash(hash, mAddress);
hash = AddToHash(hash, mStreamingGen);
return hash;
}
bool
UniqueStacks::FrameKey::NormalFrameData::operator==(const NormalFrameData& aOther) const
{
@ -358,8 +349,9 @@ UniqueStacks::FrameKey::NormalFrameData::operator==(const NormalFrameData& aOthe
bool
UniqueStacks::FrameKey::JITFrameData::operator==(const JITFrameData& aOther) const
{
return mAddress == aOther.mAddress &&
mDepth == aOther.mDepth;
return mCanonicalAddress == aOther.mCanonicalAddress &&
mDepth == aOther.mDepth &&
mRangeIndex == aOther.mRangeIndex;
}
uint32_t
@ -379,14 +371,20 @@ UniqueStacks::FrameKey::Hash() const
}
} else {
const JITFrameData& data = mData.as<JITFrameData>();
hash = AddToHash(hash, data.mAddress.Hash());
hash = AddToHash(hash, data.mCanonicalAddress);
hash = AddToHash(hash, data.mDepth);
hash = AddToHash(hash, data.mRangeIndex);
}
return hash;
}
UniqueStacks::UniqueStacks()
: mUniqueStrings(MakeUnique<UniqueJSONStrings>())
// Consume aJITFrameInfo by stealing its string table and its JIT frame info
// ranges. The JIT frame info contains JSON which refers to strings from the
// JIT frame info's string table, so our string table needs to have the same
// strings at the same indices.
UniqueStacks::UniqueStacks(JITFrameInfo&& aJITFrameInfo)
: mUniqueStrings(Move(aJITFrameInfo.mUniqueStrings))
, mJITInfoRanges(Move(aJITFrameInfo.mRanges))
{
mFrameTableWriter.StartBareList();
mStackTableWriter.StartBareList();
@ -406,45 +404,57 @@ uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
return index;
}
MOZ_MUST_USE nsTArray<UniqueStacks::FrameKey>
UniqueStacks::GetOrAddJITFrameKeysForAddress(JSContext* aContext,
const JITAddress& aJITAddress)
template<typename RangeT, typename PosT>
struct PositionInRangeComparator final
{
nsTArray<FrameKey>& frameKeys =
*mAddressToJITFrameKeysMap.LookupOrAdd(aJITAddress);
bool Equals(const RangeT& aRange, PosT aPos) const
{
return aRange.mRangeStart <= aPos && aPos < aRange.mRangeEnd;
}
if (frameKeys.IsEmpty()) {
for (JS::ProfiledFrameHandle handle :
JS::GetProfiledFrames(aContext, aJITAddress.mAddress)) {
// JIT frames with the same canonical address should be treated as the
// same frame, so set the frame key's address to the canonical address.
FrameKey frameKey(
JITAddress{ handle.canonicalAddress(), aJITAddress.mStreamingGen },
frameKeys.Length());
MaybeAddJITFrameIndex(aContext, frameKey, handle);
frameKeys.AppendElement(frameKey);
bool LessThan(const RangeT& aRange, PosT aPos) const
{
return aRange.mRangeEnd <= aPos;
}
};
Maybe<nsTArray<UniqueStacks::FrameKey>>
UniqueStacks::LookupFramesForJITAddressFromBufferPos(void* aJITAddress,
uint64_t aBufferPos)
{
size_t rangeIndex = mJITInfoRanges.BinaryIndexOf(aBufferPos,
PositionInRangeComparator<JITFrameInfoForBufferRange, uint64_t>());
MOZ_RELEASE_ASSERT(rangeIndex != mJITInfoRanges.NoIndex,
"Buffer position of jit address needs to be in one of the ranges");
using JITFrameKey = JITFrameInfoForBufferRange::JITFrameKey;
const JITFrameInfoForBufferRange& jitFrameInfoRange = mJITInfoRanges[rangeIndex];
const nsTArray<JITFrameKey>* jitFrameKeys =
jitFrameInfoRange.mJITAddressToJITFramesMap.Get(aJITAddress);
if (!jitFrameKeys) {
return Nothing();
}
// Map the array of JITFrameKeys to an array of FrameKeys, and ensure that
// each of the FrameKeys exists in mFrameToIndexMap.
nsTArray<FrameKey> frameKeys;
for (const JITFrameKey& jitFrameKey : *jitFrameKeys) {
FrameKey frameKey(jitFrameKey.mCanonicalAddress, jitFrameKey.mDepth, rangeIndex);
if (!mFrameToIndexMap.Contains(frameKey)) {
// We need to add this frame to our frame table. The JSON for this frame
// already exists in jitFrameInfoRange, we just need to splice it into
// the frame table and give it an index.
uint32_t index = mFrameToIndexMap.Count();
const nsCString* frameJSON =
jitFrameInfoRange.mJITFrameToFrameJSONMap.Get(jitFrameKey);
MOZ_RELEASE_ASSERT(frameJSON, "Should have cached JSON for this frame");
mFrameTableWriter.Splice(frameJSON->get());
mFrameToIndexMap.Put(frameKey, index);
}
MOZ_ASSERT(frameKeys.Length() > 0);
frameKeys.AppendElement(Move(frameKey));
}
// Return a copy of the array.
return nsTArray<FrameKey>(frameKeys);
}
void
UniqueStacks::MaybeAddJITFrameIndex(JSContext* aContext,
const FrameKey& aFrame,
const JS::ProfiledFrameHandle& aJITFrame)
{
uint32_t index;
if (mFrameToIndexMap.Get(aFrame, &index)) {
MOZ_ASSERT(index < mFrameToIndexMap.Count());
return;
}
index = mFrameToIndexMap.Count();
mFrameToIndexMap.Put(aFrame, index);
StreamJITFrame(aContext, aJITFrame);
return Some(Move(frameKeys));
}
uint32_t
@ -706,14 +716,6 @@ JITFrameInfo::AddInfoForRange(uint64_t aRangeStart, uint64_t aRangeEnd,
});
}
// This method will go away in the next patch.
void
UniqueStacks::StreamJITFrame(JSContext* aContext,
const JS::ProfiledFrameHandle& aJITFrame)
{
::StreamJITFrame(aContext, mFrameTableWriter, *mUniqueStrings, aJITFrame);
}
struct ProfileSample
{
uint32_t mStack;
@ -772,6 +774,7 @@ public:
bool Has() const { return mReadPos != mBuffer.mRangeEnd; }
const ProfileBufferEntry& Get() const { return mBuffer.GetEntry(mReadPos); }
void Next() { mReadPos++; }
uint64_t CurPos() { return mReadPos; }
private:
const ProfileBuffer& mBuffer;
@ -1032,15 +1035,13 @@ ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
} else if (e.Get().IsJitReturnAddr()) {
numFrames++;
// We can only process JitReturnAddr entries if we have a JSContext.
MOZ_RELEASE_ASSERT(aContext);
// A JIT frame may expand to multiple frames due to inlining.
void* pc = e.Get().u.mPtr;
UniqueStacks::JITAddress address = { pc, aUniqueStacks.CurrentGen() };
nsTArray<UniqueStacks::FrameKey> frameKeys =
aUniqueStacks.GetOrAddJITFrameKeysForAddress(aContext, address);
for (const UniqueStacks::FrameKey& frameKey : frameKeys) {
const Maybe<nsTArray<UniqueStacks::FrameKey>>& frameKeys =
aUniqueStacks.LookupFramesForJITAddressFromBufferPos(pc, e.CurPos());
MOZ_RELEASE_ASSERT(frameKeys,
"Attempting to stream samples for a buffer range for which we don't have JITFrameInfo?");
for (const UniqueStacks::FrameKey& frameKey : *frameKeys) {
stack = aUniqueStacks.AppendFrame(stack, frameKey);
}

Просмотреть файл

@ -216,30 +216,6 @@ struct JITFrameInfo final
class UniqueStacks
{
public:
// We de-duplicate information about JIT frames based on the return address
// of the frame. However, if the same UniqueStacks object is used to stream
// profiler buffer contents more than once, then in the time between the two
// stream attempts ("streaming generations"), JIT code can have been freed
// and reallocated in the same areas of memory. Consequently, during the next
// streaming generation, we may see JIT return addresses that we've already
// seen before, but which now represent completely different JS functions.
// So we need to make sure that any de-duplication takes the streaming
// generation into account and does not only compare the address itself.
// The JITAddress struct packages the two together and can be used as a hash
// key.
struct JITAddress
{
void* mAddress;
uint32_t mStreamingGen;
uint32_t Hash() const;
bool operator==(const JITAddress& aRhs) const
{
return mAddress == aRhs.mAddress && mStreamingGen == aRhs.mStreamingGen;
}
bool operator!=(const JITAddress& aRhs) const { return !(*this == aRhs); }
};
struct FrameKey {
explicit FrameKey(const char* aLocation)
: mData(NormalFrameData{
@ -253,8 +229,8 @@ public:
{
}
FrameKey(const JITAddress& aJITAddress, uint32_t aJITDepth)
: mData(JITFrameData{ aJITAddress, aJITDepth })
FrameKey(void* aJITAddress, uint32_t aJITDepth, uint32_t aRangeIndex)
: mData(JITFrameData{ aJITAddress, aJITDepth, aRangeIndex })
{
}
@ -273,8 +249,9 @@ public:
struct JITFrameData {
bool operator==(const JITFrameData& aOther) const;
JITAddress mAddress;
void* mCanonicalAddress;
uint32_t mDepth;
uint32_t mRangeIndex;
};
mozilla::Variant<NormalFrameData, JITFrameData> mData;
};
@ -306,13 +283,7 @@ public:
uint32_t mHash;
};
explicit UniqueStacks();
// Needs to be called when using a UniqueStacks object again after having
// streamed entries that are no longer in the buffer (and which could have
// been GC'ed and their memory reused).
void AdvanceStreamingGeneration() { mStreamingGeneration++; }
uint32_t CurrentGen() { return mStreamingGeneration; }
explicit UniqueStacks(JITFrameInfo&& aJITFrameInfo);
// Return a StackKey for aFrame as the stack's root frame (no prefix).
MOZ_MUST_USE StackKey BeginStack(const FrameKey& aFrame);
@ -321,9 +292,14 @@ public:
MOZ_MUST_USE StackKey AppendFrame(const StackKey& aStack,
const FrameKey& aFrame);
MOZ_MUST_USE nsTArray<FrameKey>
GetOrAddJITFrameKeysForAddress(JSContext* aContext,
const JITAddress& aJITAddress);
// Look up frame keys for the given JIT address, and ensure that our frame
// table has entries for the returned frame keys. The JSON for these frames
// is taken from mJITInfoRanges.
// aBufferPosition is needed in order to look up the correct JIT frame info
// object in mJITInfoRanges.
MOZ_MUST_USE mozilla::Maybe<nsTArray<UniqueStacks::FrameKey>>
LookupFramesForJITAddressFromBufferPos(void* aJITAddress,
uint64_t aBufferPosition);
MOZ_MUST_USE uint32_t GetOrAddFrameIndex(const FrameKey& aFrame);
MOZ_MUST_USE uint32_t GetOrAddStackIndex(const StackKey& aStack);
@ -332,35 +308,20 @@ public:
void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
private:
// Make sure that there exists a frame index for aFrame, and if there isn't
// one already, create one and call StreamJITFrame for the frame.
void MaybeAddJITFrameIndex(JSContext* aContext,
const FrameKey& aFrame,
const JS::ProfiledFrameHandle& aJITFrame);
void StreamNonJITFrame(const FrameKey& aFrame);
void StreamJITFrame(JSContext* aContext,
const JS::ProfiledFrameHandle& aJITFrame);
void StreamStack(const StackKey& aStack);
public:
mozilla::UniquePtr<UniqueJSONStrings> mUniqueStrings;
private:
// To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame,
// we cache the frame keys of frames keyed by JIT code address. All FrameKeys
// in mAddressToJITFrameKeysMap are guaranteed to be in mFrameToIndexMap.
nsClassHashtable<nsGenericHashKey<JITAddress>, nsTArray<FrameKey>> mAddressToJITFrameKeysMap;
SpliceableChunkedJSONWriter mFrameTableWriter;
nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap;
SpliceableChunkedJSONWriter mStackTableWriter;
nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap;
// Used to avoid collisions between JITAddresses that refer to different
// frames.
uint32_t mStreamingGeneration;
nsTArray<JITFrameInfoForBufferRange> mJITInfoRanges;
};
//

Просмотреть файл

@ -35,21 +35,23 @@ ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime, double aSinceTime)
{
UniquePtr<PartialThreadProfile> partialProfile = Move(mPartialProfile);
UniquePtr<UniqueStacks> uniqueStacks = partialProfile
? Move(partialProfile->mUniqueStacks)
: MakeUnique<UniqueStacks>();
uniqueStacks->AdvanceStreamingGeneration();
UniquePtr<char[]> partialSamplesJSON;
UniquePtr<char[]> partialMarkersJSON;
if (partialProfile) {
partialSamplesJSON = Move(partialProfile->mSamplesJSON);
partialMarkersJSON = Move(partialProfile->mMarkersJSON);
if (mJITFrameInfoForPreviousJSContexts &&
mJITFrameInfoForPreviousJSContexts->HasExpired(aBuffer.mRangeStart)) {
mJITFrameInfoForPreviousJSContexts = nullptr;
}
// If we have an existing JITFrameInfo in mJITFrameInfoForPreviousJSContexts,
// copy the data from it.
JITFrameInfo jitFrameInfo = mJITFrameInfoForPreviousJSContexts
? JITFrameInfo(*mJITFrameInfoForPreviousJSContexts) : JITFrameInfo();
if (aCx && mBufferPositionWhenReceivedJSContext) {
aBuffer.AddJITInfoForRange(*mBufferPositionWhenReceivedJSContext,
mThreadInfo->ThreadId(), aCx, jitFrameInfo);
}
UniqueStacks uniqueStacks(Move(jitFrameInfo));
aWriter.Start();
{
StreamSamplesAndMarkers(mThreadInfo->Name(), mThreadInfo->ThreadId(),
@ -57,9 +59,9 @@ ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
aProcessStartTime,
mThreadInfo->RegisterTime(), mUnregisterTime,
aSinceTime, aCx,
Move(partialSamplesJSON),
Move(partialMarkersJSON),
*uniqueStacks);
nullptr,
nullptr,
uniqueStacks);
aWriter.StartObjectProperty("stackTable");
{
@ -71,7 +73,7 @@ ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
aWriter.StartArrayProperty("data");
{
uniqueStacks->SpliceStackTableElements(aWriter);
uniqueStacks.SpliceStackTableElements(aWriter);
}
aWriter.EndArray();
}
@ -90,7 +92,7 @@ ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
aWriter.StartArrayProperty("data");
{
uniqueStacks->SpliceFrameTableElements(aWriter);
uniqueStacks.SpliceFrameTableElements(aWriter);
}
aWriter.EndArray();
}
@ -98,7 +100,7 @@ ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
aWriter.StartArrayProperty("stringTable");
{
uniqueStacks->mUniqueStrings->SpliceStringTableElements(aWriter);
uniqueStacks.mUniqueStrings->SpliceStringTableElements(aWriter);
}
aWriter.EndArray();
}
@ -192,96 +194,27 @@ StreamSamplesAndMarkers(const char* aName,
}
void
ProfiledThreadData::NotifyAboutToLoseJSContext(JSContext* aCx,
ProfiledThreadData::NotifyAboutToLoseJSContext(JSContext* aContext,
const TimeStamp& aProcessStartTime,
ProfileBuffer& aBuffer)
{
// This function is used to serialize the current buffer just before
// JSContext destruction.
MOZ_ASSERT(aCx);
// Unlike StreamJSObject, do not surround the samples in brackets by calling
// aWriter.{Start,End}BareList. The result string will be a comma-separated
// list of JSON object literals that will prepended by StreamJSObject into
// an existing array.
//
// Note that the UniqueStacks instance is persisted so that the frame-index
// mapping is stable across JS shutdown.
UniquePtr<UniqueStacks> uniqueStacks = mPartialProfile
? Move(mPartialProfile->mUniqueStacks)
: MakeUnique<UniqueStacks>();
uniqueStacks->AdvanceStreamingGeneration();
UniquePtr<char[]> samplesJSON;
UniquePtr<char[]> markersJSON;
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
bool haveSamples = false;
{
if (mPartialProfile && mPartialProfile->mSamplesJSON) {
b.Splice(mPartialProfile->mSamplesJSON.get());
haveSamples = true;
}
// We deliberately use a new variable instead of writing something like
// `haveSamples || aBuffer.StreamSamplesToJSON(...)` because we don't want
// to short-circuit the call.
bool streamedNewSamples =
aBuffer.StreamSamplesToJSON(b, mThreadInfo->ThreadId(),
/* aSinceTime = */ 0,
aCx, *uniqueStacks);
haveSamples = haveSamples || streamedNewSamples;
}
b.EndBareList();
// https://bugzilla.mozilla.org/show_bug.cgi?id=1428076
// If we don't have any data, keep samplesJSON set to null. That
// way we won't try to splice it into the JSON later on, which would
// result in an invalid JSON due to stray commas.
if (haveSamples) {
samplesJSON = b.WriteFunc()->CopyData();
}
if (!mBufferPositionWhenReceivedJSContext) {
return;
}
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
bool haveMarkers = false;
{
if (mPartialProfile && mPartialProfile->mMarkersJSON) {
b.Splice(mPartialProfile->mMarkersJSON.get());
haveMarkers = true;
}
MOZ_RELEASE_ASSERT(aContext);
// We deliberately use a new variable instead of writing something like
// `haveMarkers || aBuffer.StreamMarkersToJSON(...)` because we don't want
// to short-circuit the call.
bool streamedNewMarkers =
aBuffer.StreamMarkersToJSON(b, mThreadInfo->ThreadId(),
aProcessStartTime,
/* aSinceTime = */ 0, *uniqueStacks);
haveMarkers = haveMarkers || streamedNewMarkers;
}
b.EndBareList();
// https://bugzilla.mozilla.org/show_bug.cgi?id=1428076
// If we don't have any data, keep markersJSON set to null. That
// way we won't try to splice it into the JSON later on, which would
// result in an invalid JSON due to stray commas.
if (haveMarkers) {
markersJSON = b.WriteFunc()->CopyData();
}
if (mJITFrameInfoForPreviousJSContexts &&
mJITFrameInfoForPreviousJSContexts->HasExpired(aBuffer.mRangeStart)) {
mJITFrameInfoForPreviousJSContexts = nullptr;
}
mPartialProfile = MakeUnique<PartialThreadProfile>(
Move(samplesJSON), Move(markersJSON), Move(uniqueStacks));
UniquePtr<JITFrameInfo> jitFrameInfo = mJITFrameInfoForPreviousJSContexts
? Move(mJITFrameInfoForPreviousJSContexts) : MakeUnique<JITFrameInfo>();
aBuffer.AddJITInfoForRange(*mBufferPositionWhenReceivedJSContext,
mThreadInfo->ThreadId(), aContext, *jitFrameInfo);
mJITFrameInfoForPreviousJSContexts = Move(jitFrameInfo);
mBufferPositionWhenReceivedJSContext = Nothing();
// Reset the buffer. Attempting to symbolicate JS samples after mContext has
// gone away will crash.
aBuffer.Reset();
}

Просмотреть файл

@ -16,23 +16,6 @@
#include "ProfileBuffer.h"
#include "ThreadInfo.h"
// Contains data for partial profiles that get saved when
// ThreadInfo::FlushSamplesAndMarkers gets called.
struct PartialThreadProfile final
{
PartialThreadProfile(mozilla::UniquePtr<char[]>&& aSamplesJSON,
mozilla::UniquePtr<char[]>&& aMarkersJSON,
mozilla::UniquePtr<UniqueStacks>&& aUniqueStacks)
: mSamplesJSON(mozilla::Move(aSamplesJSON))
, mMarkersJSON(mozilla::Move(aMarkersJSON))
, mUniqueStacks(mozilla::Move(aUniqueStacks))
{}
mozilla::UniquePtr<char[]> mSamplesJSON;
mozilla::UniquePtr<char[]> mMarkersJSON;
mozilla::UniquePtr<UniqueStacks> mUniqueStacks;
};
// This class contains information about a thread that is only relevant while
// the profiler is running, for any threads (both alive and dead) whose thread
// name matches the "thread filter" in the current profiler run.
@ -109,11 +92,11 @@ private:
// This thread's thread info.
const RefPtr<ThreadInfo> mThreadInfo;
// JS frames in the buffer may require a live JSRuntime to stream (e.g.,
// stringifying JIT frames). In the case of JSRuntime destruction,
// FlushSamplesAndMarkers should be called to save them. These are spliced
// into the final stream.
UniquePtr<PartialThreadProfile> mPartialProfile;
// Contains JSON for JIT frames from any JSContexts that were used for this
// thread in the past.
// Null if this thread has never lost a JSContext or if all samples from
// previous JSContexts have been evicted from the profiler buffer.
UniquePtr<JITFrameInfo> mJITFrameInfoForPreviousJSContexts;
// Group B:
// The following fields are only used while this thread is alive and

Просмотреть файл

@ -28,10 +28,10 @@ ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks)
{
// This call to StreamSamplesAndMarkers() can safely pass in a non-null
// JSContext. That's because StreamSamplesAndMarkers() only accesses the
// JSContext when streaming JitReturnAddress entries, and such entries
// never appear in synchronous samples.
// Unlike ProfiledThreadData::StreamJSON, we don't need to call
// ProfileBuffer::AddJITInfoForRange because mBuffer does not contain any
// JitReturnAddr entries. For synchronous samples, JIT frames get expanded
// at sample time.
StreamSamplesAndMarkers(mName.get(), mThreadId,
*mBuffer.get(), aWriter, aProcessStartTime,
/* aRegisterTime */ TimeStamp(),