tools/memory/bloattable.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 #
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/\&/&amp;/g;
michael@0 75 s/</&lt;/g;
michael@0 76 s/>/&gt;/g;
michael@0 77 s/ /&nbsp;/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, '&amp;');
michael@0 354 // Can't use /</g because HTML interprets '</g' as ending the script!
michael@0 355 s = s.replace(/\x3C/g, '&lt;');
michael@0 356 s = s.replace(/>/g, '&gt;');
michael@0 357 s = s.replace(/ /g, '&nbsp;');
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, '&amp;');
michael@0 541 // s = s.replace(/\x3C/g, '&lt;');
michael@0 542 // s = s.replace(/>/g, '&gt;');
michael@0 543 // s = s.replace(/ /g, '&nbsp;');
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, '&amp;');
michael@0 551 // s = s.replace(/\x3C/g, '&lt;');
michael@0 552 // s = s.replace(/>/g, '&gt;');
michael@0 553 // s = s.replace(/ /g, '&nbsp;');
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 }

mercurial