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/. */
7 #ifndef nsMemoryReporterManager_h__
8 #define nsMemoryReporterManager_h__
10 #include "mozilla/Mutex.h"
11 #include "nsTHashMap.h"
12 #include "nsHashKeys.h"
13 #include "nsIMemoryReporter.h"
14 #include "nsISupports.h"
15 #include "nsServiceManagerUtils.h"
21 #if defined(MOZ_MEMORY)
22 # define HAVE_JEMALLOC_STATS 1
23 # include "mozmemory.h"
27 class MemoryReportingProcess
;
31 } // namespace mozilla
33 class mozIDOMWindowProxy
;
38 class nsMemoryReporterManager final
: public nsIMemoryReporterManager
,
39 public nsIMemoryReporter
{
40 virtual ~nsMemoryReporterManager();
43 NS_DECL_THREADSAFE_ISUPPORTS
44 NS_DECL_NSIMEMORYREPORTERMANAGER
45 NS_DECL_NSIMEMORYREPORTER
47 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
)
49 nsMemoryReporterManager();
51 // Gets the memory reporter manager service.
52 static already_AddRefed
<nsMemoryReporterManager
> GetOrCreate() {
53 nsCOMPtr
<nsIMemoryReporterManager
> imgr
=
54 do_GetService("@mozilla.org/memory-reporter-manager;1");
55 return imgr
.forget().downcast
<nsMemoryReporterManager
>();
58 typedef nsTHashMap
<nsRefPtrHashKey
<nsIMemoryReporter
>, bool>
60 typedef nsTHashMap
<nsPtrHashKey
<nsIMemoryReporter
>, bool> WeakReportersTable
;
62 // Inter-process memory reporting proceeds as follows.
64 // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
65 // synchronously gets memory reports for the current process, sets up some
66 // state (mPendingProcessesState) for when child processes report back --
67 // including a timer -- and starts telling child processes to get memory
68 // reports. Control then returns to the main event loop.
70 // The number of concurrent child process reports is limited by the pref
71 // "memory.report_concurrency" in order to prevent the memory overhead of
72 // memory reporting from causing problems, especially on B2G when swapping
73 // to compressed RAM; see bug 1154053.
75 // - HandleChildReport() is called (asynchronously) once per child process
78 // - EndProcessReport() is called (asynchronously) once per process that
79 // finishes reporting back, including the parent. If all processes do so
80 // before time-out, the timer is cancelled. If there are child processes
81 // whose requests have not yet been sent, they will be started until the
82 // concurrency limit is (again) reached.
84 // - TimeoutCallback() is called (asynchronously) if all the child processes
85 // don't respond within the time threshold.
87 // - FinishReporting() finishes things off. It is *always* called -- either
88 // from EndChildReport() (if all child processes have reported back) or
89 // from TimeoutCallback() (if time-out occurs).
91 // All operations occur on the main thread.
93 // The above sequence of steps is a "request". A partially-completed request
94 // is described as "in flight".
96 // Each request has a "generation", a unique number that identifies it. This
97 // is used to ensure that each reports from a child process corresponds to
98 // the appropriate request from the parent process. (It's easier to
99 // implement a generation system than to implement a child report request
100 // cancellation mechanism.)
102 // Failures are mostly ignored, because it's (a) typically the most sensible
103 // thing to do, and (b) often hard to do anything else. The following are
104 // the failure cases of note.
106 // - If a request is made while the previous request is in flight, the new
107 // request is ignored, as per getReports()'s specification. No error is
108 // reported, because the previous request will complete soon enough.
110 // - If one or more child processes fail to respond within the time limit,
111 // things will proceed as if they don't exist. No error is reported,
112 // because partial information is better than nothing.
114 // - If a child process reports after the time-out occurs, it is ignored.
115 // (Generation checking will ensure it is ignored even if a subsequent
116 // request is in flight; this is the main use of generations.) No error
117 // is reported, because there's nothing sensible to be done about it at
120 // - If the time-out occurs after a child process has sent some reports but
121 // before it has signaled completion (see bug 1151597), then what it
122 // successfully sent will be included, with no explicit indication that it
125 // Now, what what happens if a child process is created/destroyed in the
126 // middle of a request? Well, PendingProcessesState is initialized with an
127 // array of child process actors as of when the report started. So...
129 // - If a process is created after reporting starts, it won't be sent a
130 // request for reports. So the reported data will reflect how things were
131 // when the request began.
133 // - If a process is destroyed before it starts reporting back, the reported
134 // data will reflect how things are when the request ends.
136 // - If a process is destroyed after it starts reporting back but before it
137 // finishes, the reported data will contain a partial report for it.
139 // - If a process is destroyed after reporting back, but before all other
140 // child processes have reported back, it will be included in the reported
141 // data. So the reported data will reflect how things were when the
144 // The inconsistencies between these cases are unfortunate but difficult to
145 // avoid. It's enough of an edge case to not be worth doing more.
147 void HandleChildReport(uint32_t aGeneration
,
148 const mozilla::dom::MemoryReport
& aChildReport
);
149 void EndProcessReport(uint32_t aGeneration
, bool aSuccess
);
151 // Functions that (a) implement distinguished amounts, and (b) are outside of
154 mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap
= nullptr;
155 mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak
= nullptr;
156 mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem
= nullptr;
157 mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser
= nullptr;
158 mozilla::InfallibleAmountFn mJSMainRuntimeRealmsSystem
= nullptr;
159 mozilla::InfallibleAmountFn mJSMainRuntimeRealmsUser
= nullptr;
161 mozilla::InfallibleAmountFn mImagesContentUsedUncompressed
= nullptr;
163 mozilla::InfallibleAmountFn mStorageSQLite
= nullptr;
165 mozilla::InfallibleAmountFn mLowMemoryEventsPhysical
= nullptr;
167 mozilla::InfallibleAmountFn mGhostWindows
= nullptr;
169 AmountFns mAmountFns
;
171 // Convenience function to get RSS easily from other code. This is useful
172 // when debugging transient memory spikes with printf instrumentation.
173 static int64_t ResidentFast();
175 // Convenience function to get peak RSS easily from other code.
176 static int64_t ResidentPeak();
178 // Convenience function to get USS easily from other code. This is useful
179 // when debugging unshared memory pages for forked processes.
181 // Returns 0 if, for some reason, the resident unique memory cannot be
182 // determined - typically if there is a race between us and someone else
183 // closing the process and we lost that race.
185 static int64_t ResidentUnique(HANDLE aProcess
= nullptr);
187 // On MacOS this can sometimes be significantly slow. It should not be used
188 // except in debugging or at the request of a user (eg about:memory).
189 static int64_t ResidentUnique(mach_port_t aPort
= 0);
191 static int64_t ResidentUnique(pid_t aPid
= 0);
192 #endif // XP_{WIN, MACOSX, LINUX, *}
195 // Retrive the "phys_footprint" memory statistic on MacOS.
196 static int64_t PhysicalFootprint(mach_port_t aPort
= 0);
199 // Functions that measure per-tab memory consumption.
200 struct SizeOfTabFns
{
201 mozilla::JSSizeOfTabFn mJS
= nullptr;
202 mozilla::NonJSSizeOfTabFn mNonJS
= nullptr;
204 SizeOfTabFns mSizeOfTabFns
;
206 #ifdef HAVE_JEMALLOC_STATS
207 // These C++ only versions of HeapAllocated and HeapOverheadFraction avoid
208 // extra calls to jemalloc_stats;
209 static size_t HeapAllocated(const jemalloc_stats_t
& stats
);
210 static int64_t HeapOverheadFraction(const jemalloc_stats_t
& stats
);
214 bool IsRegistrationBlocked() MOZ_EXCLUDES(mMutex
) {
215 mozilla::MutexAutoLock
lock(mMutex
);
216 return mIsRegistrationBlocked
;
219 [[nodiscard
]] nsresult
RegisterReporterHelper(nsIMemoryReporter
* aReporter
,
220 bool aForce
, bool aStrongRef
,
223 [[nodiscard
]] nsresult
StartGettingReports();
224 // No [[nodiscard]] here because ignoring the result is common and reasonable.
225 nsresult
FinishReporting();
227 void DispatchReporter(nsIMemoryReporter
* aReporter
, bool aIsAsync
,
228 nsIHandleReportCallback
* aHandleReport
,
229 nsISupports
* aHandleReportData
, bool aAnonymize
);
231 static void TimeoutCallback(nsITimer
* aTimer
, void* aData
);
232 // Note: this timeout needs to be long enough to allow for the
233 // possibility of DMD reports and/or running on a low-end phone.
234 static const uint32_t kTimeoutLengthMS
= 180000;
236 mozilla::Mutex mMutex
;
237 bool mIsRegistrationBlocked
MOZ_GUARDED_BY(mMutex
);
239 StrongReportersTable
* mStrongReporters
MOZ_GUARDED_BY(mMutex
);
240 WeakReportersTable
* mWeakReporters
MOZ_GUARDED_BY(mMutex
);
242 // These two are only used for testing purposes.
243 StrongReportersTable
* mSavedStrongReporters
MOZ_GUARDED_BY(mMutex
);
244 WeakReportersTable
* mSavedWeakReporters
MOZ_GUARDED_BY(mMutex
);
246 uint32_t mNextGeneration
; // MainThread only
248 // Used to keep track of state of which processes are currently running and
249 // waiting to run memory reports. Holds references to parameters needed when
250 // requesting a memory report and finishing reporting.
251 struct PendingProcessesState
{
252 uint32_t mGeneration
;
255 nsCOMPtr
<nsITimer
> mTimer
;
256 nsTArray
<RefPtr
<mozilla::MemoryReportingProcess
>> mChildrenPending
;
257 uint32_t mNumProcessesRunning
;
258 uint32_t mNumProcessesCompleted
;
259 uint32_t mConcurrencyLimit
;
260 nsCOMPtr
<nsIHandleReportCallback
> mHandleReport
;
261 nsCOMPtr
<nsISupports
> mHandleReportData
;
262 nsCOMPtr
<nsIFinishReportingCallback
> mFinishReporting
;
263 nsCOMPtr
<nsISupports
> mFinishReportingData
;
264 nsString mDMDDumpIdent
;
266 PendingProcessesState(uint32_t aGeneration
, bool aAnonymize
, bool aMinimize
,
267 uint32_t aConcurrencyLimit
,
268 nsIHandleReportCallback
* aHandleReport
,
269 nsISupports
* aHandleReportData
,
270 nsIFinishReportingCallback
* aFinishReporting
,
271 nsISupports
* aFinishReportingData
,
272 const nsAString
& aDMDDumpIdent
);
275 // Used to keep track of the state of the asynchronously run memory
276 // reporters. The callback and file handle used when all memory reporters
277 // have finished are also stored here.
278 struct PendingReportersState
{
279 // Number of memory reporters currently running.
280 uint32_t mReportsPending
;
282 // Callback for when all memory reporters have completed.
283 nsCOMPtr
<nsIFinishReportingCallback
> mFinishReporting
;
284 nsCOMPtr
<nsISupports
> mFinishReportingData
;
286 // File handle to write a DMD report to if requested.
289 PendingReportersState(nsIFinishReportingCallback
* aFinishReporting
,
290 nsISupports
* aFinishReportingData
, FILE* aDMDFile
)
291 : mReportsPending(0),
292 mFinishReporting(aFinishReporting
),
293 mFinishReportingData(aFinishReportingData
),
294 mDMDFile(aDMDFile
) {}
297 // When this is non-null, a request is in flight. Note: We use manual
298 // new/delete for this because its lifetime doesn't match block scope or
299 // anything like that.
300 PendingProcessesState
* mPendingProcessesState
; // MainThread only
302 // This is reinitialized each time a call to GetReports is initiated.
303 PendingReportersState
* mPendingReportersState
; // MainThread only
305 // Used in GetHeapAllocatedAsync() to run jemalloc_stats async.
306 nsCOMPtr
<nsIEventTarget
> mThreadPool
MOZ_GUARDED_BY(mMutex
);
308 PendingProcessesState
* GetStateForGeneration(uint32_t aGeneration
);
309 [[nodiscard
]] static bool StartChildReport(
310 mozilla::MemoryReportingProcess
* aChild
,
311 const PendingProcessesState
* aState
);
314 #define NS_MEMORY_REPORTER_MANAGER_CID \
316 0xfb97e4f5, 0x32dd, 0x497a, { \
317 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 \
321 #endif // nsMemoryReporterManager_h__