Bug 568276: Check for strange-variable-combination regression. (r=brendan)
[mozilla-central.git] / tools / footprint / leak-gauge.pl
blob738f621ea1f4ee24a4ebdf3895cb9a87ddec47ee
1 #!/usr/bin/perl -w
2 # vim:sw=4:ts=4:et:
3 # ***** BEGIN LICENSE BLOCK *****
4 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 # The contents of this file are subject to the Mozilla Public License Version
7 # 1.1 (the "License"); you may not use this file except in compliance with
8 # the License. You may obtain a copy of the License at
9 # http://www.mozilla.org/MPL/
11 # Software distributed under the License is distributed on an "AS IS" basis,
12 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 # for the specific language governing rights and limitations under the
14 # License.
16 # The Original Code is leak-gauge.pl
18 # The Initial Developer of the Original Code is the Mozilla Foundation.
19 # Portions created by the Initial Developer are Copyright (C) 2005
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 # L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
39 # $Id: leak-gauge.pl,v 1.8 2008/02/08 19:55:03 dbaron%dbaron.org Exp $
40 # This script is designed to help testers isolate and simplify testcases
41 # for many classes of leaks (those that involve large graphs of core
42 # data structures) in Mozilla-based browsers. It is designed to print
43 # information about what has leaked by processing a log taken while
44 # running the browser. Such a log can be taken over a long session of
45 # normal browsing and then the log can be processed to find sites that
46 # leak. Once a site is known to leak, the logging can then be repeated
47 # to figure out under what conditions the leak occurs.
49 # The way to create this log is to set the environment variables:
50 # NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5
51 # NSPR_LOG_FILE=nspr.log (or any other filename of your choice)
52 # in your shell and then run the program.
53 # * In a Windows command prompt, set environment variables with
54 # set VAR=value
55 # * In an sh-based shell such as bash, set environment variables with
56 # export VAR=value
57 # * In a csh-based shell such as tcsh, set environment variables with
58 # setenv VAR value
60 # Then, after you have exited the browser, run this perl script over the
61 # log. Either of the following commands should work:
62 # perl ./path/to/leak-gauge.pl nspr.log
63 # cat nspr.log | perl ./path/to/leak-gauge.pl
64 # and it will tell you which of certain core objects leaked and the URLs
65 # associated with those objects.
68 # Nobody said I'm not allowed to write my own object system in perl. No
69 # classes here. Just objects and methods.
70 sub call {
71 my $func = shift;
72 my $obj = shift;
73 my $funcref = ${$obj}{$func};
74 &$funcref($obj, @_);
77 # A hash of objects (keyed by the first word of the line in the log)
78 # that have two public methods, handle_line and dump (to be called using
79 # call, above), along with any private data they need.
80 my $handlers = {
81 "DOMWINDOW" => {
82 count => 0,
83 windows => {},
84 handle_line => sub($$) {
85 my ($self, $line) = @_;
86 my $windows = ${$self}{windows};
87 if ($line =~ /^([0-9a-f]*) (\S*)/) {
88 my ($addr, $verb, $rest) = ($1, $2, $');
89 if ($verb eq "created") {
90 $rest =~ / outer=([0-9a-f]*)$/ || die "outer expected";
91 my $outer = $1;
92 ${$windows}{$addr} = { outer => $1 };
93 ++${$self}{count};
94 } elsif ($verb eq "destroyed") {
95 delete ${$windows}{$addr};
96 } elsif ($verb eq "SetNewDocument") {
97 $rest =~ /^ (.*)$/ || die "URI expected";
98 my $uri = ($1);
99 ${${$windows}{$addr}}{$uri} = 1;
103 dump => sub ($) {
104 my ($self) = @_;
105 my $windows = ${$self}{windows};
106 foreach my $addr (keys(%{$windows})) {
107 my $winobj = ${$windows}{$addr};
108 my $outer = delete ${$winobj}{outer};
109 print "Leaked " . ($outer eq "0" ? "outer" : "inner") .
110 " window $addr " .
111 ($outer eq "0" ? "" : "(outer $outer) ") .
112 "at address $addr.\n";
113 foreach my $uri (keys(%{$winobj})) {
114 print " ... with URI \"$uri\".\n";
118 summary => sub($) {
119 my ($self) = @_;
120 my $windows = ${$self}{windows};
121 print 'Leaked ' . keys(%{$windows}) . ' out of ' .
122 ${$self}{count} . " DOM Windows\n";
125 "DOCUMENT" => {
126 count => 0,
127 docs => {},
128 handle_line => sub($$) {
129 my ($self, $line) = @_;
130 # This doesn't work; I don't have time to figure out why not.
131 # my $docs = ${$self}{docs};
132 my $docs = ${$handlers}{"DOCUMENT"}{docs};
133 if ($line =~ /^([0-9a-f]*) (\S*)/) {
134 my ($addr, $verb, $rest) = ($1, $2, $');
135 if ($verb eq "created") {
136 ${$docs}{$addr} = {};
137 ++${$self}{count};
138 } elsif ($verb eq "destroyed") {
139 delete ${$docs}{$addr};
140 } elsif ($verb eq "ResetToURI" ||
141 $verb eq "StartDocumentLoad") {
142 $rest =~ /^ (.*)$/ || die "URI expected";
143 my $uri = $1;
144 my $doc_info = ${$docs}{$addr};
145 ${$doc_info}{$uri} = 1;
146 if (exists(${$doc_info}{"nim"})) {
147 ${$doc_info}{"nim"}{$uri} = 1;
152 dump => sub ($) {
153 my ($self) = @_;
154 my $docs = ${$self}{docs};
155 foreach my $addr (keys(%{$docs})) {
156 print "Leaked document at address $addr.\n";
157 foreach my $uri (keys(%{${$docs}{$addr}})) {
158 print " ... with URI \"$uri\".\n" unless $uri eq "nim";
162 summary => sub($) {
163 my ($self) = @_;
164 my $docs = ${$self}{docs};
165 print 'Leaked ' . keys(%{$docs}) . ' out of ' .
166 ${$self}{count} . " documents\n";
169 "DOCSHELL" => {
170 count => 0,
171 shells => {},
172 handle_line => sub($$) {
173 my ($self, $line) = @_;
174 my $shells = ${$self}{shells};
175 if ($line =~ /^([0-9a-f]*) (\S*)/) {
176 my ($addr, $verb, $rest) = ($1, $2, $');
177 if ($verb eq "created") {
178 ${$shells}{$addr} = {};
179 ++${$self}{count};
180 } elsif ($verb eq "destroyed") {
181 delete ${$shells}{$addr};
182 } elsif ($verb eq "InternalLoad" ||
183 $verb eq "SetCurrentURI") {
184 $rest =~ /^ (.*)$/ || die "URI expected";
185 my $uri = $1;
186 ${${$shells}{$addr}}{$uri} = 1;
190 dump => sub ($) {
191 my ($self) = @_;
192 my $shells = ${$self}{shells};
193 foreach my $addr (keys(%{$shells})) {
194 print "Leaked docshell at address $addr.\n";
195 foreach my $uri (keys(%{${$shells}{$addr}})) {
196 print " ... which loaded URI \"$uri\".\n";
200 summary => sub($) {
201 my ($self) = @_;
202 my $shells = ${$self}{shells};
203 print 'Leaked ' . keys(%{$shells}) . ' out of ' .
204 ${$self}{count} . " docshells\n";
207 "NODEINFOMANAGER" => {
208 count => 0,
209 nims => {},
210 handle_line => sub($$) {
211 my ($self, $line) = @_;
212 my $nims = ${$self}{nims};
213 if ($line =~ /^([0-9a-f]*) (\S*)/) {
214 my ($addr, $verb, $rest) = ($1, $2, $');
215 if ($verb eq "created") {
216 ${$nims}{$addr} = {};
217 ++${$self}{count};
218 } elsif ($verb eq "destroyed") {
219 delete ${$nims}{$addr};
220 } elsif ($verb eq "Init") {
221 $rest =~ /^ document=(.*)$/ ||
222 die "document pointer expected";
223 my $doc = $1;
224 if ($doc ne "0") {
225 my $nim_info = ${$nims}{$addr};
226 my $doc_info = ${$handlers}{"DOCUMENT"}{docs}{$doc};
227 foreach my $uri (keys(%{$doc_info})) {
228 ${$nim_info}{$uri} = 1;
230 ${$doc_info}{"nim"} = $nim_info;
235 dump => sub ($) {
236 my ($self) = @_;
237 my $nims = ${$self}{nims};
238 foreach my $addr (keys(%{$nims})) {
239 print "Leaked content nodes associated with node info manager at address $addr.\n";
240 foreach my $uri (keys(%{${$nims}{$addr}})) {
241 print " ... with document URI \"$uri\".\n";
245 summary => sub($) {
246 my ($self) = @_;
247 my $nims = ${$self}{nims};
248 print 'Leaked content nodes within ' . keys(%{$nims}) . ' out of ' .
249 ${$self}{count} . " documents\n";
254 while (<>) {
255 # strip off initial "-", thread id, and thread pointer; separate
256 # first word and rest
257 if (/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) ([^\n\r]*)[\n\r]*$/) {
258 my ($handler, $data) = ($1, $2);
259 if (defined(${$handlers}{$handler})) {
260 call("handle_line", ${$handlers}{$handler}, $data);
265 foreach my $key (keys(%{$handlers})) {
266 call("dump", ${$handlers}{$key});
268 print "Summary:\n";
269 foreach my $key (keys(%{$handlers})) {
270 call("summary", ${$handlers}{$key});