Bug 1393046 - Enable JS sampling on the main thread as soon as possible after the profiler has started. r=njn
authorMarkus Stange <mstange@themasta.com>
Tue, 29 Aug 2017 19:13:57 +0200
changeset 377936 8768af9e5e42
parent 377935 e4b8ea7baeb6
child 377937 cd645c43199c
push id50117
push usermstange@themasta.com
push date2017-08-31 09:33 +0000
treeherderautoland@8768af9e5e42 [default view] [failures only]
reviewersnjn
bugs1393046
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1393046 - Enable JS sampling on the main thread as soon as possible after the profiler has started. r=njn MozReview-Commit-ID: CIj3GGI3TQo
tools/profiler/core/platform.cpp
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -2635,16 +2635,56 @@ profiler_get_buffer_info_helper(uint32_t
   }
 
   *aCurrentPosition = ActivePS::Buffer(lock).mWritePos;
   *aEntries = ActivePS::Entries(lock);
   *aGeneration = ActivePS::Buffer(lock).mGeneration;
 }
 
 static void
+PollJSSamplingForCurrentThread()
+{
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  PSAutoLock lock(gPSMutex);
+
+  ThreadInfo* info = TLSInfo::Info(lock);
+  if (!info) {
+    return;
+  }
+
+  info->PollJSSampling();
+}
+
+// When the profiler is started on a background thread, we can't synchronously
+// call PollJSSampling on the main thread's ThreadInfo. And the next regular
+// call to PollJSSampling on the main thread would only happen once the main
+// thread triggers a JS interrupt callback.
+// This means that all the JS execution between profiler_start() and the first
+// JS interrupt would happen with JS sampling disabled, and we wouldn't get any
+// JS function information for that period of time.
+// So in order to start JS sampling as soon as possible, we dispatch a runnable
+// to the main thread which manually calls PollJSSamplingForCurrentThread().
+// In some cases this runnable will lose the race with the next JS interrupt.
+// That's fine; PollJSSamplingForCurrentThread() is immune to redundant calls.
+static void
+TriggerPollJSSamplingOnMainThread()
+{
+  nsCOMPtr<nsIThread> mainThread;
+  nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+  if (NS_SUCCEEDED(rv) && mainThread) {
+    nsCOMPtr<nsIRunnable> task =
+      NS_NewRunnableFunction("TriggerPollJSSamplingOnMainThread", []() {
+        PollJSSamplingForCurrentThread();
+      });
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
+  }
+}
+
+static void
 locked_profiler_start(PSLockRef aLock, int aEntries, double aInterval,
                       uint32_t aFeatures,
                       const char** aFilters, uint32_t aFilterCount)
 {
   if (LOG_TEST) {
     LOG("locked_profiler_start");
     LOG("- entries  = %d", aEntries);
     LOG("- interval = %.2f", aInterval);
@@ -2684,16 +2724,21 @@ locked_profiler_start(PSLockRef aLock, i
     if (ActivePS::ShouldProfileThread(aLock, info)) {
       info->StartProfiling();
       if (ActivePS::FeatureJS(aLock)) {
         info->StartJSSampling();
         if (info->ThreadId() == tid) {
           // We can manually poll the current thread so it starts sampling
           // immediately.
           info->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();
         }
       }
     }
   }
 
   // Dead ThreadInfos are deleted in profiler_stop(), and dead ThreadInfos
   // aren't saved when the profiler is inactive. Therefore the dead threads
   // vector should be empty here.
@@ -3071,27 +3116,17 @@ profiler_thread_is_sleeping()
   }
   return racyInfo->IsSleeping();
 }
 
 void
 profiler_js_interrupt_callback()
 {
   // This function runs on JS threads being sampled.
-
-  MOZ_RELEASE_ASSERT(CorePS::Exists());
-
-  PSAutoLock lock(gPSMutex);
-
-  ThreadInfo* info = TLSInfo::Info(lock);
-  if (!info) {
-    return;
-  }
-
-  info->PollJSSampling();
+  PollJSSamplingForCurrentThread();
 }
 
 double
 profiler_time()
 {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
   TimeDuration delta = TimeStamp::Now() - CorePS::ProcessStartTime();