Bug 1800492 - Fix division by zero when calculating GC_TENURED_SURVIVAL_RATE telemetr...
[gecko.git] / tools / performance / PerfStats.cpp
blob6b217957854468a3da3914329b7197fa7b9ce6b0
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/. */
7 #include "PerfStats.h"
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;
21 namespace mozilla {
23 #define METRIC_NAME(metric) #metric,
24 static const char* const sMetricNames[] = {
25 FOR_EACH_PERFSTATS_METRIC(METRIC_NAME)
26 #undef METRIC_NAME
27 "Invalid"};
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()) {
38 return;
41 GPUProcessManager* gpuManager = GPUProcessManager::Get();
42 GPUChild* gpuChild = nullptr;
44 if (gpuManager) {
45 gpuChild = gpuManager->GetGPUChild();
46 if (gpuChild) {
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() {
62 if (!sSingleton) {
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)] =
73 TimeStamp::Now();
76 void PerfStats::RecordMeasurementEndInternal(Metric aMetric) {
77 StaticMutexAutoLock lock(sMutex);
79 MOZ_ASSERT(sSingleton);
81 sSingleton->mRecordedTimes[static_cast<size_t>(aMetric)] +=
82 (TimeStamp::Now() -
83 sSingleton->mRecordedStarts[static_cast<size_t>(aMetric)])
84 .ToMilliseconds();
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("\": ");
117 aDest.Append(aJSON);
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();
136 if (!ctx) {
137 continue;
140 WindowGlobalParent* windowGlobal = ctx->GetCurrentWindowGlobal();
141 if (!windowGlobal) {
142 continue;
145 RefPtr<nsIURI> uri = windowGlobal->GetDocumentURI();
146 if (!uri) {
147 continue;
150 nsAutoCString url;
151 uri->GetSpec(url);
153 aWriter.StringElement(url);
155 aWriter.EndArray();
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);
165 writer.EndObject();
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);
173 writer.EndObject();
176 ~PerfStatsCollector() {
177 writer.EndArray();
178 writer.End();
179 promise.Resolve(string, __func__);
181 nsCString string;
182 JSONWriter writer;
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)) {
189 continue;
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);
203 JSONWriter w(jw);
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;
227 w.Start();
229 w.StartArrayProperty("processes");
231 w.StartObjectElement();
233 w.StringProperty("type", "parent");
234 AppendJSONStringAsProperty(collector->string, "perfstats",
235 CollectLocalPerfStatsJSONInternal());
237 w.EndObject();
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);
245 w.EndObject();
247 // We do not clear this, we only clear stored perfstats when the mask is
248 // reset.
250 GPUProcessManager* gpuManager = GPUProcessManager::Get();
251 GPUChild* gpuChild = nullptr;
253 if (gpuManager) {
254 gpuChild = gpuManager->GetGPUChild();
256 nsTArray<ContentParent*> contentParents;
257 ContentParent::GetAll(contentParents);
259 if (gpuChild) {
260 gpuChild->SendCollectPerfStatsJSON(
261 [collector, 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);
290 JSONWriter w(jw);
291 w.Start();
293 w.StartArrayProperty("metrics");
295 for (uint64_t i = 0; i < static_cast<uint64_t>(Metric::Max); i++) {
296 if (!(sCollectionMask & (1 << i))) {
297 continue;
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]);
307 w.EndObject();
310 w.EndArray();
312 w.End();
314 return jsonString;
317 } // namespace mozilla