xpcom/base/nsMemoryReporterManager.h

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f8d45e48a45e
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 "nsIMemoryReporter.h"
11 #include "nsITimer.h"
12 #include "nsServiceManagerUtils.h"
13 #include "mozilla/Mutex.h"
14 #include "nsTHashtable.h"
15 #include "nsHashKeys.h"
16
17 class nsITimer;
18
19 namespace mozilla {
20 namespace dom {
21 class MemoryReport;
22 }
23 }
24
25 class nsMemoryReporterManager : public nsIMemoryReporterManager
26 {
27 public:
28 NS_DECL_THREADSAFE_ISUPPORTS
29 NS_DECL_NSIMEMORYREPORTERMANAGER
30
31 nsMemoryReporterManager();
32 virtual ~nsMemoryReporterManager();
33
34 // Gets the memory reporter manager service.
35 static nsMemoryReporterManager* GetOrCreate()
36 {
37 nsCOMPtr<nsIMemoryReporterManager> imgr =
38 do_GetService("@mozilla.org/memory-reporter-manager;1");
39 return static_cast<nsMemoryReporterManager*>(imgr.get());
40 }
41
42 typedef nsTHashtable<nsRefPtrHashKey<nsIMemoryReporter> > StrongReportersTable;
43 typedef nsTHashtable<nsPtrHashKey<nsIMemoryReporter> > WeakReportersTable;
44
45 void IncrementNumChildProcesses();
46 void DecrementNumChildProcesses();
47
48 // Inter-process memory reporting proceeds as follows.
49 //
50 // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
51 // synchronously gets memory reports for the current process, tells all
52 // child processes to get memory reports, and sets up some state
53 // (mGetReportsState) for when the child processes report back, including a
54 // timer. Control then returns to the main event loop.
55 //
56 // - HandleChildReports() is called (asynchronously) once per child process
57 // that reports back. If all child processes report back before time-out,
58 // the timer is cancelled. (The number of child processes is part of the
59 // saved request state.)
60 //
61 // - TimeoutCallback() is called (asynchronously) if all the child processes
62 // don't respond within the time threshold.
63 //
64 // - FinishReporting() finishes things off. It is *always* called -- either
65 // from HandleChildReports() (if all child processes have reported back) or
66 // from TimeoutCallback() (if time-out occurs).
67 //
68 // All operations occur on the main thread.
69 //
70 // The above sequence of steps is a "request". A partially-completed request
71 // is described as "in flight".
72 //
73 // Each request has a "generation", a unique number that identifies it. This
74 // is used to ensure that each reports from a child process corresponds to
75 // the appropriate request from the parent process. (It's easier to
76 // implement a generation system than to implement a child report request
77 // cancellation mechanism.)
78 //
79 // Failures are mostly ignored, because it's (a) typically the most sensible
80 // thing to do, and (b) often hard to do anything else. The following are
81 // the failure cases of note.
82 //
83 // - If a request is made while the previous request is in flight, the new
84 // request is ignored, as per getReports()'s specification. No error is
85 // reported, because the previous request will complete soon enough.
86 //
87 // - If one or more child processes fail to respond within the time limit,
88 // things will proceed as if they don't exist. No error is reported,
89 // because partial information is better than nothing.
90 //
91 // - If a child process reports after the time-out occurs, it is ignored.
92 // (Generation checking will ensure it is ignored even if a subsequent
93 // request is in flight; this is the main use of generations.) No error
94 // is reported, because there's nothing sensible to be done about it at
95 // this late stage.
96 //
97 // Now, what what happens if a child process is created/destroyed in the
98 // middle of a request? Well, GetReportsState contains a copy of
99 // mNumChildProcesses which it uses to determine finished-ness. So...
100 //
101 // - If a process is created, it won't have received the request for reports,
102 // and the GetReportsState's mNumChildProcesses won't account for it. So
103 // the reported data will reflect how things were when the request began.
104 //
105 // - If a process is destroyed before reporting back, we'll just hit the
106 // time-out, because we'll have received reports (barring other errors)
107 // from N-1 child process. So the reported data will reflect how things
108 // are when the request ends.
109 //
110 // - If a process is destroyed after reporting back, but before all other
111 // child processes have reported back, it will be included in the reported
112 // data. So the reported data will reflect how things were when the
113 // request began.
114 //
115 // The inconsistencies between these three cases are unfortunate but
116 // difficult to avoid. It's enough of an edge case to not be worth doing
117 // more.
118 //
119 void HandleChildReports(
120 const uint32_t& generation,
121 const InfallibleTArray<mozilla::dom::MemoryReport>& aChildReports);
122 nsresult FinishReporting();
123
124 // Functions that (a) implement distinguished amounts, and (b) are outside of
125 // this module.
126 struct AmountFns
127 {
128 mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
129 mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
130 mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
131 mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
132
133 mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
134
135 mozilla::InfallibleAmountFn mStorageSQLite;
136
137 mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
138 mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
139
140 mozilla::InfallibleAmountFn mGhostWindows;
141
142 AmountFns() { mozilla::PodZero(this); }
143 };
144 AmountFns mAmountFns;
145
146 // Convenience function to get RSS easily from other code. This is useful
147 // when debugging transient memory spikes with printf instrumentation.
148 static int64_t ResidentFast();
149
150 // Functions that measure per-tab memory consumption.
151 struct SizeOfTabFns
152 {
153 mozilla::JSSizeOfTabFn mJS;
154 mozilla::NonJSSizeOfTabFn mNonJS;
155
156 SizeOfTabFns()
157 {
158 mozilla::PodZero(this);
159 }
160 };
161 SizeOfTabFns mSizeOfTabFns;
162
163 private:
164 nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
165 bool aForce, bool aStrongRef);
166 nsresult StartGettingReports();
167
168 static void TimeoutCallback(nsITimer* aTimer, void* aData);
169 // Note: this timeout needs to be long enough to allow for the
170 // possibility of DMD reports and/or running on a low-end phone.
171 static const uint32_t kTimeoutLengthMS = 50000;
172
173 mozilla::Mutex mMutex;
174 bool mIsRegistrationBlocked;
175
176 StrongReportersTable* mStrongReporters;
177 WeakReportersTable* mWeakReporters;
178
179 // These two are only used for testing purposes.
180 StrongReportersTable* mSavedStrongReporters;
181 WeakReportersTable* mSavedWeakReporters;
182
183 uint32_t mNumChildProcesses;
184 uint32_t mNextGeneration;
185
186 struct GetReportsState
187 {
188 uint32_t mGeneration;
189 nsCOMPtr<nsITimer> mTimer;
190 uint32_t mNumChildProcesses;
191 uint32_t mNumChildProcessesCompleted;
192 bool mParentDone;
193 nsCOMPtr<nsIHandleReportCallback> mHandleReport;
194 nsCOMPtr<nsISupports> mHandleReportData;
195 nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
196 nsCOMPtr<nsISupports> mFinishReportingData;
197 nsString mDMDDumpIdent;
198
199 GetReportsState(uint32_t aGeneration, nsITimer* aTimer,
200 uint32_t aNumChildProcesses,
201 nsIHandleReportCallback* aHandleReport,
202 nsISupports* aHandleReportData,
203 nsIFinishReportingCallback* aFinishReporting,
204 nsISupports* aFinishReportingData,
205 const nsAString &aDMDDumpIdent)
206 : mGeneration(aGeneration)
207 , mTimer(aTimer)
208 , mNumChildProcesses(aNumChildProcesses)
209 , mNumChildProcessesCompleted(0)
210 , mParentDone(false)
211 , mHandleReport(aHandleReport)
212 , mHandleReportData(aHandleReportData)
213 , mFinishReporting(aFinishReporting)
214 , mFinishReportingData(aFinishReportingData)
215 , mDMDDumpIdent(aDMDDumpIdent)
216 {
217 }
218 };
219
220 // When this is non-null, a request is in flight. Note: We use manual
221 // new/delete for this because its lifetime doesn't match block scope or
222 // anything like that.
223 GetReportsState* mGetReportsState;
224 };
225
226 #define NS_MEMORY_REPORTER_MANAGER_CID \
227 { 0xfb97e4f5, 0x32dd, 0x497a, \
228 { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
229
230 #endif // nsMemoryReporterManager_h__

mercurial