Bug 1648284 - Record the number of pending critical input events r=smaug
authorsefeng <sefeng@mozilla.com>
Fri, 26 Jun 2020 19:08:53 +0000
changeset 537645 b6a6c9c513daf47679b51b2d9ca559fb62f35c68
parent 537644 5804ed97ca7cff73b078e16181372bc51a41948a
child 537646 8ce482845fc037caee3a8498f4bf4aab6245a8be
push id37545
push usernerli@mozilla.com
push dateSat, 27 Jun 2020 09:38:32 +0000
treeherdermozilla-central@0a4b3f99d2d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1648284, 1644284
milestone79.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 1648284 - Record the number of pending critical input events r=smaug Record the number of pending critical input events that are in the IPC Channel when we about to run the timeout handler. This telemetry is mainly used for bug 1644284. We'd like to tell how often do we run timeout handlers when there are a lot of pending input events. Once we know the data, we can use it to do further setTimeout improvements. Differential Revision: https://phabricator.services.mozilla.com/D81340
dom/base/TimeoutManager.cpp
dom/base/TimeoutManager.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
toolkit/components/telemetry/Histograms.json
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPrefs_privacy.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "mozilla/TimeStamp.h"
 #include "nsINamed.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/PopupBlocker.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "TimeoutExecutor.h"
 #include "TimeoutBudgetManager.h"
 #include "mozilla/net/WebSocketEventService.h"
 #include "mozilla/MediaManager.h"
 #ifdef MOZ_GECKO_PROFILER
 #  include "ProfilerMarkerPayload.h"
 #endif
@@ -390,16 +391,37 @@ void TimeoutManager::UpdateBudget(const 
     // timeouts being executed, even though budget throttling isn't
     // active at the moment.
     mExecutionBudget = GetMaxBudget(isBackground);
   }
 
   mLastBudgetUpdate = aNow;
 }
 
+size_t TimeoutManager::GetNumPendingInputs() {
+  ContentChild* contentChild = ContentChild::GetSingleton();
+  mozilla::ipc::MessageChannel* channel =
+      contentChild ? contentChild->GetIPCChannel() : nullptr;
+
+  if (channel) {
+    size_t count = 0;
+    channel->PeekMessages([&count](const IPC::Message& aMsg) -> bool {
+      if (nsContentUtils::IsMessageCriticalInputEvent(aMsg)) {
+        // The max number we can record in the telemetry is 80,
+        // so we don't need to continue the counting.
+        if (++count > 80) {
+          return false;
+        }
+      }
+      return true;
+    });
+    return count;
+  }
+  return 0;
+}
 // The longest interval (as PRIntervalTime) we permit, or that our
 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
 // nsTimerImpl.h for details.
 #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT
 
 uint32_t TimeoutManager::sNestingLevel = 0;
 
 TimeoutManager::TimeoutManager(nsGlobalWindowInner& aWindow,
@@ -880,16 +902,18 @@ void TimeoutManager::RunTimeout(const Ti
                    timeout->mIsInterval ? "Interval" : "Timeout", this,
                    timeout.get(), timeout->mFiringId, timeout->mFiringIndex,
                    mFiringIndex));
         }
         MOZ_ASSERT(timeout->mFiringIndex > mLastFiringIndex);
         mLastFiringIndex = timeout->mFiringIndex;
 #endif
         // This timeout is good to run.
+        Telemetry::Accumulate(Telemetry::PENDING_CRITICAL_INPUT_WHEN_TIMEOUT,
+                              GetNumPendingInputs());
         bool timeout_was_cleared = window->RunTimeoutHandler(timeout, scx);
 #if MOZ_GECKO_PROFILER
         if (profiler_can_accept_markers()) {
           TimeDuration elapsed = now - timeout->SubmitTime();
           TimeDuration target = timeout->When() - timeout->SubmitTime();
           TimeDuration delta = now - timeout->When();
           TimeDuration runtime = TimeStamp::Now() - now;
           nsPrintfCString marker(
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -98,16 +98,18 @@ class TimeoutManager final {
   nsIEventTarget* EventTarget();
 
   bool BudgetThrottlingEnabled(bool aIsBackground) const;
 
   static const uint32_t InvalidFiringId;
 
   void SetLoading(bool value);
 
+  size_t GetNumPendingInputs();
+
  private:
   void MaybeStartThrottleTimeout();
 
   // Return true if |aTimeout| needs to be reinserted into the timeout list.
   bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout,
                          const TimeStamp& aLastCallbackTime,
                          const TimeStamp& aCurrentNow);
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9773,16 +9773,31 @@ bool nsContentUtils::IsMessageInputEvent
       case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
       case mozilla::dom::PBrowser::Msg_SetDocShellIsActive__ID:
         return true;
     }
   }
   return false;
 }
 
+/* static */
+bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
+  if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
+      mozilla::dom::PBrowser::PBrowserStart) {
+    switch (aMsg.type()) {
+      case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
+      case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
+        return true;
+    }
+  }
+  return false;
+}
+
 static const char* kUserInteractionInactive = "user-interaction-inactive";
 static const char* kUserInteractionActive = "user-interaction-active";
 
 void nsContentUtils::UserInteractionObserver::Init() {
   // Listen for the observer messages from EventStateManager which are telling
   // us whether or not the user is interacting.
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(this, kUserInteractionInactive, false);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3196,16 +3196,23 @@ class nsContentUtils {
 
   /**
    * Returns true if the passed-in mesasge is a pending InputEvent.
    *
    * @param aMsg  The message to check
    */
   static bool IsMessageInputEvent(const IPC::Message& aMsg);
 
+  /**
+   * Returns true if the passed-in message is a critical InputEvent.
+   *
+   * @param aMsg  The message to check
+   */
+  static bool IsMessageCriticalInputEvent(const IPC::Message& aMsg);
+
   static void AsyncPrecreateStringBundles();
 
   static bool ContentIsLink(nsIContent* aContent);
 
   static already_AddRefed<mozilla::dom::ContentFrameMessageManager>
   TryGetBrowserChildGlobal(nsISupports* aFrom);
 
   // Get a serial number for a newly created inner or outer window.
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1748,16 +1748,27 @@
     "kind": "exponential",
     "releaseChannelCollection": "opt-out",
     "low": 1,
     "high": 20000,
     "n_buckets": 100,
     "description": "Time between receiving a scroll event on the event loop and compositing its result onto the screen (ms)",
     "bug_numbers": [1500465, 1604818]
   },
+  "PENDING_CRITICAL_INPUT_WHEN_TIMEOUT": {
+    "record_in_processes": ["content"],
+    "products": ["firefox"],
+    "alert_emails": ["perfteam@mozilla.com", "seanfeng@mozilla.com"],
+    "expires_in_version": "84",
+    "kind": "enumerated",
+    "n_values": 80,
+    "releaseChannelCollection": "opt-out",
+    "description": "Measures the number of pending critical input events in the IPC channel when the timeout handler is about to run",
+    "bug_numbers": [1648284]
+  },
   "CANVAS_2D_USED": {
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "2D canvas used"
   },
   "CANVAS_WEBGL_ACCL_FAILURE_ID": {