/src/mozilla-central/docshell/base/timeline/TimelineConsumers.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 "TimelineConsumers.h" |
8 | | |
9 | | #include "mozilla/ClearOnShutdown.h" |
10 | | #include "jsapi.h" |
11 | | #include "nsAppRunner.h" // for XRE_IsContentProcess, XRE_IsParentProcess |
12 | | #include "nsDocShell.h" |
13 | | |
14 | | namespace mozilla { |
15 | | |
16 | | NS_IMPL_ISUPPORTS(TimelineConsumers, nsIObserver); |
17 | | |
18 | | StaticMutex TimelineConsumers::sMutex; |
19 | | |
20 | | // Manually manage this singleton's lifetime and destroy it before shutdown. |
21 | | // This avoids the leakchecker detecting false-positive memory leaks when |
22 | | // using automatic memory management (i.e. statically instantiating this |
23 | | // singleton inside the `Get` method), which would automatically destroy it on |
24 | | // application shutdown, but too late for the leakchecker. Sigh... |
25 | | StaticRefPtr<TimelineConsumers> TimelineConsumers::sInstance; |
26 | | |
27 | | // This flag makes sure the singleton never gets instantiated while a shutdown |
28 | | // is in progress. This can actually happen, and `ClearOnShutdown` doesn't work |
29 | | // in these cases. |
30 | | bool TimelineConsumers::sInShutdown = false; |
31 | | |
32 | | already_AddRefed<TimelineConsumers> |
33 | | TimelineConsumers::Get() |
34 | 170 | { |
35 | 170 | // Using this class is not supported yet for other processes other than |
36 | 170 | // parent or content. To avoid accidental checks to methods like `IsEmpty`, |
37 | 170 | // which would probably always be true in those cases, assert here. |
38 | 170 | // Remember, there will be different singletons available to each process. |
39 | 170 | MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess()); |
40 | 170 | |
41 | 170 | // If we are shutting down, don't bother doing anything. Note: we can only |
42 | 170 | // know whether or not we're in shutdown if we're instantiated. |
43 | 170 | if (sInShutdown) { |
44 | 0 | return nullptr; |
45 | 0 | } |
46 | 170 | |
47 | 170 | // Note: We don't simply check `sInstance` for null-ness here, since otherwise |
48 | 170 | // this can resurrect the TimelineConsumers pretty late during shutdown. |
49 | 170 | // We won't know if we're in shutdown or not though, because the singleton |
50 | 170 | // could have been destroyed or just never instantiated, so in the previous |
51 | 170 | // conditional `sInShutdown` would be false. |
52 | 170 | static bool firstTime = true; |
53 | 170 | if (firstTime) { |
54 | 3 | firstTime = false; |
55 | 3 | |
56 | 3 | StaticMutexAutoLock lock(sMutex); |
57 | 3 | sInstance = new TimelineConsumers(); |
58 | 3 | |
59 | 3 | // Make sure the initialization actually suceeds, otherwise don't allow |
60 | 3 | // access by destroying the instance immediately. |
61 | 3 | if (sInstance->Init()) { |
62 | 3 | ClearOnShutdown(&sInstance); |
63 | 3 | } else { |
64 | 0 | sInstance->RemoveObservers(); |
65 | 0 | sInstance = nullptr; |
66 | 0 | } |
67 | 3 | } |
68 | 170 | |
69 | 170 | RefPtr<TimelineConsumers> copy = sInstance.get(); |
70 | 170 | return copy.forget(); |
71 | 170 | } |
72 | | |
73 | | bool |
74 | | TimelineConsumers::Init() |
75 | 3 | { |
76 | 3 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
77 | 3 | if (!obs) { |
78 | 0 | return false; |
79 | 0 | } |
80 | 3 | if (NS_WARN_IF(NS_FAILED( |
81 | 3 | obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)))) { |
82 | 0 | return false; |
83 | 0 | } |
84 | 3 | return true; |
85 | 3 | } |
86 | | |
87 | | bool |
88 | | TimelineConsumers::RemoveObservers() |
89 | 0 | { |
90 | 0 | nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
91 | 0 | if (!obs) { |
92 | 0 | return false; |
93 | 0 | } |
94 | 0 | if (NS_WARN_IF(NS_FAILED( |
95 | 0 | obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)))) { |
96 | 0 | return false; |
97 | 0 | } |
98 | 0 | return true; |
99 | 0 | } |
100 | | |
101 | | nsresult |
102 | | TimelineConsumers::Observe(nsISupports* aSubject, |
103 | | const char* aTopic, |
104 | | const char16_t* aData) |
105 | 0 | { |
106 | 0 | if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { |
107 | 0 | sInShutdown = true; |
108 | 0 | RemoveObservers(); |
109 | 0 | return NS_OK; |
110 | 0 | } |
111 | 0 | |
112 | 0 | MOZ_ASSERT(false, "TimelineConsumers got unexpected topic!"); |
113 | 0 | return NS_ERROR_UNEXPECTED; |
114 | 0 | } |
115 | | |
116 | | TimelineConsumers::TimelineConsumers() |
117 | | : mActiveConsumers(0) |
118 | 3 | { |
119 | 3 | } |
120 | | |
121 | | void |
122 | | TimelineConsumers::AddConsumer(nsDocShell* aDocShell) |
123 | 0 | { |
124 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
125 | 0 | StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`. |
126 | 0 |
|
127 | 0 | UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved; |
128 | 0 | MOZ_ASSERT(!observed); |
129 | 0 |
|
130 | 0 | if (mActiveConsumers == 0) { |
131 | 0 | JS::SetProfileTimelineRecordingEnabled(true); |
132 | 0 | } |
133 | 0 | mActiveConsumers++; |
134 | 0 |
|
135 | 0 | ObservedDocShell* obsDocShell = new ObservedDocShell(aDocShell); |
136 | 0 | MarkersStorage* storage = static_cast<MarkersStorage*>(obsDocShell); |
137 | 0 |
|
138 | 0 | observed.reset(obsDocShell); |
139 | 0 | mMarkersStores.insertFront(storage); |
140 | 0 | } |
141 | | |
142 | | void |
143 | | TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell) |
144 | 0 | { |
145 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
146 | 0 | StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers` and `mMarkersStores`. |
147 | 0 |
|
148 | 0 | UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved; |
149 | 0 | MOZ_ASSERT(observed); |
150 | 0 |
|
151 | 0 | mActiveConsumers--; |
152 | 0 | if (mActiveConsumers == 0) { |
153 | 0 | JS::SetProfileTimelineRecordingEnabled(false); |
154 | 0 | } |
155 | 0 |
|
156 | 0 | // Clear all markers from the `mTimelineMarkers` store. |
157 | 0 | observed.get()->ClearMarkers(); |
158 | 0 | // Remove self from the `mMarkersStores` store. |
159 | 0 | observed.get()->remove(); |
160 | 0 | // Prepare for becoming a consumer later. |
161 | 0 | observed.reset(nullptr); |
162 | 0 | } |
163 | | |
164 | | bool |
165 | | TimelineConsumers::HasConsumer(nsIDocShell* aDocShell) |
166 | 0 | { |
167 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
168 | 0 | return aDocShell |
169 | 0 | ? aDocShell->GetRecordProfileTimelineMarkers() |
170 | 0 | : false; |
171 | 0 | } |
172 | | |
173 | | bool |
174 | | TimelineConsumers::IsEmpty() |
175 | 170 | { |
176 | 170 | StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers`. |
177 | 170 | return mActiveConsumers == 0; |
178 | 170 | } |
179 | | |
180 | | void |
181 | | TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell, |
182 | | const char* aName, |
183 | | MarkerTracingType aTracingType, |
184 | | MarkerStackRequest aStackRequest) |
185 | 0 | { |
186 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
187 | 0 | if (HasConsumer(aDocShell)) { |
188 | 0 | aDocShell->mObserved->AddMarker(MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest)); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | void |
193 | | TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell, |
194 | | const char* aName, |
195 | | const TimeStamp& aTime, |
196 | | MarkerTracingType aTracingType, |
197 | | MarkerStackRequest aStackRequest) |
198 | 0 | { |
199 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
200 | 0 | if (HasConsumer(aDocShell)) { |
201 | 0 | aDocShell->mObserved->AddMarker(MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest)); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | void |
206 | | TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell, |
207 | | UniquePtr<AbstractTimelineMarker>&& aMarker) |
208 | 0 | { |
209 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
210 | 0 | if (HasConsumer(aDocShell)) { |
211 | 0 | aDocShell->mObserved->AddMarker(std::move(aMarker)); |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | void |
216 | | TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell, |
217 | | const char* aName, |
218 | | MarkerTracingType aTracingType, |
219 | | MarkerStackRequest aStackRequest) |
220 | 0 | { |
221 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
222 | 0 | AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType, aStackRequest); |
223 | 0 | } |
224 | | |
225 | | void |
226 | | TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell, |
227 | | const char* aName, |
228 | | const TimeStamp& aTime, |
229 | | MarkerTracingType aTracingType, |
230 | | MarkerStackRequest aStackRequest) |
231 | 0 | { |
232 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
233 | 0 | AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aTracingType, aStackRequest); |
234 | 0 | } |
235 | | |
236 | | void |
237 | | TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell, |
238 | | UniquePtr<AbstractTimelineMarker>&& aMarker) |
239 | 0 | { |
240 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
241 | 0 | AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), std::move(aMarker)); |
242 | 0 | } |
243 | | |
244 | | void |
245 | | TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName, |
246 | | MarkerTracingType aTracingType, |
247 | | MarkerStackRequest aStackRequest /* = STACK */) |
248 | 0 | { |
249 | 0 | bool isMainThread = NS_IsMainThread(); |
250 | 0 | StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`. |
251 | 0 |
|
252 | 0 | for (MarkersStorage* storage = mMarkersStores.getFirst(); |
253 | 0 | storage != nullptr; |
254 | 0 | storage = storage->getNext()) { |
255 | 0 | UniquePtr<AbstractTimelineMarker> marker = |
256 | 0 | MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest); |
257 | 0 | if (isMainThread) { |
258 | 0 | storage->AddMarker(std::move(marker)); |
259 | 0 | } else { |
260 | 0 | storage->AddOTMTMarker(std::move(marker)); |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | void |
266 | | TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName, |
267 | | const TimeStamp& aTime, |
268 | | MarkerTracingType aTracingType, |
269 | | MarkerStackRequest aStackRequest /* = STACK */) |
270 | 0 | { |
271 | 0 | bool isMainThread = NS_IsMainThread(); |
272 | 0 | StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`. |
273 | 0 |
|
274 | 0 | for (MarkersStorage* storage = mMarkersStores.getFirst(); |
275 | 0 | storage != nullptr; |
276 | 0 | storage = storage->getNext()) { |
277 | 0 | UniquePtr<AbstractTimelineMarker> marker = |
278 | 0 | MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest); |
279 | 0 | if (isMainThread) { |
280 | 0 | storage->AddMarker(std::move(marker)); |
281 | 0 | } else { |
282 | 0 | storage->AddOTMTMarker(std::move(marker)); |
283 | 0 | } |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | | void |
288 | | TimelineConsumers::AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker) |
289 | 0 | { |
290 | 0 | bool isMainThread = NS_IsMainThread(); |
291 | 0 | StaticMutexAutoLock lock(sMutex); // for `mMarkersStores`. |
292 | 0 |
|
293 | 0 | for (MarkersStorage* storage = mMarkersStores.getFirst(); |
294 | 0 | storage != nullptr; |
295 | 0 | storage = storage->getNext()) { |
296 | 0 | UniquePtr<AbstractTimelineMarker> clone = aMarker->Clone(); |
297 | 0 | if (isMainThread) { |
298 | 0 | storage->AddMarker(std::move(clone)); |
299 | 0 | } else { |
300 | 0 | storage->AddOTMTMarker(std::move(clone)); |
301 | 0 | } |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | void |
306 | | TimelineConsumers::PopMarkers(nsDocShell* aDocShell, |
307 | | JSContext* aCx, |
308 | | nsTArray<dom::ProfileTimelineMarker>& aStore) |
309 | 0 | { |
310 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
311 | 0 |
|
312 | 0 | if (!aDocShell || !aDocShell->mObserved) { |
313 | 0 | return; |
314 | 0 | } |
315 | 0 | |
316 | 0 | aDocShell->mObserved->PopMarkers(aCx, aStore); |
317 | 0 | } |
318 | | |
319 | | } // namespace mozilla |