The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;

BEGIN {
    unshift(@INC, "lib") if -f "lib/PerlBench.pm";
}

use PerlBench qw(timeit);
use Sys::Hostname qw(hostname);
use Digest::MD5 qw(md5_hex);
use File::Path qw(mkpath);
use Getopt::Long qw(GetOptions);
use File::Spec::Functions qw(file_name_is_absolute);

my $resdir = "perlbench-results";
my $hostname = hostname();
my $perl = $^X;
my $VERBOSE = 0;

my %opt;
GetOptions(
   'verbose+' => \$VERBOSE,
) || usage();

my @tests = @ARGV ? (@ARGV) : do {
    die "Can't find any benchmarks directory" unless -d "benchmarks";
    use File::Find;
    my @f;
    File::Find::find(sub { /\.b$/ && push(@f, $File::Find::name) }, "benchmarks");
    sort @f;
};
die unless @tests;

$| = 1;

die "Need absolute perl name" unless file_name_is_absolute($perl);

print "Host: $hostname\n";
my $hostdir = "$resdir/$hostname";
if (-d $hostdir) {
    # check that this host look similar
}
else {
    mkpath($hostdir, 0, 0755) || die "Can't create $hostdir: $!";
    file("$hostdir/osname.txt", "$^O\n");
    file("$hostdir/osversion.txt", get_os_version()."\n");
    system("uname -a >$hostdir/uname-a.txt") if $^O ne "MSWin32";
    system("uname -X >$hostdir/uname-X.txt") if $^O eq "solaris";
    system("cp /proc/cpuinfo $hostdir/cpuinfo.txt") if -f "/proc/cpuinfo";
}

print "Perl: $perl\n";
my $perl_V = qx($perl -V);

my $perldir = "$hostdir/perls/" . md5_hex("$perl\0$perl_V");
if (-d $perldir) {
    # check that this perl looks similar
    die "perl -V output doesn't match"
	unless file("$perldir/config-summary.txt") eq $perl_V;
}
else {
    mkpath("$perldir/tests", 0, 0755);
    file("$perldir/path.txt", $perl);
    system(qq($perl -v >$perldir/version.txt));
    file("$perldir/config-summary.txt", $perl_V);
    system(qq($perl -MConfig=config_sh -e "print &config_sh()" >$perldir/config.sh));
}

for my $test (@tests) {
    print "Test-File: $test\n";
    run_test($test);
}


sub run_test {
    my $test = shift;
    open(my $fh, "<", $test) || die "Can't open $test: $!";
    my $header = 1;
    my %prop;
    my @init;
    my @code;
    while (<$fh>) {
	if ($header) {
	    if (/^\#\s*(\w+)\s*:\s*(.*)/) {
		$prop{lc($1)} = $2;
	    }
	    else {
		$header = 0;
	    }
	}
	else {
	    if (/^\#\#\#\s*TEST\s*$/) {
		die if @init;
		@init = @code;
		@code = ();
	    }
	    else {
		push(@code, $_);
	    }
	}
    }
    close($fh);

    print "Test-Name: $prop{name}\n" if $prop{name};

    trim(\@init);
    trim(\@code);

    my %opt;
    $opt{verbose} = 1 if $VERBOSE;
    $opt{init} = join("", @init) if @init;
    $opt{repeat} = $prop{repeat} if $prop{repeat};
    $opt{enough} = $prop{enough} if $prop{enough};

    my $start = time2iso();
    my $res = timeit(join("", @code), %opt);

    my $resfile = $start;
    $resfile =~ s/ /T/;  # spaceless iso format
    $resfile =~ s/[-:]//g;  # compact and windows compatible
    $resfile = "$perldir/tests/$resfile.pb";
    open($fh, ">", $resfile) || die "Can't create $resfile: $!";
    my $end = substr(time2iso(), 11);

    # system info
    print $fh "Date: $start ($end)\n";
    print $fh "PerlBench-Version: $PerlBench::VERSION\n";
    print $fh "Test: $test md5:", md5_hex(file($test)), "\n";
    print $fh "Perl: $perl $]\n";
    print $fh "Hostname: $hostname\n";
    if (-x "/usr/bin/uptime") {
	my($up) = qx(/usr/bin/uptime);
	chomp($up);
	$up =~ s/\s+/ /g;
	$up =~ s/^\s+//;
	print $fh "System-Uptime: $up\n";
    }

    # test results
    print $fh "Cycles: $res->{count}\n";
    printf $fh "Loop-Overhead: %.1f%%\n", 100*$res->{loop_overhead_relative};
    print $fh "Samples: $res->{n}\n";
    print $fh "Min: $res->{min}\n";
    print $fh "Med: $res->{med}\n";
    print $fh "Max: $res->{max}\n";
    print $fh "Avg: $res->{avg}\n";
    print $fh "Std-Dev: $res->{stddev}\n";

    close($fh) || die "Can't write $resfile: $!";
    print "Test-Result: $resfile\n";
}

sub time2iso
{
    my $time = shift;
    $time = time unless defined $time;
    my($sec,$min,$hour,$mday,$mon,$year) = localtime($time);
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d",
            $year+1900, $mon+1, $mday, $hour, $min, $sec);
}

sub trim {
    my $lines = shift;
    shift(@$lines) while @$lines && $lines->[0] =~ /^$/;
    pop(@$lines) while @$lines && $lines->[-1] =~ /^$/;
}

sub file {
    my $name = shift;
    if (@_) {
        my $content = shift;
        open(my $f, ">", $name) || die "Can't create '$name': $!";
        binmode($f);
        print $f $content;
        close($f) || die "Can't write to '$name': $!";
    }
    else {
        open(my $f, "<", $name) || return undef;
        binmode($f);
        local $/;
        return scalar <$f>;
    }
}

sub get_os_version {
    if ($^O eq "MSWin32") {
	my($string, $major, $minor, $build, $id, $spmajor, $spminor, $suitemask, $producttype) = Win32::GetOSVersion();
	if ($id == 0) {
	    $major = 0;
	    $minor = 0;
	}
	my $ver = "Windows $major.$minor";
	$ver .= ".$build" if $build;
	if ($id >= 2 && $major >= 4) {
	    if ($spmajor || $spminor) {
		$ver .= " SP$spmajor.$spminor";
	    }
	}
	my $name = {
	    "0.0.0" => "Win32s",
            "1.4.0" => "Windows 95",
	    "1.4.10" => "Windows 98",
            "1.4.90" => "Windows Me",
            "2.3.51" => "Windows NT 3.51",
	    "2.4.0" => "Windows NT 4",
	    "2.5.0" => "Windows 2000",
            "2.5.1" => "Windows XP",
            "2.5.2" => "Windows Server 2003",
            "2.6.0" => "Windows Vista",
        }->{"$id.$major.$minor"} || "Windows";
	$ver .= " ($name $string)";
	return $ver;
    }

    my($ver) = qx(uname -sr);
    chomp($ver);
    return $ver;
}