3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 # $Id: leak-gauge.pl,v 1.8 2008/02/08 19:55:03 dbaron%dbaron.org Exp $
8 # This script is designed to help testers isolate and simplify testcases
9 # for many classes of leaks (those that involve large graphs of core
10 # data structures) in Mozilla-based browsers. It is designed to print
11 # information about what has leaked by processing a log taken while
12 # running the browser. Such a log can be taken over a long session of
13 # normal browsing and then the log can be processed to find sites that
14 # leak. Once a site is known to leak, the logging can then be repeated
15 # to figure out under what conditions the leak occurs.
17 # The way to create this log is to set the environment variables:
18 # NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5
19 # NSPR_LOG_FILE=nspr.log (or any other filename of your choice)
20 # in your shell and then run the program.
21 # * In a Windows command prompt, set environment variables with
23 # * In an sh-based shell such as bash, set environment variables with
25 # * In a csh-based shell such as tcsh, set environment variables with
28 # Then, after you have exited the browser, run this perl script over the
29 # log. Either of the following commands should work:
30 # perl ./path/to/leak-gauge.pl nspr.log
31 # cat nspr.log | perl ./path/to/leak-gauge.pl
32 # and it will tell you which of certain core objects leaked and the URLs
33 # associated with those objects.
36 # Nobody said I'm not allowed to write my own object system in perl. No
37 # classes here. Just objects and methods.
41 my $funcref = ${$obj}{$func};
45 # A hash of objects (keyed by the first word of the line in the log)
46 # that have two public methods, handle_line and dump (to be called using
47 # call, above), along with any private data they need.
52 handle_line
=> sub($$) {
53 my ($self, $line) = @_;
54 my $windows = ${$self}{windows
};
55 if ($line =~ /^([0-9a-f]*) (\S*)/) {
56 my ($addr, $verb, $rest) = ($1, $2, $');
57 if ($verb eq "created") {
58 $rest =~ / outer=([0-9a-f]*)$/ || die "outer expected";
60 ${$windows}{$addr} = { outer => $1 };
62 } elsif ($verb eq "destroyed") {
63 delete ${$windows}{$addr};
64 } elsif ($verb eq "SetNewDocument") {
65 $rest =~ /^ (.*)$/ || die "URI expected";
67 ${${$windows}{$addr}}{$uri} = 1;
73 my $windows = ${$self}{windows};
74 foreach my $addr (keys(%{$windows})) {
75 my $winobj = ${$windows}{$addr};
76 my $outer = delete ${$winobj}{outer};
77 print "Leaked " . ($outer eq "0" ? "outer" : "inner") .
79 ($outer eq "0" ? "" : "(outer $outer) ") .
80 "at address $addr.\n";
81 foreach my $uri (keys(%{$winobj})) {
82 print " ... with URI \"$uri\".\n";
88 my $windows = ${$self}{windows};
89 print 'Leaked
' . keys(%{$windows}) . ' out of
' .
90 ${$self}{count} . " DOM Windows\n";
96 handle_line => sub($$) {
97 my ($self, $line) = @_;
98 # This doesn't work
; I don
't have time to figure out why not.
99 # my $docs = ${$self}{docs};
100 my $docs = ${$handlers}{"DOCUMENT"}{docs};
101 if ($line =~ /^([0-9a-f]*) (\S*)/) {
102 my ($addr, $verb, $rest) = ($1, $2, $');
103 if ($verb eq "created") {
104 ${$docs}{$addr} = {};
106 } elsif ($verb eq "destroyed") {
107 delete ${$docs}{$addr};
108 } elsif ($verb eq "ResetToURI" ||
109 $verb eq "StartDocumentLoad") {
110 $rest =~ /^ (.*)$/ || die "URI expected";
112 my $doc_info = ${$docs}{$addr};
113 ${$doc_info}{$uri} = 1;
114 if (exists(${$doc_info}{"nim"})) {
115 ${$doc_info}{"nim"}{$uri} = 1;
122 my $docs = ${$self}{docs
};
123 foreach my $addr (keys(%{$docs})) {
124 print "Leaked document at address $addr.\n";
125 foreach my $uri (keys(%{${$docs}{$addr}})) {
126 print " ... with URI \"$uri\".\n" unless $uri eq "nim";
132 my $docs = ${$self}{docs
};
133 print 'Leaked ' . keys(%{$docs}) . ' out of ' .
134 ${$self}{count
} . " documents\n";
140 handle_line
=> sub($$) {
141 my ($self, $line) = @_;
142 my $shells = ${$self}{shells
};
143 if ($line =~ /^([0-9a-f]*) (\S*)/) {
144 my ($addr, $verb, $rest) = ($1, $2, $');
145 if ($verb eq "created") {
146 ${$shells}{$addr} = {};
148 } elsif ($verb eq "destroyed") {
149 delete ${$shells}{$addr};
150 } elsif ($verb eq "InternalLoad" ||
151 $verb eq "SetCurrentURI") {
152 $rest =~ /^ (.*)$/ || die "URI expected";
154 ${${$shells}{$addr}}{$uri} = 1;
160 my $shells = ${$self}{shells};
161 foreach my $addr (keys(%{$shells})) {
162 print "Leaked docshell at address $addr.\n";
163 foreach my $uri (keys(%{${$shells}{$addr}})) {
164 print " ... which loaded URI \"$uri\".\n";
170 my $shells = ${$self}{shells};
171 print 'Leaked
' . keys(%{$shells}) . ' out of
' .
172 ${$self}{count} . " docshells\n";
175 "NODEINFOMANAGER" => {
178 handle_line => sub($$) {
179 my ($self, $line) = @_;
180 my $nims = ${$self}{nims};
181 if ($line =~ /^([0-9a-f]*) (\S*)/) {
182 my ($addr, $verb, $rest) = ($1, $2, $');
183 if ($verb eq "created") {
184 ${$nims}{$addr} = {};
186 } elsif ($verb eq "destroyed") {
187 delete ${$nims}{$addr};
188 } elsif ($verb eq "Init") {
189 $rest =~ /^ document=(.*)$/ ||
190 die "document pointer expected";
193 my $nim_info = ${$nims}{$addr};
194 my $doc_info = ${$handlers}{"DOCUMENT"}{docs
}{$doc};
195 foreach my $uri (keys(%{$doc_info})) {
196 ${$nim_info}{$uri} = 1;
198 ${$doc_info}{"nim"} = $nim_info;
205 my $nims = ${$self}{nims
};
206 foreach my $addr (keys(%{$nims})) {
207 print "Leaked content nodes associated with node info manager at address $addr.\n";
208 foreach my $uri (keys(%{${$nims}{$addr}})) {
209 print " ... with document URI \"$uri\".\n";
215 my $nims = ${$self}{nims
};
216 print 'Leaked content nodes within ' . keys(%{$nims}) . ' out of ' .
217 ${$self}{count
} . " documents\n";
223 # strip off initial "-", thread id, and thread pointer; separate
224 # first word and rest
225 if (/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) ([^\n\r]*)[\n\r]*$/) {
226 my ($handler, $data) = ($1, $2);
227 if (defined(${$handlers}{$handler})) {
228 call
("handle_line", ${$handlers}{$handler}, $data);
233 foreach my $key (keys(%{$handlers})) {
234 call
("dump", ${$handlers}{$key});
237 foreach my $key (keys(%{$handlers})) {
238 call
("summary", ${$handlers}{$key});