1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/memory/bloattable.pl Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,590 @@ 1.4 +#!/usr/bin/perl -w 1.5 +# 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +# bloattable [-debug] [-source] [-byte n|-obj n|-ref n] <file1> <file2> ... <filen> > <html-file> 1.11 +# 1.12 +# file1, file2, ... filen should be successive BloatView files generated from the same run. 1.13 +# Summarize them in an HTML table. Output the HTML to the standard output. 1.14 +# 1.15 +# If -debug is set, create a slightly larger html file which is more suitable for debugging this script. 1.16 +# If -source is set, create an html file that prints the html source as the output 1.17 +# If -byte n, -obj n, or -ref n is given, make the page default to showing byte, object, or reference statistics, 1.18 +# respectively, and sort by the nth column (n is zero-based, so the first column has n==0). 1.19 +# 1.20 +# See http://lxr.mozilla.org/mozilla/source/xpcom/doc/MemoryTools.html 1.21 + 1.22 +use 5.004; 1.23 +use strict; 1.24 +use diagnostics; 1.25 +use File::Basename; 1.26 +use Getopt::Long; 1.27 + 1.28 +# The generated HTML is almost entirely generated by a script. Only the <HTML>, <HEAD>, and <BODY> elements are explicit 1.29 +# because a <SCRIPT> element cannot officially be a direct descendant of an <HTML> element. 1.30 +# The script itself is almost all generated by an eval of a large string. This allows the script to reproduce itself 1.31 +# when making a new page using document.write's. Re-sorting the page causes it to regenerate itself in this way. 1.32 + 1.33 + 1.34 + 1.35 +# Return the file's modification date. 1.36 +sub fileModDate($) { 1.37 + my ($pathName) = @_; 1.38 + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = 1.39 + stat $pathName or die "Can't stat '$pathName'"; 1.40 + return $mtime; 1.41 +} 1.42 + 1.43 + 1.44 +sub fileCoreName($) { 1.45 + my ($pathName) = @_; 1.46 + my $fileName = basename($pathName, ""); 1.47 + $fileName =~ s/\..*//; 1.48 + return $fileName; 1.49 +} 1.50 + 1.51 + 1.52 +# Convert a raw string into a single-quoted JavaScript string. 1.53 +sub singleQuoteString($) { 1.54 + local ($_) = @_; 1.55 + s/\\/\\\\/g; 1.56 + s/'/\\'/g; 1.57 + s/\n/\\n/g; 1.58 + s/<\//<\\\//g; 1.59 + return "'$_'"; 1.60 +} 1.61 + 1.62 + 1.63 +# Convert a raw string into a double-quoted JavaScript string. 1.64 +sub doubleQuoteString($) { 1.65 + local ($_) = @_; 1.66 + s/\\/\\\\/g; 1.67 + s/"/\\"/g; 1.68 + s/\n/\\n/g; 1.69 + s/<\//<\\\//g; 1.70 + return "\"$_\""; 1.71 +} 1.72 + 1.73 + 1.74 +# Quote special HTML characters in the string. 1.75 +sub quoteHTML($) { 1.76 + local ($_) = @_; 1.77 + s/\&/&/g; 1.78 + s/</</g; 1.79 + s/>/>/g; 1.80 + s/ / /g; 1.81 + s/\n/<BR>\n/g; 1.82 + return $_; 1.83 +} 1.84 + 1.85 + 1.86 +# Write the generated page to the standard output. 1.87 +# The script source code is read from this file past the __END__ marker 1.88 +# @$scriptData is the JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript. 1.89 +# @$persistentScriptData is the same as @scriptData, but persists when the page reloads itself. 1.90 +# If $debug is true, generate the script directly instead of having it eval itself. 1.91 +# If $source is true, generate a script that displays the page's source instead of the page itself. 1.92 +sub generate(\@\@$$$$) { 1.93 + my ($scriptData, $persistentScriptData, $debug, $source, $showMode, $sortColumn) = @_; 1.94 + 1.95 + my @scriptSource = <DATA>; 1.96 + chomp @scriptSource; 1.97 + print <<'EOS'; 1.98 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 1.99 +<HTML> 1.100 +<HEAD> 1.101 +<SCRIPT type="text/javascript"> 1.102 +EOS 1.103 + 1.104 + foreach (@$scriptData) {print "$_\n";} 1.105 + print "\n"; 1.106 + 1.107 + print "var srcArray = [\n"; 1.108 + my @quotedScriptSource = map { 1.109 + my $line = $_; 1.110 + $line =~ s/^\s+//g; 1.111 + # $line =~ s/^\/\/SOURCE\s+//g if $source; 1.112 + $line =~ s/^\/\/.*//g; 1.113 + $line =~ s/\s+$//g; 1.114 + $line eq "" ? () : $line 1.115 + } @$persistentScriptData, @scriptSource; 1.116 + my $lastQuotedLine = pop @quotedScriptSource; 1.117 + foreach (@quotedScriptSource) {print doubleQuoteString($_), ",\n";} 1.118 + print doubleQuoteString($lastQuotedLine), "];\n\n"; 1.119 + 1.120 + if ($debug) { 1.121 + push @quotedScriptSource, $lastQuotedLine; 1.122 + foreach (@quotedScriptSource) { 1.123 + s/<\//<\\\//g; # This fails if a regexp ends with a '<'. Oh well.... 1.124 + print "$_\n"; 1.125 + } 1.126 + print "\n"; 1.127 + } else { 1.128 + print "eval(srcArray.join(\"\\n\"));\n\n"; 1.129 + } 1.130 + print "showMode = $showMode;\n"; 1.131 + print "sortColumn = $sortColumn;\n"; 1.132 + if ($source) { 1.133 + print <<'EOS'; 1.134 +function writeQuotedHTML(s) { 1.135 + document.write(quoteHTML(s.toString()).replace(/\n/g, '<BR>\n')); 1.136 +} 1.137 + 1.138 +var quotingDocument = { 1.139 + write: function () { 1.140 + for (var i = 0; i < arguments.length; i++) 1.141 + writeQuotedHTML(arguments[i]); 1.142 + }, 1.143 + writeln: function () { 1.144 + for (var i = 0; i < arguments.length; i++) 1.145 + writeQuotedHTML(arguments[i]); 1.146 + document.writeln('<BR>'); 1.147 + } 1.148 +}; 1.149 +EOS 1.150 + } else { 1.151 + print "showHead(document);\n"; 1.152 + } 1.153 + print "</SCRIPT>\n"; 1.154 + print "</HEAD>\n\n"; 1.155 + print "<BODY>\n"; 1.156 + if ($source) { 1.157 + print "<P><TT>"; 1.158 + print quoteHTML "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"; 1.159 + print quoteHTML "<HTML>\n"; 1.160 + print quoteHTML "<HEAD>\n"; 1.161 + print "<SCRIPT type=\"text/javascript\">showHead(quotingDocument);</SCRIPT>\n"; 1.162 + print quoteHTML "</HEAD>\n\n"; 1.163 + print quoteHTML "<BODY>\n"; 1.164 + print "<SCRIPT type=\"text/javascript\">showBody(quotingDocument);</SCRIPT>\n"; 1.165 + print quoteHTML "</BODY>\n"; 1.166 + print quoteHTML "</HTML>\n"; 1.167 + print "</TT></P>\n"; 1.168 + } else { 1.169 + print "<SCRIPT type=\"text/javascript\">showBody(document);</SCRIPT>\n"; 1.170 + } 1.171 + print "</BODY>\n"; 1.172 + print "</HTML>\n"; 1.173 +} 1.174 + 1.175 + 1.176 + 1.177 +# Read the bloat file into hash table $h. The hash table is indexed by class names; 1.178 +# each entry is a list with the following elements: 1.179 +# bytesAlloc Total number of bytes allocated 1.180 +# bytesNet Total number of bytes allocated but not deallocated 1.181 +# objectsAlloc Total number of objects allocated 1.182 +# objectsNet Total number of objects allocated but not deallocated 1.183 +# refsAlloc Total number of references AddRef'd 1.184 +# refsNet Total number of references AddRef'd but not Released 1.185 +# Except for TOTAL, all hash table entries refer to mutually exclusive data. 1.186 +# $sizes is a hash table indexed by class names. Each entry of that table contains the class's instance size. 1.187 +sub readBloatFile($\%\%) { 1.188 + my ($file, $h, $sizes) = @_; 1.189 + local $_; # Needed for 'while (<FILE>)' below. 1.190 + 1.191 + my $readSomething = 0; 1.192 + open FILE, $file; 1.193 + while (<FILE>) { 1.194 + if (my ($name, $size, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet) = 1.195 + /^\s*(?:\d+)\s+([\w:]+)\s+(\d+)\s+(-?\d+)\s+(\d+)\s+(-?\d+)\s*\([^()]*\)\s*(\d+)\s+(-?\d+)\s*\([^()]*\)\s*$/) { 1.196 + my $bytesAlloc; 1.197 + if ($name eq "TOTAL") { 1.198 + $size = "undefined"; 1.199 + $bytesAlloc = "undefined"; 1.200 + } else { 1.201 + $bytesAlloc = $objectsAlloc * $size; 1.202 + if ($bytesNet != $objectsNet * $size) { 1.203 + print STDERR "In '$file', class $name bytesNet != objectsNet * size: $bytesNet != $objectsNet * $size\n"; 1.204 + } 1.205 + } 1.206 + print STDERR "Duplicate entry $name in '$file'\n" if $$h{$name}; 1.207 + $$h{$name} = [$bytesAlloc, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet]; 1.208 + 1.209 + my $oldSize = $$sizes{$name}; 1.210 + print STDERR "Mismatch of sizes of class $name: $oldSize and $size\n" if defined($oldSize) && $size ne $oldSize; 1.211 + $$sizes{$name} = $size; 1.212 + $readSomething = 1; 1.213 + } elsif (/^\s*(?:\d+)\s+([\w:]+)\s/) { 1.214 + print STDERR "Unable to parse '$file' line: $_"; 1.215 + } 1.216 + } 1.217 + close FILE; 1.218 + print STDERR "No data in '$file'\n" unless $readSomething; 1.219 + return $h; 1.220 +} 1.221 + 1.222 + 1.223 +my %sizes; # <class-name> => <instance-size> 1.224 +my %tables; # <file-name> => <bloat-table>; see readBloatFile for format of <bloat-table> 1.225 + 1.226 +# Generate the JavaScript source code for the row named $c. $l can contain the initial entries of the row. 1.227 +sub genTableRowSource($$) { 1.228 + my ($l, $c) = @_; 1.229 + my $lastE; 1.230 + foreach (@ARGV) { 1.231 + my $e = $tables{$_}{$c}; 1.232 + if (defined($lastE) && !defined($e)) { 1.233 + $e = [0,0,0,0,0,0]; 1.234 + print STDERR "Class $c is defined in an earlier file but not in '$_'\n"; 1.235 + } 1.236 + if (defined $e) { 1.237 + if (defined $lastE) { 1.238 + for (my $i = 0; $i <= $#$e; $i++) { 1.239 + my $n = $$e[$i]; 1.240 + $l .= ($n eq "undefined" ? "undefined" : $n - $$lastE[$i]) . ","; 1.241 + } 1.242 + $l .= " "; 1.243 + } else { 1.244 + $l .= join(",", @$e) . ", "; 1.245 + } 1.246 + $lastE = $e; 1.247 + } else { 1.248 + $l .= "0,0,0,0,0,0, "; 1.249 + } 1.250 + } 1.251 + $l .= join(",", @$lastE); 1.252 + return "[$l]"; 1.253 +} 1.254 + 1.255 + 1.256 + 1.257 +my $debug; 1.258 +my $source; 1.259 +my $showMode; 1.260 +my $sortColumn; 1.261 +my @modeOptions; 1.262 + 1.263 +GetOptions("debug" => \$debug, "source" => \$source, "byte=i" => \$modeOptions[0], "obj=i" => \$modeOptions[1], "ref=i" => \$modeOptions[2]); 1.264 +for (my $i = 0; $i != 3; $i++) { 1.265 + my $modeOption = $modeOptions[$i]; 1.266 + if ($modeOption) { 1.267 + die "Only one of -byte, -obj, or -ref may be given" if defined $showMode; 1.268 + my $nFileColumns = scalar(@ARGV) + 1; 1.269 + die "-byte, -obj, or -ref column number out of range" if $modeOption < 0 || $modeOption >= 2 + 2*$nFileColumns; 1.270 + $showMode = $i; 1.271 + if ($modeOption >= 2) { 1.272 + $modeOption -= 2; 1.273 + $sortColumn = 2 + $showMode*2; 1.274 + if ($modeOption >= $nFileColumns) { 1.275 + $sortColumn++; 1.276 + $modeOption -= $nFileColumns; 1.277 + } 1.278 + $sortColumn += $modeOption*6; 1.279 + } else { 1.280 + $sortColumn = $modeOption; 1.281 + } 1.282 + } 1.283 +} 1.284 +unless (defined $showMode) { 1.285 + $showMode = 0; 1.286 + $sortColumn = 0; 1.287 +} 1.288 + 1.289 +# Read all of the bloat files. 1.290 +foreach (@ARGV) { 1.291 + unless ($tables{$_}) { 1.292 + my $f = $_; 1.293 + my %table; 1.294 + 1.295 + readBloatFile $_, %table, %sizes; 1.296 + $tables{$_} = \%table; 1.297 + } 1.298 +} 1.299 +die "No input" unless %sizes; 1.300 + 1.301 +my @scriptData; # JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript. 1.302 +my @persistentScriptData; # Same as @scriptData, but persists the page reloads itself. 1.303 + 1.304 +# Print a list of bloat file names. 1.305 +push @persistentScriptData, "var nFiles = " . scalar(@ARGV) . ";"; 1.306 +push @persistentScriptData, "var fileTags = [" . join(", ", map {singleQuoteString substr(fileCoreName($_), -10)} @ARGV) . "];"; 1.307 +push @persistentScriptData, "var fileNames = [" . join(", ", map {singleQuoteString $_} @ARGV) . "];"; 1.308 +push @persistentScriptData, "var fileDates = [" . join(", ", map {singleQuoteString localtime fileModDate $_} @ARGV) . "];"; 1.309 + 1.310 +# Print the bloat tables. 1.311 +push @persistentScriptData, "var totals = " . genTableRowSource('"TOTAL", undefined, ', "TOTAL") . ";"; 1.312 +push @scriptData, "var classTables = ["; 1.313 +delete $sizes{"TOTAL"}; 1.314 +my @classes = sort(keys %sizes); 1.315 +for (my $i = 0; $i <= $#classes; $i++) { 1.316 + my $c = $classes[$i]; 1.317 + push @scriptData, genTableRowSource(doubleQuoteString($c).", ".$sizes{$c}.", ", $c) . ($i == $#classes ? "];" : ","); 1.318 +} 1.319 + 1.320 +generate(@scriptData, @persistentScriptData, $debug, $source, $showMode, $sortColumn); 1.321 +1; 1.322 + 1.323 + 1.324 +# The source of the eval'd JavaScript follows. 1.325 +# Comments starting with // that are alone on a line are stripped by the Perl script. 1.326 +__END__ 1.327 + 1.328 +// showMode: 0=bytes, 1=objects, 2=references 1.329 +var showMode; 1.330 +var modeName; 1.331 +var modeNameUpper; 1.332 + 1.333 +var sortColumn; 1.334 + 1.335 +// Sort according to the sortColumn. Column 0 is sorted alphabetically in ascending order. 1.336 +// All other columns are sorted numerically in descending order, with column 0 used for a secondary sort. 1.337 +// Undefined is always listed last. 1.338 +function sortCompare(x, y) { 1.339 + if (sortColumn) { 1.340 + var xc = x[sortColumn]; 1.341 + var yc = y[sortColumn]; 1.342 + if (xc < yc || xc === undefined && yc !== undefined) return 1; 1.343 + if (yc < xc || yc === undefined && xc !== undefined) return -1; 1.344 + } 1.345 + 1.346 + var x0 = x[0]; 1.347 + var y0 = y[0]; 1.348 + if (x0 > y0 || x0 === undefined && y0 !== undefined) return 1; 1.349 + if (y0 > x0 || y0 === undefined && x0 !== undefined) return -1; 1.350 + return 0; 1.351 +} 1.352 + 1.353 + 1.354 +// Quote special HTML characters in the string. 1.355 +function quoteHTML(s) { 1.356 + s = s.replace(/&/g, '&'); 1.357 + // Can't use /</g because HTML interprets '</g' as ending the script! 1.358 + s = s.replace(/\x3C/g, '<'); 1.359 + s = s.replace(/>/g, '>'); 1.360 + s = s.replace(/ /g, ' '); 1.361 + return s; 1.362 +} 1.363 + 1.364 + 1.365 +function writeFileTable(d) { 1.366 + d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); 1.367 + d.writeln('<TR>\n<TH>Name</TH>\n<TH>File</TH>\n<TH>Date</TH>\n</TR>'); 1.368 + for (var i = 0; i < nFiles; i++) 1.369 + 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>'); 1.370 + d.writeln('</TABLE>'); 1.371 +} 1.372 + 1.373 + 1.374 +function writeReloadLink(d, column, s, rowspan) { 1.375 + d.write(rowspan == 1 ? '<TH>' : '<TH rowspan='+rowspan+'>'); 1.376 + if (column != sortColumn) 1.377 + d.write('<A href="javascript:reloadSelf('+column+','+showMode+')">'); 1.378 + d.write(s); 1.379 + if (column != sortColumn) 1.380 + d.write('</A>'); 1.381 + d.writeln('</TH>'); 1.382 +} 1.383 + 1.384 +function writeClassTableRow(d, row, base, modeName) { 1.385 + if (modeName) { 1.386 + d.writeln('<TR>\n<TH>'+modeName+'</TH>'); 1.387 + } else { 1.388 + d.writeln('<TR>\n<TD><A href="javascript:showRowDetail(\''+row[0]+'\')">'+quoteHTML(row[0])+'</A></TD>'); 1.389 + var v = row[1]; 1.390 + d.writeln('<TD class=num>'+(v === undefined ? '' : v)+'</TD>'); 1.391 + } 1.392 + for (var i = 0; i != 2; i++) { 1.393 + var c = base + i; 1.394 + for (var j = 0; j <= nFiles; j++) { 1.395 + v = row[c]; 1.396 + var style = 'num'; 1.397 + if (j != nFiles) 1.398 + if (v > 0) { 1.399 + style = 'pos'; 1.400 + v = '+'+v; 1.401 + } else 1.402 + style = 'neg'; 1.403 + d.writeln('<TD class='+style+'>'+(v === undefined ? '' : v)+'</TD>'); 1.404 + c += 6; 1.405 + } 1.406 + } 1.407 + d.writeln('</TR>'); 1.408 +} 1.409 + 1.410 +function writeClassTable(d) { 1.411 + var base = 2 + showMode*2; 1.412 + 1.413 + // Make a copy because a sort is destructive. 1.414 + var table = classTables.concat(); 1.415 + table.sort(sortCompare); 1.416 + 1.417 + d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); 1.418 + 1.419 + d.writeln('<TR>'); 1.420 + writeReloadLink(d, 0, 'Class Name', 2); 1.421 + writeReloadLink(d, 1, 'Instance<BR>Size', 2); 1.422 + d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated</TH>'); 1.423 + d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated but not freed</TH>\n</TR>'); 1.424 + d.writeln('<TR>'); 1.425 + for (var i = 0; i != 2; i++) { 1.426 + var c = base + i; 1.427 + for (var j = 0; j <= nFiles; j++) { 1.428 + writeReloadLink(d, c, j == nFiles ? 'Total' : quoteHTML(fileTags[j]), 1); 1.429 + c += 6; 1.430 + } 1.431 + } 1.432 + d.writeln('</TR>'); 1.433 + 1.434 + writeClassTableRow(d, totals, base, 0); 1.435 + for (var r = 0; r < table.length; r++) 1.436 + writeClassTableRow(d, table[r], base, 0); 1.437 + 1.438 + d.writeln('</TABLE>'); 1.439 +} 1.440 + 1.441 + 1.442 +var modeNames = ["byte", "object", "reference"]; 1.443 +var modeNamesUpper = ["Byte", "Object", "Reference"]; 1.444 +var styleSheet = '<STYLE type="TEXT/CSS">\n'+ 1.445 + 'BODY {background-color: #FFFFFF; color: #000000}\n'+ 1.446 + '.num {text-align: right}\n'+ 1.447 + '.pos {text-align: right; color: #CC0000}\n'+ 1.448 + '.neg {text-align: right; color: #009900}\n'+ 1.449 + '</STYLE>'; 1.450 + 1.451 + 1.452 +function showHead(d) { 1.453 + modeName = modeNames[showMode]; 1.454 + modeNameUpper = modeNamesUpper[showMode]; 1.455 + d.writeln('<TITLE>'+modeNameUpper+' Bloats</TITLE>'); 1.456 + d.writeln(styleSheet); 1.457 +} 1.458 + 1.459 +function showBody(d) { 1.460 + d.writeln('<H1>'+modeNameUpper+' Bloats</H1>'); 1.461 + writeFileTable(d); 1.462 + d.write('<FORM>'); 1.463 + for (var i = 0; i != 3; i++) 1.464 + if (i != showMode) { 1.465 + var newSortColumn = sortColumn; 1.466 + if (sortColumn >= 2) 1.467 + newSortColumn = sortColumn + (i-showMode)*2; 1.468 + d.write('<INPUT type="button" value="Show '+modeNamesUpper[i]+'s" onClick="reloadSelf('+newSortColumn+','+i+')">'); 1.469 + } 1.470 + d.writeln('</FORM>'); 1.471 + d.writeln('<P>The numbers do not include <CODE>malloc</CODE>\'d data such as string contents.</P>'); 1.472 + 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>'); 1.473 + writeClassTable(d); 1.474 +} 1.475 + 1.476 + 1.477 +function showRowDetail(rowName) { 1.478 + var row; 1.479 + var i; 1.480 + 1.481 + if (rowName == "TOTAL") 1.482 + row = totals; 1.483 + else { 1.484 + for (i = 0; i < classTables.length; i++) 1.485 + if (rowName == classTables[i][0]) { 1.486 + row = classTables[i]; 1.487 + break; 1.488 + } 1.489 + } 1.490 + if (row) { 1.491 + var w = window.open("", "ClassTableRowDetails"); 1.492 + var d = w.document; 1.493 + d.open(); 1.494 + d.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'); 1.495 + d.writeln('<HTML>\n<HEAD>\n<TITLE>'+quoteHTML(rowName)+' bloat details</TITLE>'); 1.496 + d.writeln(styleSheet); 1.497 + d.writeln('</HEAD>\n\n<BODY>'); 1.498 + d.writeln('<H2>'+quoteHTML(rowName)+'</H2>'); 1.499 + if (row[1] !== undefined) 1.500 + d.writeln('<P>Each instance has '+row[1]+' bytes.</P>'); 1.501 + 1.502 + d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>'); 1.503 + d.writeln('<TR>\n<TH></TH>\n<TH colspan='+(nFiles+1)+'>Allocated</TH>'); 1.504 + d.writeln('<TH colspan='+(nFiles+1)+'>Allocated but not freed</TH>\n</TR>'); 1.505 + d.writeln('<TR>\n<TH></TH>'); 1.506 + for (i = 0; i != 2; i++) 1.507 + for (var j = 0; j <= nFiles; j++) 1.508 + d.writeln('<TH>'+(j == nFiles ? 'Total' : quoteHTML(fileTags[j]))+'</TH>'); 1.509 + d.writeln('</TR>'); 1.510 + 1.511 + for (i = 0; i != 3; i++) 1.512 + writeClassTableRow(d, row, 2+i*2, modeNamesUpper[i]+'s'); 1.513 + 1.514 + d.writeln('</TABLE>\n</BODY>\n</HTML>'); 1.515 + d.close(); 1.516 + } 1.517 + return undefined; 1.518 +} 1.519 + 1.520 + 1.521 +function stringSource(s) { 1.522 + s = s.replace(/\\/g, '\\\\'); 1.523 + s = s.replace(/"/g, '\\"'); 1.524 + s = s.replace(/<\//g, '<\\/'); 1.525 + return '"'+s+'"'; 1.526 +} 1.527 + 1.528 +function reloadSelf(n,m) { 1.529 + // Need to cache these because globals go away on document.open(). 1.530 + var sa = srcArray; 1.531 + var ss = stringSource; 1.532 + var ct = classTables; 1.533 + var i; 1.534 + 1.535 + document.open(); 1.536 + // Uncomment this and comment the document.open() line above to see the reloaded page's source. 1.537 + //var w = window.open("", "NewDoc"); 1.538 + //var d = w.document; 1.539 + //var document = new Object; 1.540 + //document.write = function () { 1.541 + // for (var i = 0; i < arguments.length; i++) { 1.542 + // var s = arguments[i].toString(); 1.543 + // s = s.replace(/&/g, '&'); 1.544 + // s = s.replace(/\x3C/g, '<'); 1.545 + // s = s.replace(/>/g, '>'); 1.546 + // s = s.replace(/ /g, ' '); 1.547 + // d.write(s); 1.548 + // } 1.549 + //}; 1.550 + //document.writeln = function () { 1.551 + // for (var i = 0; i < arguments.length; i++) { 1.552 + // var s = arguments[i].toString(); 1.553 + // s = s.replace(/&/g, '&'); 1.554 + // s = s.replace(/\x3C/g, '<'); 1.555 + // s = s.replace(/>/g, '>'); 1.556 + // s = s.replace(/ /g, ' '); 1.557 + // d.write(s); 1.558 + // } 1.559 + // d.writeln('<BR>'); 1.560 + //}; 1.561 + 1.562 + document.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'); 1.563 + document.writeln('<HTML>\n<HEAD>\n<SCRIPT type="text/javascript">'); 1.564 + 1.565 + // Manually copy non-persistent script data 1.566 + if (!ct.length) 1.567 + document.writeln('var classTables = [];'); 1.568 + else { 1.569 + document.writeln('var classTables = ['); 1.570 + for (i = 0; i < ct.length; i++) { 1.571 + var row = ct[i]; 1.572 + document.write('[' + ss(row[0])); 1.573 + for (var j = 1; j < row.length; j++) 1.574 + document.write(',' + row[j]); 1.575 + document.writeln(']' + (i == ct.length-1 ? '];' : ',')); 1.576 + } 1.577 + } 1.578 + 1.579 + document.writeln('var srcArray = ['); 1.580 + for (i = 0; i < sa.length; i++) { 1.581 + document.write(ss(sa[i])); 1.582 + if (i != sa.length-1) 1.583 + document.writeln(','); 1.584 + } 1.585 + document.writeln('];'); 1.586 + document.writeln('eval(srcArray.join("\\n"));'); 1.587 + document.writeln('showMode = '+m+';'); 1.588 + document.writeln('sortColumn = '+n+';'); 1.589 + document.writeln('showHead(document);'); 1.590 + document.writeln('</SCRIPT>\n</HEAD>\n\n<BODY>\n<SCRIPT type="text/javascript">showBody(document);</SCRIPT>\n</BODY>\n</HTML>'); 1.591 + document.close(); 1.592 + return undefined; 1.593 +}