Bug 1515214 - Add markers with cause stacks and the name of the called function for setTimeout callback execution. r=jesup
authorMarkus Stange <mstange@themasta.com>
Wed, 10 Jul 2019 20:48:14 +0000
changeset 482263 442fa46b63e92bd597002f267ee4ec951dd86059
parent 482262 835a18c6213d447af34dfa1fa19bcb8ae6dade86
child 482264 d0cd86566ca73cc44f0a2ac30d13c0e15eeb33bf
push id36272
push usercsabou@mozilla.com
push dateThu, 11 Jul 2019 04:03:34 +0000
treeherdermozilla-central@925e5936677c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1515214
milestone70.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 1515214 - Add markers with cause stacks and the name of the called function for setTimeout callback execution. r=jesup These duplicate the existing setTimeout markers a bit. Differential Revision: https://phabricator.services.mozilla.com/D19194
dom/base/Timeout.cpp
dom/base/Timeout.h
dom/base/nsGlobalWindowInner.cpp
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -44,16 +44,23 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Timeout, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Timeout, Release)
 
 void Timeout::SetWhenOrTimeRemaining(const TimeStamp& aBaseTime,
                                      const TimeDuration& aDelay) {
   MOZ_DIAGNOSTIC_ASSERT(mWindow);
   mSubmitTime = aBaseTime;
 
+  mSubmitTime = aBaseTime;
+#ifdef MOZ_GECKO_PROFILER
+  if (profiler_is_active()) {
+    mCause = profiler_get_backtrace();
+  }
+#endif
+
   // If we are frozen simply set mTimeRemaining to be the "time remaining" in
   // the timeout (i.e., the interval itself).  This will be used to create a
   // new mWhen time when the window is thawed.  The end effect is that time does
   // not appear to pass for frozen windows.
   if (mWindow->IsFrozen()) {
     mWhen = TimeStamp();
     mTimeRemaining = aDelay;
     return;
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_timeout_h
 
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "GeckoProfiler.h"
 
 class nsIEventTarget;
 class nsIPrincipal;
 class nsIEventTarget;
 class nsGlobalWindowInner;
 
 namespace mozilla {
 namespace dom {
@@ -45,16 +46,20 @@ class Timeout final : public LinkedListE
   // Can only be called when not frozen.
   const TimeStamp& When() const;
 
   const TimeStamp& SubmitTime() const;
 
   // Can only be called when frozen.
   const TimeDuration& TimeRemaining() const;
 
+#ifdef MOZ_GECKO_PROFILER
+  UniqueProfilerBacktrace TakeProfilerBacktrace() { return std::move(mCause); }
+#endif
+
  private:
   // mWhen and mTimeRemaining can't be in a union, sadly, because they
   // have constructors.
   // Nominal time to run this timeout.  Use only when timeouts are not
   // frozen.
   TimeStamp mWhen;
 
   // Remaining time to wait.  Used only when timeouts are frozen.
@@ -77,16 +82,20 @@ class Timeout final : public LinkedListE
   RefPtr<nsGlobalWindowInner> mWindow;
 
   // The language-specific information about the callback.
   RefPtr<TimeoutHandler> mScriptHandler;
 
   // Interval
   TimeDuration mInterval;
 
+#ifdef MOZ_GECKO_PROFILER
+  UniqueProfilerBacktrace mCause;
+#endif
+
   // Returned as value of setTimeout()
   uint32_t mTimeoutId;
 
   // Identifies which firing level this Timeout is being processed in
   // when sync loops trigger nested firing.
   uint32_t mFiringId;
 
 #ifdef DEBUG
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -5849,16 +5849,31 @@ int32_t nsGlobalWindowInner::SetTimeoutO
 
   int32_t result;
   aError =
       mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
                                   Timeout::Reason::eTimeoutOrInterval, &result);
   return result;
 }
 
+static const char* GetTimeoutReasonString(Timeout* aTimeout) {
+  switch (aTimeout->mReason) {
+    case Timeout::Reason::eTimeoutOrInterval:
+      if (aTimeout->mIsInterval) {
+        return "setInterval handler";
+      }
+      return "setTimeout handler";
+    case Timeout::Reason::eIdleCallbackTimeout:
+      return "setIdleCallback handler (timed out)";
+    default:
+      MOZ_CRASH("Unexpected enum value");
+      return "";
+  }
+}
+
 bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
                                             nsIScriptContext* aScx) {
   // Hold on to the timeout in case mExpr or mFunObj releases its
   // doc.
   // XXXbz Our caller guarantees it'll hold on to the timeout (because
   // we're MOZ_CAN_RUN_SCRIPT), so we can probably stop doing that...
   RefPtr<Timeout> timeout = aTimeout;
   Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
@@ -5876,22 +5891,35 @@ bool nsGlobalWindowInner::RunTimeoutHand
 
   bool trackNestingLevel = !timeout->mIsInterval;
   uint32_t nestingLevel;
   if (trackNestingLevel) {
     nestingLevel = TimeoutManager::GetNestingLevel();
     TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
   }
 
-  const char* reason;
-  if (timeout->mIsInterval) {
-    reason = "setInterval handler";
-  } else {
-    reason = "setTimeout handler";
-  }
+  const char* reason = GetTimeoutReasonString(timeout);
+
+#ifdef MOZ_GECKO_PROFILER
+  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+  nsCString str;
+  if (profiler_is_active()) {
+    TimeDuration originalInterval = timeout->When() - timeout->SubmitTime();
+    str.Append(reason);
+    str.Append(" with interval ");
+    str.AppendInt(int(originalInterval.ToMilliseconds()));
+    str.Append("ms: ");
+    nsCString handlerDescription;
+    timeout->mScriptHandler->GetDescription(handlerDescription);
+    str.Append(handlerDescription);
+  }
+  AUTO_PROFILER_TEXT_MARKER_DOCSHELL_CAUSE("setTimeout callback", str, JS,
+                                           docShell,
+                                           timeout->TakeProfilerBacktrace());
+#endif
 
   bool abortIntervalHandler;
   {
     RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
 
     CallbackDebuggerNotificationGuard guard(
         this, timeout->mIsInterval
                   ? DebuggerNotificationType::SetIntervalCallback