Bug 1299118: Implement Long Tasks API internals (not DOM access to it) r=froyd,mstange
☠☠ backed out by 6a40850883ff ☠ ☠
authorRandell Jesup <rjesup@jesup.org>
Thu, 11 Oct 2018 13:22:55 -0400
changeset 496539 8f7bb583fbb516aac8016b285a622b3c69d619ad
parent 496454 3a34d4624a1a3fb84b8b152085a9717b97c8963c
child 496540 e5adc30bdf7f3d5c10b7160836c477c4abf0f17d
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroyd, mstange
bugs1299118
milestone64.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 1299118: Implement Long Tasks API internals (not DOM access to it) r=froyd,mstange
tools/profiler/core/ProfilerMarkerPayload.cpp
tools/profiler/public/ProfilerMarkerPayload.h
xpcom/threads/LazyIdleThread.cpp
xpcom/threads/nsIThread.idl
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
--- a/tools/profiler/core/ProfilerMarkerPayload.cpp
+++ b/tools/profiler/core/ProfilerMarkerPayload.cpp
@@ -259,8 +259,17 @@ StyleMarkerPayload::StreamPayload(Splice
   StreamCommonProps("Styles", aWriter, aProcessStartTime, aUniqueStacks);
   aWriter.StringProperty("category", "Paint");
   aWriter.IntProperty("elementsTraversed", mStats.mElementsTraversed);
   aWriter.IntProperty("elementsStyled", mStats.mElementsStyled);
   aWriter.IntProperty("elementsMatched", mStats.mElementsMatched);
   aWriter.IntProperty("stylesShared", mStats.mStylesShared);
   aWriter.IntProperty("stylesReused", mStats.mStylesReused);
 }
+
+void
+LongTaskMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
+                                     const TimeStamp& aProcessStartTime,
+                                     UniqueStacks& aUniqueStacks)
+{
+  StreamCommonProps("MainThreadLongTask", aWriter, aProcessStartTime, aUniqueStacks);
+  aWriter.StringProperty("category", "LongTask");
+}
--- a/tools/profiler/public/ProfilerMarkerPayload.h
+++ b/tools/profiler/public/ProfilerMarkerPayload.h
@@ -342,9 +342,21 @@ public:
   }
 
   DECL_STREAM_PAYLOAD
 
 private:
   mozilla::ServoTraversalStatistics mStats;
 };
 
+class LongTaskMarkerPayload : public ProfilerMarkerPayload
+{
+public:
+  LongTaskMarkerPayload(const mozilla::TimeStamp& aStartTime,
+                        const mozilla::TimeStamp& aEndTime)
+    : ProfilerMarkerPayload(aStartTime, aEndTime)
+  {
+  }
+
+  DECL_STREAM_PAYLOAD
+};
+
 #endif // ProfilerMarkerPayload_h
--- a/xpcom/threads/LazyIdleThread.cpp
+++ b/xpcom/threads/LazyIdleThread.cpp
@@ -468,16 +468,26 @@ LazyIdleThread::GetCanInvokeJS(bool* aCa
 
 NS_IMETHODIMP
 LazyIdleThread::SetCanInvokeJS(bool aCanInvokeJS)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+LazyIdleThread::GetLastLongTaskEnd(TimeStamp* _retval) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LazyIdleThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 LazyIdleThread::AsyncShutdown()
 {
   ASSERT_OWNING_THREAD();
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 LazyIdleThread::Shutdown()
--- a/xpcom/threads/nsIThread.idl
+++ b/xpcom/threads/nsIThread.idl
@@ -3,22 +3,26 @@
 /* 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/. */
 
 #include "nsISerialEventTarget.idl"
 
 %{C++
 #include "mozilla/AlreadyAddRefed.h"
+namespace mozilla {
+class TimeStamp;
+}
 %}
 
 [ptr] native PRThread(PRThread);
 
 native nsIEventTargetPtr(nsIEventTarget*);
 native nsISerialEventTargetPtr(nsISerialEventTarget*);
+native TimeStamp(mozilla::TimeStamp);
 
 /**
  * This interface provides a high-level abstraction for an operating system
  * thread.
  *
  * Threads have a built-in event queue, and a thread is an event target that
  * can receive nsIRunnable objects (events) to be processed on the thread.
  *
@@ -148,9 +152,16 @@ interface nsIThread : nsISerialEventTarg
    */
   [noscript,notxpcom] nsIEventTargetPtr EventTarget();
 
   /**
    * A fast C++ getter for the eventTarget. It asserts that the thread's event
    * target is an nsISerialEventTarget and then returns it.
    */
   [noscript,notxpcom] nsISerialEventTargetPtr SerialEventTarget();
+
+  /**
+   * This is set to the end of the last 50+ms event that was executed on
+   * this thread (for MainThread only).  Otherwise returns a null TimeStamp.
+   */
+  [noscript] readonly attribute TimeStamp lastLongTaskEnd;
+  [noscript] readonly attribute TimeStamp lastLongNonIdleTaskEnd;
 };
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -39,16 +39,19 @@
 #include "mozilla/ChaosMode.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsThreadSyncDispatch.h"
 #include "nsServiceManagerUtils.h"
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#include "ProfilerMarkerPayload.h"
+#endif
 #include "InputEventStatistics.h"
 #include "ThreadEventTarget.h"
 #include "ThreadDelay.h"
 
 #ifdef XP_LINUX
 #ifdef __GLIBC__
 #include <gnu/libc-version.h>
 #endif
@@ -645,16 +648,18 @@ nsThread::nsThread(NotNull<SynchronizedE
   , mShutdownRequired(false)
   , mPriority(PRIORITY_NORMAL)
   , mIsMainThread(uint8_t(aMainThread))
   , mCanInvokeJS(false)
   , mCurrentEvent(nullptr)
   , mCurrentEventStart(TimeStamp::Now())
   , mCurrentPerformanceCounter(nullptr)
 {
+  mLastLongTaskEnd = mCurrentEventStart;
+  mLastLongNonIdleTaskEnd = mCurrentEventStart;
 }
 
 
 nsThread::nsThread()
   : mEvents(nullptr)
   , mEventTarget(nullptr)
   , mShutdownContext(nullptr)
   , mScriptObserver(nullptr)
@@ -665,16 +670,18 @@ nsThread::nsThread()
   , mShutdownRequired(false)
   , mPriority(PRIORITY_NORMAL)
   , mIsMainThread(NOT_MAIN_THREAD)
   , mCanInvokeJS(false)
   , mCurrentEvent(nullptr)
   , mCurrentEventStart(TimeStamp::Now())
   , mCurrentPerformanceCounter(nullptr)
 {
+  mLastLongTaskEnd = mCurrentEventStart;
+  mLastLongNonIdleTaskEnd = mCurrentEventStart;
   MOZ_ASSERT(!NS_IsMainThread());
 }
 
 nsThread::~nsThread()
 {
   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
                "shouldn't be waiting on other threads to shutdown");
 
@@ -811,16 +818,28 @@ nsThread::GetCanInvokeJS(bool* aResult)
 NS_IMETHODIMP
 nsThread::SetCanInvokeJS(bool aCanInvokeJS)
 {
   mCanInvokeJS = aCanInvokeJS;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsThread::GetLastLongTaskEnd(TimeStamp* _retval) {
+  *_retval = mLastLongTaskEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThread::GetLastLongNonIdleTaskEnd(TimeStamp* _retval) {
+  *_retval = mLastLongNonIdleTaskEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsThread::AsyncShutdown()
 {
   LOG(("THRD(%p) async shutdown\n", this));
 
   // XXX If we make this warn, then we hit that warning at xpcom shutdown while
   //     shutting down a thread in a thread pool.  That happens b/c the thread
   //     in the thread pool is already shutdown by the thread manager.
   if (!mThread) {
@@ -1212,29 +1231,52 @@ nsThread::ProcessNextEvent(bool aMayWait
       }
 #endif
       Maybe<AutoTimeDurationHelper> timeDurationHelper;
       if (priority == EventPriority::Input) {
         timeDurationHelper.emplace();
       }
 
       // The event starts to run, storing the timestamp.
-      bool recursiveEvent = false;
+      bool recursiveEvent = mNestedEventLoopDepth > mCurrentEventLoopDepth;
+      mCurrentEventLoopDepth = mNestedEventLoopDepth;
+      if (IsMainThread() && !recursiveEvent) {
+        mCurrentEventStart = mozilla::TimeStamp::Now();
+      }
       RefPtr<mozilla::PerformanceCounter> currentPerformanceCounter;
       if (schedulerLoggingEnabled) {
-        recursiveEvent = mNestedEventLoopDepth > mCurrentEventLoopDepth;
         mCurrentEventStart = mozilla::TimeStamp::Now();
         mCurrentEvent = event;
-        mCurrentEventLoopDepth = mNestedEventLoopDepth;
         mCurrentPerformanceCounter = GetPerformanceCounter(event);
         currentPerformanceCounter = mCurrentPerformanceCounter;
       }
 
       event->Run();
 
+      mozilla::TimeDuration duration;
+      // Remember the last 50ms+ task on mainthread for Long Task.
+      if (IsMainThread() && !recursiveEvent) {
+        TimeStamp now = TimeStamp::Now();
+        duration = now - mCurrentEventStart;
+        if (duration.ToMilliseconds() > LONGTASK_BUSY_WINDOW_MS) {
+          // Idle events (gc...) don't *really* count here
+          if (priority != EventPriority::Idle) {
+            mLastLongNonIdleTaskEnd = now;
+          }
+          mLastLongTaskEnd = now;
+#ifdef MOZ_GECKO_PROFILER
+          if (profiler_is_active()) {
+              profiler_add_marker(
+                (priority != EventPriority::Idle) ? "LongTask" : "LongIdleTask",
+                MakeUnique<LongTaskMarkerPayload>(mCurrentEventStart, now));
+          }
+#endif
+        }
+      }
+
       // End of execution, we can send the duration for the group
       if (schedulerLoggingEnabled) {
        if (recursiveEvent) {
           // If we're in a recursive call, reset the timer,
           // so the parent gets its remaining execution time right.
           mCurrentEventStart = mozilla::TimeStamp::Now();
           mCurrentPerformanceCounter = currentPerformanceCounter;
         } else {
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -30,16 +30,19 @@ namespace mozilla {
 class CycleCollectedJSContext;
 class ThreadEventTarget;
 }
 
 using mozilla::NotNull;
 
 class nsThreadEnumerator;
 
+// See https://www.w3.org/TR/longtasks
+#define LONGTASK_BUSY_WINDOW_MS 50
+
 // A native thread
 class nsThread
   : public nsIThreadInternal
   , public nsISupportsPriority
   , private mozilla::LinkedListElement<nsThread>
 {
   friend mozilla::LinkedList<nsThread>;
   friend mozilla::LinkedListElement<nsThread>;
@@ -147,16 +150,19 @@ public:
   size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   size_t SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   static nsThreadEnumerator Enumerate();
 
   static uint32_t MaxActiveThreads();
 
+  const mozilla::TimeStamp& LastLongTaskEnd() { return mLastLongTaskEnd; }
+  const mozilla::TimeStamp& LastLongNonIdleTaskEnd() { return mLastLongNonIdleTaskEnd; }
+
 private:
   void DoMainThreadSpecificProcessing(bool aReallyWait);
 
 protected:
   friend class nsThreadShutdownEvent;
 
   friend class nsThreadEnumerator;
 
@@ -208,16 +214,19 @@ protected:
   PRThread* mThread;
   void*     mStackBase = nullptr;
   uint32_t  mStackSize;
   uint32_t  mThreadId;
 
   uint32_t  mNestedEventLoopDepth;
   uint32_t  mCurrentEventLoopDepth;
 
+  mozilla::TimeStamp mLastLongTaskEnd;
+  mozilla::TimeStamp mLastLongNonIdleTaskEnd;
+
   mozilla::Atomic<bool> mShutdownRequired;
 
   int8_t   mPriority;
 
   uint8_t  mIsMainThread;
 
   bool IsMainThread() const
   {