#!/usr/bin/perl5 -- # -*-Perl-*- $Usage = "Usage: $0 [-l][-i ignore_test][-N ntrials] logfile ..."; $TIMEOUT_TM = 50.0; # times > this are timeouts $avgonly = 1; # print averages only, or long list $ntrials = 20; # number of trials to use from logfile $tnamelen = 24; # test name field length # process options while ($ARGV[0] =~ /^-.*/) { if ($ARGV[0] eq "-i") { $ignore = $ARGV[1]; $ignore_tests[$#ignore_tests + 1] = $ignore; shift; shift; next; } if ($ARGV[0] eq "-l") { $avgonly = 0; shift; next; } if ($ARGV[0] =~ /^-N/) { $ntrials = $ARGV[1]; die "invalid number $ntrials\n$Usage\n" if $ntrials <= 0; shift; shift; next; } die "invalid flag: $ARGV[0]\n$Usage\n"; } die $Usage if $#ARGV < 0; # read in each of the logfiles foreach $f ( @ARGV ) { # %timesref is an associative array indexed by the # logfile name. The values are references to another # associative array indexed by test names. The values # of this array are strings which contain a space-separated # list of test times for this test from this logfile. $timesref{$f} = &read_logfile($f); $logfiles[$#logfiles + 1] = $f; } $avgonly ? &printavg : &printlong; exit; # Print out the average summary sub printavg { # inter column space $ics = " "; # print column keys foreach $i ( 0..$#logfiles ) { $id = $logfiles[$i]; $id =~ s+^.*/++; # just print basename $id =~ s/^log\.//; printf ' ' x $tnamelen . $ics . "%d = %s\n", $i, $id; } print "\n"; # print column headers print ' ' x $tnamelen, $ics, " 0 "; foreach $i ( 1..$#logfiles ) { printf $ics . " %2d ", $i; } print "\n"; # print column ------- print ' ' x $tnamelen, $ics, "-----"; foreach $i ( 1..$#logfiles ) { print $ics, '-' x 14; } print "\n"; $baselog = shift @logfiles; $basetm = $timesref{$baselog}; # The keys of %alltests are all the unique test names # from all the log files. Note that a test name may # not appear in every logfile. foreach $t (sort keys %alltests) { printf "%-$tnamelen.$tnamelen" . "s", $t; @times = split(' ', $$basetm{$t}); $baseav = &average(@times); printf $ics . "%5.2f", $baseav if $baseav; printf $ics . " N/A " if !$baseav; $both_avs = 0; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); $av = &average(@times); # If we have both averages, compute percent change if ($baseav && $av) { $pchg = 100.0 * ($av - $baseav) / $baseav; printf $ics . "%5.2f (%5.1f%%)", $av, $pchg; $both_avs++; # No base value? Print value without percent change. } elsif ($av) { printf $ics . "%5.2f ( N/A )", $av; # No value for this test from this logfile. } else { printf $ics . " N/A ( N/A )"; } } print "\n"; # If we had both averages for all of the logfiles # add the averages to each of the running totals. if ($both_avs == $#logfiles + 1) { $basetot += $baseav; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); $av = &average(@times); $avtot{$log} += $av; } } } print "\n"; printf "%-" . $tnamelen . "s", "TOTAL"; printf ' ' x (length($ics)-1) . "%6.2f", $basetot; for $log (@logfiles) { $pchg = 100.0 * ($avtot{$log} - $basetot) / $basetot; printf ' ' x (length($ics)-1) . "%6.2f (%5.1f%%)", $avtot{$log}, $pchg; } print "\n"; } sub printlong { # inter column space $ics = " "; # print column keys foreach $i ( 0..$#logfiles ) { $id = $logfiles[$i]; $id =~ s/^log\.//; printf ' ' x 14 . $ics . "%d = %s\n", $i, $id; } print "\n"; # print column headers print ' ' x 14, $ics, " 0 "; foreach $i ( 1..$#logfiles ) { printf $ics . " %2d ", $i; } print "\n"; # print column ------- print ' ' x 14, $ics, "-----"; foreach $i ( 1..$#logfiles ) { print $ics, '-' x 14; } print "\n"; $baselog = shift @logfiles; $basetm = $timesref{$baselog}; # The keys of %alltests are all the unique test names # from all the log files. Note that a test name may # not appear every logfile. foreach $t (sort keys %alltests) { # print test name printf "%s\n", $t; @times = split(' ', $$basetm{$t}); ($basehi, $baselo, $basetot) = ×_hilotot(@times); $basen = $#times + 1; $baseav = $basen ? $basetot / $basen : 0; $basestdev = &standard_deviation($baseav, @times); # ================ # Print Entries printf " %-10s", "Entries:"; printf $ics . "%2d ", $basen; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); $ntimes = $#times + 1; printf $ics . "%2d ", $ntimes; } print "\n"; # ================ # Print High printf " %-10s", "High:"; printf $ics . "%5.2f", $basehi; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); ($hi, $lo, $tot) = ×_hilotot(@times); if ($basehi && $hi) { $pchg = 100.0 * ($hi - $basehi) / $basehi; printf $ics . "%5.2f (%5.1f%%)", $hi, $pchg; } else { printf $ics . "%5.2f ( N/A )", $hi; } } print "\n"; # ================ # Print Low printf " %-10s", "Low:"; printf $ics . "%5.2f", $baselo; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); ($hi, $lo, $tot) = ×_hilotot(@times); if ($baselo && $lo) { $pchg = 100.0 * ($lo - $baselo) / $baselo; printf $ics . "%5.2f (%5.1f%%)", $lo, $pchg; } else { printf $ics . "%5.2f ( N/A )", $lo; } } print "\n"; # ================ # Print Average printf " %-10s", "Average:"; printf $ics . "%5.2f", $baseav; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); $av = &average(@times); if ($baseav && $av) { $pchg = 100.0 * ($av - $baseav) / $baseav; printf $ics . "%5.2f (%5.1f%%)", $av, $pchg; } else { printf $ics . "%5.2f ( N/A )", $av; } } print "\n"; # ================ # Print Standard Deviation printf " %-10s", "St Dev:"; printf $ics . "%5.2f", $basestdev; foreach $log (@logfiles) { @times = split(' ', ${ $timesref{$log} }{$t}); $stdev = &standard_deviation(&average(@times), @times); if ($basestdev && $stdev) { $pchg = 100.0 * ($stdev - $basestdev) / $basestdev; printf $ics . "%5.2f (%5.1f%%)", $stdev, $pchg; } else { printf $ics . "%5.2f ( N/A )", $stdev; } } print "\n"; } } # Walk through the logfile. We find lines of the form # # $ Software Manager # Startup time: 9527 milliseconds # $ Capture Tool # Startup time: 12786 milliseconds # ... # We gather test names and startup time values into the # %timeslist array and return a reference to it. # # We also fill in the global %alltests array with all the # test names across all the files. We'll use the keys of # %alltests to drive the main output loop once all the # logfiles have been read. sub read_logfile { local($logfile) = @_[0]; local(%timeslist, $test, $starttm, @stline, %trials); open(logfile) || die "Can't open $logfile: $!\n"; logproc: while () { # new test name if (/^\$\s/) { chop; # kill \n s/^\$\s*//; $test = $_; # remember test name next; } # grab the startup time for this test if (/^Startup/) { # If this test is in the list of tests we # want to ignore for this report, skip it. next if grep(/^\Q$test\E$/, @ignore_tests); # If we only want to process a specified number # of trials from the logfile (from cmdline -N option), # stop processing logfile if any one test case # reaches the limit. last logproc if $ntrials && ++$trials{$test} > $ntrials; @stline = split; $starttm = $stline[2] / 1000; # push on this time (if valid) $timeslist{$test} .= ' ' . $starttm if $starttm < $TIMEOUT_TM; # Remember test name in keys of global %alltests $alltests{$test}++; next; } } close logfile; return \%timeslist; } # pass over this list of times and # calculate interesting items. sub times_hilotot { local($hitime, $lotime, $totime); $totime = 0; foreach $tm (@_) { $hitime = $tm if $tm > $hitime; $lotime = $tm if $tm < $lotime || !$lotime; $totime += $tm; } return ($hitime, $lotime, $totime); } # compute average of list of numbers sub average { local($n, $total); local($cnt) = $#_ + 1; $total = 0; foreach $n (@_) { $total += $n; } return $cnt ? $total / $cnt : 0; } # standard_deviation($average, @numbers) sub standard_deviation { local($av) = shift; local($n, $rsum, $resid); local($cnt) = $#_ + 1; return 0 if $cnt <= 1; # stdev for 0,1 samples? # sum the residuals $rsum = 0; foreach $n (@_) { $resid = $n - $av; $rsum += $resid * $resid; } return sqrt($rsum / $cnt); }