1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/footprint/leak-gauge.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,301 @@ 1.4 +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 1.5 + "http://www.w3.org/TR/html4/strict.dtd"> 1.6 +<!-- 1.7 + vim:sw=4:ts=4:et: 1.8 + This Source Code Form is subject to the terms of the Mozilla Public 1.9 + - License, v. 2.0. If a copy of the MPL was not distributed with this 1.10 + - file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.11 +--> 1.12 +<html lang="en-US"> 1.13 +<head> 1.14 +<title>Leak Gauge</title> 1.15 + 1.16 +<style type="text/css"> 1.17 +pre { margin: 0; } 1.18 +pre.output { border: medium solid; padding: 1em; margin: 1em; } 1.19 +</style> 1.20 +<script type="text/javascript"> 1.21 + 1.22 +function runFile(file) { 1.23 + var result = "Results of processing log " + file.fileName + " :\n"; 1.24 + 1.25 + var fileReader = new FileReader(); 1.26 + fileReader.onload = function(e) 1.27 + { 1.28 + runContents(result, e.target.result); 1.29 + } 1.30 + fileReader.readAsText(file, "iso-8859-1"); 1.31 +} 1.32 + 1.33 +function runContents(result, contents) { 1.34 + // A hash of objects (keyed by the first word of the line in the log) 1.35 + // that have two public methods, handle_line and dump (to be called using 1.36 + // call, above), along with any private data they need. 1.37 + var handlers = { 1.38 + "DOMWINDOW": { 1.39 + count: 0, 1.40 + windows: {}, 1.41 + handle_line: function(line) { 1.42 + var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); 1.43 + if (match) { 1.44 + var addr = match[1]; 1.45 + var verb = match[2]; 1.46 + var rest = match[3]; 1.47 + if (verb == "created") { 1.48 + var m = rest.match(/ outer=([0-9a-f]*)$/); 1.49 + if (!m) 1.50 + throw "outer expected"; 1.51 + this.windows[addr] = { outer: m[1] }; 1.52 + ++this.count; 1.53 + } else if (verb == "destroyed") { 1.54 + delete this.windows[addr]; 1.55 + } else if (verb == "SetNewDocument") { 1.56 + var m = rest.match(/^ (.*)$/); 1.57 + if (!m) 1.58 + throw "URI expected"; 1.59 + this.windows[addr][m[1]] = true; 1.60 + } 1.61 + } 1.62 + }, 1.63 + dump: function() { 1.64 + for (var addr in this.windows) { 1.65 + var winobj = this.windows[addr]; 1.66 + var outer = winobj.outer; 1.67 + delete winobj.outer; 1.68 + result += "Leaked " + (outer == "0" ? "outer" : "inner") + 1.69 + " window " + addr + " " + 1.70 + (outer == "0" ? "" : "(outer " + outer + ") ") + 1.71 + "at address " + addr + ".\n"; 1.72 + for (var uri in winobj) { 1.73 + result += " ... with URI \"" + uri + "\".\n"; 1.74 + } 1.75 + } 1.76 + }, 1.77 + summary: function() { 1.78 + var len = 0; 1.79 + for (var w in this.windows) 1.80 + ++len; 1.81 + result += 'Leaked ' + len + ' out of ' + 1.82 + this.count + " DOM Windows\n"; 1.83 + } 1.84 + }, 1.85 + "DOCUMENT": { 1.86 + count: 0, 1.87 + docs: {}, 1.88 + handle_line: function(line) { 1.89 + var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); 1.90 + if (match) { 1.91 + var addr = match[1]; 1.92 + var verb = match[2]; 1.93 + var rest = match[3]; 1.94 + if (verb == "created") { 1.95 + this.docs[addr] = {}; 1.96 + ++this.count; 1.97 + } else if (verb == "destroyed") { 1.98 + delete this.docs[addr]; 1.99 + } else if (verb == "ResetToURI" || 1.100 + verb == "StartDocumentLoad") { 1.101 + var m = rest.match(/^ (.*)$/); 1.102 + if (!m) 1.103 + throw "URI expected"; 1.104 + var uri = m[1]; 1.105 + var doc_info = this.docs[addr]; 1.106 + doc_info[uri] = true; 1.107 + if ("nim" in doc_info) { 1.108 + doc_info["nim"][uri] = true; 1.109 + } 1.110 + } 1.111 + } 1.112 + }, 1.113 + dump: function() { 1.114 + for (var addr in this.docs) { 1.115 + var doc = this.docs[addr]; 1.116 + result += "Leaked document at address " + addr + ".\n"; 1.117 + for (var uri in doc) { 1.118 + if (uri != "nim") { 1.119 + result += " ... with URI \"" + uri + "\".\n"; 1.120 + } 1.121 + } 1.122 + } 1.123 + }, 1.124 + summary: function() { 1.125 + var len = 0; 1.126 + for (var w in this.docs) 1.127 + ++len; 1.128 + result += 'Leaked ' + len + ' out of ' + 1.129 + this.count + " documents\n"; 1.130 + } 1.131 + }, 1.132 + "DOCSHELL": { 1.133 + count: 0, 1.134 + shells: {}, 1.135 + handle_line: function(line) { 1.136 + var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); 1.137 + if (match) { 1.138 + var addr = match[1]; 1.139 + var verb = match[2]; 1.140 + var rest = match[3]; 1.141 + if (verb == "created") { 1.142 + this.shells[addr] = {}; 1.143 + ++this.count; 1.144 + } else if (verb == "destroyed") { 1.145 + delete this.shells[addr]; 1.146 + } else if (verb == "InternalLoad" || 1.147 + verb == "SetCurrentURI") { 1.148 + var m = rest.match(/^ (.*)$/); 1.149 + if (!m) 1.150 + throw "URI expected"; 1.151 + this.shells[addr][m[1]] = true; 1.152 + } 1.153 + } 1.154 + }, 1.155 + dump: function() { 1.156 + for (var addr in this.shells) { 1.157 + var doc = this.shells[addr]; 1.158 + result += "Leaked docshell at address " + addr + ".\n"; 1.159 + for (var uri in doc) { 1.160 + result += " ... which loaded URI \"" + uri + "\".\n"; 1.161 + } 1.162 + } 1.163 + }, 1.164 + summary: function() { 1.165 + var len = 0; 1.166 + for (var w in this.shells) 1.167 + ++len; 1.168 + result += 'Leaked ' + len + ' out of ' + 1.169 + this.count + " docshells\n"; 1.170 + } 1.171 + }, 1.172 + "NODEINFOMANAGER": { 1.173 + count: 0, 1.174 + nims: {}, 1.175 + handle_line: function(line) { 1.176 + var match = line.match(/^([0-9a-f]*) (\S*)(.*)/); 1.177 + if (match) { 1.178 + var addr = match[1]; 1.179 + var verb = match[2]; 1.180 + var rest = match[3]; 1.181 + if (verb == "created") { 1.182 + this.nims[addr] = {}; 1.183 + ++this.count; 1.184 + } else if (verb == "destroyed") { 1.185 + delete this.nims[addr]; 1.186 + } else if (verb == "Init") { 1.187 + var m = rest.match(/^ document=(.*)$/); 1.188 + if (!m) 1.189 + throw "document pointer expected"; 1.190 + var nim_info = this.nims[addr]; 1.191 + var doc = m[1]; 1.192 + if (doc != "0") { 1.193 + var doc_info = handlers["DOCUMENT"].docs[doc]; 1.194 + for (var uri in doc_info) { 1.195 + nim_info[uri] = true; 1.196 + } 1.197 + doc_info["nim"] = nim_info; 1.198 + } 1.199 + } 1.200 + } 1.201 + }, 1.202 + dump: function() { 1.203 + for (var addr in this.nims) { 1.204 + var nim = this.nims[addr]; 1.205 + result += "Leaked content nodes associated with node info manager at address " + addr + ".\n"; 1.206 + for (var uri in nim) { 1.207 + result += " ... with document URI \"" + uri + "\".\n"; 1.208 + } 1.209 + } 1.210 + }, 1.211 + summary: function() { 1.212 + var len = 0; 1.213 + for (var w in this.nims) 1.214 + ++len; 1.215 + result += 'Leaked content nodes in ' + len + ' out of ' + 1.216 + this.count + " documents\n"; 1.217 + } 1.218 + } 1.219 + }; 1.220 + 1.221 + var lines = contents.split(/[\r\n]+/); 1.222 + for (var j in lines) { 1.223 + var line = lines[j]; 1.224 + // strip off initial "-", thread id, and thread pointer; separate 1.225 + // first word and rest 1.226 + var matches = line.match(/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) (.*)$/); 1.227 + if (matches) { 1.228 + var handler = matches[1]; 1.229 + var data = matches[2]; 1.230 + if (typeof(handlers[handler]) != "undefined") { 1.231 + handlers[handler].handle_line(data); 1.232 + } 1.233 + } 1.234 + } 1.235 + 1.236 + for (var handler in handlers) 1.237 + handlers[handler].dump(); 1.238 + if (result.length) 1.239 + result += "\n"; 1.240 + result += "Summary:\n"; 1.241 + for (var handler in handlers) 1.242 + handlers[handler].summary(); 1.243 + result += "\n"; 1.244 + 1.245 + var out = document.createElement("pre"); 1.246 + out.className = "output"; 1.247 + out.appendChild(document.createTextNode(result)); 1.248 + document.body.appendChild(out); 1.249 +} 1.250 + 1.251 +function run() { 1.252 + var input = document.getElementById("fileinput"); 1.253 + var files = input.files; 1.254 + for (var i = 0; i < files.length; ++i) 1.255 + runfile(files[i]); 1.256 + // So the user can process the same filename again (after 1.257 + // overwriting the log), clear the value on the form input so we 1.258 + // will always get an onchange event. 1.259 + input.value = ""; 1.260 +} 1.261 + 1.262 +</script> 1.263 +</head> 1.264 +<body> 1.265 + 1.266 +<h1>Leak Gauge</h1> 1.267 + 1.268 +<pre>$Id: leak-gauge.html,v 1.8 2008/02/08 19:55:34 dbaron%dbaron.org Exp $</pre> 1.269 + 1.270 +<p>This script is designed to help testers isolate and simplify testcases 1.271 +for many classes of leaks (those that involve large graphs of core 1.272 +data structures) in Mozilla-based browsers. It is designed to print 1.273 +information about what has leaked by processing a log taken while 1.274 +running the browser. Such a log can be taken over a long session of 1.275 +normal browsing and then the log can be processed to find sites that 1.276 +leak. Once a site is known to leak, the logging can then be repeated 1.277 +to figure out under what conditions the leak occurs.</p> 1.278 + 1.279 +<p>The way to create this log is to set the environment variables:</p> 1.280 +<pre> NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5 1.281 + NSPR_LOG_FILE=nspr.log <i>(or any other filename of your choice)</i></pre> 1.282 +<p>in your shell and then run the program.</p> 1.283 +<ul> 1.284 +<li>In a Windows command prompt, set environment variables with 1.285 +<pre> set VAR=value</pre></li> 1.286 +<li> In an sh-based shell such as bash, set environment variables with 1.287 +<pre> export VAR=value</pre></li> 1.288 +<li>In a csh-based shell such as tcsh, set environment variables with 1.289 +<pre> setenv VAR value</pre></li> 1.290 +</ul> 1.291 + 1.292 +<p>Once you have this log from a complete run of the browser (you have 1.293 +to exit; otherwise it will look like everything leaked), you can load 1.294 +this page (be careful not to overwrite the log when starting the browser 1.295 +to load this page) and enter the filename of the log:</p> 1.296 + 1.297 +<p><input type="file" id="fileinput" onchange="run()"></p> 1.298 + 1.299 +<p>Then you'll see the output below, which will tell you which of 1.300 +certain core objects leaked and the URLs associated with those 1.301 +objects.</p> 1.302 + 1.303 +</body> 1.304 +</html>