Bug 1429904 - Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext. r?njn draft
authorMarkus Stange <mstange@themasta.com>
Sat, 17 Feb 2018 15:03:30 -0500
changeset 762106 6b145704aed3deaafa8460c6f4067f6547613f4d
parent 762105 77f286312bdedb30404b79a03e7a2305d60988f5
child 762107 a9da8a4acb143bb5821af85b0de2bc1bbf41dd49
push id101088
push userbmo:mstange@themasta.com
push dateThu, 01 Mar 2018 20:52:26 +0000
reviewersnjn
bugs1429904
milestone60.0a1
Bug 1429904 - Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext. r?njn This also renames FlushSamplesAndMarkers to NotifyAboutToLoseJSContext. MozReview-Commit-ID: FWinMi85yDZ
tools/profiler/core/ProfiledThreadData.cpp
tools/profiler/core/ProfiledThreadData.h
tools/profiler/core/platform.cpp
--- a/tools/profiler/core/ProfiledThreadData.cpp
+++ b/tools/profiler/core/ProfiledThreadData.cpp
@@ -187,19 +187,19 @@ StreamSamplesAndMarkers(const char* aNam
                                   aSinceTime, aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 }
 
 void
-ProfiledThreadData::FlushSamplesAndMarkers(JSContext* aCx,
-                                           const TimeStamp& aProcessStartTime,
-                                           ProfileBuffer& aBuffer)
+ProfiledThreadData::NotifyAboutToLoseJSContext(JSContext* aCx,
+                                               const TimeStamp& aProcessStartTime,
+                                               ProfileBuffer& aBuffer)
 {
   // This function is used to serialize the current buffer just before
   // JSContext destruction.
   MOZ_ASSERT(aCx);
 
   // Unlike StreamJSObject, do not surround the samples in brackets by calling
   // aWriter.{Start,End}BareList. The result string will be a comma-separated
   // list of JSON object literals that will prepended by StreamJSObject into
@@ -274,12 +274,14 @@ ProfiledThreadData::FlushSamplesAndMarke
     if (haveMarkers) {
       markersJSON = b.WriteFunc()->CopyData();
     }
   }
 
   mPartialProfile = MakeUnique<PartialThreadProfile>(
     Move(samplesJSON), Move(markersJSON), Move(uniqueStacks));
 
+  mBufferPositionWhenReceivedJSContext = Nothing();
+
   // Reset the buffer. Attempting to symbolicate JS samples after mContext has
   // gone away will crash.
   aBuffer.Reset();
 }
--- a/tools/profiler/core/ProfiledThreadData.h
+++ b/tools/profiler/core/ProfiledThreadData.h
@@ -61,44 +61,51 @@ class ProfiledThreadData final
 public:
   ProfiledThreadData(ThreadInfo* aThreadInfo, nsIEventTarget* aEventTarget);
   ~ProfiledThreadData();
 
   void NotifyUnregistered(uint64_t aBufferPosition)
   {
     mResponsiveness.reset();
     mLastSample = mozilla::Nothing();
+    MOZ_ASSERT(!mBufferPositionWhenReceivedJSContext,
+               "JSContext should have been cleared before the thread was unregistered");
     mUnregisterTime = TimeStamp::Now();
     mBufferPositionWhenUnregistered = mozilla::Some(aBufferPosition);
   }
   mozilla::Maybe<uint64_t> BufferPositionWhenUnregistered() { return mBufferPositionWhenUnregistered; }
 
   mozilla::Maybe<uint64_t>& LastSample() { return mLastSample; }
 
   void StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
                   SpliceableJSONWriter& aWriter,
                   const mozilla::TimeStamp& aProcessStartTime,
                   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(JSContext* aCx,
-                              const mozilla::TimeStamp& aProcessStartTime,
-                              ProfileBuffer& aBuffer);
-
   // Returns nullptr if this is not the main thread or if this thread is not
   // being profiled.
   ThreadResponsiveness* GetThreadResponsiveness()
   {
     ThreadResponsiveness* responsiveness = mResponsiveness.ptrOr(nullptr);
     return responsiveness;
   }
 
   const RefPtr<ThreadInfo> Info() const { return mThreadInfo; }
 
+  void NotifyReceivedJSContext(uint64_t aCurrentBufferPosition)
+  {
+    mBufferPositionWhenReceivedJSContext = mozilla::Some(aCurrentBufferPosition);
+  }
+
+  // Call this method when the JS entries inside the buffer are about to
+  // become invalid, i.e., just before JS shutdown.
+  void NotifyAboutToLoseJSContext(JSContext* aCx,
+                                  const TimeStamp& aProcessStartTime,
+                                  ProfileBuffer& aBuffer);
+
 private:
   // Group A:
   // The following fields are interesting for the entire lifetime of a
   // ProfiledThreadData object.
 
   // This thread's thread info.
   const RefPtr<ThreadInfo> mThreadInfo;
 
@@ -116,16 +123,19 @@ private:
   // information about their event loop.
   mozilla::Maybe<ThreadResponsiveness> mResponsiveness;
 
   // When sampling, this holds the position in ActivePS::mBuffer of the most
   // recent sample for this thread, or Nothing() if there is no sample for this
   // thread in the buffer.
   mozilla::Maybe<uint64_t> mLastSample;
 
+  // Only non-Nothing() if the thread currently has a JSContext.
+  mozilla::Maybe<uint64_t> mBufferPositionWhenReceivedJSContext;
+
   // Group C:
   // The following fields are only used once this thread has been unregistered.
 
   mozilla::Maybe<uint64_t> mBufferPositionWhenUnregistered;
   mozilla::TimeStamp mUnregisterTime;
 };
 
 void
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -565,21 +565,25 @@ public:
       LiveProfiledThreadData& thread = sInstance->mLiveProfiledThreads[i];
       if (thread.mRegisteredThread == aRegisteredThread) {
         return thread.mProfiledThreadData.get();
       }
     }
     return nullptr;
   }
 
-  static void AddLiveProfiledThread(PSLockRef, RegisteredThread* aRegisteredThread,
-                                    UniquePtr<ProfiledThreadData>&& aProfiledThreadData)
+  static ProfiledThreadData*
+  AddLiveProfiledThread(PSLockRef, RegisteredThread* aRegisteredThread,
+                        UniquePtr<ProfiledThreadData>&& aProfiledThreadData)
   {
     sInstance->mLiveProfiledThreads.AppendElement(
       LiveProfiledThreadData{ aRegisteredThread, Move(aProfiledThreadData) });
+
+    // Return a weak pointer to the ProfiledThreadData object.
+    return sInstance->mLiveProfiledThreads.LastElement().mProfiledThreadData.get();
   }
 
   static void UnregisterThread(PSLockRef aLockRef, RegisteredThread* aRegisteredThread)
   {
     DiscardExpiredDeadProfiledThreads(aLockRef);
 
     // Find the right entry in the mLiveProfiledThreads array and remove the
     // element, moving the ProfiledThreadData object for the thread into the
@@ -2237,24 +2241,28 @@ locked_register_thread(PSLockRef aLock, 
     MakeUnique<RegisteredThread>(info, NS_GetCurrentThreadNoCreate(),
                                  aStackTop);
 
   TLSRegisteredThread::SetRegisteredThread(aLock, registeredThread.get());
 
   if (ActivePS::Exists(aLock) &&
       ActivePS::ShouldProfileThread(aLock, info)) {
     nsCOMPtr<nsIEventTarget> eventTarget = registeredThread->GetEventTarget();
-    ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
-      MakeUnique<ProfiledThreadData>(info, eventTarget));
+    ProfiledThreadData* profiledThreadData =
+      ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
+        MakeUnique<ProfiledThreadData>(info, eventTarget));
 
     if (ActivePS::FeatureJS(aLock)) {
       // This StartJSSampling() call is on-thread, so we can poll manually to
       // start JS sampling immediately.
       registeredThread->StartJSSampling();
       registeredThread->PollJSSampling();
+      if (registeredThread->GetJSContext()) {
+        profiledThreadData->NotifyReceivedJSContext(ActivePS::Buffer(aLock).mRangeEnd);
+      }
     }
   }
 
   CorePS::AppendRegisteredThread(aLock, Move(registeredThread));
 }
 
 static void
 NotifyObservers(const char* aTopic, nsISupports* aSubject = nullptr)
@@ -2835,32 +2843,36 @@ locked_profiler_start(PSLockRef aLock, u
   int tid = Thread::GetCurrentId();
   const nsTArray<UniquePtr<RegisteredThread>>& registeredThreads =
     CorePS::RegisteredThreads(aLock);
   for (auto& registeredThread : registeredThreads) {
     RefPtr<ThreadInfo> info = registeredThread->Info();
 
     if (ActivePS::ShouldProfileThread(aLock, info)) {
       nsCOMPtr<nsIEventTarget> eventTarget = registeredThread->GetEventTarget();
-      ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
-        MakeUnique<ProfiledThreadData>(info, eventTarget));
+      ProfiledThreadData* profiledThreadData =
+        ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
+          MakeUnique<ProfiledThreadData>(info, eventTarget));
       if (ActivePS::FeatureJS(aLock)) {
         registeredThread->StartJSSampling();
         if (info->ThreadId() == tid) {
           // We can manually poll the current thread so it starts sampling
           // immediately.
           registeredThread->PollJSSampling();
         } else if (info->IsMainThread()) {
           // Dispatch a runnable to the main thread to call PollJSSampling(),
           // so that we don't have wait for the next JS interrupt callback in
           // order to start profiling JS.
           TriggerPollJSSamplingOnMainThread();
         }
       }
       registeredThread->RacyRegisteredThread().ReinitializeOnResume();
+      if (registeredThread->GetJSContext()) {
+        profiledThreadData->NotifyReceivedJSContext(0);
+      }
     }
   }
 
 #ifdef MOZ_TASK_TRACER
   if (ActivePS::FeatureTaskTracer(aLock)) {
     tasktracer::StartLogging();
   }
 #endif
@@ -3417,16 +3429,24 @@ profiler_set_js_context(JSContext* aCx)
     return;
   }
 
   registeredThread->SetJSContext(aCx);
 
   // This call is on-thread, so we can call PollJSSampling() to start JS
   // sampling immediately.
   registeredThread->PollJSSampling();
+
+  if (ActivePS::Exists(lock)) {
+    ProfiledThreadData* profiledThreadData =
+      ActivePS::GetProfiledThreadData(lock, registeredThread);
+    if (profiledThreadData) {
+      profiledThreadData->NotifyReceivedJSContext(ActivePS::Buffer(lock).mRangeEnd);
+    }
+  }
 }
 
 void
 profiler_clear_js_context()
 {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   PSAutoLock lock(gPSMutex);
@@ -3437,42 +3457,36 @@ profiler_clear_js_context()
     return;
   }
 
   JSContext* cx = registeredThread->GetJSContext();
   if (!cx) {
     return;
   }
 
-  // On JS shut down, flush the current buffer as stringifying JIT samples
-  // requires a live JSContext.
-
-  if (ActivePS::Exists(lock)) {
-    // Flush this thread's profile data, if it is being profiled.
+  if (ActivePS::Exists(lock) && ActivePS::FeatureJS(lock)) {
     ProfiledThreadData* profiledThreadData =
       ActivePS::GetProfiledThreadData(lock, registeredThread);
     if (profiledThreadData) {
-      profiledThreadData->FlushSamplesAndMarkers(cx,
-                                                 CorePS::ProcessStartTime(),
-                                                 ActivePS::Buffer(lock));
-
-      if (ActivePS::FeatureJS(lock)) {
-        // Notify the JS context that profiling for this context has stopped.
-        // Do this by calling StopJSSampling and PollJSSampling before
-        // nulling out the JSContext.
-        registeredThread->StopJSSampling();
-        registeredThread->PollJSSampling();
-
-        registeredThread->ClearJSContext();
-
-        // Tell the thread that we'd like to have JS sampling on this
-        // thread again, once it gets a new JSContext (if ever).
-        registeredThread->StartJSSampling();
-        return;
-      }
+      profiledThreadData->NotifyAboutToLoseJSContext(cx,
+                                                     CorePS::ProcessStartTime(),
+                                                     ActivePS::Buffer(lock));
+
+      // Notify the JS context that profiling for this context has stopped.
+      // Do this by calling StopJSSampling and PollJSSampling before
+      // nulling out the JSContext.
+      registeredThread->StopJSSampling();
+      registeredThread->PollJSSampling();
+
+      registeredThread->ClearJSContext();
+
+      // Tell the thread that we'd like to have JS sampling on this
+      // thread again, once it gets a new JSContext (if ever).
+      registeredThread->StartJSSampling();
+      return;
     }
   }
 
   registeredThread->ClearJSContext();
 }
 
 int
 profiler_current_thread_id()