2006-01-13 23:37:23 +00:00
|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
|
|
|
"http://www.w3.org/TR/html4/strict.dtd">
|
|
|
|
<!--
|
|
|
|
vim:sw=4:ts=4:et:
|
|
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
|
|
|
|
The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.mozilla.org/MPL/
|
|
|
|
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
for the specific language governing rights and limitations under the
|
|
|
|
License.
|
|
|
|
|
|
|
|
The Original Code is leak-gauge.pl
|
|
|
|
|
|
|
|
The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
|
|
Portions created by the Initial Developer are Copyright (C) 2005
|
|
|
|
the Initial Developer. All Rights Reserved.
|
|
|
|
|
|
|
|
Contributor(s):
|
|
|
|
L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
|
|
|
|
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
|
|
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
of those above. If you wish to allow use of your version of this file only
|
|
|
|
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
use your version of this file under the terms of the MPL, indicate your
|
|
|
|
decision by deleting the provisions above and replace them with the notice
|
|
|
|
and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
the provisions above, a recipient may use your version of this file under
|
|
|
|
the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
-->
|
|
|
|
<html lang="en-US">
|
|
|
|
<head>
|
|
|
|
<title>Leak Gauge</title>
|
|
|
|
|
|
|
|
<style type="text/css">
|
2006-01-13 23:40:19 +00:00
|
|
|
pre { margin: 0; }
|
2006-01-13 23:37:23 +00:00
|
|
|
pre.output { border: medium solid; padding: 1em; margin: 1em; }
|
|
|
|
</style>
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
|
|
|
var result = "";
|
|
|
|
|
|
|
|
function run() {
|
|
|
|
// A hash of objects (keyed by the first word of the line in the log)
|
|
|
|
// that have two public methods, handle_line and dump (to be called using
|
|
|
|
// call, above), along with any private data they need.
|
|
|
|
var handlers = {
|
|
|
|
"DOMWINDOW": {
|
|
|
|
count: 0,
|
|
|
|
windows: {},
|
|
|
|
handle_line: function(line) {
|
|
|
|
var match = line.match(/^([0-9a-f]*) (\S*)(.*)/);
|
|
|
|
if (match) {
|
|
|
|
var addr = match[1];
|
|
|
|
var verb = match[2];
|
|
|
|
var rest = match[3];
|
|
|
|
if (verb == "created") {
|
|
|
|
var m = rest.match(/ outer=([0-9a-f]*)$/);
|
|
|
|
if (!m)
|
|
|
|
throw "outer expected";
|
|
|
|
this.windows[addr] = { outer: m[1] };
|
|
|
|
++this.count;
|
|
|
|
} else if (verb == "destroyed") {
|
|
|
|
delete this.windows[addr];
|
|
|
|
} else if (verb == "SetNewDocument") {
|
|
|
|
var m = rest.match(/^ (.*)$/);
|
|
|
|
if (!m)
|
|
|
|
throw "URI expected";
|
|
|
|
this.windows[addr][m[1]] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
dump: function() {
|
|
|
|
for (var addr in this.windows) {
|
|
|
|
var winobj = this.windows[addr];
|
|
|
|
var outer = winobj.outer;
|
|
|
|
delete winobj.outer;
|
|
|
|
result += "Leaked " + (outer == "0" ? "outer" : "inner") +
|
|
|
|
" window " + addr + " " +
|
|
|
|
(outer == "0" ? "" : "(outer " + outer + ") ") +
|
|
|
|
"at address " + addr + ".\n";
|
|
|
|
for (var uri in winobj) {
|
|
|
|
result += " ... with URI \"" + uri + "\".\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
summary: function() {
|
|
|
|
var len = 0;
|
|
|
|
for (var w in this.windows)
|
|
|
|
++len;
|
|
|
|
result += 'Leaked ' + len + ' out of ' +
|
|
|
|
this.count + " DOM Windows\n";
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"DOCUMENT": {
|
|
|
|
count: 0,
|
|
|
|
docs: {},
|
|
|
|
handle_line: function(line) {
|
|
|
|
var match = line.match(/^([0-9a-f]*) (\S*)(.*)/);
|
|
|
|
if (match) {
|
|
|
|
var addr = match[1];
|
|
|
|
var verb = match[2];
|
|
|
|
var rest = match[3];
|
|
|
|
if (verb == "created") {
|
|
|
|
this.docs[addr] = {};
|
|
|
|
++this.count;
|
|
|
|
} else if (verb == "destroyed") {
|
|
|
|
delete this.docs[addr];
|
|
|
|
} else if (verb == "ResetToURI" ||
|
|
|
|
verb == "StartDocumentLoad") {
|
|
|
|
var m = rest.match(/^ (.*)$/);
|
|
|
|
if (!m)
|
|
|
|
throw "URI expected";
|
|
|
|
this.docs[addr][m[1]] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
dump: function() {
|
|
|
|
for (var addr in this.docs) {
|
|
|
|
var doc = this.docs[addr];
|
|
|
|
result += "Leaked document at address " + addr + ".\n";
|
|
|
|
for (var uri in doc) {
|
|
|
|
result += " ... with URI \"" + uri + "\".\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
summary: function() {
|
|
|
|
var len = 0;
|
|
|
|
for (var w in this.docs)
|
|
|
|
++len;
|
|
|
|
result += 'Leaked ' + len + ' out of ' +
|
|
|
|
this.count + " documents\n";
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"DOCSHELL": {
|
|
|
|
count: 0,
|
|
|
|
shells: {},
|
|
|
|
handle_line: function(line) {
|
|
|
|
var match = line.match(/^([0-9a-f]*) (\S*)(.*)/);
|
|
|
|
if (match) {
|
|
|
|
var addr = match[1];
|
|
|
|
var verb = match[2];
|
|
|
|
var rest = match[3];
|
|
|
|
if (verb == "created") {
|
|
|
|
this.shells[addr] = {};
|
|
|
|
++this.count;
|
|
|
|
} else if (verb == "destroyed") {
|
|
|
|
delete this.shells[addr];
|
|
|
|
} else if (verb == "InternalLoad" ||
|
|
|
|
verb == "SetCurrentURI") {
|
|
|
|
var m = rest.match(/^ (.*)$/);
|
|
|
|
if (!m)
|
|
|
|
throw "URI expected";
|
|
|
|
this.shells[addr][m[1]] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
dump: function() {
|
|
|
|
for (var addr in this.shells) {
|
|
|
|
var doc = this.shells[addr];
|
|
|
|
result += "Leaked docshell at address " + addr + ".\n";
|
|
|
|
for (var uri in doc) {
|
|
|
|
result += " ... which loaded URI \"" + uri + "\".\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
summary: function() {
|
|
|
|
var len = 0;
|
|
|
|
for (var w in this.shells)
|
|
|
|
++len;
|
|
|
|
result += 'Leaked ' + len + ' out of ' +
|
|
|
|
this.count + " docshells\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
|
|
|
|
|
|
|
const cs = Components.classes;
|
|
|
|
const ifs = Components.interfaces;
|
|
|
|
|
|
|
|
var filePicker = cs["@mozilla.org/filepicker;1"].
|
|
|
|
createInstance(ifs.nsIFilePicker);
|
|
|
|
filePicker.init(window, "Select NSPR Leak Log", ifs.nsIFilePicker.modeOpen);
|
|
|
|
if (filePicker.show() != ifs.nsIFilePicker.returnOK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var is = cs["@mozilla.org/network/file-input-stream;1"].
|
|
|
|
createInstance(ifs.nsIFileInputStream);
|
|
|
|
const PR_RDONLY = 0x01;
|
|
|
|
is.init(filePicker.file, PR_RDONLY, 0, 0);
|
|
|
|
if (!(is instanceof ifs.nsILineInputStream))
|
|
|
|
return;
|
|
|
|
var line = { value: "" };
|
|
|
|
do {
|
|
|
|
var more = is.readLine(line);// yuck, returns false for last valid line
|
|
|
|
|
|
|
|
// strip off initial "-", thread id, and thread pointer; separate
|
|
|
|
// first word and rest
|
|
|
|
var matches = line.value.match(/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) (.*)$/);
|
|
|
|
if (matches) {
|
|
|
|
var handler = matches[1];
|
|
|
|
var data = matches[2];
|
|
|
|
if (typeof(handlers[handler]) != "undefined") {
|
|
|
|
handlers[handler].handle_line(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (more);
|
|
|
|
|
|
|
|
for (var handler in handlers)
|
|
|
|
handlers[handler].dump();
|
|
|
|
result += "Summary:\n";
|
|
|
|
for (var handler in handlers)
|
|
|
|
handlers[handler].summary();
|
|
|
|
|
|
|
|
var out = document.createElement("pre");
|
|
|
|
out.className = "output";
|
|
|
|
out.appendChild(document.createTextNode(result));
|
|
|
|
result = "";
|
|
|
|
document.body.appendChild(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
|
|
|
|
<h1>Leak Gauge</h1>
|
|
|
|
|
2006-01-13 23:40:19 +00:00
|
|
|
<pre>$Id: leak-gauge.html,v 1.2 2006/01/13 23:40:19 dbaron%dbaron.org Exp $</pre>
|
2006-01-13 23:37:23 +00:00
|
|
|
|
|
|
|
<p>This script is designed to help testers isolate and simplify testcases
|
|
|
|
for many classes of leaks (those that involve large graphs of core
|
|
|
|
data structures) in Mozilla-based browsers. It is designed to print
|
|
|
|
information about what has leaked by processing a log taken while
|
|
|
|
running the browser. Such a log can be taken over a long session of
|
|
|
|
normal browsing and then the log can be processed to find sites that
|
|
|
|
leak. Once a site is known to leak, the logging can then be repeated
|
|
|
|
to figure out under what conditions the leak occurs.</p>
|
|
|
|
|
2006-01-13 23:40:19 +00:00
|
|
|
<p>The way to create this log is to set the environment variables:</p>
|
|
|
|
<pre> NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5
|
|
|
|
NSPR_LOG_FILE=nspr.log <i>(or any other filename of your choice)</i></pre>
|
|
|
|
<p>in your shell and then run the program.</p>
|
|
|
|
<ul>
|
|
|
|
<li>In a Windows command prompt, set environment variables with
|
|
|
|
<pre> set VAR=value</pre></li>
|
|
|
|
<li> In an sh-based shell such as bash, set environment variables with
|
|
|
|
<pre> export VAR=value</pre></li>
|
|
|
|
<li>In a csh-based shell such as tcsh, set environment variables with
|
|
|
|
<pre> setenv VAR value</pre></li>
|
|
|
|
</ul>
|
2006-01-13 23:37:23 +00:00
|
|
|
|
|
|
|
<p>Then <a href="javascript:run()">enter the filename</a> of the log and you'll
|
|
|
|
see the output, which will tell you which of certain core objects leaked and
|
|
|
|
the URLs associated with those objects.</p>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|