3 # Program: GenerateReport.pl
5 # Synopsis: Summarize a big log file into a table of values, commonly used for
6 # testing. This can generate either a plaintext table, HTML table,
7 # or Latex table, depending on whether the -html or -latex options are
10 # This script reads a report description file to specify the fields
11 # and descriptions for the columns of interest. In reads the raw log
12 # input from stdin and writes the table to stdout.
14 # Syntax: GenerateReport.pl [-html] [-latex] [-graphs] [-csv] <ReportDesc>
18 # Default values for arguments
25 while ($_ = $ARGV[0], /^[-+]/) {
27 last if /^--$/; # Stop processing arguments on --
29 # List command line options here...
30 if (/^-html$/) { $HTML = 1; next; }
31 if (/^-latex$/) { $LATEX = 1; next; }
32 if (/^-graphs$/) { $GRAPHS = 1; next; }
33 if (/^-csv$/) { $CSV = 1; next; }
35 print "Unknown option: $_ : ignoring!\n";
39 # Parameters which may be overriden by the report description file.
42 # The column to sort by, to be overridden as necessary by the report description
45 my $SortNumeric = 0; # Sort numerically or textually?
47 # If the report wants us to trim repeated path prefixes off of the start of the
48 # strings in the first column of the report, we can do that.
49 my $TrimRepeatedPrefix = 0;
50 my $TrimAllDirectories = 0;
52 # Helper functions which may be called by the report description files...
54 my ($Cols, $Col, $NumRows) = @_;
58 $Val += $Cols->[$Col] if ($Cols->[$Col] ne "*");
64 my ($Cols, $Col, @Indices) = @_;
67 foreach $Idx (@Indices) {
68 if ($Cols->[$Col+$Idx] ne "*") {
69 $result += $Cols->[$Col+$Idx];
76 # Check command line arguments...
77 die "Must specify a report description option" if (scalar(@ARGV) < 1);
79 # Read file input in one big gulp...
82 # Read raw data file and split it up into records. Each benchmarks starts with
83 # a line with a >>> prefix
85 my @Records = split />>> ========= /, <STDIN
>;
87 # Delete the first "entry" which is really stuff printed prior to starting the
91 # Read and eval the report description file now. This defines the Fields array
92 # and may potentially modify some of our global settings like the sort key.
94 my $ReportFN = $ARGV[0];
95 #print "Reading report description from $ReportFN\n";
96 open(REPORTDESC
, $ReportFN) or
97 die "Couldn't open report description '$ReportFN'!";
99 # HilightColumns - Filled in by the report if desired in HTML mode. This
100 # contains a column number if the HTML version of the output should highlight a
101 # cell in green/red if it is gt/lt 1.0 by a significant margin.
104 my @LatexColumns; # Filled in by report if it supports Latex mode
105 my %LatexColumnFormat; # Filled in by report if supports latex mode
106 my @Graphs; # Filled in by the report if supports graph mode
108 # Fill in all of the fields from the report description
109 my @Fields = eval <REPORTDESC
>;
113 # Read data into the table of values...
116 foreach $Record (@Records) {
121 if (scalar(@
$Row)) { # An actual value to read?
122 if (ref ($Row->[1])) { # Code to be executed?
123 $Val = &{$Row->[1]}(\
@RowValues, $Col);
124 } else { # Field to be read...
125 $Record =~ m/$Row->[1]/;
129 # If there is a formatting function, run it now...
131 if (scalar(@
$Row) > 2) {
132 $Val = &{$Row->[2]}($Val);
136 } else { # Just add a seperator...
140 push @RowValues, $Val;
145 if ($Record =~ m/Assertion/) {
146 # If an assertion failure occured, print it out.
147 $Assert = sprintf "\n\t\t\t%s", (grep /Assertion/, (split "\n", $Record));
149 push @RowValues, $Assert if (!$HTML);
150 push @Values, [@RowValues];
154 # If the report wants it, we can trim excess cruft off of the beginning of the
155 # first column (which is often a path).
156 if ($TrimRepeatedPrefix and scalar(@Values)) {
157 OuterLoop
: while (1) {
158 # Figure out what the first path prefix is:
159 $Values[0]->[0] =~ m
|^([^/]*/).|;
160 last OuterLoop
if (!defined($1));
162 # Now that we have the prefix, check to see if all of the entries in the
163 # table start with this prefix.
164 foreach $Row (@Values) {
165 last OuterLoop
if ((substr $Row->[0], 0, length $1) ne $1);
168 # If we get here, then all of the entries have the prefix. Remove it now.
169 foreach $Row (@Values) {
170 $Row->[0] = substr $Row->[0], length $1;
175 # If the report wants it, we can trim of all of the directories part of the
177 if ($TrimAllDirectories and scalar(@Values)) {
178 foreach $Row (@Values) {
179 $Row->[0] =~ s
|^.*/||g
;
188 @Values = sort { $lhs = $a->[$SortCol]; $rhs = $b->[$SortCol];
189 $lhs = 0 if ($lhs eq "*");
190 $rhs = 0 if ($rhs eq "*");
191 $lhs <=> $rhs } @Values;
193 @Values = sort { $a->[$SortCol] cmp $b->[$SortCol] } @Values;
195 @Values = reverse @Values if ($SortReverse);
198 # Condense the header into an easier to access array...
202 if (scalar(@
$Row)) { # Non-empty row?
203 push @Header, $Row->[0];
204 } else { # Empty row, just add seperator
216 $Attrs = " bgcolor='black' width='1'";
219 # If the user requested that we highlight this column, check to see what
220 # number it is. If it is > 1.05, we color it green, < 0.95 we use red.
221 # If it's not a number, ignore it.
222 if ($HilightColumns{$ColNo}) {
223 if ($Str =~ m/^([0-9]+).?[0-9.]*$/) {
225 $Attrs = " bgcolor='#FF7070'";
226 } elsif ($Str <= 0.95) {
227 $Attrs = " bgcolor='#FFAAAA'";
228 } elsif ($Str >= 1.15) {
229 $Attrs = " bgcolor='#80FF80'";
230 } elsif ($Str >= 1.05) {
231 $Attrs = " bgcolor='#CCFFCC'";
235 if (!$IsWhite && $Attrs eq "") {
236 # If it's not already white, make it white now.
237 $Attrs = " bgcolor=white";
241 print "<td$Attrs>$Str</td>";
245 print "<table border='0' cellspacing='0' cellpadding='0'>\n";
246 print "<tr bgcolor=#FFCC99>\n";
248 $_ = "<center><b><a href=\"#$_\">$_</a></b></center>"
252 print "\n</tr><tr bgcolor='black' height=1>";
255 foreach $Row (@Values) {
257 $IsWhite = ++$RowCount <= 2;
258 print "<tr bgcolor='" . ($IsWhite ?
"white" : "#CCCCCC") . "'>\n";
259 $RowCount = 0 if ($RowCount > 3);
261 map { printCell
($_, $ColCount++, $IsWhite); } @
$Row;
264 print "\n</table>\n";
265 } elsif ($GRAPHS) { # Graph output...
266 print "Generating gnuplot data files:\n";
268 foreach $Graph (@Graphs) {
270 my $Type = shift @Graph;
271 die "Only scatter graphs supported right now, not '$Type'!"
272 if ($Type ne "scatter");
274 my $Filename = shift @Graph;
276 print "Writing '$Filename'...\n";
277 open (FILE
, ">$Filename") or die ("Could not open file '$Filename'!");
279 my ($XCol, $YCol) = @Graph;
280 foreach $Row (@Values) {
281 print FILE
$$Row[$XCol] . "\t" . $$Row[$YCol] . "\n";
288 # Add the header for the report to the table after sorting...
289 unshift @Values, [@Header];
292 # Figure out how wide each field should be...
294 my @FieldWidths = (0) x
scalar(@Fields);
295 foreach $Value (@Values) {
296 for ($i = 0; $i < @
$Value-1; $i++) {
297 if (length($$Value[$i]) > $FieldWidths[$i]) {
298 $FieldWidths[$i] = length($$Value[$i])
305 # Print out the latexified table...
307 shift @Values; # Don't print the header...
309 # Make sure the benchmark name field is wide enough for any aliases.
310 foreach $Name (@LatexRowMapOrder) {
311 $FieldWidths[0] = length $Name if (length($Name) > $FieldWidths[0]);
314 # Print out benchmarks listed in the LatexRowMapOrder
315 for ($i = 0; $i < @LatexRowMapOrder; $i += 2) {
316 my $Name = $LatexRowMapOrder[$i];
320 # Output benchmark name...
321 printf "%-$FieldWidths[0]s", $LatexRowMapOrder[$i+1];
323 # Find the row that this benchmark name corresponds to...
324 foreach $Row (@Values) {
325 if ($Row->[0] eq $Name) {
326 for $ColNum (@LatexColumns) {
327 # Print a seperator...
328 my $Val = $Row->[$ColNum];
329 if (exists $LatexColumnFormat{$ColNum}) {
330 # If a column format routine has been specified, run it now...
331 $Val = &{$LatexColumnFormat{$ColNum}}($Val);
334 # Escape illegal latex characters
335 $Val =~ s/([%#])/\\$1/g;
337 printf " & %-$FieldWidths[$ColNum]s", $Val;
342 print "UNKNOWN Benchmark name: " . $Name;
347 } elsif ($CSV && scalar(@LatexRowMapOrder)) {
349 # Print out the table as csv in the row-order specified by LatexRowMapOrder
351 for ($i = 0; $i < @LatexRowMapOrder; $i += 2) {
352 my $Name = $LatexRowMapOrder[$i];
356 # Output benchmark name.
357 printf "$LatexRowMapOrder[$i+1]";
359 # Find the row that this benchmark name corresponds to.
360 foreach $Row (@Values) {
361 if ($Row->[0] eq $Name) {
362 for ($j = 1; $j < @
$Row-1; $j++) {
368 print "UNKNOWN Benchmark name: " . $Name;
376 # Print out the table as csv
379 foreach $Value (@Values) {
381 for ($i = 1; $i < @
$Value-1; $i++) {
382 print ",$$Value[$i]" if ($$Value[$i] ne "|");
385 # Print an extra column for the header.
386 print ",$$Value[@$Value-1]";
393 # Print out the table in plaintext format now...
395 foreach $Value (@Values) {
396 for ($i = 0; $i < @
$Value-1; $i++) {
397 printf "%-$FieldWidths[$i]s ", $$Value[$i];
400 # Print the assertion message if existant...
401 print "$$Value[@$Value-1]\n";