Bug 1328378 (part 5) - Simplify ProfileBuffer handling. r=mstange.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 27 Feb 2017 12:34:59 +1100
changeset 374569 6de72f3b7f8abb27024a34faac7fbde24ee6ec8f
parent 374568 413a21e421dbeb4be715a5cd28967719ba5bd886
child 374570 7becdd5d601a803789f817c7ae7d41b6f2d2c0d2
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1328378
milestone54.0a1
Bug 1328378 (part 5) - Simplify ProfileBuffer handling. r=mstange. Currently ThreadInfo objects all share gBuffer, while SyncProfile objects each get their own ProfileBuffer. This patch removes ThreadInfo::mBuffer to reduce this difference, taking us a step towards eliminating SyncProfile. To support this, the patch: - passes in a buffer as an additional argument in a bunch of places where the buffer used to be obtained from a ThreadInfo; - adds an mBuffer field to ProfilerBacktrace; - changes ThreadInfo::SetProfile() to SetHasProfile(); - removes ThreadInfo::{addTag,StoredMarker,bufferGeneration}(), all of which just redirected to ThreadInfo anyway; - changes ProfileBuffer so it's no longer refcounted, which is unnecessary now that gBuffer and ProfilerBacktrace::mBuffer don't have multiple references, which makes their lifetimes obvious. The patch also removes some ThreadInfo& args in functions in platform.cpp, in places where that ThreadInfo is available within the accompanying TickSampler* arg.
tools/profiler/core/ProfileBuffer.h
tools/profiler/core/ProfilerBacktrace.cpp
tools/profiler/core/SyncProfile.cpp
tools/profiler/core/SyncProfile.h
tools/profiler/core/ThreadInfo.cpp
tools/profiler/core/ThreadInfo.h
tools/profiler/core/platform-linux-android.cpp
tools/profiler/core/platform-macos.cpp
tools/profiler/core/platform-win32.cpp
tools/profiler/core/platform.cpp
tools/profiler/public/ProfilerBacktrace.h
tools/profiler/tests/gtest/ThreadProfileTest.cpp
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -7,24 +7,22 @@
 #define MOZ_PROFILE_BUFFER_H
 
 #include "ProfileBufferEntry.h"
 #include "platform.h"
 #include "ProfileJSONWriter.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/RefCounted.h"
 
-class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer>
+class ProfileBuffer final
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
-
   explicit ProfileBuffer(int aEntrySize);
 
-  virtual ~ProfileBuffer();
+  ~ProfileBuffer();
 
   void addTag(const ProfileBufferEntry& aTag);
   void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            JSContext* cx, UniqueStacks& aUniqueStacks);
   void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            UniqueStacks& aUniqueStacks);
   void DuplicateLastSample(int aThreadId, const mozilla::TimeStamp& aStartTime);
 
--- a/tools/profiler/core/ProfilerBacktrace.cpp
+++ b/tools/profiler/core/ProfilerBacktrace.cpp
@@ -4,28 +4,31 @@
  * 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 "ProfilerBacktrace.h"
 
 #include "ProfileJSONWriter.h"
 #include "SyncProfile.h"
 
-ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
-  : mProfile(aProfile)
+ProfilerBacktrace::ProfilerBacktrace(ProfileBuffer* aBuffer,
+                                     SyncProfile* aProfile)
+  : mBuffer(aBuffer)
+  , mProfile(aProfile)
 {
   MOZ_COUNT_CTOR(ProfilerBacktrace);
   MOZ_ASSERT(aProfile);
 }
 
 ProfilerBacktrace::~ProfilerBacktrace()
 {
   MOZ_COUNT_DTOR(ProfilerBacktrace);
+  delete mBuffer;
   delete mProfile;
 }
 
 void
 ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
                               UniqueStacks& aUniqueStacks)
 {
   mozilla::MutexAutoLock lock(mProfile->GetMutex());
-  mProfile->StreamJSON(aWriter, aUniqueStacks);
+  mProfile->StreamJSON(mBuffer, aWriter, aUniqueStacks);
 }
--- a/tools/profiler/core/SyncProfile.cpp
+++ b/tools/profiler/core/SyncProfile.cpp
@@ -6,23 +6,25 @@
 
 #include "SyncProfile.h"
 
 SyncProfile::SyncProfile(int aThreadId, PseudoStack* aStack)
   : ThreadInfo("SyncProfile", aThreadId, /* isMainThread = */ false, aStack,
                /* stackTop = */ nullptr)
 {
   MOZ_COUNT_CTOR(SyncProfile);
-  SetProfile(new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES));
+  SetHasProfile();
 }
 
 SyncProfile::~SyncProfile()
 {
   MOZ_COUNT_DTOR(SyncProfile);
 }
 
 // SyncProfiles' stacks are deduplicated in the context of the containing
 // profile in which the backtrace is as a marker payload.
 void
-SyncProfile::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
+SyncProfile::StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                        UniqueStacks& aUniqueStacks)
 {
-  ThreadInfo::StreamSamplesAndMarkers(aWriter, /* aSinceTime = */ 0, aUniqueStacks);
+  ThreadInfo::StreamSamplesAndMarkers(aBuffer, aWriter, /* aSinceTime = */ 0,
+                                      aUniqueStacks);
 }
--- a/tools/profiler/core/SyncProfile.h
+++ b/tools/profiler/core/SyncProfile.h
@@ -12,16 +12,17 @@
 class SyncProfile : public ThreadInfo
 {
 public:
   SyncProfile(int aThreadId, PseudoStack* aStack);
   ~SyncProfile();
 
   // SyncProfiles' stacks are deduplicated in the context of the containing
   // profile in which the backtrace is as a marker payload.
-  void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
+  void StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                  UniqueStacks& aUniqueStacks);
 
 private:
   friend class ProfilerBacktrace;
 };
 
 #endif // __SYNCPROFILE_H
 
--- a/tools/profiler/core/ThreadInfo.cpp
+++ b/tools/profiler/core/ThreadInfo.cpp
@@ -17,16 +17,17 @@ ThreadInfo::ThreadInfo(const char* aName
                        void* aStackTop)
   : mName(strdup(aName))
   , mThreadId(aThreadId)
   , mIsMainThread(aIsMainThread)
   , mPseudoStack(aPseudoStack)
   , mPlatformData(AllocPlatformData(aThreadId))
   , mStackTop(aStackTop)
   , mPendingDelete(false)
+  , mHasProfile(false)
   , mMutex(MakeUnique<mozilla::Mutex>("ThreadInfo::mMutex"))
 {
   MOZ_COUNT_CTOR(ThreadInfo);
   mThread = NS_GetCurrentThread();
 
   // We don't have to guess on mac
 #if defined(GP_OS_darwin)
   pthread_t self = pthread_self();
@@ -59,37 +60,27 @@ ThreadInfo::CanInvokeJS() const
   }
   bool result;
   mozilla::DebugOnly<nsresult> rv = mThread->GetCanInvokeJS(&result);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   return result;
 }
 
 void
-ThreadInfo::addTag(const ProfileBufferEntry& aTag)
-{
-  mBuffer->addTag(aTag);
-}
-
-void
-ThreadInfo::addStoredMarker(ProfilerMarker* aStoredMarker) {
-  mBuffer->addStoredMarker(aStoredMarker);
-}
-
-void
-ThreadInfo::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
+ThreadInfo::StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                       double aSinceTime)
 {
   // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
   if (!mUniqueStacks.isSome()) {
     mUniqueStacks.emplace(mPseudoStack->mContext);
   }
 
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
-    StreamSamplesAndMarkers(aWriter, aSinceTime, *mUniqueStacks);
+    StreamSamplesAndMarkers(aBuffer, aWriter, aSinceTime, *mUniqueStacks);
 
     aWriter.StartObjectProperty("stackTable");
     {
       {
         JSONSchemaWriter schema(aWriter);
         schema.WriteField("prefix");
         schema.WriteField("frame");
       }
@@ -128,17 +119,18 @@ ThreadInfo::StreamJSON(SpliceableJSONWri
     aWriter.EndArray();
   }
   aWriter.End();
 
   mUniqueStacks.reset();
 }
 
 void
-ThreadInfo::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter,
+ThreadInfo::StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
+                                    SpliceableJSONWriter& aWriter,
                                     double aSinceTime,
                                     UniqueStacks& aUniqueStacks)
 {
   aWriter.StringProperty("processType",
                          XRE_ChildProcessTypeToString(XRE_GetProcessType()));
 
   aWriter.StringProperty("name", Name());
   aWriter.IntProperty("tid", static_cast<int>(mThreadId));
@@ -160,17 +152,17 @@ ThreadInfo::StreamSamplesAndMarkers(Spli
       if (mSavedStreamedSamples) {
         // 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(mSavedStreamedSamples.get());
         mSavedStreamedSamples.reset();
       }
-      mBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
+      aBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
                                    mPseudoStack->mContext, aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 
   aWriter.StartObjectProperty("markers");
   {
@@ -183,25 +175,26 @@ ThreadInfo::StreamSamplesAndMarkers(Spli
 
     aWriter.StartArrayProperty("data");
     {
       if (mSavedStreamedMarkers) {
         MOZ_ASSERT(aSinceTime == 0);
         aWriter.Splice(mSavedStreamedMarkers.get());
         mSavedStreamedMarkers.reset();
       }
-      mBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime, aUniqueStacks);
+      aBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime,
+                                   aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 }
 
 void
-ThreadInfo::FlushSamplesAndMarkers()
+ThreadInfo::FlushSamplesAndMarkers(ProfileBuffer* aBuffer)
 {
   // This function is used to serialize the current buffer just before
   // JSContext destruction.
   MOZ_ASSERT(mPseudoStack->mContext);
 
   // 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
@@ -210,48 +203,49 @@ ThreadInfo::FlushSamplesAndMarkers()
   // Note that the UniqueStacks instance is persisted so that the frame-index
   // mapping is stable across JS shutdown.
   mUniqueStacks.emplace(mPseudoStack->mContext);
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
-      mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
+      aBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
                                    mPseudoStack->mContext, *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedSamples = b.WriteFunc()->CopyData();
   }
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
-      mBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
+      aBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedMarkers = b.WriteFunc()->CopyData();
   }
 
   // Reset the buffer. Attempting to symbolicate JS samples after mContext has
   // gone away will crash.
-  mBuffer->reset();
+  aBuffer->reset();
 }
 
 mozilla::Mutex&
 ThreadInfo::GetMutex()
 {
   return *mMutex.get();
 }
 
 void
-ThreadInfo::DuplicateLastSample(const TimeStamp& aStartTime)
+ThreadInfo::DuplicateLastSample(ProfileBuffer* aBuffer,
+                                const TimeStamp& aStartTime)
 {
-  mBuffer->DuplicateLastSample(mThreadId, aStartTime);
+  aBuffer->DuplicateLastSample(mThreadId, aStartTime);
 }
 
 size_t
 ThreadInfo::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   n += aMallocSizeOf(mName.get());
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -19,17 +19,17 @@ class ThreadInfo {
   virtual ~ThreadInfo();
 
   const char* Name() const { return mName.get(); }
   int ThreadId() const { return mThreadId; }
 
   bool IsMainThread() const { return mIsMainThread; }
   PseudoStack* Stack() const { return mPseudoStack; }
 
-  void SetProfile(ProfileBuffer* aBuffer) { mBuffer = aBuffer; }
+  void SetHasProfile() { mHasProfile = true; }
 
   PlatformData* GetPlatformData() const { return mPlatformData.get(); }
   void* StackTop() const { return mStackTop; }
 
   virtual void SetPendingDelete();
   bool IsPendingDelete() const { return mPendingDelete; }
 
   bool CanInvokeJS() const;
@@ -46,57 +46,53 @@ class ThreadInfo {
 
   // May be null for the main thread if the profiler was started during startup.
   nsCOMPtr<nsIThread> mThread;
 
   bool mPendingDelete;
 
   //
   // The following code is only used for threads that are being profiled, i.e.
-  // for which SetProfile() has been called.
+  // for which SetHasProfile() has been called.
   //
 
 public:
-  bool hasProfile() { return !!mBuffer; }
-
-  void addTag(const ProfileBufferEntry& aTag);
+  bool HasProfile() { return mHasProfile; }
 
-  // Track a marker which has been inserted into the thread profile.
-  // This marker can safely be deleted once the generation has
-  // expired.
-  void addStoredMarker(ProfilerMarker* aStoredMarker);
   mozilla::Mutex& GetMutex();
-  void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
+  void StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                  double aSinceTime = 0);
 
   // Call this method when the JS entries inside the buffer are about to
   // become invalid, i.e., just before JS shutdown.
-  void FlushSamplesAndMarkers();
+  void FlushSamplesAndMarkers(ProfileBuffer* aBuffer);
 
-  void DuplicateLastSample(const mozilla::TimeStamp& aStartTime);
+  void DuplicateLastSample(ProfileBuffer* aBuffer,
+                           const mozilla::TimeStamp& aStartTime);
 
   ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
 
   void UpdateThreadResponsiveness() {
     mRespInfo.Update(mIsMainThread, mThread);
   }
 
-  uint32_t bufferGeneration() const { return mBuffer->mGeneration; }
-
 protected:
-  void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
+  void StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
+                               SpliceableJSONWriter& aWriter,
+                               double aSinceTime,
                                UniqueStacks& aUniqueStacks);
 
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
 
-  RefPtr<ProfileBuffer> mBuffer;
+  bool mHasProfile;
 
   // 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;
--- a/tools/profiler/core/platform-linux-android.cpp
+++ b/tools/profiler/core/platform-linux-android.cpp
@@ -357,22 +357,22 @@ SigprofSender(void* aArg)
     if (!gIsPaused) {
       StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
       bool isFirstProfiledThread = true;
       for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
         ThreadInfo* info = (*gRegisteredThreads)[i];
 
         // This will be null if we're not interested in profiling this thread.
-        if (!info->hasProfile() || info->IsPendingDelete()) {
+        if (!info->HasProfile() || info->IsPendingDelete()) {
           continue;
         }
 
         if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-          info->DuplicateLastSample(gStartTime);
+          info->DuplicateLastSample(gBuffer, gStartTime);
           continue;
         }
 
         info->UpdateThreadResponsiveness();
 
         int threadId = info->ThreadId();
         MOZ_ASSERT(threadId != my_tid);
 
@@ -415,17 +415,17 @@ SigprofSender(void* aArg)
         // Extract the current pc and sp.
         SetSampleContext(&sample,
                          gSigHandlerCoordinator->mUContext.uc_mcontext);
         sample.threadInfo = info;
         sample.timestamp = mozilla::TimeStamp::Now();
         sample.rssMemory = rssMemory;
         sample.ussMemory = ussMemory;
 
-        Tick(&sample);
+        Tick(gBuffer, &sample);
 
         // Send message 3 to the samplee, which tells it to resume.
         r = sem_post(&gSigHandlerCoordinator->mMessage3);
         MOZ_ASSERT(r == 0);
 
         // Wait for message 4 from the samplee, which tells us that it has
         // finished with |gSigHandlerCoordinator|.
         while (true) {
--- a/tools/profiler/core/platform-macos.cpp
+++ b/tools/profiler/core/platform-macos.cpp
@@ -154,22 +154,22 @@ public:
       if (!gIsPaused) {
         StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*gRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
-          if (!info->hasProfile() || info->IsPendingDelete()) {
+          if (!info->HasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-            info->DuplicateLastSample(gStartTime);
+            info->DuplicateLastSample(gBuffer, gStartTime);
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
           SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
@@ -235,17 +235,17 @@ public:
       sample.pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
       sample.sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
       sample.fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
       sample.timestamp = mozilla::TimeStamp::Now();
       sample.threadInfo = aThreadInfo;
 
 #undef REGISTER_FIELD
 
-      Tick(&sample);
+      Tick(gBuffer, &sample);
     }
     thread_resume(profiled_thread);
   }
 
 private:
   pthread_t mThread;
 
   int mIntervalMicro;
--- a/tools/profiler/core/platform-win32.cpp
+++ b/tools/profiler/core/platform-win32.cpp
@@ -163,22 +163,22 @@ class SamplerThread
       if (!gIsPaused) {
         mozilla::StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*gRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
-          if (!info->hasProfile() || info->IsPendingDelete()) {
+          if (!info->HasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-            info->DuplicateLastSample(gStartTime);
+            info->DuplicateLastSample(gBuffer, gStartTime);
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
           SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
@@ -244,17 +244,17 @@ class SamplerThread
 #else
     sample.pc = reinterpret_cast<Address>(context.Eip);
     sample.sp = reinterpret_cast<Address>(context.Esp);
     sample.fp = reinterpret_cast<Address>(context.Ebp);
 #endif
 
     sample.context = &context;
 
-    Tick(&sample);
+    Tick(gBuffer, &sample);
 
     ResumeThread(profiled_thread);
   }
 
 private:
   HANDLE mThread;
   Thread::tid_t mThreadId;
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -102,19 +102,18 @@ MOZ_THREAD_LOCAL(PseudoStack *) tlsPseud
 static Sampler* gSampler;
 
 static std::vector<ThreadInfo*>* gRegisteredThreads = nullptr;
 static mozilla::StaticMutex gRegisteredThreadsMutex;
 
 // All accesses to gGatherer are on the main thread, so no locking is needed.
 static StaticRefPtr<mozilla::ProfileGatherer> gGatherer;
 
-// XXX: gBuffer is used on multiple threads -- including via copies in
-// ThreadInfo::mBuffer -- without any apparent synchronization(!)
-static StaticRefPtr<ProfileBuffer> gBuffer;
+// Always used in conjunction with gRegisteredThreads.
+static ProfileBuffer* gBuffer;
 
 // gThreadNameFilters is accessed from multiple threads. All accesses to it
 // must be guarded by gThreadNameFiltersMutex.
 static Vector<std::string> gThreadNameFilters;
 static mozilla::StaticMutex gThreadNameFiltersMutex;
 
 // All accesses to gFeatures are on the main thread, so no locking is needed.
 static Vector<std::string> gFeatures;
@@ -225,38 +224,38 @@ public:
   bool isSamplingCurrentThread;
   ThreadInfo* threadInfo;
   mozilla::TimeStamp timestamp;
   int64_t rssMemory;
   int64_t ussMemory;
 };
 
 static void
-AddDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr)
+AddDynamicCodeLocationTag(ProfileBuffer* aBuffer, const char* aStr)
 {
-  aInfo.addTag(ProfileBufferEntry::CodeLocation(""));
+  aBuffer->addTag(ProfileBufferEntry::CodeLocation(""));
 
   size_t strLen = strlen(aStr) + 1;   // +1 for the null terminator
   for (size_t j = 0; j < strLen; ) {
     // Store as many characters in the void* as the platform allows.
     char text[sizeof(void*)];
     size_t len = sizeof(void*) / sizeof(char);
     if (j+len >= strLen) {
       len = strLen - j;
     }
     memcpy(text, &aStr[j], len);
     j += sizeof(void*) / sizeof(char);
 
     // Cast to *((void**) to pass the text data to a void*.
-    aInfo.addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
+    aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static void
-AddPseudoEntry(volatile js::ProfileEntry& entry, ThreadInfo& aInfo,
+AddPseudoEntry(ProfileBuffer* aBuffer, volatile js::ProfileEntry& entry,
                PseudoStack* stack, void* lastpc)
 {
   // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and
   // should not be recorded in the profile.
   if (entry.hasFlag(js::ProfileEntry::BEGIN_PSEUDO_JS)) {
     return;
   }
 
@@ -264,17 +263,17 @@ AddPseudoEntry(volatile js::ProfileEntry
 
   // First entry has kind CodeLocation. Check for magic pointer bit 1 to
   // indicate copy.
   const char* sampleLabel = entry.label();
 
   if (entry.isCopyLabel()) {
     // Store the string using 1 or more EmbeddedString tags.
     // That will happen to the preceding tag.
-    AddDynamicCodeLocationTag(aInfo, sampleLabel);
+    AddDynamicCodeLocationTag(aBuffer, sampleLabel);
     if (entry.isJs()) {
       JSScript* script = entry.script();
       if (script) {
         if (!entry.pc()) {
           // The JIT only allows the top-most entry to have a nullptr pc.
           MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
 
           // If stack-walking was disabled, then that's just unfortunate.
@@ -288,35 +287,35 @@ AddPseudoEntry(volatile js::ProfileEntry
         } else {
           lineno = JS_PCToLineNumber(script, entry.pc());
         }
       }
     } else {
       lineno = entry.line();
     }
   } else {
-    aInfo.addTag(ProfileBufferEntry::CodeLocation(sampleLabel));
+    aBuffer->addTag(ProfileBufferEntry::CodeLocation(sampleLabel));
 
     // XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
     // js entries as well.
     if (entry.isCpp()) {
       lineno = entry.line();
     }
   }
 
   if (lineno != -1) {
-    aInfo.addTag(ProfileBufferEntry::LineNumber(lineno));
+    aBuffer->addTag(ProfileBufferEntry::LineNumber(lineno));
   }
 
   uint32_t category = entry.category();
   MOZ_ASSERT(!(category & js::ProfileEntry::IS_CPP_ENTRY));
   MOZ_ASSERT(!(category & js::ProfileEntry::FRAME_LABEL_COPY));
 
   if (category) {
-    aInfo.addTag(ProfileBufferEntry::Category((int)category));
+    aBuffer->addTag(ProfileBufferEntry::Category((int)category));
   }
 }
 
 struct NativeStack
 {
   void** pc_array;
   void** sp_array;
   size_t size;
@@ -336,35 +335,35 @@ struct AutoWalkJSStack
   ~AutoWalkJSStack() {
     if (walkAllowed) {
       WALKING_JS_STACK = false;
     }
   }
 };
 
 static void
-MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
+MergeStacksIntoProfile(ProfileBuffer* aBuffer, TickSample* aSample,
                        NativeStack& aNativeStack)
 {
-  PseudoStack* pseudoStack = aInfo.Stack();
+  PseudoStack* pseudoStack = aSample->threadInfo->Stack();
   volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
   // ProfilingFrameIterator to avoid incorrectly resetting the generation of
   // sampled JIT entries inside the JS engine. See note below concerning 'J'
   // entries.
   uint32_t startBufferGen;
   startBufferGen = aSample->isSamplingCurrentThread
                  ? UINT32_MAX
-                 : aInfo.bufferGeneration();
+                 : aBuffer->mGeneration;
   uint32_t jsCount = 0;
   JS::ProfilingFrameIterator::Frame jsFrames[1000];
 
   // Only walk jit stack if profiling frame iterator is turned on.
   if (pseudoStack->mContext &&
       JS::IsProfilingEnabledForContext(pseudoStack->mContext)) {
     AutoWalkJSStack autoWalkJSStack;
     const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
@@ -394,17 +393,17 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
             jsFrames[jsCount++] = frame.value();
           }
         }
       }
     }
   }
 
   // Start the sample with a root entry.
-  aInfo.addTag(ProfileBufferEntry::Sample("(root)"));
+  aBuffer->addTag(ProfileBufferEntry::Sample("(root)"));
 
   // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
   // native arrays are ordered youngest-to-oldest. We must add frames to aInfo
   // oldest-to-youngest. Thus, iterate over the pseudo-stack forwards and JS
   // and native arrays backwards. Note: this means the terminating condition
   // jsIndex and nativeIndex is being < 0.
   uint32_t pseudoIndex = 0;
   int32_t jsIndex = jsCount - 1;
@@ -467,17 +466,17 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
       volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex];
-      AddPseudoEntry(pseudoFrame, aInfo, pseudoStack, nullptr);
+      AddPseudoEntry(aBuffer, pseudoFrame, pseudoStack, nullptr);
       pseudoIndex++;
       continue;
     }
 
     // Check to see if JS jit stack frame is top-most
     if (jsStackAddr > nativeStackAddr) {
       MOZ_ASSERT(jsIndex >= 0);
       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
@@ -492,49 +491,49 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
       // amount of time, such as in nsRefreshDriver. Problematically, the
       // stored backtrace may be alive across a GC during which the profiler
       // itself is disabled. In that case, the JS engine is free to discard its
       // JIT code. This means that if we inserted such OptInfoAddr 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_Wasm) {
-        AddDynamicCodeLocationTag(aInfo, jsFrame.label);
+        AddDynamicCodeLocationTag(aBuffer, jsFrame.label);
       } else {
         MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
                    jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
-        aInfo.addTag(
+        aBuffer->addTag(
           ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
       }
 
       jsIndex--;
       continue;
     }
 
     // If we reach here, there must be a native stack entry and it must be the
     // greatest entry.
     if (nativeStackAddr) {
       MOZ_ASSERT(nativeIndex >= 0);
       void* addr = (void*)aNativeStack.pc_array[nativeIndex];
-      aInfo.addTag(ProfileBufferEntry::NativeLeafAddr(addr));
+      aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr(addr));
     }
     if (nativeIndex >= 0) {
       nativeIndex--;
     }
   }
 
   // Update the JS context with the current profile sample buffer generation.
   //
   // Do not do this for synchronous sampling, which create their own
   // ProfileBuffers.
   if (!aSample->isSamplingCurrentThread && pseudoStack->mContext) {
-    MOZ_ASSERT(aInfo.bufferGeneration() >= startBufferGen);
-    uint32_t lapCount = aInfo.bufferGeneration() - startBufferGen;
+    MOZ_ASSERT(aBuffer->mGeneration >= startBufferGen);
+    uint32_t lapCount = aBuffer->mGeneration - startBufferGen;
     JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
-                                               aInfo.bufferGeneration(),
+                                               aBuffer->mGeneration,
                                                lapCount);
   }
 }
 
 #if defined(GP_OS_windows)
 static uintptr_t GetThreadHandle(PlatformData* aData);
 #endif
 
@@ -545,17 +544,17 @@ StackWalkCallback(uint32_t aFrameNumber,
   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
   MOZ_ASSERT(nativeStack->count < nativeStack->size);
   nativeStack->sp_array[nativeStack->count] = aSP;
   nativeStack->pc_array[nativeStack->count] = aPC;
   nativeStack->count++;
 }
 
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(ProfileBuffer* aBuffer, TickSample* aSample)
 {
   void* pc_array[1000];
   void* sp_array[1000];
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
@@ -580,23 +579,23 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
   // Win64 always omits frame pointers so for it we use the slower
   // MozStackWalk().
   uintptr_t thread = GetThreadHandle(aSample->threadInfo->GetPlatformData());
   MOZ_ASSERT(thread);
   MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack,
                thread, /* platformData */ nullptr);
 #endif
 
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 }
 #endif
 
 #ifdef USE_EHABI_STACKWALK
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(Profile* aBuffer, TickSample* aSample)
 {
   void* pc_array[1000];
   void* sp_array[1000];
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
@@ -657,17 +656,17 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
                                       nativeStack.size - nativeStack.count);
 
   MergeStacksIntoProfile(aInfo, aSample, nativeStack);
 }
 #endif
 
 #ifdef USE_LUL_STACKWALK
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(ProfileBuffer* aBuffer, TickSample* aSample)
 {
   const mcontext_t* mc =
     &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
 
   lul::UnwindRegs startRegs;
   memset(&startRegs, 0, sizeof(startRegs));
 
 #if defined(GP_PLAT_amd64_linux)
@@ -704,18 +703,18 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
 #elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
 #else
 #   error "Unknown plat"
 #endif
-    uintptr_t end   = reinterpret_cast<uintptr_t>(aInfo.StackTop());
-    uintptr_t ws    = sizeof(void*);
+    uintptr_t end = reinterpret_cast<uintptr_t>(aSample->threadInfo->StackTop());
+    uintptr_t ws  = sizeof(void*);
     start &= ~(ws-1);
     end   &= ~(ws-1);
     uintptr_t nToCopy = 0;
     if (start < end) {
       nToCopy = end - start;
       if (nToCopy > lul::N_STACK_BYTES)
         nToCopy = lul::N_STACK_BYTES;
     }
@@ -749,97 +748,95 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
     reinterpret_cast<void**>(framePCs),
     reinterpret_cast<void**>(frameSPs),
     mozilla::ArrayLength(framePCs),
     0
   };
 
   nativeStack.count = framesUsed;
 
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 
   // Update stats in the LUL stats object.  Unfortunately this requires
   // three global memory operations.
   gLUL->mStats.mContext += 1;
   gLUL->mStats.mCFI     += framesUsed - 1 - scannedFramesAcquired;
   gLUL->mStats.mScanned += scannedFramesAcquired;
 }
 #endif
 
 static void
-DoSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample,
+DoSampleStackTrace(ProfileBuffer* aBuffer, TickSample* aSample,
                    bool aAddLeafAddresses)
 {
   NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 
   if (aSample && aAddLeafAddresses) {
-    aInfo.addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc));
+    aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc));
   }
 }
 
 // This function is called for each sampling period with the current program
 // counter. It is called within a signal and so must be re-entrant.
 static void
-Tick(TickSample* aSample)
+Tick(ProfileBuffer* aBuffer, TickSample* aSample)
 {
-  ThreadInfo& currThreadInfo = *aSample->threadInfo;
-
-  currThreadInfo.addTag(
-    ProfileBufferEntry::ThreadId(currThreadInfo.ThreadId()));
+  ThreadInfo& threadInfo = *aSample->threadInfo;
+
+  aBuffer->addTag(ProfileBufferEntry::ThreadId(threadInfo.ThreadId()));
 
   mozilla::TimeDuration delta = aSample->timestamp - gStartTime;
-  currThreadInfo.addTag(ProfileBufferEntry::Time(delta.ToMilliseconds()));
-
-  PseudoStack* stack = currThreadInfo.Stack();
+  aBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds()));
+
+  PseudoStack* stack = threadInfo.Stack();
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
     defined(USE_LUL_STACKWALK)
   if (gUseStackWalk) {
-    DoNativeBacktrace(currThreadInfo, aSample);
+    DoNativeBacktrace(aBuffer, aSample);
   } else {
-    DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+    DoSampleStackTrace(aBuffer, aSample, gAddLeafAddresses);
   }
 #else
-  DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+  DoSampleStackTrace(aBuffer, aSample, gAddLeafAddresses);
 #endif
 
   // Don't process the PeudoStack's markers if we're synchronously sampling the
   // current thread.
   if (!aSample->isSamplingCurrentThread) {
     ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
     while (pendingMarkersList && pendingMarkersList->peek()) {
       ProfilerMarker* marker = pendingMarkersList->popHead();
-      currThreadInfo.addStoredMarker(marker);
-      currThreadInfo.addTag(ProfileBufferEntry::Marker(marker));
+      aBuffer->addStoredMarker(marker);
+      aBuffer->addTag(ProfileBufferEntry::Marker(marker));
     }
   }
 
-  if (currThreadInfo.GetThreadResponsiveness()->HasData()) {
+  if (threadInfo.GetThreadResponsiveness()->HasData()) {
     mozilla::TimeDuration delta =
-      currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
+      threadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
         aSample->timestamp);
-    currThreadInfo.addTag(
-      ProfileBufferEntry::Responsiveness(delta.ToMilliseconds()));
+    aBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds()));
   }
 
   // rssMemory is equal to 0 when we are not recording.
   if (aSample->rssMemory != 0) {
-    currThreadInfo.addTag(ProfileBufferEntry::ResidentMemory(
+    aBuffer->addTag(ProfileBufferEntry::ResidentMemory(
       static_cast<double>(aSample->rssMemory)));
   }
 
   // ussMemory is equal to 0 when we are not recording.
   if (aSample->ussMemory != 0) {
-    currThreadInfo.addTag(ProfileBufferEntry::UnsharedMemory(
+    aBuffer->addTag(ProfileBufferEntry::UnsharedMemory(
       static_cast<double>(aSample->ussMemory)));
   }
 
   if (gLastFrameNumber != gFrameNumber) {
-    currThreadInfo.addTag(ProfileBufferEntry::FrameNumber(gFrameNumber));
+    aBuffer->addTag(ProfileBufferEntry::FrameNumber(gFrameNumber));
     gLastFrameNumber = gFrameNumber;
   }
 }
 
 // END tick/unwinding code
 ////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////
@@ -1123,26 +1120,26 @@ StreamJSON(SpliceableJSONWriter& aWriter
       gIsPaused = true;
 
       {
         StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         for (size_t i = 0; i < gRegisteredThreads->size(); i++) {
           // Thread not being profiled, skip it
           ThreadInfo* info = gRegisteredThreads->at(i);
-          if (!info->hasProfile()) {
+          if (!info->HasProfile()) {
             continue;
           }
 
           // Note that we intentionally include thread profiles which
           // have been marked for pending delete.
 
           MutexAutoLock lock(info->GetMutex());
 
-          info->StreamJSON(aWriter, aSinceTime);
+          info->StreamJSON(gBuffer, aWriter, aSinceTime);
         }
       }
 
       if (CanNotifyObservers()) {
         // Send a event asking any subprocesses (plugins) to
         // give us their information
         SubprocessClosure closure(&aWriter);
         nsCOMPtr<nsIObserverService> os =
@@ -1484,17 +1481,17 @@ ThreadSelected(const char* aThreadName)
   return false;
 }
 
 static void
 MaybeSetProfile(ThreadInfo* aInfo)
 {
   if ((aInfo->IsMainThread() || gProfileThreads) &&
       ThreadSelected(aInfo->Name())) {
-    aInfo->SetProfile(gBuffer);
+    aInfo->SetHasProfile();
   }
 }
 
 static void
 RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack,
                       bool aIsMainThread, void* stackTop)
 {
   StaticMutexAutoLock lock(gRegisteredThreadsMutex);
@@ -1988,17 +1985,17 @@ profiler_start(int aEntries, double aInt
   gIsPaused = false;
   PlatformStart(gInterval);
 
   if (gProfileJS || privacyMode) {
     mozilla::StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
       ThreadInfo* info = (*gRegisteredThreads)[i];
-      if (info->IsPendingDelete() || !info->hasProfile()) {
+      if (info->IsPendingDelete() || !info->HasProfile()) {
         continue;
       }
       info->Stack()->reinitializeOnResume();
       if (gProfileJS) {
         info->Stack()->enableJSSampling();
       }
       if (privacyMode) {
         info->Stack()->mPrivacyMode = true;
@@ -2114,17 +2111,20 @@ profiler_stop()
 #ifdef MOZ_TASK_TRACER
   if (gTaskTracer) {
     mozilla::tasktracer::StopLogging();
   }
 #endif
 
   delete gSampler;
   gSampler = nullptr;
+
+  delete gBuffer;
   gBuffer = nullptr;
+
   gEntries = 0;
   gInterval = 0;
 
   // Cancel any in-flight async profile gatherering requests.
   gGatherer->Cancel();
   gGatherer = nullptr;
 
   if (disableJS) {
@@ -2435,16 +2435,17 @@ profiler_get_backtrace()
 
   PseudoStack* stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(stack);
     return nullptr;
   }
   Thread::tid_t tid = Thread::GetCurrentId();
 
+  ProfileBuffer* buffer = new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES);
   SyncProfile* profile = new SyncProfile(tid, stack);
 
   TickSample sample;
   sample.threadInfo = profile;
 
 #if defined(HAVE_NATIVE_UNWIND)
 #if defined(GP_OS_windows) || defined(GP_OS_linux) || defined(GP_OS_android)
   tickcontext_t context;
@@ -2454,19 +2455,19 @@ profiler_get_backtrace()
 #else
 # error "unknown platform"
 #endif
 #endif
 
   sample.isSamplingCurrentThread = true;
   sample.timestamp = mozilla::TimeStamp::Now();
 
-  Tick(&sample);
-
-  return UniqueProfilerBacktrace(new ProfilerBacktrace(profile));
+  Tick(buffer, &sample);
+
+  return UniqueProfilerBacktrace(new ProfilerBacktrace(buffer, profile));
 }
 
 void
 ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace)
 {
   delete aBacktrace;
 }
 
@@ -2579,27 +2580,27 @@ void PseudoStack::flushSamplerOnJSShutdo
   gIsPaused = true;
 
   {
     StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
     for (size_t i = 0; i < gRegisteredThreads->size(); i++) {
       // Thread not being profiled, skip it.
       ThreadInfo* info = gRegisteredThreads->at(i);
-      if (!info->hasProfile() || info->IsPendingDelete()) {
+      if (!info->HasProfile() || info->IsPendingDelete()) {
         continue;
       }
 
       // Thread not profiling the context that's going away, skip it.
       if (info->Stack()->mContext != mContext) {
         continue;
       }
 
       MutexAutoLock lock(info->GetMutex());
-      info->FlushSamplesAndMarkers();
+      info->FlushSamplesAndMarkers(gBuffer);
     }
   }
 
   gIsPaused = false;
 }
 
 // We #include these files directly because it means those files can use
 // declarations from this file trivially.
--- a/tools/profiler/public/ProfilerBacktrace.h
+++ b/tools/profiler/public/ProfilerBacktrace.h
@@ -2,35 +2,37 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef __PROFILER_BACKTRACE_H
 #define __PROFILER_BACKTRACE_H
 
+class ProfileBuffer;
 class SyncProfile;
 class SpliceableJSONWriter;
 class UniqueStacks;
 
 class ProfilerBacktrace
 {
 public:
-  explicit ProfilerBacktrace(SyncProfile* aProfile);
+  explicit ProfilerBacktrace(ProfileBuffer* aBuffer, SyncProfile* aProfile);
   ~ProfilerBacktrace();
 
   // ProfilerBacktraces' stacks are deduplicated in the context of the
   // profile that contains the backtrace as a marker payload.
   //
   // That is, markers that contain backtraces should not need their own stack,
   // frame, and string tables. They should instead reuse their parent
   // profile's tables.
   void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
 
 private:
   ProfilerBacktrace(const ProfilerBacktrace&);
   ProfilerBacktrace& operator=(const ProfilerBacktrace&);
 
-  SyncProfile*  mProfile;
+  ProfileBuffer* mBuffer;
+  SyncProfile* mProfile;
 };
 
 #endif // __PROFILER_BACKTRACE_H
 
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -8,39 +8,38 @@
 #include "ProfileBufferEntry.h"
 #include "ThreadInfo.h"
 
 // Make sure we can initialize our thread profile
 TEST(ThreadProfile, Initialization) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
-  info.SetProfile(pb);
+  info.SetHasProfile();
 }
 
 // Make sure we can record one tag and read it
 TEST(ThreadProfile, InsertOneTag) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
+  ProfileBuffer* pb = new ProfileBuffer(10);
   pb->addTag(ProfileBufferEntry::Time(123.1));
   ASSERT_TRUE(pb->mEntries != nullptr);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].kind() ==
               ProfileBufferEntry::Kind::Time);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
 }
 
 // See if we can insert some tags
 TEST(ThreadProfile, InsertTagsNoWrap) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
+  ProfileBuffer* pb = new ProfileBuffer(100);
   int test_size = 50;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileBufferEntry::Time(i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   while (readPos != pb->mWritePos) {
     ASSERT_TRUE(pb->mEntries[readPos].kind() ==
@@ -53,17 +52,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
 // See if wrapping works as it should in the basic case
 TEST(ThreadProfile, InsertTagsWrap) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   // we can fit only 24 tags in this buffer because of the empty slot
   int tags = 24;
   int buffer_size = tags + 1;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
+  ProfileBuffer* pb = new ProfileBuffer(buffer_size);
   int test_size = 43;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileBufferEntry::Time(i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   int ctr = 0;
   while (readPos != pb->mWritePos) {