Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | #!/usr/bin/perl -w |
michael@0 | 2 | # vim:sw=4:ts=4:et: |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | # $Id: leak-gauge.pl,v 1.8 2008/02/08 19:55:03 dbaron%dbaron.org Exp $ |
michael@0 | 8 | # This script is designed to help testers isolate and simplify testcases |
michael@0 | 9 | # for many classes of leaks (those that involve large graphs of core |
michael@0 | 10 | # data structures) in Mozilla-based browsers. It is designed to print |
michael@0 | 11 | # information about what has leaked by processing a log taken while |
michael@0 | 12 | # running the browser. Such a log can be taken over a long session of |
michael@0 | 13 | # normal browsing and then the log can be processed to find sites that |
michael@0 | 14 | # leak. Once a site is known to leak, the logging can then be repeated |
michael@0 | 15 | # to figure out under what conditions the leak occurs. |
michael@0 | 16 | # |
michael@0 | 17 | # The way to create this log is to set the environment variables: |
michael@0 | 18 | # NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5 |
michael@0 | 19 | # NSPR_LOG_FILE=nspr.log (or any other filename of your choice) |
michael@0 | 20 | # in your shell and then run the program. |
michael@0 | 21 | # * In a Windows command prompt, set environment variables with |
michael@0 | 22 | # set VAR=value |
michael@0 | 23 | # * In an sh-based shell such as bash, set environment variables with |
michael@0 | 24 | # export VAR=value |
michael@0 | 25 | # * In a csh-based shell such as tcsh, set environment variables with |
michael@0 | 26 | # setenv VAR value |
michael@0 | 27 | # |
michael@0 | 28 | # Then, after you have exited the browser, run this perl script over the |
michael@0 | 29 | # log. Either of the following commands should work: |
michael@0 | 30 | # perl ./path/to/leak-gauge.pl nspr.log |
michael@0 | 31 | # cat nspr.log | perl ./path/to/leak-gauge.pl |
michael@0 | 32 | # and it will tell you which of certain core objects leaked and the URLs |
michael@0 | 33 | # associated with those objects. |
michael@0 | 34 | |
michael@0 | 35 | |
michael@0 | 36 | # Nobody said I'm not allowed to write my own object system in perl. No |
michael@0 | 37 | # classes here. Just objects and methods. |
michael@0 | 38 | sub call { |
michael@0 | 39 | my $func = shift; |
michael@0 | 40 | my $obj = shift; |
michael@0 | 41 | my $funcref = ${$obj}{$func}; |
michael@0 | 42 | &$funcref($obj, @_); |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | # A hash of objects (keyed by the first word of the line in the log) |
michael@0 | 46 | # that have two public methods, handle_line and dump (to be called using |
michael@0 | 47 | # call, above), along with any private data they need. |
michael@0 | 48 | my $handlers = { |
michael@0 | 49 | "DOMWINDOW" => { |
michael@0 | 50 | count => 0, |
michael@0 | 51 | windows => {}, |
michael@0 | 52 | handle_line => sub($$) { |
michael@0 | 53 | my ($self, $line) = @_; |
michael@0 | 54 | my $windows = ${$self}{windows}; |
michael@0 | 55 | if ($line =~ /^([0-9a-f]*) (\S*)/) { |
michael@0 | 56 | my ($addr, $verb, $rest) = ($1, $2, $'); |
michael@0 | 57 | if ($verb eq "created") { |
michael@0 | 58 | $rest =~ / outer=([0-9a-f]*)$/ || die "outer expected"; |
michael@0 | 59 | my $outer = $1; |
michael@0 | 60 | ${$windows}{$addr} = { outer => $1 }; |
michael@0 | 61 | ++${$self}{count}; |
michael@0 | 62 | } elsif ($verb eq "destroyed") { |
michael@0 | 63 | delete ${$windows}{$addr}; |
michael@0 | 64 | } elsif ($verb eq "SetNewDocument") { |
michael@0 | 65 | $rest =~ /^ (.*)$/ || die "URI expected"; |
michael@0 | 66 | my $uri = ($1); |
michael@0 | 67 | ${${$windows}{$addr}}{$uri} = 1; |
michael@0 | 68 | } |
michael@0 | 69 | } |
michael@0 | 70 | }, |
michael@0 | 71 | dump => sub ($) { |
michael@0 | 72 | my ($self) = @_; |
michael@0 | 73 | my $windows = ${$self}{windows}; |
michael@0 | 74 | foreach my $addr (keys(%{$windows})) { |
michael@0 | 75 | my $winobj = ${$windows}{$addr}; |
michael@0 | 76 | my $outer = delete ${$winobj}{outer}; |
michael@0 | 77 | print "Leaked " . ($outer eq "0" ? "outer" : "inner") . |
michael@0 | 78 | " window $addr " . |
michael@0 | 79 | ($outer eq "0" ? "" : "(outer $outer) ") . |
michael@0 | 80 | "at address $addr.\n"; |
michael@0 | 81 | foreach my $uri (keys(%{$winobj})) { |
michael@0 | 82 | print " ... with URI \"$uri\".\n"; |
michael@0 | 83 | } |
michael@0 | 84 | } |
michael@0 | 85 | }, |
michael@0 | 86 | summary => sub($) { |
michael@0 | 87 | my ($self) = @_; |
michael@0 | 88 | my $windows = ${$self}{windows}; |
michael@0 | 89 | print 'Leaked ' . keys(%{$windows}) . ' out of ' . |
michael@0 | 90 | ${$self}{count} . " DOM Windows\n"; |
michael@0 | 91 | } |
michael@0 | 92 | }, |
michael@0 | 93 | "DOCUMENT" => { |
michael@0 | 94 | count => 0, |
michael@0 | 95 | docs => {}, |
michael@0 | 96 | handle_line => sub($$) { |
michael@0 | 97 | my ($self, $line) = @_; |
michael@0 | 98 | # This doesn't work; I don't have time to figure out why not. |
michael@0 | 99 | # my $docs = ${$self}{docs}; |
michael@0 | 100 | my $docs = ${$handlers}{"DOCUMENT"}{docs}; |
michael@0 | 101 | if ($line =~ /^([0-9a-f]*) (\S*)/) { |
michael@0 | 102 | my ($addr, $verb, $rest) = ($1, $2, $'); |
michael@0 | 103 | if ($verb eq "created") { |
michael@0 | 104 | ${$docs}{$addr} = {}; |
michael@0 | 105 | ++${$self}{count}; |
michael@0 | 106 | } elsif ($verb eq "destroyed") { |
michael@0 | 107 | delete ${$docs}{$addr}; |
michael@0 | 108 | } elsif ($verb eq "ResetToURI" || |
michael@0 | 109 | $verb eq "StartDocumentLoad") { |
michael@0 | 110 | $rest =~ /^ (.*)$/ || die "URI expected"; |
michael@0 | 111 | my $uri = $1; |
michael@0 | 112 | my $doc_info = ${$docs}{$addr}; |
michael@0 | 113 | ${$doc_info}{$uri} = 1; |
michael@0 | 114 | if (exists(${$doc_info}{"nim"})) { |
michael@0 | 115 | ${$doc_info}{"nim"}{$uri} = 1; |
michael@0 | 116 | } |
michael@0 | 117 | } |
michael@0 | 118 | } |
michael@0 | 119 | }, |
michael@0 | 120 | dump => sub ($) { |
michael@0 | 121 | my ($self) = @_; |
michael@0 | 122 | my $docs = ${$self}{docs}; |
michael@0 | 123 | foreach my $addr (keys(%{$docs})) { |
michael@0 | 124 | print "Leaked document at address $addr.\n"; |
michael@0 | 125 | foreach my $uri (keys(%{${$docs}{$addr}})) { |
michael@0 | 126 | print " ... with URI \"$uri\".\n" unless $uri eq "nim"; |
michael@0 | 127 | } |
michael@0 | 128 | } |
michael@0 | 129 | }, |
michael@0 | 130 | summary => sub($) { |
michael@0 | 131 | my ($self) = @_; |
michael@0 | 132 | my $docs = ${$self}{docs}; |
michael@0 | 133 | print 'Leaked ' . keys(%{$docs}) . ' out of ' . |
michael@0 | 134 | ${$self}{count} . " documents\n"; |
michael@0 | 135 | } |
michael@0 | 136 | }, |
michael@0 | 137 | "DOCSHELL" => { |
michael@0 | 138 | count => 0, |
michael@0 | 139 | shells => {}, |
michael@0 | 140 | handle_line => sub($$) { |
michael@0 | 141 | my ($self, $line) = @_; |
michael@0 | 142 | my $shells = ${$self}{shells}; |
michael@0 | 143 | if ($line =~ /^([0-9a-f]*) (\S*)/) { |
michael@0 | 144 | my ($addr, $verb, $rest) = ($1, $2, $'); |
michael@0 | 145 | if ($verb eq "created") { |
michael@0 | 146 | ${$shells}{$addr} = {}; |
michael@0 | 147 | ++${$self}{count}; |
michael@0 | 148 | } elsif ($verb eq "destroyed") { |
michael@0 | 149 | delete ${$shells}{$addr}; |
michael@0 | 150 | } elsif ($verb eq "InternalLoad" || |
michael@0 | 151 | $verb eq "SetCurrentURI") { |
michael@0 | 152 | $rest =~ /^ (.*)$/ || die "URI expected"; |
michael@0 | 153 | my $uri = $1; |
michael@0 | 154 | ${${$shells}{$addr}}{$uri} = 1; |
michael@0 | 155 | } |
michael@0 | 156 | } |
michael@0 | 157 | }, |
michael@0 | 158 | dump => sub ($) { |
michael@0 | 159 | my ($self) = @_; |
michael@0 | 160 | my $shells = ${$self}{shells}; |
michael@0 | 161 | foreach my $addr (keys(%{$shells})) { |
michael@0 | 162 | print "Leaked docshell at address $addr.\n"; |
michael@0 | 163 | foreach my $uri (keys(%{${$shells}{$addr}})) { |
michael@0 | 164 | print " ... which loaded URI \"$uri\".\n"; |
michael@0 | 165 | } |
michael@0 | 166 | } |
michael@0 | 167 | }, |
michael@0 | 168 | summary => sub($) { |
michael@0 | 169 | my ($self) = @_; |
michael@0 | 170 | my $shells = ${$self}{shells}; |
michael@0 | 171 | print 'Leaked ' . keys(%{$shells}) . ' out of ' . |
michael@0 | 172 | ${$self}{count} . " docshells\n"; |
michael@0 | 173 | } |
michael@0 | 174 | }, |
michael@0 | 175 | "NODEINFOMANAGER" => { |
michael@0 | 176 | count => 0, |
michael@0 | 177 | nims => {}, |
michael@0 | 178 | handle_line => sub($$) { |
michael@0 | 179 | my ($self, $line) = @_; |
michael@0 | 180 | my $nims = ${$self}{nims}; |
michael@0 | 181 | if ($line =~ /^([0-9a-f]*) (\S*)/) { |
michael@0 | 182 | my ($addr, $verb, $rest) = ($1, $2, $'); |
michael@0 | 183 | if ($verb eq "created") { |
michael@0 | 184 | ${$nims}{$addr} = {}; |
michael@0 | 185 | ++${$self}{count}; |
michael@0 | 186 | } elsif ($verb eq "destroyed") { |
michael@0 | 187 | delete ${$nims}{$addr}; |
michael@0 | 188 | } elsif ($verb eq "Init") { |
michael@0 | 189 | $rest =~ /^ document=(.*)$/ || |
michael@0 | 190 | die "document pointer expected"; |
michael@0 | 191 | my $doc = $1; |
michael@0 | 192 | if ($doc ne "0") { |
michael@0 | 193 | my $nim_info = ${$nims}{$addr}; |
michael@0 | 194 | my $doc_info = ${$handlers}{"DOCUMENT"}{docs}{$doc}; |
michael@0 | 195 | foreach my $uri (keys(%{$doc_info})) { |
michael@0 | 196 | ${$nim_info}{$uri} = 1; |
michael@0 | 197 | } |
michael@0 | 198 | ${$doc_info}{"nim"} = $nim_info; |
michael@0 | 199 | } |
michael@0 | 200 | } |
michael@0 | 201 | } |
michael@0 | 202 | }, |
michael@0 | 203 | dump => sub ($) { |
michael@0 | 204 | my ($self) = @_; |
michael@0 | 205 | my $nims = ${$self}{nims}; |
michael@0 | 206 | foreach my $addr (keys(%{$nims})) { |
michael@0 | 207 | print "Leaked content nodes associated with node info manager at address $addr.\n"; |
michael@0 | 208 | foreach my $uri (keys(%{${$nims}{$addr}})) { |
michael@0 | 209 | print " ... with document URI \"$uri\".\n"; |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | }, |
michael@0 | 213 | summary => sub($) { |
michael@0 | 214 | my ($self) = @_; |
michael@0 | 215 | my $nims = ${$self}{nims}; |
michael@0 | 216 | print 'Leaked content nodes within ' . keys(%{$nims}) . ' out of ' . |
michael@0 | 217 | ${$self}{count} . " documents\n"; |
michael@0 | 218 | } |
michael@0 | 219 | } |
michael@0 | 220 | }; |
michael@0 | 221 | |
michael@0 | 222 | while (<>) { |
michael@0 | 223 | # strip off initial "-", thread id, and thread pointer; separate |
michael@0 | 224 | # first word and rest |
michael@0 | 225 | if (/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) ([^\n\r]*)[\n\r]*$/) { |
michael@0 | 226 | my ($handler, $data) = ($1, $2); |
michael@0 | 227 | if (defined(${$handlers}{$handler})) { |
michael@0 | 228 | call("handle_line", ${$handlers}{$handler}, $data); |
michael@0 | 229 | } |
michael@0 | 230 | } |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | foreach my $key (keys(%{$handlers})) { |
michael@0 | 234 | call("dump", ${$handlers}{$key}); |
michael@0 | 235 | } |
michael@0 | 236 | print "Summary:\n"; |
michael@0 | 237 | foreach my $key (keys(%{$handlers})) { |
michael@0 | 238 | call("summary", ${$handlers}{$key}); |
michael@0 | 239 | } |