fune/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp
Nicholas Nethercote 18fae65f38 Bug 1563139 - Remove StaticPrefs.h. r=glandium
This requires replacing inclusions of it with inclusions of more specific prefs
files.

The exception is that StaticPrefsAll.h, which is equivalent to StaticPrefs.h,
and is used in `Codegen.py` because doing something smarter is tricky and
suitable for a follow-up. As a result, any change to StaticPrefList.yaml will
still trigger recompilation of all the generated DOM bindings files, but that's
still a big improvement over trigger recompilation of every file that uses
static prefs.

Most of the changes in this commit are very boring. The only changes that are
not boring are modules/libpref/*, Codegen.py, and ServoBindings.toml.

Differential Revision: https://phabricator.services.mozilla.com/D39138

--HG--
extra : moz-landing-system : lando
2019-07-26 01:10:23 +00:00

309 lines
9.9 KiB
C++

/* -*- 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 "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/AbstractThread.h"
#include "mozilla/Logging.h"
#include "mozilla/PerformanceUtils.h"
#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
using namespace mozilla;
using namespace mozilla::dom;
static mozilla::LazyLogModule sPerfLog("PerformanceMetricsCollector");
#ifdef LOG
# undef LOG
#endif
#define LOG(args) MOZ_LOG(sPerfLog, mozilla::LogLevel::Debug, args)
namespace mozilla {
//
// class IPCTimeout
//
NS_IMPL_ISUPPORTS(IPCTimeout, nsIObserver)
// static
IPCTimeout* IPCTimeout::CreateInstance(AggregatedResults* aResults) {
MOZ_ASSERT(aResults);
uint32_t delay = StaticPrefs::dom_performance_children_results_ipc_timeout();
if (delay == 0) {
return nullptr;
}
return new IPCTimeout(aResults, delay);
}
IPCTimeout::IPCTimeout(AggregatedResults* aResults, uint32_t aDelay)
: mResults(aResults) {
MOZ_ASSERT(aResults);
MOZ_ASSERT(aDelay > 0);
mozilla::DebugOnly<nsresult> rv = NS_NewTimerWithObserver(
getter_AddRefs(mTimer), this, aDelay, nsITimer::TYPE_ONE_SHOT);
MOZ_ASSERT(NS_SUCCEEDED(rv));
LOG(("IPCTimeout timer created"));
}
IPCTimeout::~IPCTimeout() { Cancel(); }
void IPCTimeout::Cancel() {
if (mTimer) {
LOG(("IPCTimeout timer canceled"));
mTimer->Cancel();
mTimer = nullptr;
}
}
NS_IMETHODIMP
IPCTimeout::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0);
LOG(("IPCTimeout timer triggered"));
mResults->ResolveNow();
return NS_OK;
}
//
// class AggregatedResults
//
AggregatedResults::AggregatedResults(nsID aUUID,
PerformanceMetricsCollector* aCollector)
: mPendingResults(0), mCollector(aCollector), mUUID(aUUID) {
MOZ_ASSERT(aCollector);
mIPCTimeout = IPCTimeout::CreateInstance(this);
}
void AggregatedResults::Abort(nsresult aReason) {
MOZ_ASSERT(!mHolder.IsEmpty());
MOZ_ASSERT(NS_FAILED(aReason));
if (mIPCTimeout) {
mIPCTimeout->Cancel();
mIPCTimeout = nullptr;
}
mHolder.Reject(aReason, __func__);
mPendingResults = 0;
}
void AggregatedResults::ResolveNow() {
MOZ_ASSERT(!mHolder.IsEmpty());
LOG(("[%s] Early resolve", nsIDToCString(mUUID).get()));
mHolder.Resolve(mData, __func__);
mIPCTimeout = nullptr;
mCollector->ForgetAggregatedResults(mUUID);
}
void AggregatedResults::AppendResult(
const nsTArray<dom::PerformanceInfo>& aMetrics) {
if (mHolder.IsEmpty()) {
// A previous call failed and the promise was already rejected
return;
}
MOZ_ASSERT(mPendingResults > 0);
// Each PerformanceInfo is converted into a PerformanceInfoDictionary
for (const PerformanceInfo& result : aMetrics) {
mozilla::dom::Sequence<mozilla::dom::CategoryDispatchDictionary> items;
for (const CategoryDispatch& entry : result.items()) {
uint32_t count = entry.count();
if (count == 0) {
continue;
}
CategoryDispatchDictionary* item = items.AppendElement(fallible);
if (NS_WARN_IF(!item)) {
Abort(NS_ERROR_OUT_OF_MEMORY);
return;
}
item->mCategory = entry.category();
item->mCount = count;
}
PerformanceInfoDictionary* data = mData.AppendElement(fallible);
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->mMemoryInfo.mDomDom = result.memory().domDom();
data->mMemoryInfo.mDomStyle = result.memory().domStyle();
data->mMemoryInfo.mDomOther = result.memory().domOther();
data->mMemoryInfo.mGCHeapUsage = result.memory().GCHeapUsage();
data->mMemoryInfo.mMedia.mAudioSize = result.memory().media().audioSize();
data->mMemoryInfo.mMedia.mVideoSize = result.memory().media().videoSize();
data->mMemoryInfo.mMedia.mResourcesSize =
result.memory().media().resourcesSize();
data->mItems = items;
}
mPendingResults--;
if (mPendingResults) {
return;
}
LOG(("[%s] All data collected, resolving promise",
nsIDToCString(mUUID).get()));
if (mIPCTimeout) {
mIPCTimeout->Cancel();
mIPCTimeout = nullptr;
}
mHolder.Resolve(mData, __func__);
mCollector->ForgetAggregatedResults(mUUID);
}
void AggregatedResults::SetNumResultsRequired(uint32_t aNumResultsRequired) {
MOZ_ASSERT(!mPendingResults && aNumResultsRequired);
mPendingResults = aNumResultsRequired;
}
RefPtr<RequestMetricsPromise> AggregatedResults::GetPromise() {
return mHolder.Ensure(__func__);
}
//
// class PerformanceMetricsCollector (singleton)
//
// raw pointer for the singleton
PerformanceMetricsCollector* gInstance = nullptr;
PerformanceMetricsCollector::~PerformanceMetricsCollector() {
MOZ_ASSERT(gInstance == this);
gInstance = nullptr;
}
void PerformanceMetricsCollector::ForgetAggregatedResults(const nsID& aUUID) {
MOZ_ASSERT(gInstance);
MOZ_ASSERT(XRE_IsParentProcess());
// This Remove() call will trigger AggregatedResults DTOR and if its
// the last in the table, the DTOR of PerformanceMetricsCollector.
// That's why we need to make sure we hold a reference here before the call
RefPtr<PerformanceMetricsCollector> kungFuDeathGrip = this;
LOG(("[%s] Removing from the table", nsIDToCString(aUUID).get()));
mAggregatedResults.Remove(aUUID);
}
// static
RefPtr<RequestMetricsPromise> PerformanceMetricsCollector::RequestMetrics() {
MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<PerformanceMetricsCollector> pmc = gInstance;
if (!pmc) {
pmc = new PerformanceMetricsCollector();
gInstance = pmc;
}
return pmc->RequestMetricsInternal();
}
RefPtr<RequestMetricsPromise>
PerformanceMetricsCollector::RequestMetricsInternal() {
// each request has its own UUID
nsID uuid;
nsresult rv = nsContentUtils::GenerateUUIDInPlace(uuid);
if (NS_WARN_IF(NS_FAILED(rv))) {
return RequestMetricsPromise::CreateAndReject(rv, __func__);
}
LOG(("[%s] Requesting Performance Metrics", nsIDToCString(uuid).get()));
// Getting all content processes
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
uint32_t numChildren = children.Length();
// keep track of all results in an AggregatedResults instance
UniquePtr<AggregatedResults> results =
MakeUnique<AggregatedResults>(uuid, this);
RefPtr<RequestMetricsPromise> promise = results->GetPromise();
// We want to get back as many results as children + one parent if needed
uint32_t numResultsRequired = children.Length();
nsTArray<RefPtr<PerformanceInfoPromise>> localPromises =
CollectPerformanceInfo();
if (!localPromises.IsEmpty()) {
numResultsRequired++;
}
LOG(("[%s] Expecting %d results back", nsIDToCString(uuid).get(),
numResultsRequired));
results->SetNumResultsRequired(numResultsRequired);
mAggregatedResults.Put(uuid, std::move(results));
// calling all content processes via IPDL (async)
for (uint32_t i = 0; i < numChildren; i++) {
if (NS_WARN_IF(!children[i]->SendRequestPerformanceMetrics(uuid))) {
LOG(("[%s] Failed to send request to child %d", nsIDToCString(uuid).get(),
i));
mAggregatedResults.GetValue(uuid)->get()->Abort(NS_ERROR_FAILURE);
return RequestMetricsPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
LOG(("[%s] Request sent to child %d", nsIDToCString(uuid).get(), i));
}
nsTArray<RefPtr<PerformanceInfoPromise>> promises = CollectPerformanceInfo();
if (promises.IsEmpty()) {
return promise;
}
// collecting the current process PerformanceInfo
PerformanceInfoPromise::All(NS_GetCurrentThread(), localPromises)
->Then(
NS_GetCurrentThread(), __func__,
[uuid](const nsTArray<mozilla::dom::PerformanceInfo> aResult) {
LOG(("[%s] Local CollectPerformanceInfo promise resolved",
nsIDToCString(uuid).get()));
DataReceived(uuid, aResult);
},
[](const nsresult aResult) {});
return promise;
}
// static
nsresult PerformanceMetricsCollector::DataReceived(
const nsID& aUUID, const nsTArray<PerformanceInfo>& aMetrics) {
// If some content process were unresponsive on shutdown, we may get called
// here with late data received from children - so instead of asserting
// that gInstance is available, we just return.
if (!gInstance) {
LOG(("[%s] gInstance is gone", nsIDToCString(aUUID).get()));
return NS_OK;
}
MOZ_ASSERT(XRE_IsParentProcess());
return gInstance->DataReceivedInternal(aUUID, aMetrics);
}
nsresult PerformanceMetricsCollector::DataReceivedInternal(
const nsID& aUUID, const nsTArray<PerformanceInfo>& aMetrics) {
MOZ_ASSERT(gInstance == this);
UniquePtr<AggregatedResults>* results = mAggregatedResults.GetValue(aUUID);
if (!results) {
LOG(("[%s] UUID is gone from mAggregatedResults",
nsIDToCString(aUUID).get()));
return NS_ERROR_FAILURE;
}
LOG(("[%s] Received one PerformanceInfo array", nsIDToCString(aUUID).get()));
AggregatedResults* aggregatedResults = results->get();
MOZ_ASSERT(aggregatedResults);
// If this is the last result, AppendResult() will trigger the deletion
// of this collector, nothing should be done after this line.
aggregatedResults->AppendResult(aMetrics);
return NS_OK;
}
} // namespace mozilla