Bug 1436924 - Group mUniqueStacks, mSavedStreamedSamples and mSavedStreamedMarkers into a PartialThreadProfile struct that's heap-allocated on demand. r?njn draft
authorMarkus Stange <mstange@themasta.com>
Fri, 09 Feb 2018 00:56:55 -0500
changeset 752888 c0042b87351efaeab20f891f05c2c8607f736b63
parent 752766 84c46c56cea2083f52d79d2900943182b98d3267
push id98414
push userbmo:mstange@themasta.com
push dateFri, 09 Feb 2018 05:57:28 +0000
reviewersnjn
bugs1436924
milestone60.0a1
Bug 1436924 - Group mUniqueStacks, mSavedStreamedSamples and mSavedStreamedMarkers into a PartialThreadProfile struct that's heap-allocated on demand. r?njn MozReview-Commit-ID: 5WNrIWLsiM1
tools/profiler/core/ThreadInfo.cpp
tools/profiler/core/ThreadInfo.h
--- 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