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 | # |
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 | # bloattable [-debug] [-source] [-byte n|-obj n|-ref n] <file1> <file2> ... <filen> > <html-file> |
michael@0 | 8 | # |
michael@0 | 9 | # file1, file2, ... filen should be successive BloatView files generated from the same run. |
michael@0 | 10 | # Summarize them in an HTML table. Output the HTML to the standard output. |
michael@0 | 11 | # |
michael@0 | 12 | # If -debug is set, create a slightly larger html file which is more suitable for debugging this script. |
michael@0 | 13 | # If -source is set, create an html file that prints the html source as the output |
michael@0 | 14 | # If -byte n, -obj n, or -ref n is given, make the page default to showing byte, object, or reference statistics, |
michael@0 | 15 | # respectively, and sort by the nth column (n is zero-based, so the first column has n==0). |
michael@0 | 16 | # |
michael@0 | 17 | # See http://lxr.mozilla.org/mozilla/source/xpcom/doc/MemoryTools.html |
michael@0 | 18 | |
michael@0 | 19 | use 5.004; |
michael@0 | 20 | use strict; |
michael@0 | 21 | use diagnostics; |
michael@0 | 22 | use File::Basename; |
michael@0 | 23 | use Getopt::Long; |
michael@0 | 24 | |
michael@0 | 25 | # The generated HTML is almost entirely generated by a script. Only the <HTML>, <HEAD>, and <BODY> elements are explicit |
michael@0 | 26 | # because a <SCRIPT> element cannot officially be a direct descendant of an <HTML> element. |
michael@0 | 27 | # The script itself is almost all generated by an eval of a large string. This allows the script to reproduce itself |
michael@0 | 28 | # when making a new page using document.write's. Re-sorting the page causes it to regenerate itself in this way. |
michael@0 | 29 | |
michael@0 | 30 | |
michael@0 | 31 | |
michael@0 | 32 | # Return the file's modification date. |
michael@0 | 33 | sub fileModDate($) { |
michael@0 | 34 | my ($pathName) = @_; |
michael@0 | 35 | my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = |
michael@0 | 36 | stat $pathName or die "Can't stat '$pathName'"; |
michael@0 | 37 | return $mtime; |
michael@0 | 38 | } |
michael@0 | 39 | |
michael@0 | 40 | |
michael@0 | 41 | sub fileCoreName($) { |
michael@0 | 42 | my ($pathName) = @_; |
michael@0 | 43 | my $fileName = basename($pathName, ""); |
michael@0 | 44 | $fileName =~ s/\..*//; |
michael@0 | 45 | return $fileName; |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | |
michael@0 | 49 | # Convert a raw string into a single-quoted JavaScript string. |
michael@0 | 50 | sub singleQuoteString($) { |
michael@0 | 51 | local ($_) = @_; |
michael@0 | 52 | s/\\/\\\\/g; |
michael@0 | 53 | s/'/\\'/g; |
michael@0 | 54 | s/\n/\\n/g; |
michael@0 | 55 | s/<\//<\\\//g; |
michael@0 | 56 | return "'$_'"; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | |
michael@0 | 60 | # Convert a raw string into a double-quoted JavaScript string. |
michael@0 | 61 | sub doubleQuoteString($) { |
michael@0 | 62 | local ($_) = @_; |
michael@0 | 63 | s/\\/\\\\/g; |
michael@0 | 64 | s/"/\\"/g; |
michael@0 | 65 | s/\n/\\n/g; |
michael@0 | 66 | s/<\//<\\\//g; |
michael@0 | 67 | return "\"$_\""; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | |
michael@0 | 71 | # Quote special HTML characters in the string. |
michael@0 | 72 | sub quoteHTML($) { |
michael@0 | 73 | local ($_) = @_; |
michael@0 | 74 | s/\&/&/g; |
michael@0 | 75 | s/</</g; |
michael@0 | 76 | s/>/>/g; |
michael@0 | 77 | s/ / /g; |
michael@0 | 78 | s/\n/<BR>\n/g; |
michael@0 | 79 | return $_; |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | |
michael@0 | 83 | # Write the generated page to the standard output. |
michael@0 | 84 | # The script source code is read from this file past the __END__ marker |
michael@0 | 85 | # @$scriptData is the JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript. |
michael@0 | 86 | # @$persistentScriptData is the same as @scriptData, but persists when the page reloads itself. |
michael@0 | 87 | # If $debug is true, generate the script directly instead of having it eval itself. |
michael@0 | 88 | # If $source is true, generate a script that displays the page's source instead of the page itself. |
michael@0 | 89 | sub generate(\@\@$$$$) { |
michael@0 | 90 | my ($scriptData, $persistentScriptData, $debug, $source, $showMode, $sortColumn) = @_; |
michael@0 | 91 | |
michael@0 | 92 | my @scriptSource = <DATA>; |
michael@0 | 93 | chomp @scriptSource; |
michael@0 | 94 | print <<'EOS'; |
michael@0 | 95 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> |
michael@0 | 96 | <HTML> |
michael@0 | 97 | <HEAD> |
michael@0 | 98 | <SCRIPT type="text/javascript"> |
michael@0 | 99 | EOS |
michael@0 | 100 | |
michael@0 | 101 | foreach (@$scriptData) {print "$_\n";} |
michael@0 | 102 | print "\n"; |
michael@0 | 103 | |
michael@0 | 104 | print "var srcArray = [\n"; |
michael@0 | 105 | my @quotedScriptSource = map { |
michael@0 | 106 | my $line = $_; |
michael@0 | 107 | $line =~ s/^\s+//g; |
michael@0 | 108 | # $line =~ s/^\/\/SOURCE\s+//g if $source; |
michael@0 | 109 | $line =~ s/^\/\/.*//g; |
michael@0 | 110 | $line =~ s/\s+$//g; |
michael@0 | 111 | $line eq "" ? () : $line |
michael@0 | 112 | } @$persistentScriptData, @scriptSource; |
michael@0 | 113 | my $lastQuotedLine = pop @quotedScriptSource; |
michael@0 | 114 | foreach (@quotedScriptSource) {print doubleQuoteString($_), ",\n";} |
michael@0 | 115 | print doubleQuoteString($lastQuotedLine), "];\n\n"; |
michael@0 | 116 | |
michael@0 | 117 | if ($debug) { |
michael@0 | 118 | push @quotedScriptSource, $lastQuotedLine; |
michael@0 | 119 | foreach (@quotedScriptSource) { |
michael@0 | 120 | s/<\//<\\\//g; # This fails if a regexp ends with a '<'. Oh well.... |
michael@0 | 121 | print "$_\n"; |
michael@0 | 122 | } |
michael@0 | 123 | print "\n"; |
michael@0 | 124 | } else { |
michael@0 | 125 | print "eval(srcArray.join(\"\\n\"));\n\n"; |
michael@0 | 126 | } |
michael@0 | 127 | print "showMode = $showMode;\n"; |
michael@0 | 128 | print "sortColumn = $sortColumn;\n"; |
michael@0 | 129 | if ($source) { |
michael@0 | 130 | print <<'EOS'; |
michael@0 | 131 | function writeQuotedHTML(s) { |
michael@0 | 132 | document.write(quoteHTML(s.toString()).replace(/\n/g, '<BR>\n')); |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | var quotingDocument = { |
michael@0 | 136 | write: function () { |
michael@0 | 137 | for (var i = 0; i < arguments.length; i++) |
michael@0 | 138 | writeQuotedHTML(arguments[i]); |
michael@0 | 139 | }, |
michael@0 | 140 | writeln: function () { |
michael@0 | 141 | for (var i = 0; i < arguments.length; i++) |
michael@0 | 142 | writeQuotedHTML(arguments[i]); |
michael@0 | 143 | document.writeln('<BR>'); |
michael@0 | 144 | } |
michael@0 | 145 | }; |
michael@0 | 146 | EOS |
michael@0 | 147 | } else { |
michael@0 | 148 | print "showHead(document);\n"; |
michael@0 | 149 | } |
michael@0 | 150 | print "</SCRIPT>\n"; |
michael@0 | 151 | print "</HEAD>\n\n"; |
michael@0 | 152 | print "<BODY>\n"; |
michael@0 | 153 | if ($source) { |
michael@0 | 154 | print "<P><TT>"; |
michael@0 | 155 | print quoteHTML "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"; |
michael@0 | 156 | print quoteHTML "<HTML>\n"; |
michael@0 | 157 | print quoteHTML "<HEAD>\n"; |
michael@0 | 158 | print "<SCRIPT type=\"text/javascript\">showHead(quotingDocument);</SCRIPT>\n"; |
michael@0 | 159 | print quoteHTML "</HEAD>\n\n"; |
michael@0 | 160 | print quoteHTML "<BODY>\n"; |
michael@0 | 161 | print "<SCRIPT type=\"text/javascript\">showBody(quotingDocument);</SCRIPT>\n"; |
michael@0 | 162 | print quoteHTML "</BODY>\n"; |
michael@0 | 163 | print quoteHTML "</HTML>\n"; |
michael@0 | 164 | print "</TT></P>\n"; |
michael@0 | 165 | } else { |
michael@0 | 166 | print "<SCRIPT type=\"text/javascript\">showBody(document);</SCRIPT>\n"; |
michael@0 | 167 | } |
michael@0 | 168 | print "</BODY>\n"; |
michael@0 | 169 | print "</HTML>\n"; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | |
michael@0 | 173 | |
michael@0 | 174 | # Read the bloat file into hash table $h. The hash table is indexed by class names; |
michael@0 | 175 | # each entry is a list with the following elements: |
michael@0 | 176 | # bytesAlloc Total number of bytes allocated |
michael@0 | 177 | # bytesNet Total number of bytes allocated but not deallocated |
michael@0 | 178 | # objectsAlloc Total number of objects allocated |
michael@0 | 179 | # objectsNet Total number of objects allocated but not deallocated |
michael@0 | 180 | # refsAlloc Total number of references AddRef'd |
michael@0 | 181 | # refsNet Total number of references AddRef'd but not Released |
michael@0 | 182 | # Except for TOTAL, all hash table entries refer to mutually exclusive data. |
michael@0 | 183 | # $sizes is a hash table indexed by class names. Each entry of that table contains the class's instance size. |
michael@0 | 184 | sub readBloatFile($\%\%) { |
michael@0 | 185 | my ($file, $h, $sizes) = @_; |
michael@0 | 186 | local $_; # Needed for 'while (<FILE>)' below. |
michael@0 | 187 | |
michael@0 | 188 | my $readSomething = 0; |
michael@0 | 189 | open FILE, $file; |
michael@0 | 190 | while (<FILE>) { |
michael@0 | 191 | if (my ($name, $size, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet) = |
michael@0 | 192 | /^\s*(?:\d+)\s+([\w:]+)\s+(\d+)\s+(-?\d+)\s+(\d+)\s+(-?\d+)\s*\([^()]*\)\s*(\d+)\s+(-?\d+)\s*\([^()]*\)\s*$/) { |
michael@0 | 193 | my $bytesAlloc; |
michael@0 | 194 | if ($name eq "TOTAL") { |
michael@0 | 195 | $size = "undefined"; |
michael@0 | 196 | $bytesAlloc = "undefined"; |
michael@0 | 197 | } else { |
michael@0 | 198 | $bytesAlloc = $objectsAlloc * $size; |
michael@0 | 199 | if ($bytesNet != $objectsNet * $size) { |
michael@0 | 200 | print STDERR "In '$file', class $name bytesNet != objectsNet * size: $bytesNet != $objectsNet * $size\n"; |
michael@0 | 201 | } |
michael@0 | 202 | } |
michael@0 | 203 | print STDERR "Duplicate entry $name in '$file'\n" if $$h{$name}; |
michael@0 | 204 | $$h{$name} = [$bytesAlloc, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet]; |
michael@0 | 205 | |
michael@0 | 206 | my $oldSize = $$sizes{$name}; |
michael@0 | 207 | print STDERR "Mismatch of sizes of class $name: $oldSize and $size\n" if defined($oldSize) && $size ne $oldSize; |
michael@0 | 208 | $$sizes{$name} = $size; |
michael@0 | 209 | $readSomething = 1; |
michael@0 | 210 | } elsif (/^\s*(?:\d+)\s+([\w:]+)\s/) { |
michael@0 | 211 | print STDERR "Unable to parse '$file' line: $_"; |
michael@0 | 212 | } |
michael@0 | 213 | } |
michael@0 | 214 | close FILE; |
michael@0 | 215 | print STDERR "No data in '$file'\n" unless $readSomething; |
michael@0 | 216 | return $h; |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | |
michael@0 | 220 | my %sizes; # <class-name> => <instance-size> |
michael@0 | 221 | my %tables; # <file-name> => <bloat-table>; see readBloatFile for format of <bloat-table> |
michael@0 | 222 | |
michael@0 | 223 | # Generate the JavaScript source code for the row named $c. $l can contain the initial entries of the row. |
michael@0 | 224 | sub genTableRowSource($$) { |
michael@0 | 225 | my ($l, $c) = @_; |
michael@0 | 226 | my $lastE; |
michael@0 | 227 | foreach (@ARGV) { |
michael@0 | 228 | my $e = $tables{$_}{$c}; |
michael@0 | 229 | if (defined($lastE) && !defined($e)) { |
michael@0 | 230 | $e = [0,0,0,0,0,0]; |
michael@0 | 231 | print STDERR "Class $c is defined in an earlier file but not in '$_'\n"; |
michael@0 | 232 | } |
michael@0 | 233 | if (defined $e) { |
michael@0 | 234 | if (defined $lastE) { |
michael@0 | 235 | for (my $i = 0; $i <= $#$e; $i++) { |
michael@0 | 236 | my $n = $$e[$i]; |
michael@0 | 237 | $l .= ($n eq "undefined" ? "undefined" : $n - $$lastE[$i]) . ","; |
michael@0 | 238 | } |
michael@0 | 239 | $l .= " "; |
michael@0 | 240 | } else { |
michael@0 | 241 | $l .= join(",", @$e) . ", "; |
michael@0 | 242 | } |
michael@0 | 243 | $lastE = $e; |
michael@0 | 244 | } else { |
michael@0 | 245 | $l .= "0,0,0,0,0,0, "; |
michael@0 | 246 | } |
michael@0 | 247 | } |
michael@0 | 248 | $l .= join(",", @$lastE); |
michael@0 | 249 | return "[$l]"; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | |
michael@0 | 253 | |
michael@0 | 254 | my $debug; |
michael@0 | 255 | my $source; |
michael@0 | 256 | my $showMode; |
michael@0 | 257 | my $sortColumn; |
michael@0 | 258 | my @modeOptions; |
michael@0 | 259 | |
michael@0 | 260 | GetOptions("debug" => \$debug, "source" => \$source, "byte=i" => \$modeOptions[0], "obj=i" => \$modeOptions[1], "ref=i" => \$modeOptions[2]); |
michael@0 | 261 | for (my $i = 0; $i != 3; $i++) { |
michael@0 | 262 | my $modeOption = $modeOptions[$i]; |
michael@0 | 263 | if ($modeOption) { |
michael@0 | 264 | die "Only one of -byte, -obj, or -ref may be given" if defined $showMode; |
michael@0 | 265 | my $nFileColumns = scalar(@ARGV) + 1; |
michael@0 | 266 | die "-byte, -obj, or -ref column number out of range" if $modeOption < 0 || $modeOption >= 2 + 2*$nFileColumns; |
michael@0 | 267 | $showMode = $i; |
michael@0 | 268 | if ($modeOption >= 2) { |
michael@0 | 269 | $modeOption -= 2; |
michael@0 | 270 | $sortColumn = 2 + $showMode*2; |
michael@0 | 271 | if ($modeOption >= $nFileColumns) { |
michael@0 | 272 | $sortColumn++; |
michael@0 | 273 | $modeOption -= $nFileColumns; |
michael@0 | 274 | } |
michael@0 | 275 | $sortColumn += $modeOption*6; |
michael@0 | 276 | } else { |
michael@0 | 277 | $sortColumn = $modeOption; |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | } |
michael@0 | 281 | unless (defined $showMode) { |
michael@0 | 282 | $showMode = 0; |
michael@0 | 283 | $sortColumn = 0; |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | # Read all of the bloat files. |
michael@0 | 287 | foreach (@ARGV) { |
michael@0 | 288 | unless ($tables{$_}) { |
michael@0 | 289 | my $f = $_; |
michael@0 | 290 | my %table; |
michael@0 | 291 | |
michael@0 | 292 | readBloatFile $_, %table, %sizes; |
michael@0 | 293 | $tables{$_} = \%table; |
michael@0 | 294 | } |
michael@0 | 295 | } |
michael@0 | 296 | die "No input" unless %sizes; |
michael@0 | 297 | |
michael@0 | 298 | my @scriptData; # JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript. |
michael@0 | 299 | my @persistentScriptData; # Same as @scriptData, but persists the page reloads itself. |
michael@0 | 300 | |
michael@0 | 301 | # Print a list of bloat file names. |
michael@0 | 302 | push @persistentScriptData, "var nFiles = " . scalar(@ARGV) . ";"; |
michael@0 | 303 | push @persistentScriptData, "var fileTags = [" . join(", ", map {singleQuoteString substr(fileCoreName($_), -10)} @ARGV) . "];"; |
michael@0 | 304 | push @persistentScriptData, "var fileNames = [" . join(", ", map {singleQuoteString $_} @ARGV) . "];"; |
michael@0 | 305 | push @persistentScriptData, "var fileDates = [" . join(", ", map {singleQuoteString localtime fileModDate $_} @ARGV) . "];"; |
michael@0 | 306 | |
michael@0 | 307 | # Print the bloat tables. |
michael@0 | 308 | push @persistentScriptData, "var totals = " . genTableRowSource('"TOTAL", undefined, ', "TOTAL") . ";"; |
michael@0 | 309 | push @scriptData, "var classTables = ["; |
michael@0 | 310 | delete $sizes{"TOTAL"}; |
michael@0 | 311 | my @classes = sort(keys %sizes); |
michael@0 | 312 | for (my $i = 0; $i <= $#classes; $i++) { |
michael@0 | 313 | my $c = $classes[$i]; |
michael@0 | 314 | push @scriptData, genTableRowSource(doubleQuoteString($c).", ".$sizes{$c}.", ", $c) . ($i == $#classes ? "];" : ","); |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | generate(@scriptData, @persistentScriptData, $debug, $source, $showMode, $sortColumn); |
michael@0 | 318 | 1; |
michael@0 | 319 | |
michael@0 | 320 | |
michael@0 | 321 | # The source of the eval'd JavaScript follows. |
michael@0 | 322 | # Comments starting with // that are alone on a line are stripped by the Perl script. |
michael@0 | 323 | __END__ |
michael@0 | 324 | |
michael@0 | 325 | // showMode: 0=bytes, 1=objects, 2=references |
michael@0 | 326 | var showMode; |
michael@0 | 327 | var modeName; |
michael@0 | 328 | var modeNameUpper; |
michael@0 | 329 | |
michael@0 | 330 | var sortColumn; |
michael@0 | 331 | |
michael@0 | 332 | // Sort according to the sortColumn. Column 0 is sorted alphabetically in ascending order. |
michael@0 | 333 | // All other columns are sorted numerically in descending order, with column 0 used for a secondary sort. |
michael@0 | 334 | // Undefined is always listed last. |
michael@0 | 335 | function sortCompare(x, y) { |
michael@0 | 336 | if (sortColumn) { |
michael@0 | 337 | var xc = x[sortColumn]; |
michael@0 | 338 | var yc = y[sortColumn]; |
michael@0 | 339 | if (xc < yc || xc === undefined && yc !== undefined) return 1; |
michael@0 | 340 | if (yc < xc || yc === undefined && xc !== undefined) return -1; |
michael@0 | 341 | } |
michael@0 | 342 | |
michael@0 | 343 | var x0 = x[0]; |
michael@0 | 344 | var y0 = y[0]; |
michael@0 | 345 | if (x0 > y0 || x0 === undefined && y0 !== undefined) return 1; |
michael@0 | 346 | if (y0 > x0 || y0 === undefined && x0 !== undefined) return -1; |
michael@0 | 347 | return 0; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | |
michael@0 | 351 | // Quote special HTML characters in the string. |
michael@0 | 352 | function quoteHTML(s) { |
michael@0 | 353 | s = s.replace(/&/g, '&'); |
michael@0 | 354 | // Can't use /</g because HTML interprets '</g' as ending the script! |
michael@0 | 355 | s = s.replace(/\x3C/g, '<'); |
michael@0 | 356 | s = s.replace(/>/g, '>'); |
michael@0 | 357 | s = s.replace(/ /g, ' '); |
michael@0 | 358 | return s; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | |
michael@0 | 362 | function writeFileTable(d) { |
michael@0 | 363 | d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); |
michael@0 | 364 | d.writeln('<TR>\n<TH>Name</TH>\n<TH>File</TH>\n<TH>Date</TH>\n</TR>'); |
michael@0 | 365 | for (var i = 0; i < nFiles; i++) |
michael@0 | 366 | d.writeln('<TR>\n<TD>'+quoteHTML(fileTags[i])+'</TD>\n<TD><TT>'+quoteHTML(fileNames[i])+'</TT></TD>\n<TD>'+quoteHTML(fileDates[i])+'</TD>\n</TR>'); |
michael@0 | 367 | d.writeln('</TABLE>'); |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | |
michael@0 | 371 | function writeReloadLink(d, column, s, rowspan) { |
michael@0 | 372 | d.write(rowspan == 1 ? '<TH>' : '<TH rowspan='+rowspan+'>'); |
michael@0 | 373 | if (column != sortColumn) |
michael@0 | 374 | d.write('<A href="javascript:reloadSelf('+column+','+showMode+')">'); |
michael@0 | 375 | d.write(s); |
michael@0 | 376 | if (column != sortColumn) |
michael@0 | 377 | d.write('</A>'); |
michael@0 | 378 | d.writeln('</TH>'); |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | function writeClassTableRow(d, row, base, modeName) { |
michael@0 | 382 | if (modeName) { |
michael@0 | 383 | d.writeln('<TR>\n<TH>'+modeName+'</TH>'); |
michael@0 | 384 | } else { |
michael@0 | 385 | d.writeln('<TR>\n<TD><A href="javascript:showRowDetail(\''+row[0]+'\')">'+quoteHTML(row[0])+'</A></TD>'); |
michael@0 | 386 | var v = row[1]; |
michael@0 | 387 | d.writeln('<TD class=num>'+(v === undefined ? '' : v)+'</TD>'); |
michael@0 | 388 | } |
michael@0 | 389 | for (var i = 0; i != 2; i++) { |
michael@0 | 390 | var c = base + i; |
michael@0 | 391 | for (var j = 0; j <= nFiles; j++) { |
michael@0 | 392 | v = row[c]; |
michael@0 | 393 | var style = 'num'; |
michael@0 | 394 | if (j != nFiles) |
michael@0 | 395 | if (v > 0) { |
michael@0 | 396 | style = 'pos'; |
michael@0 | 397 | v = '+'+v; |
michael@0 | 398 | } else |
michael@0 | 399 | style = 'neg'; |
michael@0 | 400 | d.writeln('<TD class='+style+'>'+(v === undefined ? '' : v)+'</TD>'); |
michael@0 | 401 | c += 6; |
michael@0 | 402 | } |
michael@0 | 403 | } |
michael@0 | 404 | d.writeln('</TR>'); |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | function writeClassTable(d) { |
michael@0 | 408 | var base = 2 + showMode*2; |
michael@0 | 409 | |
michael@0 | 410 | // Make a copy because a sort is destructive. |
michael@0 | 411 | var table = classTables.concat(); |
michael@0 | 412 | table.sort(sortCompare); |
michael@0 | 413 | |
michael@0 | 414 | d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); |
michael@0 | 415 | |
michael@0 | 416 | d.writeln('<TR>'); |
michael@0 | 417 | writeReloadLink(d, 0, 'Class Name', 2); |
michael@0 | 418 | writeReloadLink(d, 1, 'Instance<BR>Size', 2); |
michael@0 | 419 | d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated</TH>'); |
michael@0 | 420 | d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated but not freed</TH>\n</TR>'); |
michael@0 | 421 | d.writeln('<TR>'); |
michael@0 | 422 | for (var i = 0; i != 2; i++) { |
michael@0 | 423 | var c = base + i; |
michael@0 | 424 | for (var j = 0; j <= nFiles; j++) { |
michael@0 | 425 | writeReloadLink(d, c, j == nFiles ? 'Total' : quoteHTML(fileTags[j]), 1); |
michael@0 | 426 | c += 6; |
michael@0 | 427 | } |
michael@0 | 428 | } |
michael@0 | 429 | d.writeln('</TR>'); |
michael@0 | 430 | |
michael@0 | 431 | writeClassTableRow(d, totals, base, 0); |
michael@0 | 432 | for (var r = 0; r < table.length; r++) |
michael@0 | 433 | writeClassTableRow(d, table[r], base, 0); |
michael@0 | 434 | |
michael@0 | 435 | d.writeln('</TABLE>'); |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | |
michael@0 | 439 | var modeNames = ["byte", "object", "reference"]; |
michael@0 | 440 | var modeNamesUpper = ["Byte", "Object", "Reference"]; |
michael@0 | 441 | var styleSheet = '<STYLE type="TEXT/CSS">\n'+ |
michael@0 | 442 | 'BODY {background-color: #FFFFFF; color: #000000}\n'+ |
michael@0 | 443 | '.num {text-align: right}\n'+ |
michael@0 | 444 | '.pos {text-align: right; color: #CC0000}\n'+ |
michael@0 | 445 | '.neg {text-align: right; color: #009900}\n'+ |
michael@0 | 446 | '</STYLE>'; |
michael@0 | 447 | |
michael@0 | 448 | |
michael@0 | 449 | function showHead(d) { |
michael@0 | 450 | modeName = modeNames[showMode]; |
michael@0 | 451 | modeNameUpper = modeNamesUpper[showMode]; |
michael@0 | 452 | d.writeln('<TITLE>'+modeNameUpper+' Bloats</TITLE>'); |
michael@0 | 453 | d.writeln(styleSheet); |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | function showBody(d) { |
michael@0 | 457 | d.writeln('<H1>'+modeNameUpper+' Bloats</H1>'); |
michael@0 | 458 | writeFileTable(d); |
michael@0 | 459 | d.write('<FORM>'); |
michael@0 | 460 | for (var i = 0; i != 3; i++) |
michael@0 | 461 | if (i != showMode) { |
michael@0 | 462 | var newSortColumn = sortColumn; |
michael@0 | 463 | if (sortColumn >= 2) |
michael@0 | 464 | newSortColumn = sortColumn + (i-showMode)*2; |
michael@0 | 465 | d.write('<INPUT type="button" value="Show '+modeNamesUpper[i]+'s" onClick="reloadSelf('+newSortColumn+','+i+')">'); |
michael@0 | 466 | } |
michael@0 | 467 | d.writeln('</FORM>'); |
michael@0 | 468 | d.writeln('<P>The numbers do not include <CODE>malloc</CODE>\'d data such as string contents.</P>'); |
michael@0 | 469 | d.writeln('<P>Click on a column heading to sort by that column. Click on a class name to see details for that class.</P>'); |
michael@0 | 470 | writeClassTable(d); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | |
michael@0 | 474 | function showRowDetail(rowName) { |
michael@0 | 475 | var row; |
michael@0 | 476 | var i; |
michael@0 | 477 | |
michael@0 | 478 | if (rowName == "TOTAL") |
michael@0 | 479 | row = totals; |
michael@0 | 480 | else { |
michael@0 | 481 | for (i = 0; i < classTables.length; i++) |
michael@0 | 482 | if (rowName == classTables[i][0]) { |
michael@0 | 483 | row = classTables[i]; |
michael@0 | 484 | break; |
michael@0 | 485 | } |
michael@0 | 486 | } |
michael@0 | 487 | if (row) { |
michael@0 | 488 | var w = window.open("", "ClassTableRowDetails"); |
michael@0 | 489 | var d = w.document; |
michael@0 | 490 | d.open(); |
michael@0 | 491 | d.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'); |
michael@0 | 492 | d.writeln('<HTML>\n<HEAD>\n<TITLE>'+quoteHTML(rowName)+' bloat details</TITLE>'); |
michael@0 | 493 | d.writeln(styleSheet); |
michael@0 | 494 | d.writeln('</HEAD>\n\n<BODY>'); |
michael@0 | 495 | d.writeln('<H2>'+quoteHTML(rowName)+'</H2>'); |
michael@0 | 496 | if (row[1] !== undefined) |
michael@0 | 497 | d.writeln('<P>Each instance has '+row[1]+' bytes.</P>'); |
michael@0 | 498 | |
michael@0 | 499 | d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); |
michael@0 | 500 | d.writeln('<TR>\n<TH></TH>\n<TH colspan='+(nFiles+1)+'>Allocated</TH>'); |
michael@0 | 501 | d.writeln('<TH colspan='+(nFiles+1)+'>Allocated but not freed</TH>\n</TR>'); |
michael@0 | 502 | d.writeln('<TR>\n<TH></TH>'); |
michael@0 | 503 | for (i = 0; i != 2; i++) |
michael@0 | 504 | for (var j = 0; j <= nFiles; j++) |
michael@0 | 505 | d.writeln('<TH>'+(j == nFiles ? 'Total' : quoteHTML(fileTags[j]))+'</TH>'); |
michael@0 | 506 | d.writeln('</TR>'); |
michael@0 | 507 | |
michael@0 | 508 | for (i = 0; i != 3; i++) |
michael@0 | 509 | writeClassTableRow(d, row, 2+i*2, modeNamesUpper[i]+'s'); |
michael@0 | 510 | |
michael@0 | 511 | d.writeln('</TABLE>\n</BODY>\n</HTML>'); |
michael@0 | 512 | d.close(); |
michael@0 | 513 | } |
michael@0 | 514 | return undefined; |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | |
michael@0 | 518 | function stringSource(s) { |
michael@0 | 519 | s = s.replace(/\\/g, '\\\\'); |
michael@0 | 520 | s = s.replace(/"/g, '\\"'); |
michael@0 | 521 | s = s.replace(/<\//g, '<\\/'); |
michael@0 | 522 | return '"'+s+'"'; |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | function reloadSelf(n,m) { |
michael@0 | 526 | // Need to cache these because globals go away on document.open(). |
michael@0 | 527 | var sa = srcArray; |
michael@0 | 528 | var ss = stringSource; |
michael@0 | 529 | var ct = classTables; |
michael@0 | 530 | var i; |
michael@0 | 531 | |
michael@0 | 532 | document.open(); |
michael@0 | 533 | // Uncomment this and comment the document.open() line above to see the reloaded page's source. |
michael@0 | 534 | //var w = window.open("", "NewDoc"); |
michael@0 | 535 | //var d = w.document; |
michael@0 | 536 | //var document = new Object; |
michael@0 | 537 | //document.write = function () { |
michael@0 | 538 | // for (var i = 0; i < arguments.length; i++) { |
michael@0 | 539 | // var s = arguments[i].toString(); |
michael@0 | 540 | // s = s.replace(/&/g, '&'); |
michael@0 | 541 | // s = s.replace(/\x3C/g, '<'); |
michael@0 | 542 | // s = s.replace(/>/g, '>'); |
michael@0 | 543 | // s = s.replace(/ /g, ' '); |
michael@0 | 544 | // d.write(s); |
michael@0 | 545 | // } |
michael@0 | 546 | //}; |
michael@0 | 547 | //document.writeln = function () { |
michael@0 | 548 | // for (var i = 0; i < arguments.length; i++) { |
michael@0 | 549 | // var s = arguments[i].toString(); |
michael@0 | 550 | // s = s.replace(/&/g, '&'); |
michael@0 | 551 | // s = s.replace(/\x3C/g, '<'); |
michael@0 | 552 | // s = s.replace(/>/g, '>'); |
michael@0 | 553 | // s = s.replace(/ /g, ' '); |
michael@0 | 554 | // d.write(s); |
michael@0 | 555 | // } |
michael@0 | 556 | // d.writeln('<BR>'); |
michael@0 | 557 | //}; |
michael@0 | 558 | |
michael@0 | 559 | document.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'); |
michael@0 | 560 | document.writeln('<HTML>\n<HEAD>\n<SCRIPT type="text/javascript">'); |
michael@0 | 561 | |
michael@0 | 562 | // Manually copy non-persistent script data |
michael@0 | 563 | if (!ct.length) |
michael@0 | 564 | document.writeln('var classTables = [];'); |
michael@0 | 565 | else { |
michael@0 | 566 | document.writeln('var classTables = ['); |
michael@0 | 567 | for (i = 0; i < ct.length; i++) { |
michael@0 | 568 | var row = ct[i]; |
michael@0 | 569 | document.write('[' + ss(row[0])); |
michael@0 | 570 | for (var j = 1; j < row.length; j++) |
michael@0 | 571 | document.write(',' + row[j]); |
michael@0 | 572 | document.writeln(']' + (i == ct.length-1 ? '];' : ',')); |
michael@0 | 573 | } |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | document.writeln('var srcArray = ['); |
michael@0 | 577 | for (i = 0; i < sa.length; i++) { |
michael@0 | 578 | document.write(ss(sa[i])); |
michael@0 | 579 | if (i != sa.length-1) |
michael@0 | 580 | document.writeln(','); |
michael@0 | 581 | } |
michael@0 | 582 | document.writeln('];'); |
michael@0 | 583 | document.writeln('eval(srcArray.join("\\n"));'); |
michael@0 | 584 | document.writeln('showMode = '+m+';'); |
michael@0 | 585 | document.writeln('sortColumn = '+n+';'); |
michael@0 | 586 | document.writeln('showHead(document);'); |
michael@0 | 587 | document.writeln('</SCRIPT>\n</HEAD>\n\n<BODY>\n<SCRIPT type="text/javascript">showBody(document);</SCRIPT>\n</BODY>\n</HTML>'); |
michael@0 | 588 | document.close(); |
michael@0 | 589 | return undefined; |
michael@0 | 590 | } |