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