|
1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" |
|
2 "http://www.w3.org/TR/html4/strict.dtd"> |
|
3 <!-- |
|
4 vim:sw=4:ts=4:et: |
|
5 This Source Code Form is subject to the terms of the Mozilla Public |
|
6 - License, v. 2.0. If a copy of the MPL was not distributed with this |
|
7 - file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
8 --> |
|
9 <html lang="en-US"> |
|
10 <head> |
|
11 <title>Leak Gauge</title> |
|
12 |
|
13 <style type="text/css"> |
|
14 pre { margin: 0; } |
|
15 pre.output { border: medium solid; padding: 1em; margin: 1em; } |
|
16 </style> |
|
17 <script type="text/javascript"> |
|
18 |
|
19 function runFile(file) { |
|
20 var result = "Results of processing log " + file.fileName + " :\n"; |
|
21 |
|
22 var fileReader = new FileReader(); |
|
23 fileReader.onload = function(e) |
|
24 { |
|
25 runContents(result, e.target.result); |
|
26 } |
|
27 fileReader.readAsText(file, "iso-8859-1"); |
|
28 } |
|
29 |
|
30 function runContents(result, contents) { |
|
31 // A hash of objects (keyed by the first word of the line in the log) |
|
32 // that have two public methods, handle_line and dump (to be called using |
|
33 // call, above), along with any private data they need. |
|
34 var handlers = { |
|
35 "DOMWINDOW": { |
|
36 count: 0, |
|
37 windows: {}, |
|
38 handle_line: function(line) { |
|
39 var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); |
|
40 if (match) { |
|
41 var addr = match[1]; |
|
42 var verb = match[2]; |
|
43 var rest = match[3]; |
|
44 if (verb == "created") { |
|
45 var m = rest.match(/ outer=([0-9a-f]*)$/); |
|
46 if (!m) |
|
47 throw "outer expected"; |
|
48 this.windows[addr] = { outer: m[1] }; |
|
49 ++this.count; |
|
50 } else if (verb == "destroyed") { |
|
51 delete this.windows[addr]; |
|
52 } else if (verb == "SetNewDocument") { |
|
53 var m = rest.match(/^ (.*)$/); |
|
54 if (!m) |
|
55 throw "URI expected"; |
|
56 this.windows[addr][m[1]] = true; |
|
57 } |
|
58 } |
|
59 }, |
|
60 dump: function() { |
|
61 for (var addr in this.windows) { |
|
62 var winobj = this.windows[addr]; |
|
63 var outer = winobj.outer; |
|
64 delete winobj.outer; |
|
65 result += "Leaked " + (outer == "0" ? "outer" : "inner") + |
|
66 " window " + addr + " " + |
|
67 (outer == "0" ? "" : "(outer " + outer + ") ") + |
|
68 "at address " + addr + ".\n"; |
|
69 for (var uri in winobj) { |
|
70 result += " ... with URI \"" + uri + "\".\n"; |
|
71 } |
|
72 } |
|
73 }, |
|
74 summary: function() { |
|
75 var len = 0; |
|
76 for (var w in this.windows) |
|
77 ++len; |
|
78 result += 'Leaked ' + len + ' out of ' + |
|
79 this.count + " DOM Windows\n"; |
|
80 } |
|
81 }, |
|
82 "DOCUMENT": { |
|
83 count: 0, |
|
84 docs: {}, |
|
85 handle_line: function(line) { |
|
86 var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); |
|
87 if (match) { |
|
88 var addr = match[1]; |
|
89 var verb = match[2]; |
|
90 var rest = match[3]; |
|
91 if (verb == "created") { |
|
92 this.docs[addr] = {}; |
|
93 ++this.count; |
|
94 } else if (verb == "destroyed") { |
|
95 delete this.docs[addr]; |
|
96 } else if (verb == "ResetToURI" || |
|
97 verb == "StartDocumentLoad") { |
|
98 var m = rest.match(/^ (.*)$/); |
|
99 if (!m) |
|
100 throw "URI expected"; |
|
101 var uri = m[1]; |
|
102 var doc_info = this.docs[addr]; |
|
103 doc_info[uri] = true; |
|
104 if ("nim" in doc_info) { |
|
105 doc_info["nim"][uri] = true; |
|
106 } |
|
107 } |
|
108 } |
|
109 }, |
|
110 dump: function() { |
|
111 for (var addr in this.docs) { |
|
112 var doc = this.docs[addr]; |
|
113 result += "Leaked document at address " + addr + ".\n"; |
|
114 for (var uri in doc) { |
|
115 if (uri != "nim") { |
|
116 result += " ... with URI \"" + uri + "\".\n"; |
|
117 } |
|
118 } |
|
119 } |
|
120 }, |
|
121 summary: function() { |
|
122 var len = 0; |
|
123 for (var w in this.docs) |
|
124 ++len; |
|
125 result += 'Leaked ' + len + ' out of ' + |
|
126 this.count + " documents\n"; |
|
127 } |
|
128 }, |
|
129 "DOCSHELL": { |
|
130 count: 0, |
|
131 shells: {}, |
|
132 handle_line: function(line) { |
|
133 var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); |
|
134 if (match) { |
|
135 var addr = match[1]; |
|
136 var verb = match[2]; |
|
137 var rest = match[3]; |
|
138 if (verb == "created") { |
|
139 this.shells[addr] = {}; |
|
140 ++this.count; |
|
141 } else if (verb == "destroyed") { |
|
142 delete this.shells[addr]; |
|
143 } else if (verb == "InternalLoad" || |
|
144 verb == "SetCurrentURI") { |
|
145 var m = rest.match(/^ (.*)$/); |
|
146 if (!m) |
|
147 throw "URI expected"; |
|
148 this.shells[addr][m[1]] = true; |
|
149 } |
|
150 } |
|
151 }, |
|
152 dump: function() { |
|
153 for (var addr in this.shells) { |
|
154 var doc = this.shells[addr]; |
|
155 result += "Leaked docshell at address " + addr + ".\n"; |
|
156 for (var uri in doc) { |
|
157 result += " ... which loaded URI \"" + uri + "\".\n"; |
|
158 } |
|
159 } |
|
160 }, |
|
161 summary: function() { |
|
162 var len = 0; |
|
163 for (var w in this.shells) |
|
164 ++len; |
|
165 result += 'Leaked ' + len + ' out of ' + |
|
166 this.count + " docshells\n"; |
|
167 } |
|
168 }, |
|
169 "NODEINFOMANAGER": { |
|
170 count: 0, |
|
171 nims: {}, |
|
172 handle_line: function(line) { |
|
173 var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); |
|
174 if (match) { |
|
175 var addr = match[1]; |
|
176 var verb = match[2]; |
|
177 var rest = match[3]; |
|
178 if (verb == "created") { |
|
179 this.nims[addr] = {}; |
|
180 ++this.count; |
|
181 } else if (verb == "destroyed") { |
|
182 delete this.nims[addr]; |
|
183 } else if (verb == "Init") { |
|
184 var m = rest.match(/^ document=(.*)$/); |
|
185 if (!m) |
|
186 throw "document pointer expected"; |
|
187 var nim_info = this.nims[addr]; |
|
188 var doc = m[1]; |
|
189 if (doc != "0") { |
|
190 var doc_info = handlers["DOCUMENT"].docs[doc]; |
|
191 for (var uri in doc_info) { |
|
192 nim_info[uri] = true; |
|
193 } |
|
194 doc_info["nim"] = nim_info; |
|
195 } |
|
196 } |
|
197 } |
|
198 }, |
|
199 dump: function() { |
|
200 for (var addr in this.nims) { |
|
201 var nim = this.nims[addr]; |
|
202 result += "Leaked content nodes associated with node info manager at address " + addr + ".\n"; |
|
203 for (var uri in nim) { |
|
204 result += " ... with document URI \"" + uri + "\".\n"; |
|
205 } |
|
206 } |
|
207 }, |
|
208 summary: function() { |
|
209 var len = 0; |
|
210 for (var w in this.nims) |
|
211 ++len; |
|
212 result += 'Leaked content nodes in ' + len + ' out of ' + |
|
213 this.count + " documents\n"; |
|
214 } |
|
215 } |
|
216 }; |
|
217 |
|
218 var lines = contents.split(/[\r\n]+/); |
|
219 for (var j in lines) { |
|
220 var line = lines[j]; |
|
221 // strip off initial "-", thread id, and thread pointer; separate |
|
222 // first word and rest |
|
223 var matches = line.match(/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) (.*)$/); |
|
224 if (matches) { |
|
225 var handler = matches[1]; |
|
226 var data = matches[2]; |
|
227 if (typeof(handlers[handler]) != "undefined") { |
|
228 handlers[handler].handle_line(data); |
|
229 } |
|
230 } |
|
231 } |
|
232 |
|
233 for (var handler in handlers) |
|
234 handlers[handler].dump(); |
|
235 if (result.length) |
|
236 result += "\n"; |
|
237 result += "Summary:\n"; |
|
238 for (var handler in handlers) |
|
239 handlers[handler].summary(); |
|
240 result += "\n"; |
|
241 |
|
242 var out = document.createElement("pre"); |
|
243 out.className = "output"; |
|
244 out.appendChild(document.createTextNode(result)); |
|
245 document.body.appendChild(out); |
|
246 } |
|
247 |
|
248 function run() { |
|
249 var input = document.getElementById("fileinput"); |
|
250 var files = input.files; |
|
251 for (var i = 0; i < files.length; ++i) |
|
252 runfile(files[i]); |
|
253 // So the user can process the same filename again (after |
|
254 // overwriting the log), clear the value on the form input so we |
|
255 // will always get an onchange event. |
|
256 input.value = ""; |
|
257 } |
|
258 |
|
259 </script> |
|
260 </head> |
|
261 <body> |
|
262 |
|
263 <h1>Leak Gauge</h1> |
|
264 |
|
265 <pre>$Id: leak-gauge.html,v 1.8 2008/02/08 19:55:34 dbaron%dbaron.org Exp $</pre> |
|
266 |
|
267 <p>This script is designed to help testers isolate and simplify testcases |
|
268 for many classes of leaks (those that involve large graphs of core |
|
269 data structures) in Mozilla-based browsers. It is designed to print |
|
270 information about what has leaked by processing a log taken while |
|
271 running the browser. Such a log can be taken over a long session of |
|
272 normal browsing and then the log can be processed to find sites that |
|
273 leak. Once a site is known to leak, the logging can then be repeated |
|
274 to figure out under what conditions the leak occurs.</p> |
|
275 |
|
276 <p>The way to create this log is to set the environment variables:</p> |
|
277 <pre> NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5 |
|
278 NSPR_LOG_FILE=nspr.log <i>(or any other filename of your choice)</i></pre> |
|
279 <p>in your shell and then run the program.</p> |
|
280 <ul> |
|
281 <li>In a Windows command prompt, set environment variables with |
|
282 <pre> set VAR=value</pre></li> |
|
283 <li> In an sh-based shell such as bash, set environment variables with |
|
284 <pre> export VAR=value</pre></li> |
|
285 <li>In a csh-based shell such as tcsh, set environment variables with |
|
286 <pre> setenv VAR value</pre></li> |
|
287 </ul> |
|
288 |
|
289 <p>Once you have this log from a complete run of the browser (you have |
|
290 to exit; otherwise it will look like everything leaked), you can load |
|
291 this page (be careful not to overwrite the log when starting the browser |
|
292 to load this page) and enter the filename of the log:</p> |
|
293 |
|
294 <p><input type="file" id="fileinput" onchange="run()"></p> |
|
295 |
|
296 <p>Then you'll see the output below, which will tell you which of |
|
297 certain core objects leaked and the URLs associated with those |
|
298 objects.</p> |
|
299 |
|
300 </body> |
|
301 </html> |