1*5759b3d2Safresh1#!perl -w 2*5759b3d2Safresh1# 3*5759b3d2Safresh1# harness-timer-report.pl 4*5759b3d2Safresh1# 5*5759b3d2Safresh1# - read in the time-annotated outputs of 6*5759b3d2Safresh1# "env HARNESS_TIMER=1 make test" or 7*5759b3d2Safresh1# "make test_harness" 8*5759b3d2Safresh1# - convert the milliseconds to seconds 9*5759b3d2Safresh1# - compute a couple of derived values 10*5759b3d2Safresh1# - cpu: the sum of 'self' and 'kids' 11*5759b3d2Safresh1# - ratio of the wallclock and the cpu 12*5759b3d2Safresh1# - optionally show header, the sum, or the max of each colum 13*5759b3d2Safresh1# - sort the rows in various ways 14*5759b3d2Safresh1# - default ordering by 'cpu' seconds 15*5759b3d2Safresh1# - optionally scale the column values by either the sum or the max 16*5759b3d2Safresh1# - optionally display only rows that have rows of at least / at most a limit 17*5759b3d2Safresh1# 18*5759b3d2Safresh1# The --sort option has a few canned sorting rules. If those are 19*5759b3d2Safresh1# not to your liking, there is always sort(1). 20*5759b3d2Safresh1# 21*5759b3d2Safresh1# Example usages: 22*5759b3d2Safresh1# 23*5759b3d2Safresh1# perl harness-timer-report.pl log 24*5759b3d2Safresh1# perl harness-timer-report.pl --sort=wall log 25*5759b3d2Safresh1# perl harness-timer-report.pl --scale=sum log 26*5759b3d2Safresh1# perl harness-timer-report.pl --scale=sum --min=0.01 log 27*5759b3d2Safresh1# perl harness-timer-report.pl --show=header,max,sum log 28*5759b3d2Safresh1# perl harness-timer-report.pl --min=wall=10 log 29*5759b3d2Safresh1 30*5759b3d2Safresh1use strict; 31*5759b3d2Safresh1use warnings; 32*5759b3d2Safresh1 33*5759b3d2Safresh1use File::Basename qw[basename]; 34*5759b3d2Safresh1 35*5759b3d2Safresh1our $ME = basename($0); 36*5759b3d2Safresh1 37*5759b3d2Safresh1use Getopt::Long; 38*5759b3d2Safresh1 39*5759b3d2Safresh1sub usage { 40*5759b3d2Safresh1 die <<__EOF__; 41*5759b3d2Safresh1$ME: Usage: 42*5759b3d2Safresh1$ME [--scale=[sum|max]] 43*5759b3d2Safresh1 [--sort=[cpu|wall|ratio|self|kids|test|name]] 44*5759b3d2Safresh1 [--show=header,sum,max] 45*5759b3d2Safresh1 [--min=[[cpu|wall|ratio|self|kids]=value,...]] 46*5759b3d2Safresh1 [--max=[[cpu|wall|ratio|self|kids]=value,...]] 47*5759b3d2Safresh1 [--order] 48*5759b3d2Safresh1 [--help|--usage] 49*5759b3d2Safresh1 [logfile] 50*5759b3d2Safresh1 51*5759b3d2Safresh1The --order includes the original test order as the last column. 52*5759b3d2Safresh1The logfile default is STDIN. 53*5759b3d2Safresh1__EOF__ 54*5759b3d2Safresh1} 55*5759b3d2Safresh1 56*5759b3d2Safresh1my %Opt; 57*5759b3d2Safresh1usage() 58*5759b3d2Safresh1 unless 59*5759b3d2Safresh1 GetOptions( 60*5759b3d2Safresh1 'scale=s' => \$Opt{scale}, 61*5759b3d2Safresh1 'sort=s' => \$Opt{sort}, 62*5759b3d2Safresh1 'show=s' => \$Opt{show}, 63*5759b3d2Safresh1 'min=s' => \$Opt{min}, 64*5759b3d2Safresh1 'max=s' => \$Opt{max}, 65*5759b3d2Safresh1 'order' => \$Opt{order}, 66*5759b3d2Safresh1 'help|usage' => \$Opt{help}, 67*5759b3d2Safresh1 ); 68*5759b3d2Safresh1usage() if $Opt{help}; 69*5759b3d2Safresh1 70*5759b3d2Safresh1my %SHOW; 71*5759b3d2Safresh1if (defined $Opt{show}) { 72*5759b3d2Safresh1 for my $s (split(/,/, $Opt{show})) { 73*5759b3d2Safresh1 if ($s =~ /^(header|sum|max)$/) { 74*5759b3d2Safresh1 $SHOW{$s}++; 75*5759b3d2Safresh1 } else { 76*5759b3d2Safresh1 die "$ME: Unexpected --show='$s'\n"; 77*5759b3d2Safresh1 } 78*5759b3d2Safresh1 } 79*5759b3d2Safresh1} 80*5759b3d2Safresh1my %MIN; 81*5759b3d2Safresh1if (defined $Opt{min}) { 82*5759b3d2Safresh1 for my $s (split(/,/, $Opt{min})) { 83*5759b3d2Safresh1 if ($s =~ /^(wall|cpu|kids|self|ratio)=(\d+(?:\.\d+)?)$/) { 84*5759b3d2Safresh1 $MIN{$1} = $2; 85*5759b3d2Safresh1 } else { 86*5759b3d2Safresh1 die "$ME: Unexpected --min='$s'\n"; 87*5759b3d2Safresh1 } 88*5759b3d2Safresh1 } 89*5759b3d2Safresh1} 90*5759b3d2Safresh1my %MAX; 91*5759b3d2Safresh1if (defined $Opt{max}) { 92*5759b3d2Safresh1 for my $s (split(/,/, $Opt{max})) { 93*5759b3d2Safresh1 if ($s =~ /^(wall|cpu|kids|self|ratio)=(\d+(?:\.\d+)?)$/) { 94*5759b3d2Safresh1 $MAX{$1} = $2; 95*5759b3d2Safresh1 } else { 96*5759b3d2Safresh1 die "$ME: Unexpected --max='$s'\n"; 97*5759b3d2Safresh1 } 98*5759b3d2Safresh1 } 99*5759b3d2Safresh1} 100*5759b3d2Safresh1 101*5759b3d2Safresh1use List::Util qw[max]; 102*5759b3d2Safresh1 103*5759b3d2Safresh1my ($sa, $sb, $sc, $sd, $se); 104*5759b3d2Safresh1my ($ma, $mb, $mc, $md, $me); 105*5759b3d2Safresh1 106*5759b3d2Safresh1my $logfn; 107*5759b3d2Safresh1my $logfh; 108*5759b3d2Safresh1if (@ARGV == 1) { 109*5759b3d2Safresh1 $logfn = $ARGV[0]; 110*5759b3d2Safresh1 open($logfh, "<", $logfn) or die "$ME: Failed to open logfn: $logfn\n"; 111*5759b3d2Safresh1} elsif (@ARGV == 0) { 112*5759b3d2Safresh1 $logfn = "-"; 113*5759b3d2Safresh1 $logfh = *STDIN; 114*5759b3d2Safresh1} else { 115*5759b3d2Safresh1 die "$ME: Unexpected logfile arguments: @ARGV\n"; 116*5759b3d2Safresh1} 117*5759b3d2Safresh1 118*5759b3d2Safresh1my $order = 0; 119*5759b3d2Safresh1my @t; 120*5759b3d2Safresh1 121*5759b3d2Safresh1while (<$logfh>) { 122*5759b3d2Safresh1 my ($test, $wall, $self, $kids); 123*5759b3d2Safresh1 # Output of "env HARNESS_TIMER=1 make test": 124*5759b3d2Safresh1 # t/re/pat ....................................................... ok 2876 ms 2660 ms 210 ms 125*5759b3d2Safresh1 if (m{^#\s+(\S+)\s+\.+\s+ok\s+(\d+)\s+ms\s+(\d+)\s+ms\s+(\d+)\s+ms$}) { 126*5759b3d2Safresh1 ($test, $wall, $self, $kids) = ($1, $2, $3, $4); 127*5759b3d2Safresh1 } 128*5759b3d2Safresh1 # Output of "env HARNESS_TIMER=1 make test_harness": 129*5759b3d2Safresh1 # [08:26:11] base/cond.t ........................................................ ok 2 ms ( 0.00 usr 0.00 sys + 0.00 cusr 0.00 csys = 0.00 CPU) 130*5759b3d2Safresh1 if (m{^\[.+?\]+\s+(\S+)\s+\.+\s+ok\s+(\d+)\s+ms\s+\(\s*(\d+\.\d+)\s+usr\s+\s+(\d+\.\d+)\s+sys\s+\+\s+(\d+\.\d+)\s+cusr\s+(\d+\.\d+)\s+csys\s+=\s+(\d+\.\d+)\s+CPU\)}) { 131*5759b3d2Safresh1 $test = $1; 132*5759b3d2Safresh1 $wall = $2; 133*5759b3d2Safresh1 $self = $3 + $4; 134*5759b3d2Safresh1 $kids = $5 + $6; 135*5759b3d2Safresh1 $test =~ s{^\.\./}{}; # "../lib/foo" -> "../lib/foo" 136*5759b3d2Safresh1 } 137*5759b3d2Safresh1 next unless defined $test && defined $wall && $wall > 0; 138*5759b3d2Safresh1 # Milliseconds to seconds. 139*5759b3d2Safresh1 $wall /= 1000; 140*5759b3d2Safresh1 $self /= 1000; 141*5759b3d2Safresh1 $kids /= 1000; 142*5759b3d2Safresh1 my $cpu = $self + $kids; 143*5759b3d2Safresh1 my $ratio = $cpu / $wall; 144*5759b3d2Safresh1 push @t, [ $test, $wall, $self, $kids, $cpu, $ratio, $order++ ]; 145*5759b3d2Safresh1 $sa += $wall; 146*5759b3d2Safresh1 $sb += $self; 147*5759b3d2Safresh1 $sc += $kids; 148*5759b3d2Safresh1 $sd += $cpu; 149*5759b3d2Safresh1 $ma = max($wall, $ma // $wall); 150*5759b3d2Safresh1 $mb = max($self, $mb // $self); 151*5759b3d2Safresh1 $mc = max($kids, $mc // $kids); 152*5759b3d2Safresh1 $md = max($cpu, $md // $cpu); 153*5759b3d2Safresh1 $me = max($ratio, $md // $ratio); 154*5759b3d2Safresh1} 155*5759b3d2Safresh1 156*5759b3d2Safresh1die "$ME: No input detected in '$logfn'\n" unless @t; 157*5759b3d2Safresh1 158*5759b3d2Safresh1# Compute the sum for the ratio only after the loop. 159*5759b3d2Safresh1$se = $sd / $sa; 160*5759b3d2Safresh1 161*5759b3d2Safresh1my %SORTER = 162*5759b3d2Safresh1 ( 163*5759b3d2Safresh1 'cpu' => 164*5759b3d2Safresh1 sub { $b->[4] <=> $a->[4] || 165*5759b3d2Safresh1 $b->[1] <=> $a->[1] || 166*5759b3d2Safresh1 $a->[0] cmp $b->[0] }, 167*5759b3d2Safresh1 'wall' => 168*5759b3d2Safresh1 sub { $b->[1] <=> $a->[1] || 169*5759b3d2Safresh1 $b->[4] <=> $a->[4] || 170*5759b3d2Safresh1 $a->[0] cmp $b->[0] }, 171*5759b3d2Safresh1 'ratio' => 172*5759b3d2Safresh1 sub { $b->[5] <=> $a->[5] || 173*5759b3d2Safresh1 $b->[4] <=> $a->[4] || 174*5759b3d2Safresh1 $b->[1] <=> $a->[1] || 175*5759b3d2Safresh1 $a->[0] cmp $b->[0] }, 176*5759b3d2Safresh1 'self' => 177*5759b3d2Safresh1 sub { $b->[2] <=> $a->[2] || 178*5759b3d2Safresh1 $b->[3] <=> $a->[3] || 179*5759b3d2Safresh1 $a->[0] cmp $b->[0] }, 180*5759b3d2Safresh1 'kids' => 181*5759b3d2Safresh1 sub { $b->[3] <=> $a->[3] || 182*5759b3d2Safresh1 $b->[2] <=> $a->[2] || 183*5759b3d2Safresh1 $a->[0] cmp $b->[0] }, 184*5759b3d2Safresh1 'test' => 185*5759b3d2Safresh1 sub { $a->[6] <=> $b->[6] }, 186*5759b3d2Safresh1 'name' => 187*5759b3d2Safresh1 sub { $a->[0] cmp $b->[0] }, 188*5759b3d2Safresh1 ); 189*5759b3d2Safresh1my $sorter; 190*5759b3d2Safresh1 191*5759b3d2Safresh1$Opt{sort} //= 'cpu'; 192*5759b3d2Safresh1 193*5759b3d2Safresh1die "$ME: Unexpected --sort='$Opt{sort}'\n" 194*5759b3d2Safresh1 unless defined $SORTER{$Opt{sort}}; 195*5759b3d2Safresh1 196*5759b3d2Safresh1@t = sort { $SORTER{$Opt{sort}}->() } @t; 197*5759b3d2Safresh1 198*5759b3d2Safresh1if (defined $Opt{scale}) { 199*5759b3d2Safresh1 my ($ta, $tb, $tc, $td, $te) = 200*5759b3d2Safresh1 $Opt{scale} eq 'sum' ? 201*5759b3d2Safresh1 ($sa, $sb, $sc, $sd, $se) : 202*5759b3d2Safresh1 $Opt{scale} eq 'max' ? 203*5759b3d2Safresh1 ($ma, $mb, $mc, $md, $me) : 204*5759b3d2Safresh1 die "$ME: Unexpected --scale='$Opt{scale}'"; 205*5759b3d2Safresh1 206*5759b3d2Safresh1 my @u; 207*5759b3d2Safresh1 for my $t (@t) { 208*5759b3d2Safresh1 push @u, [ $t->[0], 209*5759b3d2Safresh1 $t->[1] / $ta, $t->[2] / $tb, 210*5759b3d2Safresh1 $t->[3] / $tc, $t->[4] / $td, 211*5759b3d2Safresh1 $t->[5] / $te, $t->[6] ]; 212*5759b3d2Safresh1 } 213*5759b3d2Safresh1 @t = @u; 214*5759b3d2Safresh1} 215*5759b3d2Safresh1 216*5759b3d2Safresh1if ($SHOW{header}) { 217*5759b3d2Safresh1 my @header = qw[TEST WALL SELF KIDS CPU RATIO]; 218*5759b3d2Safresh1 if ($Opt{order}) { 219*5759b3d2Safresh1 push @header, 'ORDER'; 220*5759b3d2Safresh1 } 221*5759b3d2Safresh1 print join(" ", @header), "\n"; 222*5759b3d2Safresh1} 223*5759b3d2Safresh1if ($SHOW{sum}) { 224*5759b3d2Safresh1 print join(" ", "SUM", 225*5759b3d2Safresh1 map { sprintf("%.6f", $_) } $sa, $sb, $sc, $sd, $se), 226*5759b3d2Safresh1 "\n"; 227*5759b3d2Safresh1} 228*5759b3d2Safresh1if ($SHOW{max}) { 229*5759b3d2Safresh1 print join(" ", "MAX", 230*5759b3d2Safresh1 map { sprintf("%.6f", $_) } $ma, $mb, $mc, $md, $me), 231*5759b3d2Safresh1 "\n"; 232*5759b3d2Safresh1} 233*5759b3d2Safresh1 234*5759b3d2Safresh1my %N2I = (wall => 1, 235*5759b3d2Safresh1 self => 2, 236*5759b3d2Safresh1 kids => 3, 237*5759b3d2Safresh1 cpu => 4, 238*5759b3d2Safresh1 ratio => 5); 239*5759b3d2Safresh1 240*5759b3d2Safresh1sub row_is_skippable { 241*5759b3d2Safresh1 my ($t) = @_; 242*5759b3d2Safresh1 if (scalar keys %MIN) { 243*5759b3d2Safresh1 for my $k (grep { exists $MIN{$_} } keys %N2I) { 244*5759b3d2Safresh1 if ($t->[$N2I{$k}] < $MIN{$k}) { 245*5759b3d2Safresh1 return 1; 246*5759b3d2Safresh1 } 247*5759b3d2Safresh1 } 248*5759b3d2Safresh1 } 249*5759b3d2Safresh1 if (scalar keys %MAX) { 250*5759b3d2Safresh1 for my $k (grep { exists $MAX{$_} } keys %N2I) { 251*5759b3d2Safresh1 if ($t->[$N2I{$k}] > $MAX{$k}) { 252*5759b3d2Safresh1 return 1; 253*5759b3d2Safresh1 } 254*5759b3d2Safresh1 } 255*5759b3d2Safresh1 } 256*5759b3d2Safresh1 return 0; 257*5759b3d2Safresh1} 258*5759b3d2Safresh1 259*5759b3d2Safresh1for my $t (@t) { 260*5759b3d2Safresh1 next if row_is_skippable($t); 261*5759b3d2Safresh1 my $out = sprintf("%s %.6f %.6f %.6f %.6f %.6f", 262*5759b3d2Safresh1 $t->[0], $t->[1], $t->[2], $t->[3], $t->[4], $t->[5]); 263*5759b3d2Safresh1 if ($Opt{order}) { 264*5759b3d2Safresh1 $out .= " $t->[6]"; 265*5759b3d2Safresh1 } 266*5759b3d2Safresh1 print $out, "\n"; 267*5759b3d2Safresh1} 268*5759b3d2Safresh1 269*5759b3d2Safresh1exit(0); 270