|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet type="text/css" href="chrome://global/skin"?> |
|
3 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> |
|
4 <window title="about:memory" |
|
5 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
6 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> |
|
7 <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> |
|
8 |
|
9 <!-- This file tests the saving and loading of memory reports to/from file in |
|
10 about:memory. --> |
|
11 |
|
12 <!-- test results are displayed in the html:body --> |
|
13 <body xmlns="http://www.w3.org/1999/xhtml"></body> |
|
14 |
|
15 <!-- test code goes here --> |
|
16 <script type="application/javascript"> |
|
17 <![CDATA[ |
|
18 "use strict"; |
|
19 |
|
20 const Cc = Components.classes; |
|
21 const Ci = Components.interfaces; |
|
22 let mgr = Cc["@mozilla.org/memory-reporter-manager;1"]. |
|
23 getService(Ci.nsIMemoryReporterManager); |
|
24 |
|
25 // Hide all the real reporters; we'll restore them at the end. |
|
26 mgr.blockRegistrationAndHideExistingReporters(); |
|
27 |
|
28 // Setup a minimal number of fake reporters. |
|
29 const KB = 1024; |
|
30 const MB = KB * KB; |
|
31 const HEAP = Ci.nsIMemoryReporter.KIND_HEAP; |
|
32 const OTHER = Ci.nsIMemoryReporter.KIND_OTHER; |
|
33 const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES; |
|
34 |
|
35 let fakeReporters = [ |
|
36 { collectReports: function(aCbObj, aClosure) { |
|
37 function f(aP, aK, aA, aD) { |
|
38 aCbObj.callback("", aP, aK, BYTES, aA, aD, aClosure); |
|
39 } |
|
40 f("heap-allocated", OTHER, 250 * MB, "Heap allocated."); |
|
41 f("explicit/a/b", HEAP, 50 * MB, "A b."); |
|
42 f("other/a", OTHER, 0.2 * MB, "Other a."); |
|
43 f("other/b", OTHER, 0.1 * MB, "Other b."); |
|
44 } |
|
45 } |
|
46 ]; |
|
47 |
|
48 for (let i = 0; i < fakeReporters.length; i++) { |
|
49 mgr.registerStrongReporterEvenIfBlocked(fakeReporters[i]); |
|
50 } |
|
51 |
|
52 ]]> |
|
53 </script> |
|
54 |
|
55 <iframe id="amFrame" height="400" src="about:memory"></iframe> |
|
56 |
|
57 <script type="application/javascript"> |
|
58 <![CDATA[ |
|
59 function finish() |
|
60 { |
|
61 mgr.unblockRegistrationAndRestoreOriginalReporters(); |
|
62 SimpleTest.finish(); |
|
63 } |
|
64 |
|
65 // Load the given file into the frame, then copy+paste the entire frame and |
|
66 // check that the cut text matches what we expect. |
|
67 function test(aFilename, aFilename2, aExpected, aDumpFirst, aNext) { |
|
68 let frame = document.getElementById("amFrame"); |
|
69 frame.focus(); |
|
70 |
|
71 let doc = frame.contentWindow.document; |
|
72 let verbosity = doc.getElementById("verbose"); |
|
73 verbosity.checked = true; |
|
74 |
|
75 function getFilePath(aFilename) { |
|
76 let file = Cc["@mozilla.org/file/directory_service;1"] |
|
77 .getService(Components.interfaces.nsIProperties) |
|
78 .get("CurWorkD", Components.interfaces.nsIFile); |
|
79 file.append("chrome"); |
|
80 file.append("toolkit"); |
|
81 file.append("components"); |
|
82 file.append("aboutmemory"); |
|
83 file.append("tests"); |
|
84 file.append(aFilename); |
|
85 return file.path; |
|
86 } |
|
87 |
|
88 let filePath = getFilePath(aFilename); |
|
89 |
|
90 let e = document.createEvent('Event'); |
|
91 e.initEvent('change', true, true); |
|
92 |
|
93 function check() { |
|
94 // Initialize the clipboard contents. |
|
95 SpecialPowers.clipboardCopyString("initial clipboard value"); |
|
96 |
|
97 let numFailures = 0, maxFailures = 30; |
|
98 |
|
99 // Because the file load is async, we don't know when it will finish and |
|
100 // the output will show up. So we poll. |
|
101 function copyPasteAndCheck() { |
|
102 // Copy and paste frame contents, and filter out non-deterministic |
|
103 // differences. |
|
104 synthesizeKey("A", {accelKey: true}); |
|
105 synthesizeKey("C", {accelKey: true}); |
|
106 let actual = SpecialPowers.getClipboardData("text/unicode"); |
|
107 actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)"); |
|
108 |
|
109 if (actual === aExpected) { |
|
110 SimpleTest.ok(true, "Clipboard has the expected contents"); |
|
111 aNext(); |
|
112 } else { |
|
113 numFailures++; |
|
114 if (numFailures === maxFailures) { |
|
115 ok(false, "pasted text doesn't match"); |
|
116 dump("******EXPECTED******\n"); |
|
117 dump(aExpected); |
|
118 dump("*******ACTUAL*******\n"); |
|
119 dump(actual); |
|
120 dump("********************\n"); |
|
121 finish(); |
|
122 } else { |
|
123 setTimeout(copyPasteAndCheck, 100); |
|
124 } |
|
125 } |
|
126 } |
|
127 copyPasteAndCheck(); |
|
128 } |
|
129 |
|
130 if (!aFilename2) { |
|
131 function loadAndCheck() { |
|
132 let fileInput1 = |
|
133 frame.contentWindow.document.getElementById("fileInput1"); |
|
134 fileInput1.value = filePath; // this works because it's a chrome test |
|
135 |
|
136 fileInput1.dispatchEvent(e); |
|
137 check(); |
|
138 } |
|
139 |
|
140 if (aDumpFirst) { |
|
141 let dumper = Cc["@mozilla.org/memory-info-dumper;1"]. |
|
142 getService(Ci.nsIMemoryInfoDumper); |
|
143 dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null); |
|
144 } else { |
|
145 loadAndCheck(); |
|
146 } |
|
147 |
|
148 } else { |
|
149 let fileInput2 = |
|
150 frame.contentWindow.document.getElementById("fileInput2"); |
|
151 fileInput2.value = filePath; // this works because it's a chrome test |
|
152 |
|
153 // Hack alert: fileInput2's onchange handler calls fileInput2.click(). |
|
154 // But we don't want that to happen, because we want to bypass the file |
|
155 // picker for the test. So we set |e.skipClick|, which causes |
|
156 // fileInput2.click() to be skipped, and dispatch the second change event |
|
157 // directly ourselves. |
|
158 |
|
159 e.skipClick = true; |
|
160 fileInput2.dispatchEvent(e); |
|
161 |
|
162 let filePath2 = getFilePath(aFilename2); |
|
163 fileInput2.value = filePath2; // this works because it's a chrome test |
|
164 |
|
165 let e2 = document.createEvent('Event'); |
|
166 e2.initEvent('change', true, true); |
|
167 fileInput2.dispatchEvent(e); |
|
168 |
|
169 check(); |
|
170 } |
|
171 } |
|
172 |
|
173 // Returns a function that chains together multiple test() calls. |
|
174 function chain(aPieces) { |
|
175 let x = aPieces.shift(); |
|
176 if (x) { |
|
177 return function() { test(x.filename, x.filename2, x.expected, x.dumpFirst, chain(aPieces)); } |
|
178 } else { |
|
179 return function() { finish(); }; |
|
180 } |
|
181 } |
|
182 |
|
183 let expectedGood = |
|
184 "\ |
|
185 Explicit-only process\n\ |
|
186 \n\ |
|
187 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ |
|
188 Explicit Allocations\n\ |
|
189 \n\ |
|
190 100,000 B (100.0%) -- explicit\n\ |
|
191 └──100,000 B (100.0%) ── a/b\n\ |
|
192 \n\ |
|
193 Other Measurements\n\ |
|
194 \n\ |
|
195 End of Explicit-only process\n\ |
|
196 Main Process (pid NNN)\n\ |
|
197 Explicit Allocations\n\ |
|
198 \n\ |
|
199 262,144,000 B (100.0%) -- explicit\n\ |
|
200 ├──209,715,200 B (80.00%) ── heap-unclassified\n\ |
|
201 └───52,428,800 B (20.00%) ── a/b\n\ |
|
202 \n\ |
|
203 Other Measurements\n\ |
|
204 \n\ |
|
205 1,024 B (100.0%) -- compartments\n\ |
|
206 └──1,024 B (100.0%) ── system/a\n\ |
|
207 \n\ |
|
208 1,024 B (100.0%) -- ghost-windows\n\ |
|
209 └──1,024 B (100.0%) ── a\n\ |
|
210 \n\ |
|
211 314,572 B (100.0%) -- other\n\ |
|
212 ├──209,715 B (66.67%) ── a\n\ |
|
213 └──104,857 B (33.33%) ── b\n\ |
|
214 \n\ |
|
215 1,024 B (100.0%) -- pss\n\ |
|
216 └──1,024 B (100.0%) ── a\n\ |
|
217 \n\ |
|
218 1,024 B (100.0%) -- rss\n\ |
|
219 └──1,024 B (100.0%) ── a\n\ |
|
220 \n\ |
|
221 1,024 B (100.0%) -- size\n\ |
|
222 └──1,024 B (100.0%) ── a\n\ |
|
223 \n\ |
|
224 1,024 B (100.0%) -- swap\n\ |
|
225 └──1,024 B (100.0%) ── a\n\ |
|
226 \n\ |
|
227 262,144,000 B ── heap-allocated\n\ |
|
228 \n\ |
|
229 End of Main Process (pid NNN)\n\ |
|
230 Other-only process\n\ |
|
231 Other Measurements\n\ |
|
232 \n\ |
|
233 200,000 B (100.0%) -- a\n\ |
|
234 ├──100,000 B (50.00%) ── b\n\ |
|
235 └──100,000 B (50.00%) ── c\n\ |
|
236 \n\ |
|
237 500,000 B ── heap-allocated\n\ |
|
238 \n\ |
|
239 End of Other-only process\n\ |
|
240 "; |
|
241 |
|
242 let expectedGood2 = |
|
243 "\ |
|
244 Main Process (pid NNN)\n\ |
|
245 Explicit Allocations\n\ |
|
246 \n\ |
|
247 262,144,000 B (100.0%) -- explicit\n\ |
|
248 ├──209,715,200 B (80.00%) ── heap-unclassified\n\ |
|
249 └───52,428,800 B (20.00%) ── a/b\n\ |
|
250 \n\ |
|
251 Other Measurements\n\ |
|
252 \n\ |
|
253 314,572 B (100.0%) -- other\n\ |
|
254 ├──209,715 B (66.67%) ── a\n\ |
|
255 └──104,857 B (33.33%) ── b\n\ |
|
256 \n\ |
|
257 262,144,000 B ── heap-allocated\n\ |
|
258 \n\ |
|
259 End of Main Process (pid NNN)\n\ |
|
260 "; |
|
261 |
|
262 // This is the output for a malformed data file. |
|
263 let expectedBad = |
|
264 "\ |
|
265 Invalid memory report(s): missing 'hasMozMallocUsableSize' property\ |
|
266 "; |
|
267 |
|
268 // This is the output for a diff. |
|
269 let expectedDiff = |
|
270 "\ |
|
271 P\n\ |
|
272 \n\ |
|
273 WARNING: the 'heap-allocated' memory reporter does not work for this platform and/or configuration. This means that 'heap-unclassified' is not shown and the 'explicit' tree shows less memory than it should.\n\ |
|
274 Explicit Allocations\n\ |
|
275 \n\ |
|
276 -10,005 B (100.0%) -- explicit\n\ |
|
277 ├──-10,000 B (99.95%) ── storage/prefixset/goog-phish-shavar\n\ |
|
278 ├───────-6 B (00.06%) ── spell-check [2]\n\ |
|
279 └────────1 B (-0.01%) ── xpcom/category-manager\n\ |
|
280 \n\ |
|
281 Other Measurements\n\ |
|
282 \n\ |
|
283 3,000 B ── canvas-2d-pixel-bytes [2] [+]\n\ |
|
284 -100 B ── foobar [-]\n\ |
|
285 \n\ |
|
286 End of P\n\ |
|
287 P2 (pid NNN)\n\ |
|
288 Other Measurements\n\ |
|
289 \n\ |
|
290 11 B ── z 0xNNN\n\ |
|
291 \n\ |
|
292 End of P2 (pid NNN)\n\ |
|
293 P3\n\ |
|
294 Other Measurements\n\ |
|
295 \n\ |
|
296 -55 B ── p3 [-]\n\ |
|
297 \n\ |
|
298 End of P3\n\ |
|
299 P4\n\ |
|
300 Other Measurements\n\ |
|
301 \n\ |
|
302 66 B ── p4 [+]\n\ |
|
303 \n\ |
|
304 End of P4\n\ |
|
305 P7\n\ |
|
306 Other Measurements\n\ |
|
307 \n\ |
|
308 7 B (100.0%) -- p7\n\ |
|
309 ├──4 B (57.14%) ── c [+]\n\ |
|
310 └──3 B (42.86%) ── b [+]\n\ |
|
311 \n\ |
|
312 -5 B ── p7 [-]\n\ |
|
313 \n\ |
|
314 End of P7\n\ |
|
315 P8\n\ |
|
316 Other Measurements\n\ |
|
317 \n\ |
|
318 -22 B (100.0%) -- p8\n\ |
|
319 └──-22 B (100.0%) -- a\n\ |
|
320 ├──-11 B (50.00%) -- b\n\ |
|
321 │ ├───-7 B (31.82%) -- c\n\ |
|
322 │ │ ├──-4 B (18.18%) ── e [-]\n\ |
|
323 │ │ └──-3 B (13.64%) ── d [-]\n\ |
|
324 │ ├───-5 B (22.73%) ── f [-]\n\ |
|
325 │ └────1 B (-4.55%) ── (fake child) [!]\n\ |
|
326 └──-11 B (50.00%) -- g\n\ |
|
327 ├───-7 B (31.82%) ── i [-]\n\ |
|
328 ├───-6 B (27.27%) ── h [-]\n\ |
|
329 └────2 B (-9.09%) ── (fake child) [!]\n\ |
|
330 \n\ |
|
331 End of P8\n\ |
|
332 "; |
|
333 |
|
334 let frames = [ |
|
335 // This loads a pre-existing file that is valid. |
|
336 { filename: "memory-reports-good.json", expected: expectedGood, dumpFirst: false }, |
|
337 |
|
338 // This dumps to a file and then reads it back in. |
|
339 { filename: "memory-reports-dumped.json.gz", expected: expectedGood2, dumpFirst: true }, |
|
340 |
|
341 // This loads a pre-existing file that is invalid. |
|
342 { filename: "memory-reports-bad.json", expected: expectedBad, dumpFirst: false }, |
|
343 |
|
344 // This loads a pre-existing diff file. |
|
345 { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiff, dumpFirst: false } |
|
346 ]; |
|
347 |
|
348 SimpleTest.waitForFocus(chain(frames)); |
|
349 |
|
350 SimpleTest.waitForExplicitFinish(); |
|
351 ]]> |
|
352 </script> |
|
353 </window> |