Bug 1342306 (part 2) - Avoid profiler_time() calls deep in profiler streaming code. r=mstange.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 27 Feb 2017 13:52:58 +1100
changeset 375031 bd730342c51508fd903a657030ae281163bf2c90
parent 375030 54a86d54e1c131ba32d7f5cb40651c44cfa4ed9f
child 375032 76a96fd623d4304b9a6149deaf723c80e66d351f
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
bugs1342306
milestone54.0a1
Bug 1342306 (part 2) - Avoid profiler_time() calls deep in profiler streaming code. r=mstange. Because profiler_time() is going to need the global lock when I add it, and the lock will already be held when streaming is happening, so it'll cause the thread to deadlock itself. Unfortunately this requires adding an |aStartTime| parameter to a lot of functions, but this is the least worst way I can think of handling it. This also removes the need for one of the profiler_time() functions, which the patch removes.
tools/profiler/core/ProfileBuffer.h
tools/profiler/core/ProfileBufferEntry.cpp
tools/profiler/core/ProfilerBacktrace.cpp
tools/profiler/core/ProfilerMarkers.cpp
tools/profiler/core/ThreadInfo.cpp
tools/profiler/core/ThreadInfo.h
tools/profiler/core/platform.cpp
tools/profiler/public/GeckoProfiler.h
tools/profiler/public/ProfilerBacktrace.h
tools/profiler/public/ProfilerMarkers.h
tools/profiler/public/PseudoStack.h
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -17,17 +17,19 @@ class ProfileBuffer final
 public:
   explicit ProfileBuffer(int aEntrySize);
 
   ~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,
+  void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
+                           const mozilla::TimeStamp& aStartTime,
+                           double aSinceTime,
                            UniqueStacks& aUniqueStacks);
   bool DuplicateLastSample(int aThreadId, const mozilla::TimeStamp& aStartTime);
 
   void addStoredMarker(ProfilerMarker* aStoredMarker);
 
   // The following two methods are not signal safe! They delete markers.
   void deleteExpiredStoredMarkers();
   void reset();
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -714,29 +714,33 @@ void ProfileBuffer::StreamSamplesToJSON(
     }
     readPos = (readPos + 1) % mEntrySize;
   }
   if (sample.isSome()) {
     WriteSample(aWriter, *sample);
   }
 }
 
-void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
-                                        double aSinceTime, UniqueStacks& aUniqueStacks)
+void
+ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
+                                   int aThreadId,
+                                   const TimeStamp& aStartTime,
+                                   double aSinceTime,
+                                   UniqueStacks& aUniqueStacks)
 {
   int readPos = mReadPos;
   int currentThreadID = -1;
   while (readPos != mWritePos) {
     ProfileBufferEntry entry = mEntries[readPos];
     if (entry.isThreadId()) {
       currentThreadID = entry.mTagInt;
     } else if (currentThreadID == aThreadId && entry.isMarker()) {
       const ProfilerMarker* marker = entry.getMarker();
       if (marker->GetTime() >= aSinceTime) {
-        entry.getMarker()->StreamJSON(aWriter, aUniqueStacks);
+        entry.getMarker()->StreamJSON(aWriter, aStartTime, aUniqueStacks);
       }
     }
     readPos = (readPos + 1) % mEntrySize;
   }
 }
 
 int ProfileBuffer::FindLastSampleOfThread(int aThreadId)
 {
--- a/tools/profiler/core/ProfilerBacktrace.cpp
+++ b/tools/profiler/core/ProfilerBacktrace.cpp
@@ -22,14 +22,15 @@ ProfilerBacktrace::~ProfilerBacktrace()
 {
   MOZ_COUNT_DTOR(ProfilerBacktrace);
   delete mBuffer;
   delete mThreadInfo;
 }
 
 void
 ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
+                              const TimeStamp& aStartTime,
                               UniqueStacks& aUniqueStacks)
 {
   mozilla::MutexAutoLock lock(mThreadInfo->GetMutex());
-  mThreadInfo->StreamSamplesAndMarkers(mBuffer, aWriter, /* aSinceTime */ 0,
-                                       aUniqueStacks);
+  mThreadInfo->StreamSamplesAndMarkers(mBuffer, aWriter, aStartTime,
+                                       /* aSinceTime */ 0, aUniqueStacks);
 }
--- a/tools/profiler/core/ProfilerMarkers.cpp
+++ b/tools/profiler/core/ProfilerMarkers.cpp
@@ -24,30 +24,32 @@ ProfilerMarkerPayload::ProfilerMarkerPay
 
 ProfilerMarkerPayload::~ProfilerMarkerPayload()
 {
 }
 
 void
 ProfilerMarkerPayload::streamCommonProps(const char* aMarkerType,
                                          SpliceableJSONWriter& aWriter,
+                                         const TimeStamp& aStartTime,
                                          UniqueStacks& aUniqueStacks)
 {
   MOZ_ASSERT(aMarkerType);
   aWriter.StringProperty("type", aMarkerType);
   if (!mStartTime.IsNull()) {
-    aWriter.DoubleProperty("startTime", profiler_time(mStartTime));
+    aWriter.DoubleProperty("startTime",
+                           (mStartTime - aStartTime).ToMilliseconds());
   }
   if (!mEndTime.IsNull()) {
-    aWriter.DoubleProperty("endTime", profiler_time(mEndTime));
+    aWriter.DoubleProperty("endTime", (mEndTime - aStartTime).ToMilliseconds());
   }
   if (mStack) {
     aWriter.StartObjectProperty("stack");
     {
-      mStack->StreamJSON(aWriter, aUniqueStacks);
+      mStack->StreamJSON(aWriter, aStartTime, aUniqueStacks);
     }
     aWriter.EndObject();
   }
 }
 
 ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData)
   : mCategory(aCategory)
   , mMetaData(aMetaData)
@@ -64,19 +66,20 @@ ProfilerMarkerTracing::ProfilerMarkerTra
 {
   if (aCause) {
     SetStack(mozilla::Move(aCause));
   }
 }
 
 void
 ProfilerMarkerTracing::StreamPayload(SpliceableJSONWriter& aWriter,
+                                     const TimeStamp& aStartTime,
                                      UniqueStacks& aUniqueStacks)
 {
-  streamCommonProps("tracing", aWriter, aUniqueStacks);
+  streamCommonProps("tracing", aWriter, aStartTime, aUniqueStacks);
 
   if (GetCategory()) {
     aWriter.StringProperty("category", GetCategory());
   }
   if (GetMetaData() != TRACING_DEFAULT) {
     if (GetMetaData() == TRACING_INTERVAL_START) {
       aWriter.StringProperty("interval", "start");
     } else if (GetMetaData() == TRACING_INTERVAL_END) {
@@ -95,35 +98,39 @@ GPUMarkerPayload::GPUMarkerPayload(
   , mCpuTimeStart(aCpuTimeStart)
   , mCpuTimeEnd(aCpuTimeEnd)
   , mGpuTimeStart(aGpuTimeStart)
   , mGpuTimeEnd(aGpuTimeEnd)
 { }
 
 void
 GPUMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                const TimeStamp& aStartTime,
                                 UniqueStacks& aUniqueStacks)
 {
-  streamCommonProps("gpu_timer_query", aWriter, aUniqueStacks);
+  streamCommonProps("gpu_timer_query", aWriter, aStartTime, aUniqueStacks);
 
-  aWriter.DoubleProperty("cpustart", profiler_time(mCpuTimeStart));
-  aWriter.DoubleProperty("cpuend", profiler_time(mCpuTimeEnd));
+  aWriter.DoubleProperty("cpustart",
+                         (mCpuTimeStart - aStartTime).ToMilliseconds());
+  aWriter.DoubleProperty("cpuend",
+                         (mCpuTimeEnd - aStartTime).ToMilliseconds());
   aWriter.IntProperty("gpustart", (int)mGpuTimeStart);
   aWriter.IntProperty("gpuend", (int)mGpuTimeEnd);
 }
 
 ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
   : mImg(aImg)
 { }
 
 void
 ProfilerMarkerImagePayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                          const TimeStamp& aStartTime,
                                           UniqueStacks& aUniqueStacks)
 {
-  streamCommonProps("innerHTML", aWriter, aUniqueStacks);
+  streamCommonProps("innerHTML", aWriter, aStartTime, aUniqueStacks);
   // TODO: Finish me
   //aWriter.NameValue("innerHTML", "<img src=''/>");
 }
 
 IOMarkerPayload::IOMarkerPayload(const char* aSource,
                                  const char* aFilename,
                                  const mozilla::TimeStamp& aStartTime,
                                  const mozilla::TimeStamp& aEndTime,
@@ -136,19 +143,21 @@ IOMarkerPayload::IOMarkerPayload(const c
   MOZ_ASSERT(aSource);
 }
 
 IOMarkerPayload::~IOMarkerPayload(){
   free(mFilename);
 }
 
 void
-IOMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
+IOMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                               const TimeStamp& aStartTime,
+                               UniqueStacks& aUniqueStacks)
 {
-  streamCommonProps("io", aWriter, aUniqueStacks);
+  streamCommonProps("io", aWriter, aStartTime, aUniqueStacks);
   aWriter.StringProperty("source", mSource);
   if (mFilename != nullptr) {
     aWriter.StringProperty("filename", mFilename);
   }
 }
 
 DOMEventMarkerPayload::DOMEventMarkerPayload(const nsAString& aType, uint16_t aPhase,
                                              const mozilla::TimeStamp& aStartTime,
@@ -159,19 +168,21 @@ DOMEventMarkerPayload::DOMEventMarkerPay
 {
 }
 
 DOMEventMarkerPayload::~DOMEventMarkerPayload()
 {
 }
 
 void
-DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
+DOMEventMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                     const TimeStamp& aStartTime,
+                                     UniqueStacks& aUniqueStacks)
 {
-  streamCommonProps("DOMEvent", aWriter, aUniqueStacks);
+  streamCommonProps("DOMEvent", aWriter, aStartTime, aUniqueStacks);
   aWriter.StringProperty("type", NS_ConvertUTF16toUTF8(mType).get());
   aWriter.IntProperty("phase", mPhase);
 }
 
 void
 ProfilerJSEventMarker(const char *event)
 {
     PROFILER_MARKER(event);
@@ -182,16 +193,17 @@ LayerTranslationPayload::LayerTranslatio
   : ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
   , mLayer(aLayer)
   , mPoint(aPoint)
 {
 }
 
 void
 LayerTranslationPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                       const TimeStamp& aStartTime,
                                        UniqueStacks& aUniqueStacks)
 {
   const size_t bufferSize = 32;
   char buffer[bufferSize];
   SprintfLiteral(buffer, "%p", mLayer);
 
   aWriter.StringProperty("layer", buffer);
   aWriter.IntProperty("x", mPoint.x);
@@ -201,26 +213,31 @@ LayerTranslationPayload::StreamPayload(S
 
 TouchDataPayload::TouchDataPayload(const mozilla::ScreenIntPoint& aPoint)
   : ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
 {
   mPoint = aPoint;
 }
 
 void
-TouchDataPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
+TouchDataPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                const TimeStamp& aStartTime,
+                                UniqueStacks& aUniqueStacks)
 {
   aWriter.IntProperty("x", mPoint.x);
   aWriter.IntProperty("y", mPoint.y);
 }
 
 VsyncPayload::VsyncPayload(mozilla::TimeStamp aVsyncTimestamp)
   : ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp, nullptr)
   , mVsyncTimestamp(aVsyncTimestamp)
 {
 }
 
 void
-VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
+VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                            const TimeStamp& aStartTime,
+                            UniqueStacks& aUniqueStacks)
 {
-  aWriter.DoubleProperty("vsync", profiler_time(mVsyncTimestamp));
+  aWriter.DoubleProperty("vsync",
+                         (mVsyncTimestamp - aStartTime).ToMilliseconds());
   aWriter.StringProperty("category", "VsyncTimestamp");
 }
--- a/tools/profiler/core/ThreadInfo.cpp
+++ b/tools/profiler/core/ThreadInfo.cpp
@@ -61,26 +61,27 @@ ThreadInfo::CanInvokeJS() const
   bool result;
   mozilla::DebugOnly<nsresult> rv = mThread->GetCanInvokeJS(&result);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   return result;
 }
 
 void
 ThreadInfo::StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
-                       double aSinceTime)
+                       const TimeStamp& aStartTime, double aSinceTime)
 {
   // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
   if (!mUniqueStacks.isSome()) {
     mUniqueStacks.emplace(mPseudoStack->mContext);
   }
 
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
-    StreamSamplesAndMarkers(aBuffer, aWriter, aSinceTime, *mUniqueStacks);
+    StreamSamplesAndMarkers(aBuffer, aWriter, aStartTime, aSinceTime,
+                            *mUniqueStacks);
 
     aWriter.StartObjectProperty("stackTable");
     {
       {
         JSONSchemaWriter schema(aWriter);
         schema.WriteField("prefix");
         schema.WriteField("frame");
       }
@@ -121,16 +122,17 @@ ThreadInfo::StreamJSON(ProfileBuffer* aB
   aWriter.End();
 
   mUniqueStacks.reset();
 }
 
 void
 ThreadInfo::StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
                                     SpliceableJSONWriter& aWriter,
+                                    const TimeStamp& aStartTime,
                                     double aSinceTime,
                                     UniqueStacks& aUniqueStacks)
 {
   aWriter.StringProperty("processType",
                          XRE_ChildProcessTypeToString(XRE_GetProcessType()));
 
   aWriter.StringProperty("name", Name());
   aWriter.IntProperty("tid", static_cast<int>(mThreadId));
@@ -175,26 +177,28 @@ ThreadInfo::StreamSamplesAndMarkers(Prof
 
     aWriter.StartArrayProperty("data");
     {
       if (mSavedStreamedMarkers) {
         MOZ_ASSERT(aSinceTime == 0);
         aWriter.Splice(mSavedStreamedMarkers.get());
         mSavedStreamedMarkers.reset();
       }
-      aBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime,
+      aBuffer->StreamMarkersToJSON(aWriter, mThreadId, aStartTime, aSinceTime,
                                    aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 }
 
 void
-ThreadInfo::FlushSamplesAndMarkers(ProfileBuffer* aBuffer)
+ThreadInfo::FlushSamplesAndMarkers(ProfileBuffer* aBuffer,
+                                   const TimeStamp& aStartTime)
+
 {
   // 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
@@ -214,17 +218,18 @@ ThreadInfo::FlushSamplesAndMarkers(Profi
     b.EndBareList();
     mSavedStreamedSamples = b.WriteFunc()->CopyData();
   }
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
-      aBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
+      aBuffer->StreamMarkersToJSON(b, mThreadId, aStartTime,
+                                   /* aSinceTime = */ 0, *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedMarkers = b.WriteFunc()->CopyData();
   }
 
   // Reset the buffer. Attempting to symbolicate JS samples after mContext has
   // gone away will crash.
   aBuffer->reset();
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -54,30 +54,32 @@ class ThreadInfo {
   // for which SetHasProfile() has been called.
   //
 
 public:
   bool HasProfile() { return mHasProfile; }
 
   mozilla::Mutex& GetMutex();
   void StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
-                  double aSinceTime = 0);
+                  const mozilla::TimeStamp& aStartTime, double aSinceTime);
 
   // Call this method when the JS entries inside the buffer are about to
   // become invalid, i.e., just before JS shutdown.
-  void FlushSamplesAndMarkers(ProfileBuffer* aBuffer);
+  void FlushSamplesAndMarkers(ProfileBuffer* aBuffer,
+                              const mozilla::TimeStamp& aStartTime);
 
   ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
 
   void UpdateThreadResponsiveness() {
     mRespInfo.Update(mIsMainThread, mThread);
   }
 
   void StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
                                SpliceableJSONWriter& aWriter,
+                               const mozilla::TimeStamp& aStartTime,
                                double aSinceTime,
                                UniqueStacks& aUniqueStacks);
 
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1129,17 +1129,17 @@ StreamJSON(SpliceableJSONWriter& aWriter
             continue;
           }
 
           // Note that we intentionally include thread profiles which
           // have been marked for pending delete.
 
           MutexAutoLock lock(info->GetMutex());
 
-          info->StreamJSON(gBuffer, aWriter, aSinceTime);
+          info->StreamJSON(gBuffer, aWriter, gStartTime, aSinceTime);
         }
       }
 
       if (CanNotifyObservers()) {
         // Send a event asking any subprocesses (plugins) to
         // give us their information
         SubprocessClosure closure(&aWriter);
         nsCOMPtr<nsIObserverService> os =
@@ -1203,32 +1203,33 @@ ProfilerMarker::SetGeneration(uint32_t a
 }
 
 double
 ProfilerMarker::GetTime() const {
   return mTime;
 }
 
 void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter,
+                                const TimeStamp& aStartTime,
                                 UniqueStacks& aUniqueStacks) const
 {
   // Schema:
   //   [name, time, data]
 
   aWriter.StartArrayElement();
   {
     aUniqueStacks.mUniqueStrings.WriteElement(aWriter, GetMarkerName());
     aWriter.DoubleElement(mTime);
     // TODO: Store the callsite for this marker if available:
     // if have location data
     //   b.NameValue(marker, "location", ...);
     if (mPayload) {
       aWriter.StartObjectElement();
       {
-          mPayload->StreamPayload(aWriter, aUniqueStacks);
+          mPayload->StreamPayload(aWriter, aStartTime, aUniqueStacks);
       }
       aWriter.EndObject();
     }
   }
   aWriter.EndArray();
 }
 
 // Verbosity control for the profiler.  The aim is to check env var
@@ -2378,30 +2379,23 @@ profiler_js_operation_callback()
   if (!stack) {
     return;
   }
 
   stack->jsOperationCallback();
 }
 
 double
-profiler_time(const mozilla::TimeStamp& aTime)
-{
-  // This function runs both on and off the main thread.
-
-  mozilla::TimeDuration delta = aTime - gStartTime;
-  return delta.ToMilliseconds();
-}
-
-double
 profiler_time()
 {
   // This function runs both on and off the main thread.
 
-  return profiler_time(mozilla::TimeStamp::Now());
+  mozilla::TimeDuration delta =
+    mozilla::TimeStamp::Now() - gStartTime;
+  return delta.ToMilliseconds();
 }
 
 bool
 profiler_in_privacy_mode()
 {
   // This function runs both on and off the main thread.
 
   PseudoStack *stack = tlsPseudoStack.get();
@@ -2593,17 +2587,17 @@ void PseudoStack::flushSamplerOnJSShutdo
       }
 
       // Thread not profiling the context that's going away, skip it.
       if (info->Stack()->mContext != mContext) {
         continue;
       }
 
       MutexAutoLock lock(info->GetMutex());
-      info->FlushSamplesAndMarkers(gBuffer);
+      info->FlushSamplesAndMarkers(gBuffer, gStartTime);
     }
   }
 
   gIsPaused = false;
 }
 
 // We #include these files directly because it means those files can use
 // declarations from this file trivially.
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -277,17 +277,16 @@ PROFILER_FUNC_VOID(profiler_thread_sleep
 PROFILER_FUNC_VOID(profiler_thread_wake())
 PROFILER_FUNC(bool profiler_thread_is_sleeping(), false)
 
 // Call by the JSRuntime's operation callback. This is used to enable
 // profiling on auxilerary threads.
 PROFILER_FUNC_VOID(profiler_js_operation_callback())
 
 PROFILER_FUNC(double profiler_time(), 0)
-PROFILER_FUNC(double profiler_time(const mozilla::TimeStamp& aTime), 0)
 
 PROFILER_FUNC(bool profiler_in_privacy_mode(), false)
 
 PROFILER_FUNC_VOID(profiler_log(const char *str))
 PROFILER_FUNC_VOID(profiler_log(const char *fmt, va_list args))
 
 // End of the functions defined whether the profiler is enabled or not.
 
--- a/tools/profiler/public/ProfilerBacktrace.h
+++ b/tools/profiler/public/ProfilerBacktrace.h
@@ -19,17 +19,19 @@ public:
   ~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);
+  void StreamJSON(SpliceableJSONWriter& aWriter,
+                  const mozilla::TimeStamp& aStartTime,
+                  UniqueStacks& aUniqueStacks);
 
 private:
   ProfilerBacktrace(const ProfilerBacktrace&);
   ProfilerBacktrace& operator=(const ProfilerBacktrace&);
 
   ProfileBuffer* mBuffer;
   ThreadInfo* mThreadInfo;
 };
--- a/tools/profiler/public/ProfilerMarkers.h
+++ b/tools/profiler/public/ProfilerMarkers.h
@@ -46,25 +46,28 @@ public:
    * Called from the main thread
    */
   virtual ~ProfilerMarkerPayload();
 
   /**
    * Called from the main thread
    */
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) = 0;
 
   mozilla::TimeStamp GetStartTime() const { return mStartTime; }
 
 protected:
   /**
    * Called from the main thread
    */
-  void streamCommonProps(const char* aMarkerType, SpliceableJSONWriter& aWriter,
+  void streamCommonProps(const char* aMarkerType,
+                         SpliceableJSONWriter& aWriter,
+                         const mozilla::TimeStamp& aStartTime,
                          UniqueStacks& aUniqueStacks);
 
   void SetStack(UniqueProfilerBacktrace aStack) { mStack = mozilla::Move(aStack); }
 
 private:
   mozilla::TimeStamp  mStartTime;
   mozilla::TimeStamp  mEndTime;
   UniqueProfilerBacktrace  mStack;
@@ -76,60 +79,64 @@ public:
   ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData);
   ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData,
                         UniqueProfilerBacktrace aCause);
 
   const char *GetCategory() const { return mCategory; }
   TracingMetadata GetMetaData() const { return mMetaData; }
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   const char *mCategory;
   TracingMetadata mMetaData;
 };
 
 class ProfilerMarkerImagePayload : public ProfilerMarkerPayload
 {
 public:
   explicit ProfilerMarkerImagePayload(gfxASurface *aImg);
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   RefPtr<gfxASurface> mImg;
 };
 
 class IOMarkerPayload : public ProfilerMarkerPayload
 {
 public:
   IOMarkerPayload(const char* aSource, const char* aFilename, const mozilla::TimeStamp& aStartTime,
                   const mozilla::TimeStamp& aEndTime,
                   UniqueProfilerBacktrace aStack);
   ~IOMarkerPayload();
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   const char* mSource;
   char* mFilename;
 };
 
 class DOMEventMarkerPayload : public ProfilerMarkerPayload
 {
 public:
   DOMEventMarkerPayload(const nsAString& aType, uint16_t aPhase,
                         const mozilla::TimeStamp& aStartTime,
                         const mozilla::TimeStamp& aEndTime);
   ~DOMEventMarkerPayload();
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   nsString mType;
   uint16_t mPhase;
 };
 
 /**
@@ -138,16 +145,17 @@ private:
  */
 class LayerTranslationPayload : public ProfilerMarkerPayload
 {
 public:
   LayerTranslationPayload(mozilla::layers::Layer* aLayer,
                           mozilla::gfx::Point aPoint);
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::layers::Layer* mLayer;
   mozilla::gfx::Point mPoint;
 };
 
 #include "Units.h"    // For ScreenIntPoint
@@ -158,48 +166,51 @@ private:
  */
 class TouchDataPayload : public ProfilerMarkerPayload
 {
 public:
   explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint);
   virtual ~TouchDataPayload() {}
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::ScreenIntPoint mPoint;
 };
 
 /**
  * Tracks when a vsync occurs according to the HardwareComposer.
  */
 class VsyncPayload : public ProfilerMarkerPayload
 {
 public:
   explicit VsyncPayload(mozilla::TimeStamp aVsyncTimestamp);
   virtual ~VsyncPayload() {}
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::TimeStamp mVsyncTimestamp;
 };
 
 class GPUMarkerPayload : public ProfilerMarkerPayload
 {
 public:
   GPUMarkerPayload(const mozilla::TimeStamp& aCpuTimeStart,
                    const mozilla::TimeStamp& aCpuTimeEnd,
                    uint64_t aGpuTimeStart,
                    uint64_t aGpuTimeEnd);
   ~GPUMarkerPayload() {}
 
   virtual void StreamPayload(SpliceableJSONWriter& aWriter,
+                             const mozilla::TimeStamp& aStartTime,
                              UniqueStacks& aUniqueStacks) override;
 
 private:
   mozilla::TimeStamp mCpuTimeStart;
   mozilla::TimeStamp mCpuTimeEnd;
   uint64_t mGpuTimeStart;
   uint64_t mGpuTimeEnd;
 };
--- a/tools/profiler/public/PseudoStack.h
+++ b/tools/profiler/public/PseudoStack.h
@@ -63,16 +63,17 @@ public:
   explicit ProfilerMarker(const char* aMarkerName,
                           ProfilerMarkerPayload* aPayload = nullptr,
                           double aTime = 0);
   ~ProfilerMarker();
 
   const char* GetMarkerName() const { return mMarkerName; }
 
   void StreamJSON(SpliceableJSONWriter& aWriter,
+                  const mozilla::TimeStamp& aStartTime,
                   UniqueStacks& aUniqueStacks) const;
 
   void SetGeneration(uint32_t aGenID);
 
   bool HasExpired(uint32_t aGenID) const { return mGenID + 2 <= aGenID; }
 
   double GetTime() const;