1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Mounir Lamouri <mounir.lamouri@mozilla.com> (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsWindowMemoryReporter.h"
39 : #include "nsGlobalWindow.h"
40 :
41 :
42 1404 : nsWindowMemoryReporter::nsWindowMemoryReporter()
43 : {
44 1404 : }
45 :
46 4242 : NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter, nsIMemoryMultiReporter)
47 :
48 : /* static */
49 : void
50 1404 : nsWindowMemoryReporter::Init()
51 : {
52 : // The memory reporter manager is going to own this object.
53 1404 : NS_RegisterMemoryMultiReporter(new nsWindowMemoryReporter());
54 1404 : }
55 :
56 : static bool
57 0 : AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
58 : {
59 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
60 0 : nsCOMPtr<nsIURI> uri;
61 :
62 0 : if (doc) {
63 0 : uri = doc->GetDocumentURI();
64 : }
65 :
66 0 : if (!uri) {
67 0 : nsIPrincipal *principal = aWindow->GetPrincipal();
68 :
69 0 : if (principal) {
70 0 : principal->GetURI(getter_AddRefs(uri));
71 : }
72 : }
73 :
74 0 : if (!uri) {
75 0 : return false;
76 : }
77 :
78 0 : nsCString spec;
79 0 : uri->GetSpec(spec);
80 :
81 : // A hack: replace forward slashes with '\\' so they aren't
82 : // treated as path separators. Users of the reporters
83 : // (such as about:memory) have to undo this change.
84 0 : spec.ReplaceChar('/', '\\');
85 :
86 0 : aStr += spec;
87 :
88 0 : return true;
89 : }
90 :
91 0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMStyleMallocSizeOf, "windows")
92 :
93 : static nsresult
94 0 : CollectWindowReports(nsGlobalWindow *aWindow,
95 : nsWindowSizes *aWindowTotalSizes,
96 : nsIMemoryMultiReporterCallback *aCb,
97 : nsISupports *aClosure)
98 : {
99 : // DOM window objects fall into one of three categories:
100 : // - "active" windows are currently either displayed in an active
101 : // tab, or a child of such a window.
102 : // - "cached" windows are in the fastback cache.
103 : // - "other" windows are closed (or navigated away from w/o being
104 : // cached) yet held alive by either a website or our code. The
105 : // latter case may be a memory leak, but not necessarily.
106 : //
107 : // For inner windows we show how much memory the window and its
108 : // document etc use, and we report those per URI, where the URI is
109 : // the document URI, if available, or the codebase of the principal
110 : // in the window. In the case where we're unable to find a URI we're
111 : // dealing with a chrome window with no document in it (or
112 : // somesuch), and for that we make the URI be the string "[system]".
113 : //
114 : // For outer windows we simply group them all together and just show
115 : // the combined count and amount of memory used, which is generally
116 : // a constant amount per window (since all the actual data lives in
117 : // the inner window).
118 : //
119 : // The path we give to the reporter callback for inner windows are
120 : // as follows:
121 : //
122 : // explicit/window-objects/<category>/top=<top-outer-id> (inner=<top-inner-id>)/inner-window(id=<id>, uri=<uri>)
123 : //
124 : // Where:
125 : // - <category> is active, cached, or other, as described above.
126 : // - <top-outer-id> is the window id (nsPIDOMWindow::WindowID()) of
127 : // the top outer window (i.e. tab, or top level chrome window).
128 : // - <top-inner-id> is the window id of the top window's inner
129 : // window.
130 : // - <id> is the window id of the inner window in question.
131 : // - <uri> is the URI per above description.
132 : //
133 : // Exposing the window ids is done to get logical grouping in
134 : // about:memory, and also for debuggability since one can get to the
135 : // nsGlobalWindow for a window id by calling the static method
136 : // nsGlobalWindow::GetInnerWindowWithId(id) (or
137 : // GetOuterWindowWithId(id) in a debugger.
138 : //
139 : // For outer windows we simply use:
140 : //
141 : // explicit/window-objects/<category>/outer-windows
142 : //
143 : // Which gives us simple counts of how many outer windows (and their
144 : // combined sizes) per category.
145 :
146 0 : nsCAutoString windowPath("explicit/window-objects/");
147 :
148 0 : nsIDocShell *docShell = aWindow->GetDocShell();
149 :
150 0 : nsGlobalWindow *top = aWindow->GetTop();
151 0 : nsWindowSizes windowSizes(DOMStyleMallocSizeOf);
152 0 : aWindow->SizeOfIncludingThis(&windowSizes);
153 :
154 0 : if (docShell && aWindow->IsFrozen()) {
155 0 : windowPath += NS_LITERAL_CSTRING("cached/");
156 0 : } else if (docShell) {
157 0 : windowPath += NS_LITERAL_CSTRING("active/");
158 : } else {
159 0 : windowPath += NS_LITERAL_CSTRING("other/");
160 : }
161 :
162 0 : if (aWindow->IsInnerWindow()) {
163 0 : windowPath += NS_LITERAL_CSTRING("top=");
164 :
165 0 : if (top) {
166 0 : windowPath.AppendInt(top->WindowID());
167 :
168 0 : nsGlobalWindow *topInner = top->GetCurrentInnerWindowInternal();
169 0 : if (topInner) {
170 0 : windowPath += NS_LITERAL_CSTRING(" (inner=");
171 0 : windowPath.AppendInt(topInner->WindowID());
172 0 : windowPath += NS_LITERAL_CSTRING(")");
173 : }
174 : } else {
175 0 : windowPath += NS_LITERAL_CSTRING("none");
176 : }
177 :
178 0 : windowPath += NS_LITERAL_CSTRING("/inner-window(id=");
179 0 : windowPath.AppendInt(aWindow->WindowID());
180 0 : windowPath += NS_LITERAL_CSTRING(", uri=");
181 :
182 0 : if (!AppendWindowURI(aWindow, windowPath)) {
183 0 : windowPath += NS_LITERAL_CSTRING("[system]");
184 : }
185 :
186 0 : windowPath += NS_LITERAL_CSTRING(")");
187 : } else {
188 : // Combine all outer windows per section (active/cached/other) as
189 : // they basically never contain anything of interest, and are
190 : // always pretty much the same size.
191 :
192 0 : windowPath += NS_LITERAL_CSTRING("outer-windows");
193 : }
194 :
195 : #define REPORT(_path1, _path2, _amount, _desc) \
196 : do { \
197 : if (_amount > 0) { \
198 : nsCAutoString path(_path1); \
199 : path += _path2; \
200 : nsresult rv; \
201 : rv = aCb->Callback(EmptyCString(), path, nsIMemoryReporter::KIND_HEAP,\
202 : nsIMemoryReporter::UNITS_BYTES, _amount, \
203 : NS_LITERAL_CSTRING(_desc), aClosure); \
204 : NS_ENSURE_SUCCESS(rv, rv); \
205 : } \
206 : } while (0)
207 :
208 0 : REPORT(windowPath, "/dom", windowSizes.mDOM,
209 : "Memory used by a window and the DOM within it.");
210 0 : aWindowTotalSizes->mDOM += windowSizes.mDOM;
211 :
212 0 : REPORT(windowPath, "/style-sheets", windowSizes.mStyleSheets,
213 : "Memory used by style sheets within a window.");
214 0 : aWindowTotalSizes->mStyleSheets += windowSizes.mStyleSheets;
215 :
216 0 : REPORT(windowPath, "/layout/arenas", windowSizes.mLayoutArenas,
217 : "Memory used by layout PresShell, PresContext, and other related "
218 : "areas within a window.");
219 0 : aWindowTotalSizes->mLayoutArenas += windowSizes.mLayoutArenas;
220 :
221 0 : REPORT(windowPath, "/layout/style-sets", windowSizes.mLayoutStyleSets,
222 : "Memory used by style sets within a window.");
223 0 : aWindowTotalSizes->mLayoutStyleSets += windowSizes.mLayoutStyleSets;
224 :
225 0 : REPORT(windowPath, "/layout/text-runs", windowSizes.mLayoutTextRuns,
226 : "Memory used for text-runs (glyph layout) in the PresShell's frame "
227 : "tree, within a window.");
228 0 : aWindowTotalSizes->mLayoutTextRuns += windowSizes.mLayoutTextRuns;
229 :
230 : #undef REPORT
231 :
232 0 : return NS_OK;
233 : }
234 :
235 : typedef nsTArray< nsRefPtr<nsGlobalWindow> > WindowArray;
236 :
237 : static
238 : PLDHashOperator
239 0 : GetWindows(const PRUint64& aId, nsGlobalWindow*& aWindow, void* aClosure)
240 : {
241 0 : ((WindowArray *)aClosure)->AppendElement(aWindow);
242 :
243 0 : return PL_DHASH_NEXT;
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : nsWindowMemoryReporter::GetName(nsACString &aName)
248 : {
249 0 : aName.AssignLiteral("window-objects");
250 0 : return NS_OK;
251 : }
252 :
253 : NS_IMETHODIMP
254 3 : nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
255 : nsISupports* aClosure)
256 : {
257 : nsGlobalWindow::WindowByIdTable* windowsById =
258 3 : nsGlobalWindow::GetWindowsTable();
259 3 : NS_ENSURE_TRUE(windowsById, NS_OK);
260 :
261 : // Hold on to every window in memory so that window objects can't be
262 : // destroyed while we're calling the memory reporter callback.
263 6 : WindowArray windows;
264 3 : windowsById->Enumerate(GetWindows, &windows);
265 :
266 : // Collect window memory usage.
267 3 : nsRefPtr<nsGlobalWindow> *w = windows.Elements();
268 3 : nsRefPtr<nsGlobalWindow> *end = w + windows.Length();
269 3 : nsWindowSizes windowTotalSizes(NULL);
270 3 : for (; w != end; ++w) {
271 0 : nsresult rv = CollectWindowReports(*w, &windowTotalSizes, aCb, aClosure);
272 0 : NS_ENSURE_SUCCESS(rv, rv);
273 : }
274 :
275 : #define REPORT(_path, _amount, _desc) \
276 : do { \
277 : nsresult rv; \
278 : rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
279 : nsIMemoryReporter::KIND_OTHER, \
280 : nsIMemoryReporter::UNITS_BYTES, _amount, \
281 : NS_LITERAL_CSTRING(_desc), aClosure); \
282 : NS_ENSURE_SUCCESS(rv, rv); \
283 : } while (0)
284 :
285 3 : REPORT("window-objects-dom", windowTotalSizes.mDOM,
286 : "Memory used for the DOM within windows. "
287 : "This is the sum of all windows' 'dom' numbers.");
288 :
289 3 : REPORT("window-objects-style-sheets", windowTotalSizes.mStyleSheets,
290 : "Memory used for style sheets within windows. "
291 : "This is the sum of all windows' 'style-sheets' numbers.");
292 :
293 3 : REPORT("window-objects-layout-arenas", windowTotalSizes.mLayoutArenas,
294 : "Memory used by layout PresShell, PresContext, and other related "
295 : "areas within windows. This is the sum of all windows' "
296 : "'layout/arenas' numbers.");
297 :
298 3 : REPORT("window-objects-layout-style-sets", windowTotalSizes.mLayoutStyleSets,
299 : "Memory used for style sets within windows. "
300 : "This is the sum of all windows' 'layout/style-sets' numbers.");
301 :
302 3 : REPORT("window-objects-layout-text-runs", windowTotalSizes.mLayoutTextRuns,
303 : "Memory used for text runs within windows. "
304 : "This is the sum of all windows' 'layout/text-runs' numbers.");
305 :
306 : #undef REPORT
307 :
308 3 : return NS_OK;
309 : }
310 :
311 : NS_IMETHODIMP
312 3 : nsWindowMemoryReporter::GetExplicitNonHeap(PRInt64* aAmount)
313 : {
314 : // This reporter only measures heap memory.
315 3 : *aAmount = 0;
316 3 : return NS_OK;
317 : }
318 :
319 :
|