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