#!/usr/local/bin/perl -w
use strict;
use POSIX "floor";
# any commands in this list will have timings calculated for their sub-commands also.
my @do_sub_commands = ( qw(p4) );
#my @do_sub_commands = ();
# sum numbers found in arguments (recursively), which may be:
# scalars, lists, list refs, or hash refs (values of hash are summed)
# dies if anything else encountered.
sub sum {
my $total = 0;
for(@_) {
if( ! ref ) {
die "'$_' is not a number" unless /^[0-9.]+/ && ( /\./g ) <= 1;
$total += $_ ;
}
elsif( ref eq "ARRAY" ) {
$total += sum( @$_ );
}
elsif( ref eq "HASH" ) {
$total += sum( values %$_ );
}
else {
die "can't sum a " . ref() . " reference.";
}
}
return $total;
}
sub test_sum {
my @list = ( 2, 3, 4 );
my %h = ( a => 1, b => 2, c => 3 );
print "\n";
print sum( 0, 1, 2, 3, 4, 5 ), "\n";
print sum( @list ), "\n";
print sum( \@list ), "\n";
print sum( \@list, 1 ), "\n";
print sum( \@list, @list ), "\n";
print sum( \%h ), "\n";
print sum( \%h, 1 ), "\n";
print sum( \%h, \@list ), "\n";
print sum( \%h, \@list, 3 ), "\n";
}
my %perl_times;
my %os_times;
my %overhead_times;
my %run_counts;
my @stack;
while (<>) {
chomp;
my @f = split;
if( @f < 3 ) {
warn "not enough fields on line $. of $ARGV\n";
next;
}
my $time = $f[0];
my $what = $f[1];
if( $what !~ /^(BEG|END|ELA)$/ ) {
warn "line $. of profile log was not a BEG, END or ELA marker\n";
next;
}
#--------------------
# get parts of command line
my %this_data;
my %subcmd_data;
$this_data{time} = $time;
$this_data{what} = $what;
my $command = $f[2];
shift @f for(1..3);
$this_data{command} = $command;
#--------------------
# if we want detail profiling on the sub-commands of this command,
# figure out what the sub-command is, and add its timings.
my $subcommand;
if ( grep { $_ eq $command } @do_sub_commands ) {
if( $what eq "ELA" ) {
while( @f ) {
if( $f[0] =~ /^-/ ) {
shift @f; # shift off the option flag
if( $f[0] eq '-' || $f[0] !~ /^-/ ) {
shift @f; # shift off the parameter
}
}
else {
$subcommand = shift @f;
last;
}
}
}
else {
$subcommand = shift @f;
}
die "??? no subcommand found for $command '$_'\n"
unless defined $subcommand;
}
if( defined $subcommand ) {
$subcmd_data{time} = $time;
$subcmd_data{what} = $what;
$subcmd_data{command} = "$command $subcommand";
}
#--------------------
# add up times
if( $this_data{what} eq "BEG" ) {
push @stack, \%this_data;
$run_counts{ $this_data{command} }++ ;
if( keys %subcmd_data ) {
push @stack, \%subcmd_data ;
$run_counts{ $subcmd_data{command} }++ ;
}
}
elsif( $this_data{what} eq "ELA" ) {
$os_times { $this_data{command} } += $this_data{time};
$os_times { $subcmd_data{command} } += $subcmd_data{time}
if( defined $subcommand ) ;
}
else { # 'END'
if( defined $subcommand ) {
my %prev_data = %{ pop @stack };
die "END command did not match BEG command at top of stack.\n"
unless $subcmd_data{command} eq $prev_data{command};
my $elapsed = $subcmd_data{time} - $prev_data{time};
$perl_times{ $subcmd_data{command} } += $elapsed;
}
my %prev_data = %{ pop @stack };
die "END command did not match BEG command at top of stack.\n"
unless $this_data{command} eq $prev_data{command};
my $elapsed = $this_data{time} - $prev_data{time};
$perl_times{ $this_data{command} } += $elapsed;
}
}
die "No input data\n" unless $.;
my $total_key = "~TOTAL"; # sorts last
my $os_times_present = keys %os_times;
$perl_times{$total_key} = sum \%perl_times;
$os_times{$total_key} = sum \%os_times
if $os_times_present;
## print "total perl times: $perl_times{$total_key}\n";
## print "total os times: $os_times{$total_key}\n";
## print join "", map { sprintf "%10.6f seconds (via perl) in $_ \n", $perl_times{$_} } sort keys %perl_times;
## print join "", map { sprintf "%10.6f seconds (via time) in $_ \n", $os_times {$_} } sort keys %os_times;
my @keys_both = grep { exists $os_times{$_} } keys %perl_times;
$overhead_times{$_} = $perl_times{$_} - $os_times{$_} for @keys_both;
## print join "", map { sprintf "%10.6f seconds overhead in $_\n", $perl_times{$_} - $os_times{$_} } sort @keys_both;
my %all_unique_keys = map { $_ => 1 } sort( keys %perl_times, keys %os_times );
$all_unique_keys{'~'} = 1; # separator before TOTAL
sub percentage {
my ($num, $denom) = @_;
return floor ( .5 + $num / $denom * 100 ) ;
}
# args:
# 1. timing hash ref
# 2. hash key
sub print_timing {
my ($h, $k) = @_;
die unless $h && $k;
# print timing
my $timing;
if( defined $h->{$k} ) {
$timing = $h->{$k};
printf "%6.2f ", $timing;
}
else {
print " ";
}
# if this is a subcommand, print percent that subcommand is of command
if( defined $timing && $k =~ /(\S+)\s+\S+/ ) {
# print percentage of total command time if this is a subcommand
printf " %2d%% ", percentage $h->{$k}, $h->{$1} ;
}
else {
printf " ";
}
# print timing per each commmand
if( defined $timing && exists $run_counts{$k} ) {
my $time_each = $timing / $run_counts{$k} ;
printf "%6.3f ", $time_each;
}
else {
print " ";
}
print "| ";
}
my $max_key_len = 0;
for( keys %all_unique_keys ) {
$max_key_len = length if length > $max_key_len;
}
## ---------------------
## output
sub underline {
print "-" for 1..$max_key_len;
print "-----";
print "----"; # over run counts
for(1..3) {
print "-------"; # over timing
print "-----"; # over %
print "-------"; # over time per each
print "--";
last unless $os_times_present;
}
print "\n";
}
print "\n";
underline;
# print header
print " " for 1..$max_key_len;
print " | ";
print "runs"; # over run count
print " | ";
for(
" perl ",
"os time",
"ovrhead"
) {
print "$_"; # over number
print " (%) "; # over %
print " each "; # over time per each
print "| ";
last unless $os_times_present;
}
print "\n";
# header underline
underline;
# print data
for( sort keys %all_unique_keys ) {
my $key = $_;
if( $key eq "~" ) { # special underline mark
underline;
next;
}
$key =~ s/~// ; # special sorting character
$key .= " " while length $key < $max_key_len;
print $key, " | ";
if( defined $run_counts{$_} ) {
printf "%4d ", $run_counts{$_};
}
else {
print " ";
}
print "| ";
print_timing \%perl_times, $_ ;
if( $os_times_present ) {
print_timing \%os_times, $_ ;
print_timing \%overhead_times, $_ ;
}
print "\n";
}
print "\n";
syntax highlighted by Code2HTML, v. 0.9.1