#!/usr/bin/perl -w # # Program: GenerateReport.pl # # Synopsis: Summarize a big log file into a table of values, commonly used for # testing. This can generate either a plaintext table, HTML table, # or Latex table, depending on whether the -html or -latex options are # specified. # # This script reads a report description file to specify the fields # and descriptions for the columns of interest. In reads the raw log # input from stdin and writes the table to stdout. # # Syntax: GenerateReport.pl [-html] [-latex] [-graphs] # < Input > Output # # Default values for arguments my $HTML = 0; my $LATEX = 0; my $GRAPHS = 0; # Parse arguments... while ($_ = $ARGV[0], /^[-+]/) { shift; last if /^--$/; # Stop processing arguments on -- # List command line options here... if (/^-html$/) { $HTML = 1; next; } if (/^-latex$/) { $LATEX = 1; next; } if (/^-graphs$/) { $GRAPHS = 1; next; } print "Unknown option: $_ : ignoring!\n"; } # # Parameters which may be overriden by the report description file. # # The column to sort by, to be overridden as necessary by the report description my $SortCol = 0; my $SortReverse = 0; my $SortNumeric = 0; # Sort numerically or textually? # If the report wants us to trim repeated path prefixes off of the start of the # strings in the first column of the report, we can do that. my $TrimRepeatedPrefix = 0; # Helper functions which may be called by the report description files... sub SumCols { my ($Cols, $Col, $NumRows) = @_; $Val = 0; while ($NumRows) { $Col--; $NumRows--; $Val += $Cols->[$Col] if ($Cols->[$Col] ne "*"); } return $Val; } # Check command line arguments... die "Must specify a report description option" if (scalar(@ARGV) < 1); # Read file input in one big gulp... undef $/; # Read raw data file and split it up into records. Each benchmarks starts with # a line with a >>> prefix # my @Records = split />>> ========= /, ; # Delete the first "entry" which is really stuff printed prior to starting the # first test. shift @Records; # Read and eval the report description file now. This defines the Fields array # and may potentially modify some of our global settings like the sort key. # my $ReportFN = $ARGV[0]; #print "Reading report description from $ReportFN\n"; open(REPORTDESC, $ReportFN) or die "Couldn't open report description '$ReportFN'!"; my @LatexColumns; # Filled in by report if it supports Latex mode my %LatexColumnFormat; # Filled in by report if supports latex mode my @Graphs; # Filled in by the report if supports graph mode # Fill in all of the fields from the report description my @Fields = eval ; # # Read data into the table of values... # my @Values; foreach $Record (@Records) { my @RowValues; my $Col = 0; for $Row (@Fields) { my $Val = "*"; if (scalar(@$Row)) { # An actual value to read? if (ref ($Row->[1])) { # Code to be executed? $Val = &{$Row->[1]}(\@RowValues, $Col); } else { # Field to be read... $Record =~ m/$Row->[1]/; if (!defined($1)) { $Val = "*"; } else { # If there is a formatting function, run it now... $Val = $1; if (scalar(@$Row) > 2) { $Val = &{$Row->[2]}($Val); } } } } else { # Just add a seperator... $Val = "|"; } push @RowValues, $Val; $Col++; } my $Assert = ""; if ($Record =~ m/Assertion/) { # If an assertion failure occured, print it out. $Assert = sprintf "\n\t\t\t%s", (grep /Assertion/, (split "\n", $Record)); } push @RowValues, $Assert if (!$HTML); push @Values, [@RowValues]; } # If the report wants it, we can trim excess cruft off of the beginning of the # first column (which is often a path). if ($TrimRepeatedPrefix and scalar(@Values)) { OuterLoop: while (1) { # Figure out what the first path prefix is: $Values[0]->[0] =~ m|([^/]+/).|; last OuterLoop if (!defined($1)); # Now that we have the prefix, check to see if all of the entries in the # table start with this prefix. foreach $Row (@Values) { last OuterLoop if ((substr $Row->[0], 0, length $1) ne $1); } # If we get here, then all of the entries have the prefix. Remove it now. foreach $Row (@Values) { $Row->[0] = substr $Row->[0], length $1; } } } # # Sort table now... # if ($SortNumeric) { @Values = sort { $a->[$SortCol] <=> $b->[$SortCol] } @Values; } else { @Values = sort { $a->[$SortCol] cmp $b->[$SortCol] } @Values; } @Values = reverse @Values if ($SortReverse); # # Condense the header into an easier to access array... # my @Header; for $Row (@Fields) { if (scalar(@$Row)) { # Non-empty row? push @Header, $Row->[0]; } else { # Empty row, just add seperator push @Header, "|"; } } if ($HTML) { sub printCell { my $Str = shift; if ($Str eq '|') { print ""; } else { print "
$Str
\n"; }; ""; } sub printLine { print "\n"; } print "\n"; print "\n"; map { print ""; $_ = "
$_
" if $_ ne "|"; printCell $_ } @Header; print ""; print "\n"; print "" x (2*@Header+1); print "\n"; my $RowCount = 0; foreach $Row (@Values) { my $IsWhite; $IsWhite = ++$RowCount <= 2; print "\n"; $RowCount = 0 if ($RowCount > 3); map { printLine($IsWhite); printCell $_ } @$Row; printLine($IsWhite); print "\n\n"; } print "\n
\n"; } elsif ($GRAPHS) { # Graph output... print "Generating gnuplot data files:\n"; my $GraphNo = 0; foreach $Graph (@Graphs) { my @Graph = @$Graph; my $Type = shift @Graph; die "Only scatter graphs supported right now, not '$Type'!" if ($Type ne "scatter"); my $Filename = shift @Graph; print "Writing '$Filename'...\n"; open (FILE, ">$Filename") or die ("Could not open file '$Filename'!"); my ($XCol, $YCol) = @Graph; foreach $Row (@Values) { print FILE $$Row[$XCol] . "\t" . $$Row[$YCol] . "\n"; } close FILE; ++$GraphNo; } } else { # Add the header for the report to the table after sorting... unshift @Values, [@Header]; # # Figure out how wide each field should be... # my @FieldWidths = (0) x scalar(@Fields); foreach $Value (@Values) { for ($i = 0; $i < @$Value-1; $i++) { if (length($$Value[$i]) > $FieldWidths[$i]) { $FieldWidths[$i] = length($$Value[$i]) } } } if ($LATEX) { # # Print out the latexified table... # shift @Values; # Don't print the header... # Make sure the benchmark name field is wide enough for any aliases. foreach $Name (@LatexRowMapOrder) { $FieldWidths[0] = length $Name if (length($Name) > $FieldWidths[0]); } # Print out benchmarks listed in the LatexRowMapOrder for ($i = 0; $i < @LatexRowMapOrder; $i += 2) { my $Name = $LatexRowMapOrder[$i]; if ($Name eq '-') { print "\\hline\n"; } else { # Output benchmark name... printf "%-$FieldWidths[0]s", $LatexRowMapOrder[$i+1]; # Find the row that this benchmark name corresponds to... foreach $Row (@Values) { if ($Row->[0] eq $Name) { for $ColNum (@LatexColumns) { # Print a seperator... my $Val = $Row->[$ColNum]; if (exists $LatexColumnFormat{$ColNum}) { # If a column format routine has been specified, run it now... $Val = &{$LatexColumnFormat{$ColNum}}($Val); } # Escape illegal latex characters $Val =~ s/([%#])/\\$1/g; printf " & %-$FieldWidths[$ColNum]s", $Val; } goto Done; } } print "UNKNOWN Benchmark name: " . $Name; Done: print "\\\\\n"; } } } else { # # Print out the table in plaintext format now... # foreach $Value (@Values) { for ($i = 0; $i < @$Value-1; $i++) { printf "%-$FieldWidths[$i]s ", $$Value[$i]; } # Print the assertion message if existant... print "$$Value[@$Value-1]\n"; } } }