tools/footprint/leak-gauge.pl

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rwxr-xr-x

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 }

mercurial