LCOV - code coverage report
Current view: top level - xpcom/base - nsMemoryReporterManager.h (source / functions) Hit Total Coverage
Test: output.info Lines: 11 17 64.7 %
Date: 2017-07-14 16:53:18 Functions: 3 6 50.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             : #ifndef nsMemoryReporterManager_h__
       8             : #define nsMemoryReporterManager_h__
       9             : 
      10             : #include "mozilla/Mutex.h"
      11             : #include "nsDataHashtable.h"
      12             : #include "nsHashKeys.h"
      13             : #include "nsIEventTarget.h"
      14             : #include "nsIMemoryReporter.h"
      15             : #include "nsITimer.h"
      16             : #include "nsServiceManagerUtils.h"
      17             : #include "nsDataHashtable.h"
      18             : 
      19             : namespace mozilla {
      20             : class MemoryReportingProcess;
      21             : namespace dom {
      22             : class MemoryReport;
      23             : } // namespace dom
      24             : } // namespace mozilla
      25             : 
      26             : class nsITimer;
      27             : 
      28             : class nsMemoryReporterManager final : public nsIMemoryReporterManager
      29             : {
      30             :   virtual ~nsMemoryReporterManager();
      31             : 
      32             : public:
      33             :   NS_DECL_THREADSAFE_ISUPPORTS
      34             :   NS_DECL_NSIMEMORYREPORTERMANAGER
      35             : 
      36             :   nsMemoryReporterManager();
      37             : 
      38             :   // Gets the memory reporter manager service.
      39         150 :   static nsMemoryReporterManager* GetOrCreate()
      40             :   {
      41             :     nsCOMPtr<nsIMemoryReporterManager> imgr =
      42         300 :       do_GetService("@mozilla.org/memory-reporter-manager;1");
      43         300 :     return static_cast<nsMemoryReporterManager*>(imgr.get());
      44             :   }
      45             : 
      46             :   typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
      47             :   typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
      48             : 
      49             :   // Inter-process memory reporting proceeds as follows.
      50             :   //
      51             :   // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
      52             :   //   synchronously gets memory reports for the current process, sets up some
      53             :   //   state (mPendingProcessesState) for when child processes report back --
      54             :   //   including a timer -- and starts telling child processes to get memory
      55             :   //   reports.  Control then returns to the main event loop.
      56             :   //
      57             :   //   The number of concurrent child process reports is limited by the pref
      58             :   //   "memory.report_concurrency" in order to prevent the memory overhead of
      59             :   //   memory reporting from causing problems, especially on B2G when swapping
      60             :   //   to compressed RAM; see bug 1154053.
      61             :   //
      62             :   // - HandleChildReport() is called (asynchronously) once per child process
      63             :   //   reporter callback.
      64             :   //
      65             :   // - EndProcessReport() is called (asynchronously) once per process that
      66             :   //   finishes reporting back, including the parent.  If all processes do so
      67             :   //   before time-out, the timer is cancelled.  If there are child processes
      68             :   //   whose requests have not yet been sent, they will be started until the
      69             :   //   concurrency limit is (again) reached.
      70             :   //
      71             :   // - TimeoutCallback() is called (asynchronously) if all the child processes
      72             :   //   don't respond within the time threshold.
      73             :   //
      74             :   // - FinishReporting() finishes things off.  It is *always* called -- either
      75             :   //   from EndChildReport() (if all child processes have reported back) or
      76             :   //   from TimeoutCallback() (if time-out occurs).
      77             :   //
      78             :   // All operations occur on the main thread.
      79             :   //
      80             :   // The above sequence of steps is a "request".  A partially-completed request
      81             :   // is described as "in flight".
      82             :   //
      83             :   // Each request has a "generation", a unique number that identifies it.  This
      84             :   // is used to ensure that each reports from a child process corresponds to
      85             :   // the appropriate request from the parent process.  (It's easier to
      86             :   // implement a generation system than to implement a child report request
      87             :   // cancellation mechanism.)
      88             :   //
      89             :   // Failures are mostly ignored, because it's (a) typically the most sensible
      90             :   // thing to do, and (b) often hard to do anything else.  The following are
      91             :   // the failure cases of note.
      92             :   //
      93             :   // - If a request is made while the previous request is in flight, the new
      94             :   //   request is ignored, as per getReports()'s specification.  No error is
      95             :   //   reported, because the previous request will complete soon enough.
      96             :   //
      97             :   // - If one or more child processes fail to respond within the time limit,
      98             :   //   things will proceed as if they don't exist.  No error is reported,
      99             :   //   because partial information is better than nothing.
     100             :   //
     101             :   // - If a child process reports after the time-out occurs, it is ignored.
     102             :   //   (Generation checking will ensure it is ignored even if a subsequent
     103             :   //   request is in flight;  this is the main use of generations.)  No error
     104             :   //   is reported, because there's nothing sensible to be done about it at
     105             :   //   this late stage.
     106             :   //
     107             :   // - If the time-out occurs after a child process has sent some reports but
     108             :   //   before it has signaled completion (see bug 1151597), then what it
     109             :   //   successfully sent will be included, with no explicit indication that it
     110             :   //   is incomplete.
     111             :   //
     112             :   // Now, what what happens if a child process is created/destroyed in the
     113             :   // middle of a request?  Well, PendingProcessesState is initialized with an array
     114             :   // of child process actors as of when the report started.  So...
     115             :   //
     116             :   // - If a process is created after reporting starts, it won't be sent a
     117             :   //   request for reports.  So the reported data will reflect how things were
     118             :   //   when the request began.
     119             :   //
     120             :   // - If a process is destroyed before it starts reporting back, the reported
     121             :   //   data will reflect how things are when the request ends.
     122             :   //
     123             :   // - If a process is destroyed after it starts reporting back but before it
     124             :   //   finishes, the reported data will contain a partial report for it.
     125             :   //
     126             :   // - If a process is destroyed after reporting back, but before all other
     127             :   //   child processes have reported back, it will be included in the reported
     128             :   //   data.  So the reported data will reflect how things were when the
     129             :   //   request began.
     130             :   //
     131             :   // The inconsistencies between these cases are unfortunate but difficult to
     132             :   // avoid.  It's enough of an edge case to not be worth doing more.
     133             :   //
     134             :   void HandleChildReport(uint32_t aGeneration,
     135             :                          const mozilla::dom::MemoryReport& aChildReport);
     136             :   void EndProcessReport(uint32_t aGeneration, bool aSuccess);
     137             : 
     138             :   // Functions that (a) implement distinguished amounts, and (b) are outside of
     139             :   // this module.
     140             :   struct AmountFns
     141             :   {
     142             :     mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
     143             :     mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
     144             :     mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
     145             :     mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
     146             : 
     147             :     mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
     148             : 
     149             :     mozilla::InfallibleAmountFn mStorageSQLite;
     150             : 
     151             :     mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
     152             :     mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
     153             : 
     154             :     mozilla::InfallibleAmountFn mGhostWindows;
     155             : 
     156           3 :     AmountFns()
     157           3 :     {
     158           3 :       mozilla::PodZero(this);
     159           3 :     }
     160             :   };
     161             :   AmountFns mAmountFns;
     162             : 
     163             :   // Convenience function to get RSS easily from other code.  This is useful
     164             :   // when debugging transient memory spikes with printf instrumentation.
     165             :   static int64_t ResidentFast();
     166             : 
     167             :   // Convenience function to get peak RSS easily from other code.
     168             :   static int64_t ResidentPeak();
     169             : 
     170             :   // Convenience function to get USS easily from other code.  This is useful
     171             :   // when debugging unshared memory pages for forked processes.
     172             :   static int64_t ResidentUnique();
     173             : 
     174             :   // Functions that measure per-tab memory consumption.
     175             :   struct SizeOfTabFns
     176             :   {
     177             :     mozilla::JSSizeOfTabFn    mJS;
     178             :     mozilla::NonJSSizeOfTabFn mNonJS;
     179             : 
     180           3 :     SizeOfTabFns()
     181           3 :     {
     182           3 :       mozilla::PodZero(this);
     183           3 :     }
     184             :   };
     185             :   SizeOfTabFns mSizeOfTabFns;
     186             : 
     187             : private:
     188             :   MOZ_MUST_USE nsresult
     189             :   RegisterReporterHelper(nsIMemoryReporter* aReporter,
     190             :                          bool aForce, bool aStrongRef, bool aIsAsync);
     191             : 
     192             :   MOZ_MUST_USE nsresult StartGettingReports();
     193             :   // No MOZ_MUST_USE here because ignoring the result is common and reasonable.
     194             :   nsresult FinishReporting();
     195             : 
     196             :   void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
     197             :                         nsIHandleReportCallback* aHandleReport,
     198             :                         nsISupports* aHandleReportData,
     199             :                         bool aAnonymize);
     200             : 
     201             :   static void TimeoutCallback(nsITimer* aTimer, void* aData);
     202             :   // Note: this timeout needs to be long enough to allow for the
     203             :   // possibility of DMD reports and/or running on a low-end phone.
     204             :   static const uint32_t kTimeoutLengthMS = 50000;
     205             : 
     206             :   mozilla::Mutex mMutex;
     207             :   bool mIsRegistrationBlocked;
     208             : 
     209             :   StrongReportersTable* mStrongReporters;
     210             :   WeakReportersTable* mWeakReporters;
     211             : 
     212             :   // These two are only used for testing purposes.
     213             :   StrongReportersTable* mSavedStrongReporters;
     214             :   WeakReportersTable* mSavedWeakReporters;
     215             : 
     216             :   uint32_t mNextGeneration;
     217             : 
     218             :   // Used to keep track of state of which processes are currently running and
     219             :   // waiting to run memory reports. Holds references to parameters needed when
     220             :   // requesting a memory report and finishing reporting.
     221           0 :   struct PendingProcessesState
     222             :   {
     223             :     uint32_t                             mGeneration;
     224             :     bool                                 mAnonymize;
     225             :     bool                                 mMinimize;
     226             :     nsCOMPtr<nsITimer>                   mTimer;
     227             :     nsTArray<RefPtr<mozilla::MemoryReportingProcess>> mChildrenPending;
     228             :     uint32_t                             mNumProcessesRunning;
     229             :     uint32_t                             mNumProcessesCompleted;
     230             :     uint32_t                             mConcurrencyLimit;
     231             :     nsCOMPtr<nsIHandleReportCallback>    mHandleReport;
     232             :     nsCOMPtr<nsISupports>                mHandleReportData;
     233             :     nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
     234             :     nsCOMPtr<nsISupports>                mFinishReportingData;
     235             :     nsString                             mDMDDumpIdent;
     236             : 
     237             :     PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
     238             :                           uint32_t aConcurrencyLimit,
     239             :                           nsIHandleReportCallback* aHandleReport,
     240             :                           nsISupports* aHandleReportData,
     241             :                           nsIFinishReportingCallback* aFinishReporting,
     242             :                           nsISupports* aFinishReportingData,
     243             :                           const nsAString& aDMDDumpIdent);
     244             :   };
     245             : 
     246             :   // Used to keep track of the state of the asynchronously run memory
     247             :   // reporters. The callback and file handle used when all memory reporters
     248             :   // have finished are also stored here.
     249           0 :   struct PendingReportersState
     250             :   {
     251             :     // Number of memory reporters currently running.
     252             :     uint32_t mReportsPending;
     253             : 
     254             :     // Callback for when all memory reporters have completed.
     255             :     nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
     256             :     nsCOMPtr<nsISupports> mFinishReportingData;
     257             : 
     258             :     // File handle to write a DMD report to if requested.
     259             :     FILE* mDMDFile;
     260             : 
     261           0 :     PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
     262             :                         nsISupports* aFinishReportingData,
     263             :                         FILE* aDMDFile)
     264           0 :       : mReportsPending(0)
     265             :       , mFinishReporting(aFinishReporting)
     266             :       , mFinishReportingData(aFinishReportingData)
     267           0 :       , mDMDFile(aDMDFile)
     268             :     {
     269           0 :     }
     270             :   };
     271             : 
     272             :   // When this is non-null, a request is in flight.  Note: We use manual
     273             :   // new/delete for this because its lifetime doesn't match block scope or
     274             :   // anything like that.
     275             :   PendingProcessesState* mPendingProcessesState;
     276             : 
     277             :   // This is reinitialized each time a call to GetReports is initiated.
     278             :   PendingReportersState* mPendingReportersState;
     279             : 
     280             :   // Used in GetHeapAllocatedAsync() to run jemalloc_stats async.
     281             :   nsCOMPtr<nsIEventTarget> mThreadPool;
     282             : 
     283             :   PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
     284             :   static MOZ_MUST_USE bool
     285             :   StartChildReport(mozilla::MemoryReportingProcess* aChild,
     286             :                    const PendingProcessesState* aState);
     287             : };
     288             : 
     289             : #define NS_MEMORY_REPORTER_MANAGER_CID \
     290             : { 0xfb97e4f5, 0x32dd, 0x497a, \
     291             : { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
     292             : 
     293             : #endif // nsMemoryReporterManager_h__

Generated by: LCOV version 1.13