mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
591 lines
18 KiB
Perl
Executable File
591 lines
18 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# bloattable [-debug] [-source] [-byte n|-obj n|-ref n] <file1> <file2> ... <filen> > <html-file>
|
|
#
|
|
# file1, file2, ... filen should be successive BloatView files generated from the same run.
|
|
# Summarize them in an HTML table. Output the HTML to the standard output.
|
|
#
|
|
# If -debug is set, create a slightly larger html file which is more suitable for debugging this script.
|
|
# If -source is set, create an html file that prints the html source as the output
|
|
# If -byte n, -obj n, or -ref n is given, make the page default to showing byte, object, or reference statistics,
|
|
# respectively, and sort by the nth column (n is zero-based, so the first column has n==0).
|
|
#
|
|
# See http://lxr.mozilla.org/mozilla/source/xpcom/doc/MemoryTools.html
|
|
|
|
use 5.004;
|
|
use strict;
|
|
use diagnostics;
|
|
use File::Basename;
|
|
use Getopt::Long;
|
|
|
|
# The generated HTML is almost entirely generated by a script. Only the <HTML>, <HEAD>, and <BODY> elements are explicit
|
|
# because a <SCRIPT> element cannot officially be a direct descendant of an <HTML> element.
|
|
# The script itself is almost all generated by an eval of a large string. This allows the script to reproduce itself
|
|
# when making a new page using document.write's. Re-sorting the page causes it to regenerate itself in this way.
|
|
|
|
|
|
|
|
# Return the file's modification date.
|
|
sub fileModDate($) {
|
|
my ($pathName) = @_;
|
|
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) =
|
|
stat $pathName or die "Can't stat '$pathName'";
|
|
return $mtime;
|
|
}
|
|
|
|
|
|
sub fileCoreName($) {
|
|
my ($pathName) = @_;
|
|
my $fileName = basename($pathName, "");
|
|
$fileName =~ s/\..*//;
|
|
return $fileName;
|
|
}
|
|
|
|
|
|
# Convert a raw string into a single-quoted JavaScript string.
|
|
sub singleQuoteString($) {
|
|
local ($_) = @_;
|
|
s/\\/\\\\/g;
|
|
s/'/\\'/g;
|
|
s/\n/\\n/g;
|
|
s/<\//<\\\//g;
|
|
return "'$_'";
|
|
}
|
|
|
|
|
|
# Convert a raw string into a double-quoted JavaScript string.
|
|
sub doubleQuoteString($) {
|
|
local ($_) = @_;
|
|
s/\\/\\\\/g;
|
|
s/"/\\"/g;
|
|
s/\n/\\n/g;
|
|
s/<\//<\\\//g;
|
|
return "\"$_\"";
|
|
}
|
|
|
|
|
|
# Quote special HTML characters in the string.
|
|
sub quoteHTML($) {
|
|
local ($_) = @_;
|
|
s/\&/&/g;
|
|
s/</</g;
|
|
s/>/>/g;
|
|
s/ / /g;
|
|
s/\n/<BR>\n/g;
|
|
return $_;
|
|
}
|
|
|
|
|
|
# Write the generated page to the standard output.
|
|
# The script source code is read from this file past the __END__ marker
|
|
# @$scriptData is the JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript.
|
|
# @$persistentScriptData is the same as @scriptData, but persists when the page reloads itself.
|
|
# If $debug is true, generate the script directly instead of having it eval itself.
|
|
# If $source is true, generate a script that displays the page's source instead of the page itself.
|
|
sub generate(\@\@$$$$) {
|
|
my ($scriptData, $persistentScriptData, $debug, $source, $showMode, $sortColumn) = @_;
|
|
|
|
my @scriptSource = <DATA>;
|
|
chomp @scriptSource;
|
|
print <<'EOS';
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
|
<HTML>
|
|
<HEAD>
|
|
<SCRIPT type="text/javascript">
|
|
EOS
|
|
|
|
foreach (@$scriptData) {print "$_\n";}
|
|
print "\n";
|
|
|
|
print "var srcArray = [\n";
|
|
my @quotedScriptSource = map {
|
|
my $line = $_;
|
|
$line =~ s/^\s+//g;
|
|
# $line =~ s/^\/\/SOURCE\s+//g if $source;
|
|
$line =~ s/^\/\/.*//g;
|
|
$line =~ s/\s+$//g;
|
|
$line eq "" ? () : $line
|
|
} @$persistentScriptData, @scriptSource;
|
|
my $lastQuotedLine = pop @quotedScriptSource;
|
|
foreach (@quotedScriptSource) {print doubleQuoteString($_), ",\n";}
|
|
print doubleQuoteString($lastQuotedLine), "];\n\n";
|
|
|
|
if ($debug) {
|
|
push @quotedScriptSource, $lastQuotedLine;
|
|
foreach (@quotedScriptSource) {
|
|
s/<\//<\\\//g; # This fails if a regexp ends with a '<'. Oh well....
|
|
print "$_\n";
|
|
}
|
|
print "\n";
|
|
} else {
|
|
print "eval(srcArray.join(\"\\n\"));\n\n";
|
|
}
|
|
print "showMode = $showMode;\n";
|
|
print "sortColumn = $sortColumn;\n";
|
|
if ($source) {
|
|
print <<'EOS';
|
|
function writeQuotedHTML(s) {
|
|
document.write(quoteHTML(s.toString()).replace(/\n/g, '<BR>\n'));
|
|
}
|
|
|
|
var quotingDocument = {
|
|
write: function () {
|
|
for (var i = 0; i < arguments.length; i++)
|
|
writeQuotedHTML(arguments[i]);
|
|
},
|
|
writeln: function () {
|
|
for (var i = 0; i < arguments.length; i++)
|
|
writeQuotedHTML(arguments[i]);
|
|
document.writeln('<BR>');
|
|
}
|
|
};
|
|
EOS
|
|
} else {
|
|
print "showHead(document);\n";
|
|
}
|
|
print "</SCRIPT>\n";
|
|
print "</HEAD>\n\n";
|
|
print "<BODY>\n";
|
|
if ($source) {
|
|
print "<P><TT>";
|
|
print quoteHTML "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n";
|
|
print quoteHTML "<HTML>\n";
|
|
print quoteHTML "<HEAD>\n";
|
|
print "<SCRIPT type=\"text/javascript\">showHead(quotingDocument);</SCRIPT>\n";
|
|
print quoteHTML "</HEAD>\n\n";
|
|
print quoteHTML "<BODY>\n";
|
|
print "<SCRIPT type=\"text/javascript\">showBody(quotingDocument);</SCRIPT>\n";
|
|
print quoteHTML "</BODY>\n";
|
|
print quoteHTML "</HTML>\n";
|
|
print "</TT></P>\n";
|
|
} else {
|
|
print "<SCRIPT type=\"text/javascript\">showBody(document);</SCRIPT>\n";
|
|
}
|
|
print "</BODY>\n";
|
|
print "</HTML>\n";
|
|
}
|
|
|
|
|
|
|
|
# Read the bloat file into hash table $h. The hash table is indexed by class names;
|
|
# each entry is a list with the following elements:
|
|
# bytesAlloc Total number of bytes allocated
|
|
# bytesNet Total number of bytes allocated but not deallocated
|
|
# objectsAlloc Total number of objects allocated
|
|
# objectsNet Total number of objects allocated but not deallocated
|
|
# refsAlloc Total number of references AddRef'd
|
|
# refsNet Total number of references AddRef'd but not Released
|
|
# Except for TOTAL, all hash table entries refer to mutually exclusive data.
|
|
# $sizes is a hash table indexed by class names. Each entry of that table contains the class's instance size.
|
|
sub readBloatFile($\%\%) {
|
|
my ($file, $h, $sizes) = @_;
|
|
local $_; # Needed for 'while (<FILE>)' below.
|
|
|
|
my $readSomething = 0;
|
|
open FILE, $file;
|
|
while (<FILE>) {
|
|
if (my ($name, $size, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet) =
|
|
/^\s*(?:\d+)\s+([\w:]+)\s+(\d+)\s+(-?\d+)\s+(\d+)\s+(-?\d+)\s*\([^()]*\)\s*(\d+)\s+(-?\d+)\s*\([^()]*\)\s*$/) {
|
|
my $bytesAlloc;
|
|
if ($name eq "TOTAL") {
|
|
$size = "undefined";
|
|
$bytesAlloc = "undefined";
|
|
} else {
|
|
$bytesAlloc = $objectsAlloc * $size;
|
|
if ($bytesNet != $objectsNet * $size) {
|
|
print STDERR "In '$file', class $name bytesNet != objectsNet * size: $bytesNet != $objectsNet * $size\n";
|
|
}
|
|
}
|
|
print STDERR "Duplicate entry $name in '$file'\n" if $$h{$name};
|
|
$$h{$name} = [$bytesAlloc, $bytesNet, $objectsAlloc, $objectsNet, $refsAlloc, $refsNet];
|
|
|
|
my $oldSize = $$sizes{$name};
|
|
print STDERR "Mismatch of sizes of class $name: $oldSize and $size\n" if defined($oldSize) && $size ne $oldSize;
|
|
$$sizes{$name} = $size;
|
|
$readSomething = 1;
|
|
} elsif (/^\s*(?:\d+)\s+([\w:]+)\s/) {
|
|
print STDERR "Unable to parse '$file' line: $_";
|
|
}
|
|
}
|
|
close FILE;
|
|
print STDERR "No data in '$file'\n" unless $readSomething;
|
|
return $h;
|
|
}
|
|
|
|
|
|
my %sizes; # <class-name> => <instance-size>
|
|
my %tables; # <file-name> => <bloat-table>; see readBloatFile for format of <bloat-table>
|
|
|
|
# Generate the JavaScript source code for the row named $c. $l can contain the initial entries of the row.
|
|
sub genTableRowSource($$) {
|
|
my ($l, $c) = @_;
|
|
my $lastE;
|
|
foreach (@ARGV) {
|
|
my $e = $tables{$_}{$c};
|
|
if (defined($lastE) && !defined($e)) {
|
|
$e = [0,0,0,0,0,0];
|
|
print STDERR "Class $c is defined in an earlier file but not in '$_'\n";
|
|
}
|
|
if (defined $e) {
|
|
if (defined $lastE) {
|
|
for (my $i = 0; $i <= $#$e; $i++) {
|
|
my $n = $$e[$i];
|
|
$l .= ($n eq "undefined" ? "undefined" : $n - $$lastE[$i]) . ",";
|
|
}
|
|
$l .= " ";
|
|
} else {
|
|
$l .= join(",", @$e) . ", ";
|
|
}
|
|
$lastE = $e;
|
|
} else {
|
|
$l .= "0,0,0,0,0,0, ";
|
|
}
|
|
}
|
|
$l .= join(",", @$lastE);
|
|
return "[$l]";
|
|
}
|
|
|
|
|
|
|
|
my $debug;
|
|
my $source;
|
|
my $showMode;
|
|
my $sortColumn;
|
|
my @modeOptions;
|
|
|
|
GetOptions("debug" => \$debug, "source" => \$source, "byte=i" => \$modeOptions[0], "obj=i" => \$modeOptions[1], "ref=i" => \$modeOptions[2]);
|
|
for (my $i = 0; $i != 3; $i++) {
|
|
my $modeOption = $modeOptions[$i];
|
|
if ($modeOption) {
|
|
die "Only one of -byte, -obj, or -ref may be given" if defined $showMode;
|
|
my $nFileColumns = scalar(@ARGV) + 1;
|
|
die "-byte, -obj, or -ref column number out of range" if $modeOption < 0 || $modeOption >= 2 + 2*$nFileColumns;
|
|
$showMode = $i;
|
|
if ($modeOption >= 2) {
|
|
$modeOption -= 2;
|
|
$sortColumn = 2 + $showMode*2;
|
|
if ($modeOption >= $nFileColumns) {
|
|
$sortColumn++;
|
|
$modeOption -= $nFileColumns;
|
|
}
|
|
$sortColumn += $modeOption*6;
|
|
} else {
|
|
$sortColumn = $modeOption;
|
|
}
|
|
}
|
|
}
|
|
unless (defined $showMode) {
|
|
$showMode = 0;
|
|
$sortColumn = 0;
|
|
}
|
|
|
|
# Read all of the bloat files.
|
|
foreach (@ARGV) {
|
|
unless ($tables{$_}) {
|
|
my $f = $_;
|
|
my %table;
|
|
|
|
readBloatFile $_, %table, %sizes;
|
|
$tables{$_} = \%table;
|
|
}
|
|
}
|
|
die "No input" unless %sizes;
|
|
|
|
my @scriptData; # JavaScript source for the tables passed to JavaScript. Each entry is one line of JavaScript.
|
|
my @persistentScriptData; # Same as @scriptData, but persists the page reloads itself.
|
|
|
|
# Print a list of bloat file names.
|
|
push @persistentScriptData, "var nFiles = " . scalar(@ARGV) . ";";
|
|
push @persistentScriptData, "var fileTags = [" . join(", ", map {singleQuoteString substr(fileCoreName($_), -10)} @ARGV) . "];";
|
|
push @persistentScriptData, "var fileNames = [" . join(", ", map {singleQuoteString $_} @ARGV) . "];";
|
|
push @persistentScriptData, "var fileDates = [" . join(", ", map {singleQuoteString localtime fileModDate $_} @ARGV) . "];";
|
|
|
|
# Print the bloat tables.
|
|
push @persistentScriptData, "var totals = " . genTableRowSource('"TOTAL", undefined, ', "TOTAL") . ";";
|
|
push @scriptData, "var classTables = [";
|
|
delete $sizes{"TOTAL"};
|
|
my @classes = sort(keys %sizes);
|
|
for (my $i = 0; $i <= $#classes; $i++) {
|
|
my $c = $classes[$i];
|
|
push @scriptData, genTableRowSource(doubleQuoteString($c).", ".$sizes{$c}.", ", $c) . ($i == $#classes ? "];" : ",");
|
|
}
|
|
|
|
generate(@scriptData, @persistentScriptData, $debug, $source, $showMode, $sortColumn);
|
|
1;
|
|
|
|
|
|
# The source of the eval'd JavaScript follows.
|
|
# Comments starting with // that are alone on a line are stripped by the Perl script.
|
|
__END__
|
|
|
|
// showMode: 0=bytes, 1=objects, 2=references
|
|
var showMode;
|
|
var modeName;
|
|
var modeNameUpper;
|
|
|
|
var sortColumn;
|
|
|
|
// Sort according to the sortColumn. Column 0 is sorted alphabetically in ascending order.
|
|
// All other columns are sorted numerically in descending order, with column 0 used for a secondary sort.
|
|
// Undefined is always listed last.
|
|
function sortCompare(x, y) {
|
|
if (sortColumn) {
|
|
var xc = x[sortColumn];
|
|
var yc = y[sortColumn];
|
|
if (xc < yc || xc === undefined && yc !== undefined) return 1;
|
|
if (yc < xc || yc === undefined && xc !== undefined) return -1;
|
|
}
|
|
|
|
var x0 = x[0];
|
|
var y0 = y[0];
|
|
if (x0 > y0 || x0 === undefined && y0 !== undefined) return 1;
|
|
if (y0 > x0 || y0 === undefined && x0 !== undefined) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Quote special HTML characters in the string.
|
|
function quoteHTML(s) {
|
|
s = s.replace(/&/g, '&');
|
|
// Can't use /</g because HTML interprets '</g' as ending the script!
|
|
s = s.replace(/\x3C/g, '<');
|
|
s = s.replace(/>/g, '>');
|
|
s = s.replace(/ /g, ' ');
|
|
return s;
|
|
}
|
|
|
|
|
|
function writeFileTable(d) {
|
|
d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');
|
|
d.writeln('<TR>\n<TH>Name</TH>\n<TH>File</TH>\n<TH>Date</TH>\n</TR>');
|
|
for (var i = 0; i < nFiles; i++)
|
|
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>');
|
|
d.writeln('</TABLE>');
|
|
}
|
|
|
|
|
|
function writeReloadLink(d, column, s, rowspan) {
|
|
d.write(rowspan == 1 ? '<TH>' : '<TH rowspan='+rowspan+'>');
|
|
if (column != sortColumn)
|
|
d.write('<A href="javascript:reloadSelf('+column+','+showMode+')">');
|
|
d.write(s);
|
|
if (column != sortColumn)
|
|
d.write('</A>');
|
|
d.writeln('</TH>');
|
|
}
|
|
|
|
function writeClassTableRow(d, row, base, modeName) {
|
|
if (modeName) {
|
|
d.writeln('<TR>\n<TH>'+modeName+'</TH>');
|
|
} else {
|
|
d.writeln('<TR>\n<TD><A href="javascript:showRowDetail(\''+row[0]+'\')">'+quoteHTML(row[0])+'</A></TD>');
|
|
var v = row[1];
|
|
d.writeln('<TD class=num>'+(v === undefined ? '' : v)+'</TD>');
|
|
}
|
|
for (var i = 0; i != 2; i++) {
|
|
var c = base + i;
|
|
for (var j = 0; j <= nFiles; j++) {
|
|
v = row[c];
|
|
var style = 'num';
|
|
if (j != nFiles)
|
|
if (v > 0) {
|
|
style = 'pos';
|
|
v = '+'+v;
|
|
} else
|
|
style = 'neg';
|
|
d.writeln('<TD class='+style+'>'+(v === undefined ? '' : v)+'</TD>');
|
|
c += 6;
|
|
}
|
|
}
|
|
d.writeln('</TR>');
|
|
}
|
|
|
|
function writeClassTable(d) {
|
|
var base = 2 + showMode*2;
|
|
|
|
// Make a copy because a sort is destructive.
|
|
var table = classTables.concat();
|
|
table.sort(sortCompare);
|
|
|
|
d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');
|
|
|
|
d.writeln('<TR>');
|
|
writeReloadLink(d, 0, 'Class Name', 2);
|
|
writeReloadLink(d, 1, 'Instance<BR>Size', 2);
|
|
d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated</TH>');
|
|
d.writeln('<TH colspan='+(nFiles+1)+'>'+modeNameUpper+'s allocated but not freed</TH>\n</TR>');
|
|
d.writeln('<TR>');
|
|
for (var i = 0; i != 2; i++) {
|
|
var c = base + i;
|
|
for (var j = 0; j <= nFiles; j++) {
|
|
writeReloadLink(d, c, j == nFiles ? 'Total' : quoteHTML(fileTags[j]), 1);
|
|
c += 6;
|
|
}
|
|
}
|
|
d.writeln('</TR>');
|
|
|
|
writeClassTableRow(d, totals, base, 0);
|
|
for (var r = 0; r < table.length; r++)
|
|
writeClassTableRow(d, table[r], base, 0);
|
|
|
|
d.writeln('</TABLE>');
|
|
}
|
|
|
|
|
|
var modeNames = ["byte", "object", "reference"];
|
|
var modeNamesUpper = ["Byte", "Object", "Reference"];
|
|
var styleSheet = '<STYLE type="TEXT/CSS">\n'+
|
|
'BODY {background-color: #FFFFFF; color: #000000}\n'+
|
|
'.num {text-align: right}\n'+
|
|
'.pos {text-align: right; color: #CC0000}\n'+
|
|
'.neg {text-align: right; color: #009900}\n'+
|
|
'</STYLE>';
|
|
|
|
|
|
function showHead(d) {
|
|
modeName = modeNames[showMode];
|
|
modeNameUpper = modeNamesUpper[showMode];
|
|
d.writeln('<TITLE>'+modeNameUpper+' Bloats</TITLE>');
|
|
d.writeln(styleSheet);
|
|
}
|
|
|
|
function showBody(d) {
|
|
d.writeln('<H1>'+modeNameUpper+' Bloats</H1>');
|
|
writeFileTable(d);
|
|
d.write('<FORM>');
|
|
for (var i = 0; i != 3; i++)
|
|
if (i != showMode) {
|
|
var newSortColumn = sortColumn;
|
|
if (sortColumn >= 2)
|
|
newSortColumn = sortColumn + (i-showMode)*2;
|
|
d.write('<INPUT type="button" value="Show '+modeNamesUpper[i]+'s" onClick="reloadSelf('+newSortColumn+','+i+')">');
|
|
}
|
|
d.writeln('</FORM>');
|
|
d.writeln('<P>The numbers do not include <CODE>malloc</CODE>\'d data such as string contents.</P>');
|
|
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>');
|
|
writeClassTable(d);
|
|
}
|
|
|
|
|
|
function showRowDetail(rowName) {
|
|
var row;
|
|
var i;
|
|
|
|
if (rowName == "TOTAL")
|
|
row = totals;
|
|
else {
|
|
for (i = 0; i < classTables.length; i++)
|
|
if (rowName == classTables[i][0]) {
|
|
row = classTables[i];
|
|
break;
|
|
}
|
|
}
|
|
if (row) {
|
|
var w = window.open("", "ClassTableRowDetails");
|
|
var d = w.document;
|
|
d.open();
|
|
d.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">');
|
|
d.writeln('<HTML>\n<HEAD>\n<TITLE>'+quoteHTML(rowName)+' bloat details</TITLE>');
|
|
d.writeln(styleSheet);
|
|
d.writeln('</HEAD>\n\n<BODY>');
|
|
d.writeln('<H2>'+quoteHTML(rowName)+'</H2>');
|
|
if (row[1] !== undefined)
|
|
d.writeln('<P>Each instance has '+row[1]+' bytes.</P>');
|
|
|
|
d.writeln('<TABLE border=1 cellspacing=1 cellpadding=0>');
|
|
d.writeln('<TR>\n<TH></TH>\n<TH colspan='+(nFiles+1)+'>Allocated</TH>');
|
|
d.writeln('<TH colspan='+(nFiles+1)+'>Allocated but not freed</TH>\n</TR>');
|
|
d.writeln('<TR>\n<TH></TH>');
|
|
for (i = 0; i != 2; i++)
|
|
for (var j = 0; j <= nFiles; j++)
|
|
d.writeln('<TH>'+(j == nFiles ? 'Total' : quoteHTML(fileTags[j]))+'</TH>');
|
|
d.writeln('</TR>');
|
|
|
|
for (i = 0; i != 3; i++)
|
|
writeClassTableRow(d, row, 2+i*2, modeNamesUpper[i]+'s');
|
|
|
|
d.writeln('</TABLE>\n</BODY>\n</HTML>');
|
|
d.close();
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
|
|
function stringSource(s) {
|
|
s = s.replace(/\\/g, '\\\\');
|
|
s = s.replace(/"/g, '\\"');
|
|
s = s.replace(/<\//g, '<\\/');
|
|
return '"'+s+'"';
|
|
}
|
|
|
|
function reloadSelf(n,m) {
|
|
// Need to cache these because globals go away on document.open().
|
|
var sa = srcArray;
|
|
var ss = stringSource;
|
|
var ct = classTables;
|
|
var i;
|
|
|
|
document.open();
|
|
// Uncomment this and comment the document.open() line above to see the reloaded page's source.
|
|
//var w = window.open("", "NewDoc");
|
|
//var d = w.document;
|
|
//var document = new Object;
|
|
//document.write = function () {
|
|
// for (var i = 0; i < arguments.length; i++) {
|
|
// var s = arguments[i].toString();
|
|
// s = s.replace(/&/g, '&');
|
|
// s = s.replace(/\x3C/g, '<');
|
|
// s = s.replace(/>/g, '>');
|
|
// s = s.replace(/ /g, ' ');
|
|
// d.write(s);
|
|
// }
|
|
//};
|
|
//document.writeln = function () {
|
|
// for (var i = 0; i < arguments.length; i++) {
|
|
// var s = arguments[i].toString();
|
|
// s = s.replace(/&/g, '&');
|
|
// s = s.replace(/\x3C/g, '<');
|
|
// s = s.replace(/>/g, '>');
|
|
// s = s.replace(/ /g, ' ');
|
|
// d.write(s);
|
|
// }
|
|
// d.writeln('<BR>');
|
|
//};
|
|
|
|
document.writeln('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">');
|
|
document.writeln('<HTML>\n<HEAD>\n<SCRIPT type="text/javascript">');
|
|
|
|
// Manually copy non-persistent script data
|
|
if (!ct.length)
|
|
document.writeln('var classTables = [];');
|
|
else {
|
|
document.writeln('var classTables = [');
|
|
for (i = 0; i < ct.length; i++) {
|
|
var row = ct[i];
|
|
document.write('[' + ss(row[0]));
|
|
for (var j = 1; j < row.length; j++)
|
|
document.write(',' + row[j]);
|
|
document.writeln(']' + (i == ct.length-1 ? '];' : ','));
|
|
}
|
|
}
|
|
|
|
document.writeln('var srcArray = [');
|
|
for (i = 0; i < sa.length; i++) {
|
|
document.write(ss(sa[i]));
|
|
if (i != sa.length-1)
|
|
document.writeln(',');
|
|
}
|
|
document.writeln('];');
|
|
document.writeln('eval(srcArray.join("\\n"));');
|
|
document.writeln('showMode = '+m+';');
|
|
document.writeln('sortColumn = '+n+';');
|
|
document.writeln('showHead(document);');
|
|
document.writeln('</SCRIPT>\n</HEAD>\n\n<BODY>\n<SCRIPT type="text/javascript">showBody(document);</SCRIPT>\n</BODY>\n</HTML>');
|
|
document.close();
|
|
return undefined;
|
|
}
|