Coverage Report

Created: 2018-09-25 14:53

/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