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
--- 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