Bug 1202706 - Part 2: Add support for recording and reporting use counters for workers; r=bzbarsky
authorEdgar Chen <echen@mozilla.com>
Wed, 27 Nov 2019 23:22:27 +0000
changeset 505565 9eeb03a503fb86df387049e878c0c5dd76e0387c
parent 505564 4989bb2f4f69eae11fa05cc9386edf3d65ccb0c6
child 505566 95b8b6b94213f4549642f0137d6e3ff0129ef68a
push id102347
push userechen@mozilla.com
push dateWed, 04 Dec 2019 23:23:46 +0000
treeherderautoland@2dfc53add3de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1202706
milestone73.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 1202706 - Part 2: Add support for recording and reporting use counters for workers; r=bzbarsky Differential Revision: https://phabricator.services.mozilla.com/D53662
dom/base/UseCounter.h
dom/base/moz.build
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/base/UseCounter.h
+++ b/dom/base/UseCounter.h
@@ -54,11 +54,24 @@ enum UseCounter : int16_t {
 #define COUNTED_UNKNOWN_PROPERTY(name_, method_) \
   eUseCounter_unknown_property_##method_,
 #include "mozilla/CountedUnknownProperties.h"
 #undef COUNTED_UNKNOWN_PROPERTY
 
   eUseCounter_Count
 };
 
+enum class UseCounterWorker : int16_t {
+  Unknown = -1,
+#define USE_COUNTER_DOM_METHOD(interface_, name_) interface_##_##name_,
+#define USE_COUNTER_DOM_ATTRIBUTE(interface_, name_) \
+  interface_##_##name_##_getter, interface_##_##name_##_setter,
+#define USE_COUNTER_CUSTOM(name_, desc_) Custom_##name_,
+#include "mozilla/dom/UseCounterWorkerList.h"
+#undef USE_COUNTER_DOM_METHOD
+#undef USE_COUNTER_DOM_ATTRIBUTE
+#undef USE_COUNTER_CUSTOM
+  Count
+};
+
 }  // namespace mozilla
 
 #endif
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -131,16 +131,17 @@ EXPORTS.mozilla += [
     'ScriptableContentIterator.h',
     'SelectionChangeEventDispatcher.h',
     'TextInputProcessor.h',
     'UseCounter.h',
 ]
 
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
+    '!UseCounterWorkerList.h',
     'AbstractRange.h',
     'AnonymousContent.h',
     'Attr.h',
     'BarProps.h',
     'BindContext.h',
     'BodyConsumer.h',
     'BodyStream.h',
     'BodyUtil.h',
@@ -538,10 +539,13 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser'
     DEFINES['HAVE_SIDEBAR'] = True
 
 if CONFIG['MOZ_X11']:
     CXXFLAGS += CONFIG['TK_CFLAGS']
 
 GeneratedFile('UseCounterList.h', script='gen-usecounters.py',
               entry_point='use_counter_list', inputs=['UseCounters.conf'])
 
+GeneratedFile('UseCounterWorkerList.h', script='gen-usecounters.py',
+              entry_point='use_counter_list', inputs=['UseCountersWorker.conf'])
+
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2138,16 +2138,17 @@ WorkerPrivate::WorkerPrivate(
       mWorkerHybridEventTarget(
           new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
       mParentStatus(Pending),
       mStatus(Pending),
       mBusyCount(0),
       mLoadingWorkerScript(false),
       mCreationTimeStamp(TimeStamp::Now()),
       mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
+      mReportedUseCounters(false),
       mAgentClusterId(aAgentClusterId),
       mWorkerThreadAccessible(aParent),
       mPostSyncLoopOperations(0),
       mParentWindowPaused(false),
       mCancelAllPendingRunnables(false),
       mWorkerScriptExecutedSuccessfully(false),
       mFetchHandlerWasAdded(false),
       mMainThreadObjectsForgotten(false),
@@ -3438,16 +3439,19 @@ void WorkerPrivate::ClearMainEventQueue(
       RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
       static_cast<nsIRunnable*>(runnable.get())->Run();
     }
   } else {
     nsIThread* currentThread = NS_GetCurrentThread();
     MOZ_ASSERT(currentThread);
 
     NS_ProcessPendingEvents(currentThread);
+
+    // We are about to destroy worker, report all use counters.
+    ReportUseCounters();
   }
 
   MOZ_ASSERT(mCancelAllPendingRunnables);
   mCancelAllPendingRunnables = false;
 }
 
 void WorkerPrivate::ClearDebuggerEventQueue() {
   while (!mDebuggerQueue.IsEmpty()) {
@@ -3856,16 +3860,83 @@ void WorkerPrivate::DispatchCancelingRun
   // At the same time, we want to be sure that we interrupt infinite loops.
   // The following runnable starts a timer that cancel the worker, from the
   // parent thread, after CANCELING_TIMEOUT millseconds.
   RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
       new CancelingWithTimeoutOnParentRunnable(this);
   rr->Dispatch();
 }
 
+void WorkerPrivate::ReportUseCounters() {
+  AssertIsOnWorkerThread();
+
+  static const bool kDebugUseCounters = false;
+
+  if (mReportedUseCounters) {
+    return;
+  }
+  mReportedUseCounters = true;
+
+  if (Telemetry::HistogramUseCounterWorkerCount <= 0 || IsChromeWorker()) {
+    return;
+  }
+
+  const size_t type = Type();
+  switch (type) {
+    case WorkerTypeDedicated:
+      Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_DESTROYED, 1);
+      break;
+    case WorkerTypeShared:
+      Telemetry::Accumulate(Telemetry::SHARED_WORKER_DESTROYED, 1);
+      break;
+    case WorkerTypeService:
+      Telemetry::Accumulate(Telemetry::SERVICE_WORKER_DESTROYED, 1);
+      break;
+    default:
+      MOZ_ASSERT(false, "Unknown worker type");
+      return;
+  }
+
+  if (kDebugUseCounters) {
+    nsAutoCString path(Domain());
+    path.AppendLiteral("(");
+    NS_ConvertUTF16toUTF8 script(ScriptURL());
+    path.Append(script);
+    path.AppendPrintf(", 0x%p)", static_cast<void*>(this));
+    printf("-- Worker use counters for %s --\n", path.get());
+  }
+
+  static_assert(
+      static_cast<size_t>(UseCounterWorker::Count) * 3 ==
+          static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount),
+      "There should be three histograms (dedicated and shared and "
+      "servie) for each worker use counter");
+  const size_t count = static_cast<size_t>(UseCounterWorker::Count);
+  const size_t factor =
+      static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount) / count;
+  MOZ_ASSERT(factor > type);
+
+  for (size_t c = 0; c < count; ++c) {
+    // Histograms for worker use counters use the same order as the worker types
+    // , so we can use the worker type to index to corresponding histogram.
+    Telemetry::HistogramID id = static_cast<Telemetry::HistogramID>(
+        Telemetry::HistogramFirstUseCounterWorker + c * factor + type);
+    MOZ_ASSERT(id <= Telemetry::HistogramLastUseCounterWorker);
+
+    if (bool value = GetUseCounter(static_cast<UseCounterWorker>(c))) {
+      Telemetry::Accumulate(id, 1);
+
+      if (kDebugUseCounters) {
+        const char* name = Telemetry::GetHistogramName(id);
+        printf("  %s  #%d: %d\n", name, id, value);
+      }
+    }
+  }
+}
+
 void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget,
                                  bool aResult) {
   AssertIsOnWorkerThread();
   AssertValidSyncLoop(aSyncLoopTarget);
 
   MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
 
   for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1,8 +1,9 @@
+
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_workers_workerprivate_h__
 #define mozilla_dom_workers_workerprivate_h__
@@ -11,16 +12,17 @@
 #include "mozilla/dom/WorkerStatus.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RelativeTimeline.h"
 #include "mozilla/StorageAccess.h"
 #include "mozilla/ThreadSafeWeakPtr.h"
+#include "mozilla/UseCounter.h"
 #include "nsContentUtils.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIEventTarget.h"
 #include "nsTObserverArray.h"
 
 #include "js/ContextOptions.h"
 #include "mozilla/dom/RemoteWorkerChild.h"
 #include "mozilla/dom/Worker.h"
@@ -32,17 +34,18 @@
 
 class nsIThreadInternal;
 
 namespace mozilla {
 class ThrottledEventQueue;
 namespace dom {
 
 // If you change this, the corresponding list in nsIWorkerDebugger.idl needs
-// to be updated too.
+// to be updated too. And histograms enum for worker use counters uses the same
+// order of worker type. Please also update dom/base/usecounters.py.
 enum WorkerType { WorkerTypeDedicated, WorkerTypeShared, WorkerTypeService };
 
 class ClientInfo;
 class ClientSource;
 class Function;
 class MessagePort;
 class UniqueMessagePortId;
 class PerformanceStorage;
@@ -895,16 +898,23 @@ class WorkerPrivate : public RelativeTim
 
   const nsID& AgentClusterId() const { return mAgentClusterId; }
 
   bool IsSharedMemoryAllowed() const;
 
   // https://whatpr.org/html/4734/structured-data.html#cross-origin-isolated
   bool CrossOriginIsolated() const;
 
+  void SetUseCounter(UseCounterWorker aUseCounter) {
+    MOZ_ASSERT(!mReportedUseCounters);
+    MOZ_ASSERT(aUseCounter > UseCounterWorker::Unknown);
+    AssertIsOnWorkerThread();
+    mUseCounters[static_cast<size_t>(aUseCounter)] = true;
+  }
+
  private:
   WorkerPrivate(
       WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
       WorkerType aWorkerType, const nsAString& aWorkerName,
       const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
       nsString&& aId, const nsID& aAgentClusterId,
       const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy);
 
@@ -990,16 +1000,24 @@ class WorkerPrivate : public RelativeTim
 
   // This method dispatches a simple runnable that starts the shutdown procedure
   // after a self.close(). This method is called after a ClearMainEventQueue()
   // to be sure that the canceling runnable is the only one in the queue.  We
   // need this async operation to be sure that all the current JS code is
   // executed.
   void DispatchCancelingRunnable();
 
+  bool GetUseCounter(UseCounterWorker aUseCounter) {
+    MOZ_ASSERT(aUseCounter > UseCounterWorker::Unknown);
+    AssertIsOnWorkerThread();
+    return mUseCounters[static_cast<size_t>(aUseCounter)];
+  }
+
+  void ReportUseCounters();
+
   class EventTarget;
   friend class EventTarget;
   friend class AutoSyncLoopHolder;
 
   struct TimeoutInfo;
 
   class MemoryReporter;
   friend class MemoryReporter;
@@ -1106,16 +1124,24 @@ class WorkerPrivate : public RelativeTim
   // thread before crashing because hanging.
   Atomic<uint64_t> mBusyCount;
 
   Atomic<bool> mLoadingWorkerScript;
 
   TimeStamp mCreationTimeStamp;
   DOMHighResTimeStamp mCreationTimeHighRes;
 
+  // Flags for use counters used directly by this worker.
+  static_assert(sizeof(UseCounterWorker) <= sizeof(size_t),
+                "UseCounterWorker is too big");
+  static_assert(UseCounterWorker::Count >= static_cast<UseCounterWorker>(0),
+                "Should be non-negative value and safe to cast to unsigned");
+  std::bitset<static_cast<size_t>(UseCounterWorker::Count)> mUseCounters;
+  bool mReportedUseCounters;
+
   // This is created while creating the WorkerPrivate, so it's safe to be
   // touched on any thread.
   const nsID mAgentClusterId;
 
   // Things touched on worker thread only.
   struct WorkerThreadAccessible {
     explicit WorkerThreadAccessible(WorkerPrivate* aParent);