Bug 1477943 - Add a unique id per PerformanceCounter instance - r?baku,froydnj draft
authorTarek Ziadé <tarek@mozilla.com>
Fri, 27 Jul 2018 11:44:22 +0200
changeset 823410 1b89cf85e398774d69f39fd0a8615eaefd306436
parent 823333 8f2f847b2f9dc5643eeb9935d603a3b30686f972
child 823470 79f07beb7d27c1f7fe2aaf33a5a979435e505154
child 823591 7b6cf3c1b9627ddccae10dddcb64e04984671ac7
push id117679
push usertziade@mozilla.com
push dateFri, 27 Jul 2018 10:12:58 +0000
reviewersbaku, froydnj
bugs1477943
milestone63.0a1
Bug 1477943 - Add a unique id per PerformanceCounter instance - r?baku,froydnj This new id is added in the PerformanceInfo data and helps consumers distinguish counters. MozReview-Commit-ID: 7kEmqJcVggM
dom/base/DocGroup.cpp
dom/chrome-webidl/ChromeUtils.webidl
dom/ipc/DOMTypes.ipdlh
dom/tests/browser/perfmetrics/browser_test_performance_metrics.js
dom/workers/WorkerDebugger.cpp
toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
xpcom/threads/PerformanceCounter.cpp
xpcom/threads/PerformanceCounter.h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -121,21 +121,22 @@ DocGroup::ReportPerformanceInfo()
 
   // now that we have the host and window ids, let's look at the perf counters
   for (uint32_t index = 0; index < (uint32_t)TaskCategory::Count; index++) {
     TaskCategory category = static_cast<TaskCategory>(index);
     count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category));
     CategoryDispatch item = CategoryDispatch(index, count);
     if (!items.AppendElement(item, fallible)) {
       NS_ERROR("Could not complete the operation");
-      return PerformanceInfo(host, pid, windowID, duration, false, isTopLevel, items);
+      break;
     }
   }
 
-  return PerformanceInfo(host, pid, windowID, duration, false, isTopLevel, items);
+  return PerformanceInfo(host, pid, windowID, duration, mPerformanceCounter->GetID(),
+                         false, isTopLevel, items);
 }
 
 nsresult
 DocGroup::Dispatch(TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
   if (mPerformanceCounter) {
     mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory));
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -367,16 +367,17 @@ dictionary CategoryDispatchDictionary
   unsigned short count = 0;
 };
 
 dictionary PerformanceInfoDictionary {
   ByteString host = "";
   unsigned long pid = 0;
   unsigned long long windowId = 0;
   unsigned long long duration = 0;
+  unsigned long long counterId = 0;
   boolean isWorker = false;
   boolean isTopLevel = false;
   sequence<CategoryDispatchDictionary> items = [];
 };
 
 /**
  * Used by requestIOActivity() to return the number of bytes
  * that were read (rx) and/or written (tx) for a given location.
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -153,16 +153,18 @@ struct PerformanceInfo
   // Host of the document, if any
   nsCString host;
   // process id
   uint32_t pid;
   // window id
   uint64_t windowId;
   // Execution time in microseconds
   uint64_t duration;
+  // Counter ID (unique across processes)
+  uint64_t counterId;
   // True if the data is collected in a worker
   bool isWorker;
   // True if the document window is the top window
   bool isTopLevel;
   // Counters per category. For workers, a single entry
   CategoryDispatch[] items;
 };
 
--- a/dom/tests/browser/perfmetrics/browser_test_performance_metrics.js
+++ b/dom/tests/browser/perfmetrics/browser_test_performance_metrics.js
@@ -66,19 +66,23 @@ add_task(async function test() {
     let total = 0;
     let isTopLevel = false;
     let aboutMemoryFound = false;
     let parentProcessEvent = false;
     let workerEvent = false;
     let subFrameIds = [];
     let topLevelIds = [];
     let sharedWorker = false;
+    let counterIds = [];
 
     function exploreResults(data) {
       for (let entry of data) {
+        if (!counterIds.includes(entry.pid + ":" + entry.counterId)) {
+          counterIds.push(entry.pid + ":" + entry.counterId);
+        }
         sharedWorker = entry.host.endsWith("shared_worker.js") || sharedWorker;
 
         Assert.ok(entry.host != "" || entry.windowId !=0,
                   "An entry should have a host or a windowId");
         if (entry.windowId != 0 && !entry.isToplevel && !entry.isWorker && !subFrameIds.includes(entry.windowId)) {
           subFrameIds.push(entry.windowId);
         }
         if (entry.isTopLevel && !topLevelIds.includes(entry.windowId)) {
@@ -118,16 +122,18 @@ add_task(async function test() {
     Assert.ok(workerDuration > 0, "Worker duration should be positive");
     Assert.ok(workerTotal > 0, "Worker count should be positive");
     Assert.ok(duration > 0, "Duration should be positive");
     Assert.ok(total > 0, "Should get a positive count");
     Assert.ok(parentProcessEvent, "parent process sent back some events");
     Assert.ok(isTopLevel, "example.com as a top level window");
     Assert.ok(aboutMemoryFound, "about:memory");
     Assert.ok(sharedWorker, "We got some info from a shared worker");
+    let numCounters = counterIds.length;
+    Assert.ok(numCounters > 10, "This test generated at least " + numCounters + " unique ounters");
 
     // checking that subframes are not orphans
     for (let frameId of subFrameIds) {
       Assert.ok(topLevelIds.includes(frameId), "subframe is not orphan ");
     }
 
     // Doing a second call, we shoud get bigger values
     let previousWorkerDuration = workerDuration;
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -509,25 +509,27 @@ WorkerDebugger::ReportPerformanceInfo()
   nsCString url = scriptURI->GetSpecOrDefault();
 
   // Workers only produce metrics for a single category - DispatchCategory::Worker.
   // We still return an array of CategoryDispatch so the PerformanceInfo
   // struct is common to all performance counters throughout Firefox.
   FallibleTArray<CategoryDispatch> items;
   uint64_t duration = 0;
   uint16_t count = 0;
+  uint64_t perfId = 0;
 
   RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
   if (perf) {
+    perfId = perf->GetID();
     count =  perf->GetTotalDispatchCount();
     duration = perf->GetExecutionDuration();
     CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
     if (!items.AppendElement(item, fallible)) {
       NS_ERROR("Could not complete the operation");
-      return PerformanceInfo(url, pid, windowID, duration, true, isTopLevel, items);
+      return PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel, items);
     }
   }
 
-  return PerformanceInfo(url, pid, windowID, duration, true, isTopLevel, items);
+  return PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel, items);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
+++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
@@ -80,16 +80,17 @@ AggregatedResults::AppendResult(const ns
     if (NS_WARN_IF(!data)) {
       Abort(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
     data->mPid = result.pid();
     data->mWindowId = result.windowId();
     data->mHost.Assign(result.host());
     data->mDuration = result.duration();
+    data->mCounterId = result.counterId();
     data->mIsWorker = result.isWorker();
     data->mIsTopLevel = result.isTopLevel();
     data->mItems = items;
   }
 
   mPendingResults--;
   if (mPendingResults) {
     return;
--- a/xpcom/threads/PerformanceCounter.cpp
+++ b/xpcom/threads/PerformanceCounter.cpp
@@ -1,47 +1,65 @@
 /* -*- 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/. */
 
+#include "mozilla/Atomics.h"
 #include "mozilla/Logging.h"
 #include "mozilla/PerformanceCounter.h"
 
 static mozilla::LazyLogModule sPerformanceCounter("PerformanceCounter");
 #ifdef LOG
 #undef LOG
 #endif
 #define LOG(args) MOZ_LOG(sPerformanceCounter, mozilla::LogLevel::Debug, args)
 
+// Global counter used by PerformanceCounter CTOR via NextCounterID().
+static Atomic<uint64_t> gNextCounterID(0);
+
+static uint64_t
+NextCounterID()
+{
+  // This can return the same value on different processes but
+  // we're fine with this behavior because consumers can use a (pid, counter_id)
+  // tuple to make instances globally unique in a browser session.
+  return ++gNextCounterID;
+}
+
+
 // this instance is the extension for the worker
 const DispatchCategory DispatchCategory::Worker = DispatchCategory((uint32_t)TaskCategory::Count);
 
 PerformanceCounter::PerformanceCounter(const nsACString& aName)
   : mExecutionDuration(0),
     mTotalDispatchCount(0),
     mDispatchCounter(),
-    mName(aName)
+    mName(aName),
+    mID(NextCounterID())
 {
+  LOG(("PerformanceCounter created with ID %" PRIu64, mID));
 }
 
 void
 PerformanceCounter::IncrementDispatchCounter(DispatchCategory aCategory)
 {
   mDispatchCounter[aCategory.GetValue()] += 1;
   mTotalDispatchCount += 1;
-  LOG(("[%s] Total dispatch %" PRIu64, mName.get(), uint64_t(mTotalDispatchCount)));
+  LOG(("[%s][%" PRIu64 "] Total dispatch %" PRIu64, mName.get(),
+      GetID(), uint64_t(mTotalDispatchCount)));
 }
 
 void
 PerformanceCounter::IncrementExecutionDuration(uint32_t aMicroseconds)
 {
   mExecutionDuration += aMicroseconds;
-  LOG(("[%s] Total duration %" PRIu64, mName.get(), uint64_t(mExecutionDuration)));
+  LOG(("[%s][%" PRIu64 "] Total duration %" PRIu64, mName.get(),
+      GetID(), uint64_t(mExecutionDuration)));
 }
 
 const DispatchCounter&
 PerformanceCounter::GetDispatchCounter()
 {
   return mDispatchCounter;
 }
 
@@ -57,8 +75,14 @@ PerformanceCounter::GetTotalDispatchCoun
   return mTotalDispatchCount;
 }
 
 uint32_t
 PerformanceCounter::GetDispatchCount(DispatchCategory aCategory)
 {
   return mDispatchCounter[aCategory.GetValue()];
 }
+
+uint64_t
+PerformanceCounter::GetID() const
+{
+  return mID;
+}
--- a/xpcom/threads/PerformanceCounter.h
+++ b/xpcom/threads/PerformanceCounter.h
@@ -108,20 +108,34 @@ public:
    */
   uint32_t GetDispatchCount(DispatchCategory aCategory);
 
   /**
    * Returns the total number of dispatches.
    */
   uint64_t GetTotalDispatchCount();
 
+  /**
+   * Returns the unique id for the instance.
+   *
+   * Used to distinguish instances since the lifespan of
+   * a PerformanceCounter can be shorter than the
+   * host it's tracking. That leads to edge cases
+   * where a counter appears to have values that go
+   * backwards. Having this id let the consumers
+   * detect that they are dealing with a new counter
+   * when it happens.
+   */
+  uint64_t GetID() const;
+
 private:
   ~PerformanceCounter() {}
 
   Atomic<uint64_t> mExecutionDuration;
   Atomic<uint64_t> mTotalDispatchCount;
   DispatchCounter mDispatchCounter;
   nsCString mName;
+  const uint64_t mID;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_PerformanceCounter_h