tools/memory/bloattable.pl

changeset 0
6474c204b198
     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/\&/&amp;/g;
    1.78 +	s/</&lt;/g;
    1.79 +	s/>/&gt;/g;
    1.80 +	s/ /&nbsp;/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, '&amp;');
   1.357 +	// Can't use /</g because HTML interprets '</g' as ending the script!
   1.358 +	s = s.replace(/\x3C/g, '&lt;');
   1.359 +	s = s.replace(/>/g, '&gt;');
   1.360 +	s = s.replace(/ /g, '&nbsp;');
   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, '&amp;');
   1.544 +	//		s = s.replace(/\x3C/g, '&lt;');
   1.545 +	//		s = s.replace(/>/g, '&gt;');
   1.546 +	//		s = s.replace(/ /g, '&nbsp;');
   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, '&amp;');
   1.554 +	//		s = s.replace(/\x3C/g, '&lt;');
   1.555 +	//		s = s.replace(/>/g, '&gt;');
   1.556 +	//		s = s.replace(/ /g, '&nbsp;');
   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 +}

mercurial