Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/ds/nsObserverService.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 "mozilla/Logging.h"
8
#include "nsAutoPtr.h"
9
#include "nsIConsoleService.h"
10
#include "nsIObserverService.h"
11
#include "nsIObserver.h"
12
#include "nsIScriptError.h"
13
#include "nsObserverService.h"
14
#include "nsObserverList.h"
15
#include "nsServiceManagerUtils.h"
16
#include "nsThreadUtils.h"
17
#include "nsEnumeratorUtils.h"
18
#include "xpcpublic.h"
19
#include "mozilla/net/NeckoCommon.h"
20
#include "mozilla/Services.h"
21
#include "mozilla/Telemetry.h"
22
#include "mozilla/TimeStamp.h"
23
#include "nsString.h"
24
#include "GeckoProfiler.h"
25
26
static const uint32_t kMinTelemetryNotifyObserversLatencyMs = 1;
27
28
// Log module for nsObserverService logging...
29
//
30
// To enable logging (see prlog.h for full details):
31
//
32
//    set MOZ_LOG=ObserverService:5
33
//    set MOZ_LOG_FILE=service.log
34
//
35
// This enables LogLevel::Debug level information and places all output in
36
// the file service.log.
37
static mozilla::LazyLogModule sObserverServiceLog("ObserverService");
38
371
#define LOG(x) MOZ_LOG(sObserverServiceLog, mozilla::LogLevel::Debug, x)
39
40
using namespace mozilla;
41
42
NS_IMETHODIMP
43
nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
44
                                  nsISupports* aData, bool aAnonymize)
45
0
{
46
0
  struct SuspectObserver
47
0
  {
48
0
    SuspectObserver(const char* aTopic, size_t aReferentCount)
49
0
      : mTopic(aTopic)
50
0
      , mReferentCount(aReferentCount)
51
0
    {}
52
0
    const char* mTopic;
53
0
    size_t mReferentCount;
54
0
  };
55
0
56
0
  size_t totalNumStrong = 0;
57
0
  size_t totalNumWeakAlive = 0;
58
0
  size_t totalNumWeakDead = 0;
59
0
  nsTArray<SuspectObserver> suspectObservers;
60
0
61
0
  for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
62
0
    nsObserverList* observerList = iter.Get();
63
0
    if (!observerList) {
64
0
      continue;
65
0
    }
66
0
67
0
    size_t topicNumStrong = 0;
68
0
    size_t topicNumWeakAlive = 0;
69
0
    size_t topicNumWeakDead = 0;
70
0
71
0
    nsTArray<ObserverRef>& observers = observerList->mObservers;
72
0
    for (uint32_t i = 0; i < observers.Length(); i++) {
73
0
      if (observers[i].isWeakRef) {
74
0
        nsCOMPtr<nsIObserver> observerRef(
75
0
          do_QueryReferent(observers[i].asWeak()));
76
0
        if (observerRef) {
77
0
          topicNumWeakAlive++;
78
0
        } else {
79
0
          topicNumWeakDead++;
80
0
        }
81
0
      } else {
82
0
        topicNumStrong++;
83
0
      }
84
0
    }
85
0
86
0
    totalNumStrong += topicNumStrong;
87
0
    totalNumWeakAlive += topicNumWeakAlive;
88
0
    totalNumWeakDead += topicNumWeakDead;
89
0
90
0
    // Keep track of topics that have a suspiciously large number
91
0
    // of referents (symptom of leaks).
92
0
    size_t topicTotal = topicNumStrong + topicNumWeakAlive + topicNumWeakDead;
93
0
    if (topicTotal > kSuspectReferentCount) {
94
0
      SuspectObserver suspect(observerList->GetKey(), topicTotal);
95
0
      suspectObservers.AppendElement(suspect);
96
0
    }
97
0
  }
98
0
99
0
  // These aren't privacy-sensitive and so don't need anonymizing.
100
0
  for (uint32_t i = 0; i < suspectObservers.Length(); i++) {
101
0
    SuspectObserver& suspect = suspectObservers[i];
102
0
    nsPrintfCString suspectPath("observer-service-suspect/referent(topic=%s)",
103
0
                                suspect.mTopic);
104
0
    aHandleReport->Callback(
105
0
      /* process */ EmptyCString(),
106
0
      suspectPath, KIND_OTHER, UNITS_COUNT, suspect.mReferentCount,
107
0
      NS_LITERAL_CSTRING("A topic with a suspiciously large number of "
108
0
                         "referents.  This may be symptomatic of a leak "
109
0
                         "if the number of referents is high with "
110
0
                         "respect to the number of windows."),
111
0
      aData);
112
0
  }
113
0
114
0
  MOZ_COLLECT_REPORT(
115
0
    "observer-service/referent/strong", KIND_OTHER, UNITS_COUNT,
116
0
    totalNumStrong,
117
0
    "The number of strong references held by the observer service.");
118
0
119
0
  MOZ_COLLECT_REPORT(
120
0
    "observer-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
121
0
    totalNumWeakAlive,
122
0
    "The number of weak references held by the observer service that are "
123
0
    "still alive.");
124
0
125
0
  MOZ_COLLECT_REPORT(
126
0
    "observer-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
127
0
    totalNumWeakDead,
128
0
    "The number of weak references held by the observer service that are "
129
0
    "dead.");
130
0
131
0
  return NS_OK;
132
0
}
133
134
////////////////////////////////////////////////////////////////////////////////
135
// nsObserverService Implementation
136
137
NS_IMPL_ISUPPORTS(nsObserverService,
138
                  nsIObserverService,
139
                  nsObserverService,
140
                  nsIMemoryReporter)
141
142
nsObserverService::nsObserverService()
143
  : mShuttingDown(false)
144
3
{
145
3
}
146
147
nsObserverService::~nsObserverService(void)
148
0
{
149
0
  Shutdown();
150
0
}
151
152
void
153
nsObserverService::RegisterReporter()
154
0
{
155
0
  RegisterWeakMemoryReporter(this);
156
0
}
157
158
void
159
nsObserverService::Shutdown()
160
0
{
161
0
  UnregisterWeakMemoryReporter(this);
162
0
163
0
  mShuttingDown = true;
164
0
165
0
  mObserverTopicTable.Clear();
166
0
}
167
168
nsresult
169
nsObserverService::Create(nsISupports* aOuter, const nsIID& aIID,
170
                          void** aInstancePtr)
171
3
{
172
3
  LOG(("nsObserverService::Create()"));
173
3
174
3
  RefPtr<nsObserverService> os = new nsObserverService();
175
3
176
3
  if (!os) {
177
0
    return NS_ERROR_OUT_OF_MEMORY;
178
0
  }
179
3
180
3
  // The memory reporter can not be immediately registered here because
181
3
  // the nsMemoryReporterManager may attempt to get the nsObserverService
182
3
  // during initialization, causing a recursive GetService.
183
3
  NS_DispatchToCurrentThread(
184
3
    NewRunnableMethod("nsObserverService::RegisterReporter",
185
3
                      os,
186
3
                      &nsObserverService::RegisterReporter));
187
3
188
3
  return os->QueryInterface(aIID, aInstancePtr);
189
3
}
190
191
#define NS_ENSURE_VALIDCALL \
192
368
    if (!NS_IsMainThread()) {                                     \
193
0
        MOZ_CRASH("Using observer service off the main thread!"); \
194
0
        return NS_ERROR_UNEXPECTED;                               \
195
0
    }                                                             \
196
368
    if (mShuttingDown) {                                          \
197
0
        NS_ERROR("Using observer service after XPCOM shutdown!"); \
198
0
        return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;                  \
199
0
    }
200
201
NS_IMETHODIMP
202
nsObserverService::AddObserver(nsIObserver* aObserver, const char* aTopic,
203
                               bool aOwnsWeak)
204
347
{
205
347
  LOG(("nsObserverService::AddObserver(%p: %s)",
206
347
       (void*)aObserver, aTopic));
207
347
208
347
  NS_ENSURE_VALIDCALL
209
347
  if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
210
0
    return NS_ERROR_INVALID_ARG;
211
0
  }
212
347
213
347
  // Specifically allow http-on-opening-request and http-on-stop-request in the
214
347
  // child process; see bug 1269765.
215
347
  if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8) &&
216
347
      strcmp(aTopic, "http-on-opening-request") &&
217
347
      strcmp(aTopic, "http-on-stop-request")) {
218
0
    nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
219
0
    nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
220
0
    error->Init(NS_LITERAL_STRING("http-on-* observers only work in the parent process"),
221
0
                EmptyString(), EmptyString(), 0, 0,
222
0
                nsIScriptError::warningFlag, "chrome javascript",
223
0
                false /* from private window */);
224
0
    console->LogMessage(error);
225
0
226
0
    return NS_ERROR_NOT_IMPLEMENTED;
227
0
  }
228
347
229
347
  nsObserverList* observerList = mObserverTopicTable.PutEntry(aTopic);
230
347
  if (!observerList) {
231
0
    return NS_ERROR_OUT_OF_MEMORY;
232
0
  }
233
347
234
347
  return observerList->AddObserver(aObserver, aOwnsWeak);
235
347
}
236
237
NS_IMETHODIMP
238
nsObserverService::RemoveObserver(nsIObserver* aObserver, const char* aTopic)
239
9
{
240
9
  LOG(("nsObserverService::RemoveObserver(%p: %s)",
241
9
       (void*)aObserver, aTopic));
242
9
  NS_ENSURE_VALIDCALL
243
9
  if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
244
0
    return NS_ERROR_INVALID_ARG;
245
0
  }
246
9
247
9
  nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
248
9
  if (!observerList) {
249
9
    return NS_ERROR_FAILURE;
250
9
  }
251
0
252
0
  /* This death grip is to protect against stupid consumers who call
253
0
     RemoveObserver from their Destructor, see bug 485834/bug 325392. */
254
0
  nsCOMPtr<nsIObserver> kungFuDeathGrip(aObserver);
255
0
  return observerList->RemoveObserver(aObserver);
256
0
}
257
258
NS_IMETHODIMP
259
nsObserverService::EnumerateObservers(const char* aTopic,
260
                                      nsISimpleEnumerator** anEnumerator)
261
0
{
262
0
  NS_ENSURE_VALIDCALL
263
0
  if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) {
264
0
    return NS_ERROR_INVALID_ARG;
265
0
  }
266
0
267
0
  nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
268
0
  if (!observerList) {
269
0
    return NS_NewEmptyEnumerator(anEnumerator);
270
0
  }
271
0
272
0
  observerList->GetObserverList(anEnumerator);
273
0
  return NS_OK;
274
0
}
275
276
// Enumerate observers of aTopic and call Observe on each.
277
NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports* aSubject,
278
                                                 const char* aTopic,
279
                                                 const char16_t* aSomeData)
280
12
{
281
12
  LOG(("nsObserverService::NotifyObservers(%s)", aTopic));
282
12
283
12
  NS_ENSURE_VALIDCALL
284
12
  if (NS_WARN_IF(!aTopic)) {
285
0
    return NS_ERROR_INVALID_ARG;
286
0
  }
287
12
288
12
  mozilla::TimeStamp start = TimeStamp::Now();
289
12
290
12
  AUTO_PROFILER_LABEL_DYNAMIC_CSTR(
291
12
    "nsObserverService::NotifyObservers", OTHER, aTopic);
292
12
293
12
  nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
294
12
  if (observerList) {
295
0
    observerList->NotifyObservers(aSubject, aTopic, aSomeData);
296
0
  }
297
12
298
12
  uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
299
12
  if (latencyMs >= kMinTelemetryNotifyObserversLatencyMs) {
300
0
    Telemetry::Accumulate(Telemetry::NOTIFY_OBSERVERS_LATENCY_MS,
301
0
                          nsDependentCString(aTopic),
302
0
                          latencyMs);
303
0
  }
304
12
305
12
  return NS_OK;
306
12
}
307
308
NS_IMETHODIMP
309
nsObserverService::UnmarkGrayStrongObservers()
310
0
{
311
0
  NS_ENSURE_VALIDCALL
312
0
313
0
  nsCOMArray<nsIObserver> strongObservers;
314
0
  for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
315
0
    nsObserverList* aObserverList = iter.Get();
316
0
    if (aObserverList) {
317
0
      aObserverList->AppendStrongObservers(strongObservers);
318
0
    }
319
0
  }
320
0
321
0
  for (uint32_t i = 0; i < strongObservers.Length(); ++i) {
322
0
    xpc_TryUnmarkWrappedGrayObject(strongObservers[i]);
323
0
  }
324
0
325
0
  return NS_OK;
326
0
}