|
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__ |