LCOV - code coverage report
Current view: top level - xpcom/ds - nsObserverService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 48 135 35.6 %
Date: 2017-07-14 16:53:18 Functions: 9 15 60.0 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13