Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / base / nsMemoryReporterManager.h
blob70645245210a0539faa5f69e50d97afff258a9d9
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"
17 #ifdef XP_WIN
18 # include <windows.h>
19 #endif // XP_WIN
21 #if defined(MOZ_MEMORY)
22 # define HAVE_JEMALLOC_STATS 1
23 # include "mozmemory.h"
24 #endif // MOZ_MEMORY
26 namespace mozilla {
27 class MemoryReportingProcess;
28 namespace dom {
29 class MemoryReport;
30 } // namespace dom
31 } // namespace mozilla
33 class mozIDOMWindowProxy;
34 class nsIEventTarget;
35 class nsIRunnable;
36 class nsITimer;
38 class nsMemoryReporterManager final : public nsIMemoryReporterManager,
39 public nsIMemoryReporter {
40 virtual ~nsMemoryReporterManager();
42 public:
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>
59 StrongReportersTable;
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
76 // reporter callback.
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
118 // this late stage.
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
123 // is incomplete.
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
142 // request began.
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
152 // this module.
153 struct AmountFns {
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.
184 #ifdef XP_WIN
185 static int64_t ResidentUnique(HANDLE aProcess = nullptr);
186 #elif XP_MACOSX
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);
190 #else
191 static int64_t ResidentUnique(pid_t aPid = 0);
192 #endif // XP_{WIN, MACOSX, LINUX, *}
194 #ifdef XP_MACOSX
195 // Retrive the "phys_footprint" memory statistic on MacOS.
196 static int64_t PhysicalFootprint(mach_port_t aPort = 0);
197 #endif
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);
211 #endif
213 private:
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,
221 bool aIsAsync);
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;
253 bool mAnonymize;
254 bool mMinimize;
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.
287 FILE* mDMDFile;
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__