Bug 1553254 - Part 1: Allow low-overhead selective collection of internal performance probes through ChromeUtils. r=brennie,nika

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

--HG--
extra : rebase_source : 2484b8d3d15495e63b9e02139aafd3e78a710f37
This commit is contained in:
Bas Schouten 2019-05-21 21:01:31 +02:00
Родитель c19a162836
Коммит 202c0599be
14 изменённых файлов: 468 добавлений и 0 удалений

Просмотреть файл

@ -15,6 +15,7 @@
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/PerfStats.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcInfo.h"
#include "mozilla/RDDProcessManager.h"
@ -865,6 +866,35 @@ already_AddRefed<Promise> ChromeUtils::RequestPerformanceMetrics(
return domPromise.forget();
}
void ChromeUtils::SetPerfStatsCollectionMask(GlobalObject& aGlobal,
uint64_t aMask) {
PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
}
already_AddRefed<Promise> ChromeUtils::CollectPerfStats(GlobalObject& aGlobal,
ErrorResult& aRv) {
// Creating a JS promise
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<PerfStats::PerfStatsPromise> extPromise =
PerfStats::CollectPerfStatsJSON();
extPromise->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[promise](const nsCString& aResult) {
promise->MaybeResolve(NS_ConvertUTF8toUTF16(aResult));
},
[promise](bool aValue) { promise->MaybeReject(NS_ERROR_FAILURE); });
return promise.forget();
}
constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
/* static */

Просмотреть файл

@ -140,6 +140,11 @@ class ChromeUtils {
static already_AddRefed<Promise> RequestPerformanceMetrics(
GlobalObject& aGlobal, ErrorResult& aRv);
static void SetPerfStatsCollectionMask(GlobalObject& aGlobal, uint64_t aMask);
static already_AddRefed<Promise> CollectPerfStats(GlobalObject& aGlobal,
ErrorResult& aRv);
static already_AddRefed<Promise> RequestProcInfo(GlobalObject& aGlobal,
ErrorResult& aRv);

Просмотреть файл

@ -362,6 +362,23 @@ partial namespace ChromeUtils {
[Throws]
Promise<sequence<PerformanceInfoDictionary>> requestPerformanceMetrics();
/**
* Set the collection of specific detailed performance timing information.
* Selecting 0 for the mask will end existing collection. All metrics that
* are chosen will be cleared after updating the mask.
*
* @param aCollectionMask A bitmask where each bit corresponds to a metric
* to be collected as listed in PerfStats::Metric.
*/
void setPerfStatsCollectionMask(unsigned long long aCollectionMask);
/**
* Collect results of detailed performance timing information.
* The output is a JSON string containing performance timings.
*/
[Throws]
Promise<DOMString> collectPerfStats();
/**
* Returns a Promise containing a sequence of I/O activities
*/

Просмотреть файл

@ -20,6 +20,7 @@
#include "mozilla/LookAndFeel.h"
#include "mozilla/MemoryTelemetry.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PerfStats.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/RemoteDecoderManagerChild.h"
@ -2433,6 +2434,18 @@ mozilla::ipc::IPCResult ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar) {
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvUpdatePerfStatsCollectionMask(
const uint64_t& aMask) {
PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvCollectPerfStatsJSON(
CollectPerfStatsJSONResolver&& aResolver) {
aResolver(PerfStats::CollectLocalPerfStatsJSON());
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvDataStoragePut(
const nsString& aFilename, const DataStorageItem& aItem) {
RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);

Просмотреть файл

@ -349,6 +349,12 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref);
mozilla::ipc::IPCResult RecvVarUpdate(const GfxVarUpdate& pref);
mozilla::ipc::IPCResult RecvUpdatePerfStatsCollectionMask(
const uint64_t& aMask);
mozilla::ipc::IPCResult RecvCollectPerfStatsJSON(
CollectPerfStatsJSONResolver&& aResolver);
mozilla::ipc::IPCResult RecvDataStoragePut(const nsString& aFilename,
const DataStorageItem& aItem);

Просмотреть файл

@ -482,6 +482,9 @@ child:
async PreferenceUpdate(Pref pref);
async VarUpdate(GfxVarUpdate var);
async UpdatePerfStatsCollectionMask(uint64_t aMask);
async CollectPerfStatsJSON() returns (nsCString aStats);
async DataStoragePut(nsString aFilename, DataStorageItem aItem);
async DataStorageRemove(nsString aFilename, nsCString aKey, DataStorageType aType);
async DataStorageClear(nsString aFilename);

Просмотреть файл

@ -14,6 +14,7 @@
#include "GPUProcessHost.h"
#include "GPUProcessManager.h"
#include "mozilla/Assertions.h"
#include "mozilla/PerfStats.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
@ -483,6 +484,18 @@ mozilla::ipc::IPCResult GPUParent::RecvShutdownVR() {
return IPC_OK();
}
mozilla::ipc::IPCResult GPUParent::RecvUpdatePerfStatsCollectionMask(
const uint64_t& aMask) {
PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
return IPC_OK();
}
mozilla::ipc::IPCResult GPUParent::RecvCollectPerfStatsJSON(
CollectPerfStatsJSONResolver&& aResolver) {
aResolver(PerfStats::CollectLocalPerfStatsJSON());
return IPC_OK();
}
void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
if (AbnormalShutdown == aWhy) {
NS_WARNING("Shutting down GPU process early due to a crash!");

Просмотреть файл

@ -77,6 +77,11 @@ class GPUParent final : public PGPUParent {
const Maybe<ipc::FileDescriptor>& DMDFile);
mozilla::ipc::IPCResult RecvShutdownVR();
mozilla::ipc::IPCResult RecvUpdatePerfStatsCollectionMask(
const uint64_t& aMask);
mozilla::ipc::IPCResult RecvCollectPerfStatsJSON(
CollectPerfStatsJSONResolver&& aResolver);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:

Просмотреть файл

@ -95,6 +95,10 @@ parent:
FileDescriptor? DMDFile);
async ShutdownVR();
// Functions supporting PerfStats data collection.
async UpdatePerfStatsCollectionMask(uint64_t aMask);
async CollectPerfStatsJSON() returns (nsCString aStats);
child:
// Sent when the GPU process has initialized devices. This occurs once, after
// Init().

Просмотреть файл

@ -131,6 +131,7 @@ if CONFIG['MOZ_JPROF']:
DIRS += [
'/tools/code-coverage',
'/tools/performance',
'/tools/power',
'/tools/profiler',
]

Просмотреть файл

@ -29,6 +29,9 @@ with Files("lint/**"):
with Files("profiler/**"):
BUG_COMPONENT = ("Core", "Gecko Profiler")
with Files("performance/**"):
BUG_COMPONENT = ("Core", "Gecko Profiler")
with Files("quitter/**"):
BUG_COMPONENT = ("Testing", "General")

Просмотреть файл

@ -0,0 +1,260 @@
/* -*- Mode: C++; tab-width: 20; 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 "PerfStats.h"
#include "nsAppRunner.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/gfx/GPUChild.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/JSONWriter.h"
using namespace mozilla::dom;
using namespace mozilla::gfx;
namespace mozilla {
static const char* const sMetricNames[] = {"DisplayList Building",
"Rasterizing",
"LayerBuilding",
"Layer Transactions",
"Compositing",
"Reflowing",
"Styling"};
PerfStats::MetricMask PerfStats::sCollectionMask = 0;
StaticMutex PerfStats::sMutex;
StaticAutoPtr<PerfStats> PerfStats::sSingleton;
void PerfStats::SetCollectionMask(MetricMask aMask) {
sCollectionMask = aMask;
for (uint64_t i = 0; i < static_cast<uint64_t>(Metric::Max); i++) {
if (!(sCollectionMask & 1 << i)) {
continue;
}
GetSingleton()->mRecordedTimes[i] = 0;
}
if (!XRE_IsParentProcess()) {
return;
}
GPUProcessManager* gpuManager = GPUProcessManager::Get();
GPUChild* gpuChild = nullptr;
if (gpuManager) {
gpuChild = gpuManager->GetGPUChild();
if (gpuChild) {
gpuChild->SendUpdatePerfStatsCollectionMask(aMask);
}
}
nsTArray<ContentParent*> contentParents;
ContentParent::GetAll(contentParents);
for (ContentParent* parent : contentParents) {
Unused << parent->SendUpdatePerfStatsCollectionMask(aMask);
}
}
PerfStats* PerfStats::GetSingleton() {
if (!sSingleton) {
sSingleton = new PerfStats;
}
return sSingleton.get();
}
void PerfStats::RecordMeasurementStartInternal(Metric aMetric) {
StaticMutexAutoLock lock(sMutex);
GetSingleton()->mRecordedStarts[static_cast<size_t>(aMetric)] =
TimeStamp::Now();
}
void PerfStats::RecordMeasurementEndInternal(Metric aMetric) {
StaticMutexAutoLock lock(sMutex);
MOZ_ASSERT(sSingleton);
sSingleton->mRecordedTimes[static_cast<size_t>(aMetric)] +=
(TimeStamp::Now() -
sSingleton->mRecordedStarts[static_cast<size_t>(aMetric)])
.ToMilliseconds();
}
struct StringWriteFunc : public JSONWriteFunc {
nsCString& mString;
explicit StringWriteFunc(nsCString& aString) : mString(aString) {}
virtual void Write(const char* aStr) override { mString.Append(aStr); }
};
void AppendJSONStringAsProperty(nsCString& aDest, const char* aPropertyName,
const nsCString& aJSON) {
// We need to manually append into the string here, since JSONWriter has no
// way to allow us to write an existing JSON object into a property.
aDest.Append(",\n\"");
aDest.Append(aPropertyName);
aDest.Append("\": ");
aDest.Append(aJSON);
}
struct PerfStatsCollector {
PerfStatsCollector() : writer(MakeUnique<StringWriteFunc>(string)) {}
void AppendPerfStats(const nsCString& aString, ContentParent* aParent) {
writer.StartObjectElement();
writer.StringProperty("type", "content");
writer.IntProperty("id", aParent->ChildID());
const ManagedContainer<PBrowserParent>& browsers =
aParent->ManagedPBrowserParent();
writer.StartArrayProperty("urls");
for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
RefPtr<BrowserParent> parent =
BrowserParent::GetFrom(iter.Get()->GetKey());
CanonicalBrowsingContext* ctx = parent->GetBrowsingContext();
if (!ctx) {
continue;
}
WindowGlobalParent* windowGlobal = ctx->GetCurrentWindowGlobal();
if (!windowGlobal) {
continue;
}
RefPtr<nsIURI> uri = windowGlobal->GetDocumentURI();
if (!uri) {
continue;
}
nsAutoCString url;
uri->GetSpec(url);
writer.StringElement(url.BeginReading());
}
writer.EndArray();
AppendJSONStringAsProperty(string, "perfstats", aString);
writer.EndObject();
}
void AppendPerfStats(const nsCString& aString, GPUChild* aChild) {
writer.StartObjectElement();
writer.StringProperty("type", "gpu");
writer.IntProperty("id", aChild->Id());
AppendJSONStringAsProperty(string, "perfstats", aString);
writer.EndObject();
}
~PerfStatsCollector() {
writer.EndArray();
writer.End();
promise.Resolve(string, __func__);
}
nsCString string;
JSONWriter writer;
MozPromiseHolder<PerfStats::PerfStatsPromise> promise;
};
auto PerfStats::CollectPerfStatsJSONInternal() -> RefPtr<PerfStatsPromise> {
if (!PerfStats::sCollectionMask) {
return PerfStatsPromise::CreateAndReject(false, __func__);
}
if (!XRE_IsParentProcess()) {
return PerfStatsPromise::CreateAndResolve(
CollectLocalPerfStatsJSONInternal(), __func__);
}
std::shared_ptr<PerfStatsCollector> collector =
std::make_shared<PerfStatsCollector>();
JSONWriter& w = collector->writer;
w.Start();
{
w.StartArrayProperty("processes");
{
w.StartObjectElement();
{
w.StringProperty("type", "parent");
AppendJSONStringAsProperty(collector->string, "perfstats",
CollectLocalPerfStatsJSONInternal());
}
w.EndObject();
GPUProcessManager* gpuManager = GPUProcessManager::Get();
GPUChild* gpuChild = nullptr;
if (gpuManager) {
gpuChild = gpuManager->GetGPUChild();
}
nsTArray<ContentParent*> contentParents;
ContentParent::GetAll(contentParents);
if (gpuChild) {
gpuChild->SendCollectPerfStatsJSON(
[collector, gpuChild](const nsCString& aString) {
collector->AppendPerfStats(aString, gpuChild);
},
// The only feasible errors here are if something goes wrong in the
// the bridge, we choose to ignore those.
[](mozilla::ipc::ResponseRejectReason) {});
}
for (ContentParent* parent : contentParents) {
RefPtr<ContentParent> parentRef = parent;
parent->SendCollectPerfStatsJSON(
[collector, parentRef](const nsCString& aString) {
collector->AppendPerfStats(aString, parentRef.get());
},
// The only feasible errors here are if something goes wrong in the
// the bridge, we choose to ignore those.
[](mozilla::ipc::ResponseRejectReason) {});
}
}
}
return collector->promise.Ensure(__func__);
}
nsCString PerfStats::CollectLocalPerfStatsJSONInternal() {
StaticMutexAutoLock lock(PerfStats::sMutex);
nsCString jsonString;
JSONWriter w(MakeUnique<StringWriteFunc>(jsonString));
w.Start();
{
w.StartArrayProperty("metrics");
{
for (uint64_t i = 0; i < static_cast<uint64_t>(Metric::Max); i++) {
if (!(sCollectionMask & (1 << i))) {
continue;
}
w.StartObjectElement();
{
w.IntProperty("id", i);
w.StringProperty("metric", sMetricNames[i]);
w.DoubleProperty("time", mRecordedTimes[i]);
}
w.EndObject();
}
}
w.EndArray();
}
w.End();
return jsonString;
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,91 @@
/* -*- 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 PerfStats_h
#define PerfStats_h
#include "mozilla/TimeStamp.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/MozPromise.h"
#include <memory>
#include <string>
#include <limits>
namespace mozilla {
class PerfStats {
public:
typedef MozPromise<nsCString, bool, true> PerfStatsPromise;
enum class Metric : uint32_t {
DisplayListBuilding = 0,
Rasterizing,
LayerBuilding,
LayerTransactions,
Compositing,
Reflowing,
Styling,
Max
};
// MetricMask is a bitmask based on 'Metric', i.e. Metric::LayerBuilding (2)
// is synonymous to 1 << 2 in MetricMask.
using MetricMask = uint64_t;
static void RecordMeasurementStart(Metric aMetric) {
if (!(sCollectionMask & (1 << static_cast<uint64_t>(aMetric)))) {
return;
}
RecordMeasurementStartInternal(aMetric);
}
static void RecordMeasurementEnd(Metric aMetric) {
if (!(sCollectionMask & (1 << static_cast<uint64_t>(aMetric)))) {
return;
}
RecordMeasurementEndInternal(aMetric);
}
template <Metric N>
class AutoMetricRecording {
public:
AutoMetricRecording() { PerfStats::RecordMeasurementStart(N); }
~AutoMetricRecording() { PerfStats::RecordMeasurementEnd(N); }
};
static void SetCollectionMask(MetricMask aMask);
static RefPtr<PerfStatsPromise> CollectPerfStatsJSON() {
return GetSingleton()->CollectPerfStatsJSONInternal();
}
static nsCString CollectLocalPerfStatsJSON() {
return GetSingleton()->CollectLocalPerfStatsJSONInternal();
}
private:
static PerfStats* GetSingleton();
static void RecordMeasurementStartInternal(Metric aMetric);
static void RecordMeasurementEndInternal(Metric aMetric);
RefPtr<PerfStatsPromise> CollectPerfStatsJSONInternal();
nsCString CollectLocalPerfStatsJSONInternal();
static MetricMask sCollectionMask;
static StaticMutex sMutex;
static StaticAutoPtr<PerfStats> sSingleton;
TimeStamp mRecordedStarts[static_cast<size_t>(Metric::Max)];
double mRecordedTimes[static_cast<size_t>(Metric::Max)];
};
static_assert(1 << (static_cast<uint64_t>(PerfStats::Metric::Max) - 1) <=
std::numeric_limits<PerfStats::MetricMask>::max(),
"More metrics than can fit into sCollectionMask bitmask");
} // namespace mozilla
#endif // PerfStats_h

Просмотреть файл

@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
UNIFIED_SOURCES += [
'PerfStats.cpp',
]
EXPORTS.mozilla += [
'PerfStats.h',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'