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