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__