Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "CheckerboardReportService.h"
7 :
8 : #include "gfxPrefs.h" // for gfxPrefs
9 : #include "jsapi.h" // for JS_Now
10 : #include "MainThreadUtils.h" // for NS_IsMainThread
11 : #include "mozilla/Assertions.h" // for MOZ_ASSERT
12 : #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
13 : #include "mozilla/Unused.h"
14 : #include "mozilla/dom/CheckerboardReportServiceBinding.h" // for dom::CheckerboardReports
15 : #include "mozilla/gfx/GPUParent.h"
16 : #include "mozilla/gfx/GPUProcessManager.h"
17 : #include "nsContentUtils.h" // for nsContentUtils
18 : #include "nsXULAppAPI.h"
19 :
20 : namespace mozilla {
21 : namespace layers {
22 :
23 3 : /*static*/ StaticRefPtr<CheckerboardEventStorage> CheckerboardEventStorage::sInstance;
24 :
25 : /*static*/ already_AddRefed<CheckerboardEventStorage>
26 0 : CheckerboardEventStorage::GetInstance()
27 : {
28 : // The instance in the parent process does all the work, so if this is getting
29 : // called in the child process something is likely wrong.
30 0 : MOZ_ASSERT(XRE_IsParentProcess());
31 :
32 0 : MOZ_ASSERT(NS_IsMainThread());
33 0 : if (!sInstance) {
34 0 : sInstance = new CheckerboardEventStorage();
35 0 : ClearOnShutdown(&sInstance);
36 : }
37 0 : RefPtr<CheckerboardEventStorage> instance = sInstance.get();
38 0 : return instance.forget();
39 : }
40 :
41 : void
42 0 : CheckerboardEventStorage::Report(uint32_t aSeverity, const std::string& aLog)
43 : {
44 0 : if (!NS_IsMainThread()) {
45 0 : RefPtr<Runnable> task = NS_NewRunnableFunction(
46 0 : "layers::CheckerboardEventStorage::Report", [aSeverity, aLog]() -> void {
47 0 : CheckerboardEventStorage::Report(aSeverity, aLog);
48 0 : });
49 0 : NS_DispatchToMainThread(task.forget());
50 0 : return;
51 : }
52 :
53 0 : if (XRE_IsGPUProcess()) {
54 0 : if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
55 0 : nsCString log(aLog.c_str());
56 0 : Unused << gpu->SendReportCheckerboard(aSeverity, log);
57 : }
58 0 : return;
59 : }
60 :
61 0 : RefPtr<CheckerboardEventStorage> storage = GetInstance();
62 0 : storage->ReportCheckerboard(aSeverity, aLog);
63 : }
64 :
65 : void
66 0 : CheckerboardEventStorage::ReportCheckerboard(uint32_t aSeverity, const std::string& aLog)
67 : {
68 0 : MOZ_ASSERT(NS_IsMainThread());
69 :
70 0 : if (aSeverity == 0) {
71 : // This code assumes all checkerboard reports have a nonzero severity.
72 0 : return;
73 : }
74 :
75 0 : CheckerboardReport severe(aSeverity, JS_Now(), aLog);
76 0 : CheckerboardReport recent;
77 :
78 : // First look in the "severe" reports to see if the new one belongs in that
79 : // list.
80 0 : for (int i = 0; i < SEVERITY_MAX_INDEX; i++) {
81 0 : if (mCheckerboardReports[i].mSeverity >= severe.mSeverity) {
82 0 : continue;
83 : }
84 : // The new one deserves to be in the "severe" list. Take the one getting
85 : // bumped off the list, and put it in |recent| for possible insertion into
86 : // the recents list.
87 0 : recent = mCheckerboardReports[SEVERITY_MAX_INDEX - 1];
88 :
89 : // Shuffle the severe list down, insert the new one.
90 0 : for (int j = SEVERITY_MAX_INDEX - 1; j > i; j--) {
91 0 : mCheckerboardReports[j] = mCheckerboardReports[j - 1];
92 : }
93 0 : mCheckerboardReports[i] = severe;
94 0 : severe.mSeverity = 0; // mark |severe| as inserted
95 0 : break;
96 : }
97 :
98 : // If |severe.mSeverity| is nonzero, the incoming report didn't get inserted
99 : // into the severe list; put it into |recent| for insertion into the recent
100 : // list.
101 0 : if (severe.mSeverity) {
102 0 : MOZ_ASSERT(recent.mSeverity == 0, "recent should be empty here");
103 0 : recent = severe;
104 : } // else |recent| may hold a report that got knocked out of the severe list.
105 :
106 0 : if (recent.mSeverity == 0) {
107 : // Nothing to be inserted into the recent list.
108 0 : return;
109 : }
110 :
111 : // If it wasn't in the "severe" list, add it to the "recent" list.
112 0 : for (int i = SEVERITY_MAX_INDEX; i < RECENT_MAX_INDEX; i++) {
113 0 : if (mCheckerboardReports[i].mTimestamp >= recent.mTimestamp) {
114 0 : continue;
115 : }
116 : // |recent| needs to be inserted at |i|. Shuffle the remaining ones down
117 : // and insert it.
118 0 : for (int j = RECENT_MAX_INDEX - 1; j > i; j--) {
119 0 : mCheckerboardReports[j] = mCheckerboardReports[j - 1];
120 : }
121 0 : mCheckerboardReports[i] = recent;
122 0 : break;
123 : }
124 : }
125 :
126 : void
127 0 : CheckerboardEventStorage::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
128 : {
129 0 : MOZ_ASSERT(NS_IsMainThread());
130 :
131 0 : for (int i = 0; i < RECENT_MAX_INDEX; i++) {
132 0 : CheckerboardReport& r = mCheckerboardReports[i];
133 0 : if (r.mSeverity == 0) {
134 0 : continue;
135 : }
136 0 : dom::CheckerboardReport report;
137 0 : report.mSeverity.Construct() = r.mSeverity;
138 0 : report.mTimestamp.Construct() = r.mTimestamp / 1000; // micros to millis
139 0 : report.mLog.Construct() = NS_ConvertUTF8toUTF16(r.mLog.c_str(), r.mLog.size());
140 0 : report.mReason.Construct() = (i < SEVERITY_MAX_INDEX)
141 0 : ? dom::CheckerboardReason::Severe
142 : : dom::CheckerboardReason::Recent;
143 0 : aOutReports.AppendElement(report);
144 : }
145 0 : }
146 :
147 : } // namespace layers
148 :
149 : namespace dom {
150 :
151 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CheckerboardReportService, mParent)
152 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CheckerboardReportService, AddRef)
153 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CheckerboardReportService, Release)
154 :
155 : /*static*/ bool
156 0 : CheckerboardReportService::IsEnabled(JSContext* aCtx, JSObject* aGlobal)
157 : {
158 : // Only allow this in the parent process
159 0 : if (!XRE_IsParentProcess()) {
160 0 : return false;
161 : }
162 : // Allow privileged code or about:checkerboard (unprivileged) to access this.
163 0 : return nsContentUtils::IsSystemCaller(aCtx)
164 0 : || nsContentUtils::IsSpecificAboutPage(aGlobal, "about:checkerboard");
165 : }
166 :
167 : /*static*/ already_AddRefed<CheckerboardReportService>
168 0 : CheckerboardReportService::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv)
169 : {
170 0 : RefPtr<CheckerboardReportService> ces = new CheckerboardReportService(aGlobal.GetAsSupports());
171 0 : return ces.forget();
172 : }
173 :
174 0 : CheckerboardReportService::CheckerboardReportService(nsISupports* aParent)
175 0 : : mParent(aParent)
176 : {
177 0 : }
178 :
179 : JSObject*
180 0 : CheckerboardReportService::WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto)
181 : {
182 0 : return CheckerboardReportServiceBinding::Wrap(aCtx, this, aGivenProto);
183 : }
184 :
185 : nsISupports*
186 0 : CheckerboardReportService::GetParentObject()
187 : {
188 0 : return mParent;
189 : }
190 :
191 : void
192 0 : CheckerboardReportService::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
193 : {
194 : RefPtr<mozilla::layers::CheckerboardEventStorage> instance =
195 0 : mozilla::layers::CheckerboardEventStorage::GetInstance();
196 0 : MOZ_ASSERT(instance);
197 0 : instance->GetReports(aOutReports);
198 0 : }
199 :
200 : bool
201 0 : CheckerboardReportService::IsRecordingEnabled() const
202 : {
203 0 : return gfxPrefs::APZRecordCheckerboarding();
204 : }
205 :
206 : void
207 0 : CheckerboardReportService::SetRecordingEnabled(bool aEnabled)
208 : {
209 0 : gfxPrefs::SetAPZRecordCheckerboarding(aEnabled);
210 0 : }
211 :
212 : void
213 0 : CheckerboardReportService::FlushActiveReports()
214 : {
215 0 : MOZ_ASSERT(XRE_IsParentProcess());
216 0 : gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
217 0 : if (gpu && gpu->NotifyGpuObservers("APZ:FlushActiveCheckerboard")) {
218 0 : return;
219 : }
220 :
221 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
222 0 : MOZ_ASSERT(obsSvc);
223 0 : if (obsSvc) {
224 0 : obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
225 : }
226 : }
227 :
228 : } // namespace dom
229 : } // namespace mozilla
|