xpcom/base/nsMemoryReporterManager.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/base/nsMemoryReporterManager.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,230 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef nsMemoryReporterManager_h__
    1.11 +#define nsMemoryReporterManager_h__
    1.12 +
    1.13 +#include "nsIMemoryReporter.h"
    1.14 +#include "nsITimer.h"
    1.15 +#include "nsServiceManagerUtils.h"
    1.16 +#include "mozilla/Mutex.h"
    1.17 +#include "nsTHashtable.h"
    1.18 +#include "nsHashKeys.h"
    1.19 +
    1.20 +class nsITimer;
    1.21 +
    1.22 +namespace mozilla {
    1.23 +namespace dom {
    1.24 +class MemoryReport;
    1.25 +}
    1.26 +}
    1.27 +
    1.28 +class nsMemoryReporterManager : public nsIMemoryReporterManager
    1.29 +{
    1.30 +public:
    1.31 +  NS_DECL_THREADSAFE_ISUPPORTS
    1.32 +  NS_DECL_NSIMEMORYREPORTERMANAGER
    1.33 +
    1.34 +  nsMemoryReporterManager();
    1.35 +  virtual ~nsMemoryReporterManager();
    1.36 +
    1.37 +  // Gets the memory reporter manager service.
    1.38 +  static nsMemoryReporterManager* GetOrCreate()
    1.39 +  {
    1.40 +    nsCOMPtr<nsIMemoryReporterManager> imgr =
    1.41 +      do_GetService("@mozilla.org/memory-reporter-manager;1");
    1.42 +    return static_cast<nsMemoryReporterManager*>(imgr.get());
    1.43 +  }
    1.44 +
    1.45 +  typedef nsTHashtable<nsRefPtrHashKey<nsIMemoryReporter> > StrongReportersTable;
    1.46 +  typedef nsTHashtable<nsPtrHashKey<nsIMemoryReporter> > WeakReportersTable;
    1.47 +
    1.48 +  void IncrementNumChildProcesses();
    1.49 +  void DecrementNumChildProcesses();
    1.50 +
    1.51 +  // Inter-process memory reporting proceeds as follows.
    1.52 +  //
    1.53 +  // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
    1.54 +  //   synchronously gets memory reports for the current process, tells all
    1.55 +  //   child processes to get memory reports, and sets up some state
    1.56 +  //   (mGetReportsState) for when the child processes report back, including a
    1.57 +  //   timer.  Control then returns to the main event loop.
    1.58 +  //
    1.59 +  // - HandleChildReports() is called (asynchronously) once per child process
    1.60 +  //   that reports back.  If all child processes report back before time-out,
    1.61 +  //   the timer is cancelled.  (The number of child processes is part of the
    1.62 +  //   saved request state.)
    1.63 +  //
    1.64 +  // - TimeoutCallback() is called (asynchronously) if all the child processes
    1.65 +  //   don't respond within the time threshold.
    1.66 +  //
    1.67 +  // - FinishReporting() finishes things off.  It is *always* called -- either
    1.68 +  //   from HandleChildReports() (if all child processes have reported back) or
    1.69 +  //   from TimeoutCallback() (if time-out occurs).
    1.70 +  //
    1.71 +  // All operations occur on the main thread.
    1.72 +  //
    1.73 +  // The above sequence of steps is a "request".  A partially-completed request
    1.74 +  // is described as "in flight".
    1.75 +  //
    1.76 +  // Each request has a "generation", a unique number that identifies it.  This
    1.77 +  // is used to ensure that each reports from a child process corresponds to
    1.78 +  // the appropriate request from the parent process.  (It's easier to
    1.79 +  // implement a generation system than to implement a child report request
    1.80 +  // cancellation mechanism.)
    1.81 +  //
    1.82 +  // Failures are mostly ignored, because it's (a) typically the most sensible
    1.83 +  // thing to do, and (b) often hard to do anything else.  The following are
    1.84 +  // the failure cases of note.
    1.85 +  //
    1.86 +  // - If a request is made while the previous request is in flight, the new
    1.87 +  //   request is ignored, as per getReports()'s specification.  No error is
    1.88 +  //   reported, because the previous request will complete soon enough.
    1.89 +  //
    1.90 +  // - If one or more child processes fail to respond within the time limit,
    1.91 +  //   things will proceed as if they don't exist.  No error is reported,
    1.92 +  //   because partial information is better than nothing.
    1.93 +  //
    1.94 +  // - If a child process reports after the time-out occurs, it is ignored.
    1.95 +  //   (Generation checking will ensure it is ignored even if a subsequent
    1.96 +  //   request is in flight;  this is the main use of generations.)  No error
    1.97 +  //   is reported, because there's nothing sensible to be done about it at
    1.98 +  //   this late stage.
    1.99 +  //
   1.100 +  // Now, what what happens if a child process is created/destroyed in the
   1.101 +  // middle of a request?  Well, GetReportsState contains a copy of
   1.102 +  // mNumChildProcesses which it uses to determine finished-ness.  So...
   1.103 +  //
   1.104 +  // - If a process is created, it won't have received the request for reports,
   1.105 +  //   and the GetReportsState's mNumChildProcesses won't account for it.  So
   1.106 +  //   the reported data will reflect how things were when the request began.
   1.107 +  //
   1.108 +  // - If a process is destroyed before reporting back, we'll just hit the
   1.109 +  //   time-out, because we'll have received reports (barring other errors)
   1.110 +  //   from N-1 child process.  So the reported data will reflect how things
   1.111 +  //   are when the request ends.
   1.112 +  //
   1.113 +  // - If a process is destroyed after reporting back, but before all other
   1.114 +  //   child processes have reported back, it will be included in the reported
   1.115 +  //   data.  So the reported data will reflect how things were when the
   1.116 +  //   request began.
   1.117 +  //
   1.118 +  // The inconsistencies between these three cases are unfortunate but
   1.119 +  // difficult to avoid.  It's enough of an edge case to not be worth doing
   1.120 +  // more.
   1.121 +  //
   1.122 +  void HandleChildReports(
   1.123 +    const uint32_t& generation,
   1.124 +    const InfallibleTArray<mozilla::dom::MemoryReport>& aChildReports);
   1.125 +  nsresult FinishReporting();
   1.126 +
   1.127 +  // Functions that (a) implement distinguished amounts, and (b) are outside of
   1.128 +  // this module.
   1.129 +  struct AmountFns
   1.130 +  {
   1.131 +    mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
   1.132 +    mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
   1.133 +    mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
   1.134 +    mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
   1.135 +
   1.136 +    mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
   1.137 +
   1.138 +    mozilla::InfallibleAmountFn mStorageSQLite;
   1.139 +
   1.140 +    mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
   1.141 +    mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
   1.142 +
   1.143 +    mozilla::InfallibleAmountFn mGhostWindows;
   1.144 +
   1.145 +    AmountFns() { mozilla::PodZero(this); }
   1.146 +  };
   1.147 +  AmountFns mAmountFns;
   1.148 +
   1.149 +  // Convenience function to get RSS easily from other code.  This is useful
   1.150 +  // when debugging transient memory spikes with printf instrumentation.
   1.151 +  static int64_t ResidentFast();
   1.152 +
   1.153 +  // Functions that measure per-tab memory consumption.
   1.154 +  struct SizeOfTabFns
   1.155 +  {
   1.156 +    mozilla::JSSizeOfTabFn    mJS;
   1.157 +    mozilla::NonJSSizeOfTabFn mNonJS;
   1.158 +
   1.159 +    SizeOfTabFns()
   1.160 +    {
   1.161 +      mozilla::PodZero(this);
   1.162 +    }
   1.163 +  };
   1.164 +  SizeOfTabFns mSizeOfTabFns;
   1.165 +
   1.166 +private:
   1.167 +  nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
   1.168 +                                  bool aForce, bool aStrongRef);
   1.169 +  nsresult StartGettingReports();
   1.170 +
   1.171 +  static void TimeoutCallback(nsITimer* aTimer, void* aData);
   1.172 +  // Note: this timeout needs to be long enough to allow for the
   1.173 +  // possibility of DMD reports and/or running on a low-end phone.
   1.174 +  static const uint32_t kTimeoutLengthMS = 50000;
   1.175 +
   1.176 +  mozilla::Mutex mMutex;
   1.177 +  bool mIsRegistrationBlocked;
   1.178 +
   1.179 +  StrongReportersTable* mStrongReporters;
   1.180 +  WeakReportersTable* mWeakReporters;
   1.181 +
   1.182 +  // These two are only used for testing purposes.
   1.183 +  StrongReportersTable* mSavedStrongReporters;
   1.184 +  WeakReportersTable* mSavedWeakReporters;
   1.185 +
   1.186 +  uint32_t mNumChildProcesses;
   1.187 +  uint32_t mNextGeneration;
   1.188 +
   1.189 +  struct GetReportsState
   1.190 +  {
   1.191 +    uint32_t                             mGeneration;
   1.192 +    nsCOMPtr<nsITimer>                   mTimer;
   1.193 +    uint32_t                             mNumChildProcesses;
   1.194 +    uint32_t                             mNumChildProcessesCompleted;
   1.195 +    bool                                 mParentDone;
   1.196 +    nsCOMPtr<nsIHandleReportCallback>    mHandleReport;
   1.197 +    nsCOMPtr<nsISupports>                mHandleReportData;
   1.198 +    nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
   1.199 +    nsCOMPtr<nsISupports>                mFinishReportingData;
   1.200 +    nsString                             mDMDDumpIdent;
   1.201 +
   1.202 +    GetReportsState(uint32_t aGeneration, nsITimer* aTimer,
   1.203 +                    uint32_t aNumChildProcesses,
   1.204 +                    nsIHandleReportCallback* aHandleReport,
   1.205 +                    nsISupports* aHandleReportData,
   1.206 +                    nsIFinishReportingCallback* aFinishReporting,
   1.207 +                    nsISupports* aFinishReportingData,
   1.208 +                    const nsAString &aDMDDumpIdent)
   1.209 +      : mGeneration(aGeneration)
   1.210 +      , mTimer(aTimer)
   1.211 +      , mNumChildProcesses(aNumChildProcesses)
   1.212 +      , mNumChildProcessesCompleted(0)
   1.213 +      , mParentDone(false)
   1.214 +      , mHandleReport(aHandleReport)
   1.215 +      , mHandleReportData(aHandleReportData)
   1.216 +      , mFinishReporting(aFinishReporting)
   1.217 +      , mFinishReportingData(aFinishReportingData)
   1.218 +      , mDMDDumpIdent(aDMDDumpIdent)
   1.219 +    {
   1.220 +    }
   1.221 +  };
   1.222 +
   1.223 +  // When this is non-null, a request is in flight.  Note: We use manual
   1.224 +  // new/delete for this because its lifetime doesn't match block scope or
   1.225 +  // anything like that.
   1.226 +  GetReportsState* mGetReportsState;
   1.227 +};
   1.228 +
   1.229 +#define NS_MEMORY_REPORTER_MANAGER_CID \
   1.230 +{ 0xfb97e4f5, 0x32dd, 0x497a, \
   1.231 +{ 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
   1.232 +
   1.233 +#endif // nsMemoryReporterManager_h__

mercurial