1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsAppRunner.h"
9 #include "mozilla/dom/BrowserParent.h"
10 #include "mozilla/dom/CanonicalBrowsingContext.h"
11 #include "mozilla/dom/ContentParent.h"
12 #include "mozilla/dom/ContentProcessManager.h"
13 #include "mozilla/dom/WindowGlobalParent.h"
14 #include "mozilla/gfx/GPUChild.h"
15 #include "mozilla/gfx/GPUProcessManager.h"
16 #include "mozilla/JSONStringWriteFuncs.h"
18 using namespace mozilla::dom
;
19 using namespace mozilla::gfx
;
23 #define METRIC_NAME(metric) #metric,
24 static const char* const sMetricNames
[] = {
25 FOR_EACH_PERFSTATS_METRIC(METRIC_NAME
)
29 PerfStats::MetricMask
PerfStats::sCollectionMask
= 0;
30 StaticMutex
PerfStats::sMutex
;
31 StaticAutoPtr
<PerfStats
> PerfStats::sSingleton
;
33 void PerfStats::SetCollectionMask(MetricMask aMask
) {
34 sCollectionMask
= aMask
;
35 GetSingleton()->ResetCollection();
37 if (!XRE_IsParentProcess()) {
41 GPUProcessManager
* gpuManager
= GPUProcessManager::Get();
42 GPUChild
* gpuChild
= nullptr;
45 gpuChild
= gpuManager
->GetGPUChild();
47 gpuChild
->SendUpdatePerfStatsCollectionMask(aMask
);
51 nsTArray
<ContentParent
*> contentParents
;
52 ContentParent::GetAll(contentParents
);
54 for (ContentParent
* parent
: contentParents
) {
55 Unused
<< parent
->SendUpdatePerfStatsCollectionMask(aMask
);
59 PerfStats::MetricMask
PerfStats::GetCollectionMask() { return sCollectionMask
; }
61 PerfStats
* PerfStats::GetSingleton() {
63 sSingleton
= new PerfStats
;
66 return sSingleton
.get();
69 void PerfStats::RecordMeasurementStartInternal(Metric aMetric
) {
70 StaticMutexAutoLock
lock(sMutex
);
72 GetSingleton()->mRecordedStarts
[static_cast<size_t>(aMetric
)] =
76 void PerfStats::RecordMeasurementEndInternal(Metric aMetric
) {
77 StaticMutexAutoLock
lock(sMutex
);
79 MOZ_ASSERT(sSingleton
);
81 sSingleton
->mRecordedTimes
[static_cast<size_t>(aMetric
)] +=
83 sSingleton
->mRecordedStarts
[static_cast<size_t>(aMetric
)])
85 sSingleton
->mRecordedCounts
[static_cast<size_t>(aMetric
)]++;
88 void PerfStats::RecordMeasurementInternal(Metric aMetric
,
89 TimeDuration aDuration
) {
90 StaticMutexAutoLock
lock(sMutex
);
92 MOZ_ASSERT(sSingleton
);
94 sSingleton
->mRecordedTimes
[static_cast<size_t>(aMetric
)] +=
95 aDuration
.ToMilliseconds();
96 sSingleton
->mRecordedCounts
[static_cast<size_t>(aMetric
)]++;
99 void PerfStats::RecordMeasurementCounterInternal(Metric aMetric
,
100 uint64_t aIncrementAmount
) {
101 StaticMutexAutoLock
lock(sMutex
);
103 MOZ_ASSERT(sSingleton
);
105 sSingleton
->mRecordedTimes
[static_cast<size_t>(aMetric
)] +=
106 double(aIncrementAmount
);
107 sSingleton
->mRecordedCounts
[static_cast<size_t>(aMetric
)]++;
110 void AppendJSONStringAsProperty(nsCString
& aDest
, const char* aPropertyName
,
111 const nsACString
& aJSON
) {
112 // We need to manually append into the string here, since JSONWriter has no
113 // way to allow us to write an existing JSON object into a property.
114 aDest
.Append(",\n\"");
115 aDest
.Append(aPropertyName
);
116 aDest
.Append("\": ");
120 static void WriteContentParent(nsCString
& aRawString
, JSONWriter
& aWriter
,
121 const nsACString
& aString
,
122 ContentParent
* aParent
) {
123 aWriter
.StringProperty("type", "content");
124 aWriter
.IntProperty("id", aParent
->ChildID());
125 const ManagedContainer
<PBrowserParent
>& browsers
=
126 aParent
->ManagedPBrowserParent();
128 aWriter
.StartArrayProperty("urls");
129 for (const auto& key
: browsers
) {
130 // This only reports -current- URLs, not ones that may have been here in
131 // the past, this is unfortunate especially for processes which are dying
132 // and that have no more active URLs.
133 RefPtr
<BrowserParent
> parent
= BrowserParent::GetFrom(key
);
135 CanonicalBrowsingContext
* ctx
= parent
->GetBrowsingContext();
140 WindowGlobalParent
* windowGlobal
= ctx
->GetCurrentWindowGlobal();
145 RefPtr
<nsIURI
> uri
= windowGlobal
->GetDocumentURI();
153 aWriter
.StringElement(url
);
156 AppendJSONStringAsProperty(aRawString
, "perfstats", aString
);
159 struct PerfStatsCollector
{
160 PerfStatsCollector() : writer(MakeUnique
<JSONStringRefWriteFunc
>(string
)) {}
162 void AppendPerfStats(const nsCString
& aString
, ContentParent
* aParent
) {
163 writer
.StartObjectElement();
164 WriteContentParent(string
, writer
, aString
, aParent
);
168 void AppendPerfStats(const nsCString
& aString
, GPUChild
* aChild
) {
169 writer
.StartObjectElement();
170 writer
.StringProperty("type", "gpu");
171 writer
.IntProperty("id", aChild
->Id());
172 AppendJSONStringAsProperty(string
, "perfstats", aString
);
176 ~PerfStatsCollector() {
179 promise
.Resolve(string
, __func__
);
183 MozPromiseHolder
<PerfStats::PerfStatsPromise
> promise
;
186 void PerfStats::ResetCollection() {
187 for (uint64_t i
= 0; i
< static_cast<uint64_t>(Metric::Max
); i
++) {
188 if (!(sCollectionMask
& 1 << i
)) {
192 mRecordedTimes
[i
] = 0;
193 mRecordedCounts
[i
] = 0;
196 mStoredPerfStats
.Clear();
199 void PerfStats::StorePerfStatsInternal(dom::ContentParent
* aParent
,
200 const nsACString
& aPerfStats
) {
201 nsCString jsonString
;
202 JSONStringRefWriteFunc
jw(jsonString
);
205 // To generate correct JSON here we don't call start and end. That causes
206 // this to use Single Line mode, sadly.
207 WriteContentParent(jsonString
, w
, aPerfStats
, aParent
);
209 mStoredPerfStats
.AppendElement(jsonString
);
212 auto PerfStats::CollectPerfStatsJSONInternal() -> RefPtr
<PerfStatsPromise
> {
213 if (!PerfStats::sCollectionMask
) {
214 return PerfStatsPromise::CreateAndReject(false, __func__
);
217 if (!XRE_IsParentProcess()) {
218 return PerfStatsPromise::CreateAndResolve(
219 CollectLocalPerfStatsJSONInternal(), __func__
);
222 std::shared_ptr
<PerfStatsCollector
> collector
=
223 std::make_shared
<PerfStatsCollector
>();
225 JSONWriter
& w
= collector
->writer
;
229 w
.StartArrayProperty("processes");
231 w
.StartObjectElement();
233 w
.StringProperty("type", "parent");
234 AppendJSONStringAsProperty(collector
->string
, "perfstats",
235 CollectLocalPerfStatsJSONInternal());
239 // Append any processes that closed earlier.
240 for (nsCString
& string
: mStoredPerfStats
) {
241 w
.StartObjectElement();
242 // This trick makes indentation even more messed up than it already
243 // was. However it produces technically correct JSON.
244 collector
->string
.Append(string
);
247 // We do not clear this, we only clear stored perfstats when the mask is
250 GPUProcessManager
* gpuManager
= GPUProcessManager::Get();
251 GPUChild
* gpuChild
= nullptr;
254 gpuChild
= gpuManager
->GetGPUChild();
256 nsTArray
<ContentParent
*> contentParents
;
257 ContentParent::GetAll(contentParents
);
260 gpuChild
->SendCollectPerfStatsJSON(
261 [collector
, gpuChild
= RefPtr
{gpuChild
}](const nsCString
& aString
) {
262 collector
->AppendPerfStats(aString
, gpuChild
);
264 // The only feasible errors here are if something goes wrong in the
265 // the bridge, we choose to ignore those.
266 [](mozilla::ipc::ResponseRejectReason
) {});
268 for (ContentParent
* parent
: contentParents
) {
269 RefPtr
<ContentParent
> parentRef
= parent
;
270 parent
->SendCollectPerfStatsJSON(
271 [collector
, parentRef
](const nsCString
& aString
) {
272 collector
->AppendPerfStats(aString
, parentRef
.get());
274 // The only feasible errors here are if something goes wrong in the
275 // the bridge, we choose to ignore those.
276 [](mozilla::ipc::ResponseRejectReason
) {});
281 return collector
->promise
.Ensure(__func__
);
284 nsCString
PerfStats::CollectLocalPerfStatsJSONInternal() {
285 StaticMutexAutoLock
lock(PerfStats::sMutex
);
287 nsCString jsonString
;
289 JSONStringRefWriteFunc
jw(jsonString
);
293 w
.StartArrayProperty("metrics");
295 for (uint64_t i
= 0; i
< static_cast<uint64_t>(Metric::Max
); i
++) {
296 if (!(sCollectionMask
& (1 << i
))) {
300 w
.StartObjectElement();
302 w
.IntProperty("id", i
);
303 w
.StringProperty("metric", MakeStringSpan(sMetricNames
[i
]));
304 w
.DoubleProperty("time", mRecordedTimes
[i
]);
305 w
.IntProperty("count", mRecordedCounts
[i
]);
317 } // namespace mozilla