xref: /openbsd-src/gnu/usr.bin/perl/Porting/harness-timer-report.pl (revision 5759b3d249badf144a6240f7eec4dcf9df003e6b)
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