Bug 1436924 - Group mUniqueStacks, mSavedStreamedSamples and mSavedStreamedMarkers into a PartialThreadProfile struct that's heap-allocated on demand. r?njn
MozReview-Commit-ID: 5WNrIWLsiM1
--- a/tools/profiler/core/ThreadInfo.cpp
+++ b/tools/profiler/core/ThreadInfo.cpp
@@ -63,55 +63,59 @@ ThreadInfo::StartProfiling()
mRacyInfo->ReinitializeOnResume();
mResponsiveness.emplace(mThread, mIsMainThread);
}
void
ThreadInfo::StopProfiling()
{
mResponsiveness.reset();
- mUniqueStacks.reset();
- mSavedStreamedSamples = nullptr;
- mSavedStreamedMarkers = nullptr;
+ mPartialProfile = nullptr;
mIsBeingProfiled = false;
}
void
ThreadInfo::StreamJSON(const ProfileBuffer& aBuffer,
SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime, double aSinceTime)
{
- // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
- if (!mUniqueStacks.isSome()) {
- mUniqueStacks.emplace(mContext);
+ UniquePtr<PartialThreadProfile> partialProfile = Move(mPartialProfile);
+
+ UniquePtr<UniqueStacks> uniqueStacks = partialProfile
+ ? Move(partialProfile->mUniqueStacks)
+ : MakeUnique<UniqueStacks>(mContext);
+
+ UniquePtr<char[]> partialSamplesJSON;
+ UniquePtr<char[]> partialMarkersJSON;
+ if (partialProfile) {
+ partialSamplesJSON = Move(partialProfile->mSamplesJSON);
+ partialMarkersJSON = Move(partialProfile->mMarkersJSON);
}
aWriter.Start();
{
StreamSamplesAndMarkers(Name(), ThreadId(), aBuffer, aWriter,
aProcessStartTime,
mRegisterTime, mUnregisterTime,
aSinceTime, mContext,
- mSavedStreamedSamples.get(),
- mSavedStreamedMarkers.get(),
- *mUniqueStacks);
- mSavedStreamedSamples = nullptr;
- mSavedStreamedMarkers = nullptr;
+ Move(partialSamplesJSON),
+ Move(partialMarkersJSON),
+ *uniqueStacks);
aWriter.StartObjectProperty("stackTable");
{
{
JSONSchemaWriter schema(aWriter);
schema.WriteField("prefix");
schema.WriteField("frame");
}
aWriter.StartArrayProperty("data");
{
- mUniqueStacks->SpliceStackTableElements(aWriter);
+ uniqueStacks->SpliceStackTableElements(aWriter);
}
aWriter.EndArray();
}
aWriter.EndObject();
aWriter.StartObjectProperty("frameTable");
{
{
@@ -120,45 +124,44 @@ ThreadInfo::StreamJSON(const ProfileBuff
schema.WriteField("implementation");
schema.WriteField("optimizations");
schema.WriteField("line");
schema.WriteField("category");
}
aWriter.StartArrayProperty("data");
{
- mUniqueStacks->SpliceFrameTableElements(aWriter);
+ uniqueStacks->SpliceFrameTableElements(aWriter);
}
aWriter.EndArray();
}
aWriter.EndObject();
aWriter.StartArrayProperty("stringTable");
{
- mUniqueStacks->mUniqueStrings.SpliceStringTableElements(aWriter);
+ uniqueStacks->mUniqueStrings.SpliceStringTableElements(aWriter);
}
aWriter.EndArray();
}
+
aWriter.End();
-
- mUniqueStacks.reset();
}
void
StreamSamplesAndMarkers(const char* aName,
int aThreadId,
const ProfileBuffer& aBuffer,
SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
const TimeStamp& aRegisterTime,
const TimeStamp& aUnregisterTime,
double aSinceTime,
JSContext* aContext,
- char* aSavedStreamedSamples,
- char* aSavedStreamedMarkers,
+ UniquePtr<char[]>&& aPartialSamplesJSON,
+ UniquePtr<char[]>&& aPartialMarkersJSON,
UniqueStacks& aUniqueStacks)
{
aWriter.StringProperty("processType",
XRE_ChildProcessTypeToString(XRE_GetProcessType()));
aWriter.StringProperty("name", aName);
aWriter.IntProperty("tid", static_cast<int64_t>(aThreadId));
aWriter.IntProperty("pid", static_cast<int64_t>(getpid()));
@@ -185,22 +188,22 @@ StreamSamplesAndMarkers(const char* aNam
schema.WriteField("time");
schema.WriteField("responsiveness");
schema.WriteField("rss");
schema.WriteField("uss");
}
aWriter.StartArrayProperty("data");
{
- if (aSavedStreamedSamples) {
+ if (aPartialSamplesJSON) {
// We would only have saved streamed samples during shutdown
// streaming, which cares about dumping the entire buffer, and thus
// should have passed in 0 for aSinceTime.
MOZ_ASSERT(aSinceTime == 0);
- aWriter.Splice(aSavedStreamedSamples);
+ aWriter.Splice(aPartialSamplesJSON.get());
}
aBuffer.StreamSamplesToJSON(aWriter, aThreadId, aSinceTime,
aContext, aUniqueStacks);
}
aWriter.EndArray();
}
aWriter.EndObject();
@@ -210,19 +213,19 @@ StreamSamplesAndMarkers(const char* aNam
JSONSchemaWriter schema(aWriter);
schema.WriteField("name");
schema.WriteField("time");
schema.WriteField("data");
}
aWriter.StartArrayProperty("data");
{
- if (aSavedStreamedMarkers) {
+ if (aPartialMarkersJSON) {
MOZ_ASSERT(aSinceTime == 0);
- aWriter.Splice(aSavedStreamedMarkers);
+ aWriter.Splice(aPartialMarkersJSON.get());
}
aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime,
aSinceTime, aUniqueStacks);
}
aWriter.EndArray();
}
aWriter.EndObject();
}
@@ -237,96 +240,98 @@ ThreadInfo::FlushSamplesAndMarkers(const
// 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.
- mUniqueStacks.emplace(mContext);
+ UniquePtr<UniqueStacks> uniqueStacks = mPartialProfile
+ ? Move(mPartialProfile->mUniqueStacks)
+ : MakeUnique<UniqueStacks>(mContext);
+
+ UniquePtr<char[]> samplesJSON;
+ UniquePtr<char[]> markersJSON;
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
bool haveSamples = false;
{
- if (mSavedStreamedSamples) {
- b.Splice(mSavedStreamedSamples.get());
+ 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, ThreadId(), /* aSinceTime = */ 0,
- mContext, *mUniqueStacks);
+ mContext, *uniqueStacks);
haveSamples = haveSamples || streamedNewSamples;
}
b.EndBareList();
// https://bugzilla.mozilla.org/show_bug.cgi?id=1428076
- // If we don't have any data, keep mSavedStreamSamples set to null. That
+ // 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) {
- mSavedStreamedSamples = b.WriteFunc()->CopyData();
- } else {
- mSavedStreamedSamples = nullptr;
+ samplesJSON = b.WriteFunc()->CopyData();
}
}
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
bool haveMarkers = false;
{
- if (mSavedStreamedMarkers) {
- b.Splice(mSavedStreamedMarkers.get());
+ if (mPartialProfile && mPartialProfile->mMarkersJSON) {
+ b.Splice(mPartialProfile->mMarkersJSON.get());
haveMarkers = true;
}
// 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, ThreadId(), aProcessStartTime,
- /* aSinceTime = */ 0, *mUniqueStacks);
+ /* 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 mSavedStreamMarkers set to null. That
+ // 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) {
- mSavedStreamedMarkers = b.WriteFunc()->CopyData();
- } else {
- mSavedStreamedMarkers = nullptr;
+ markersJSON = b.WriteFunc()->CopyData();
}
}
+ mPartialProfile = MakeUnique<PartialThreadProfile>(
+ Move(samplesJSON), Move(markersJSON), Move(uniqueStacks));
+
// Reset the buffer. Attempting to symbolicate JS samples after mContext has
// gone away will crash.
aBuffer.Reset();
}
size_t
ThreadInfo::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += aMallocSizeOf(mName.get());
n += mRacyInfo->SizeOfIncludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mPlatformData
- // - mSavedStreamedSamples
- // - mSavedStreamedMarkers
- // - mUniqueStacks
+ // - mPartialProfile
//
// The following members are not measured:
// - mThread: because it is non-owning
return n;
}
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -161,16 +161,33 @@ private:
// before we can tell that duplication is allowed.
//
static const int AWAKE = 0;
static const int SLEEPING_NOT_OBSERVED = 1;
static const int SLEEPING_OBSERVED = 2;
mozilla::Atomic<int> mSleep;
};
+// 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 the info for a single thread.
//
// Note: A thread's ThreadInfo can be held onto after the thread itself exits,
// because we may need to output profiling information about that thread. But
// some of the fields in this class are only relevant while the thread is
// alive. It's possible that this class could be refactored so there is a
// clearer split between those fields and the fields that are still relevant
// after the thread exists.
@@ -322,19 +339,17 @@ public:
private:
bool mIsBeingProfiled;
// 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.
- mozilla::UniquePtr<char[]> mSavedStreamedSamples;
- mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
- mozilla::Maybe<UniqueStacks> mUniqueStacks;
+ UniquePtr<PartialThreadProfile> mPartialProfile;
// This is used only for nsIThreads.
mozilla::Maybe<ThreadResponsiveness> mResponsiveness;
public:
// If this is a JS thread, this is its JSContext, which is required for any
// JS sampling.
JSContext* mContext;
@@ -397,13 +412,13 @@ void
StreamSamplesAndMarkers(const char* aName, int aThreadId,
const ProfileBuffer& aBuffer,
SpliceableJSONWriter& aWriter,
const mozilla::TimeStamp& aProcessStartTime,
const TimeStamp& aRegisterTime,
const TimeStamp& aUnregisterTime,
double aSinceTime,
JSContext* aContext,
- char* aSavedStreamedSamples,
- char* aSavedStreamedMarkers,
+ UniquePtr<char[]>&& aPartialSamplesJSON,
+ UniquePtr<char[]>&& aPartialMarkersJSON,
UniqueStacks& aUniqueStacks);
#endif // ThreadInfo_h