#!/usr/bin/perl -w
eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell
#========================================================================
#
# parse_iozone
#
# DESCRIPTION
#
# Tool for parsing iozone data files in different ways, and creating
# reports, graphs, etc. from them.
#
# AUTHOR
#   Bryce W. Harrington <bryce@bryceharrington.org>
#
# COPYRIGHT
#   Copyright (C) 2006 Bryce W. Harrington
#   All Rights Reserved.
#
#   This program is free software; you can redistribute it and/or
#   modify it under the same terms as Perl itself.
#
#========================================================================

use strict;
use warnings;
use Test::Parser::Iozone;
use Pod::Usage;
use Getopt::Long qw(:config no_ignore_case bundling); ;

#------------------------------------------------------------------------
# User config area
#------------------------------------------------------------------------

our $opt_version      = 0;    # Prints the version and exits
our $opt_help         = 0;    # Prints a brief help message
our $opt_helplong     = 0;    # Prints a long help message
our $opt_man          = 0;    # Prints a manual page (detailed help)
our $opt_debug        = 0;    # Prints debug messages
our $opt_2d_plot      = 0;    # Basic plots for each run
our $opt_3d_plot      = 0;    # 3D plots for each run (Unimplemented)
our $opt_hist_plot    = 0;    # Historical plots of all runs 
our $opt_comp_plot    = 0;    # Comparison type plot
our @opt_comp_names   = ();   # Names of data sets being compared
our @opt_file_sizes   = (64,1024,16384,262144); # file sizes in kb's
our @opt_record_sizes = (64); # record sizes in kb's
our $opt_no_summary   = 0;    # Suppress general summary report

Getopt::Long::Configure ("bundling", "no_ignore_case");
GetOptions(
           "version|V",        
           "help|h",           
           "helplong|H",       
           "man|",             
           "debug|D=i",        
           "2d-plot|p",
           "3d-plot",
           "hist-plot",
           "comp-plot|c",
           "comp-names|n=s@"   => \@opt_comp_names,,
           "file-sizes|f=i@"   => \@opt_file_sizes,
           "record-sizes|r=i@" => \@opt_record_sizes,
           "no-summary|N",
           ) or pod2usage(-verbose => 0, -exitstatus => 0);

version_and_exit() if $opt_version;
pod2usage(-verbose => 0, -exitstatus => 0) if $opt_help;
pod2usage(-verbose => 1, -exitstatus => 0) if $opt_helplong;

@opt_comp_names = split(/,/, join(',', @opt_comp_names));
@opt_file_sizes = split(/,/, join(',', @opt_file_sizes));
@opt_record_sizes = split(/,/, join(',', @opt_record_sizes));

#========================================================================
# Subroutines
#------------------------------------------------------------------------

=head2 version_and_exit()

Displays text describing the version of the script

=cut

sub version_and_exit
{
    my $NAME = $0;
    my $VERSION = Test::Parser::Iozone->VERSION;
    print "$NAME.  Test::Parser::Iozone version $VERSION\n";
    print "Copyright (C) 2006 Bryce W. Harrington <bryce\@bryceharrington.org>\n";
    print "This program is free software; you can redistribute it and/or\n";
    print "modify it under the same terms as Perl itself.\n";
    exit(0);
}

sub main {
    my @parsers;

    if (@ARGV<1) {
        push @ARGV, \*STDIN;
    }

    INPUT:  foreach my $input (@ARGV) {
        my $retval = Test::Parser::END_OF_RECORD;
        my $input_stream;

        # If the user has specified multi-record operation, we can't
        # rely on T:P:Iozone's file open code and must work with 
        # streams, because we'll be creating multiple T:P:Iozone 
        # objects per file.  

        # Open the file for streaming
        if (ref($input)) {
            $input_stream = $input;
            warn "Parsing input stream...\n" if $opt_debug>0;
        } elsif (-f $input) {
            if (! open(FILE, "<$input")) {
                warn "Could not open $input for reading: $!\n";
                next INPUT;
            }
            $input_stream = \*FILE;
        } else {
            warn "Parsing file '$input'...\n" if $opt_debug>0;
        }

        # Now iterate over the contents of the stream as long as
        # there are more records
        while ($retval == Test::Parser::END_OF_RECORD){
            my $parser = new Test::Parser::Iozone
                or die "Couldn't create Test::Parser::Iozone object\n";
            $retval = $parser->parse($input_stream);

            # This is a total hack, but we can do better once TRPI is implemented...
            if ($opt_hist_plot && !ref($input) 
                && $input =~ m#^(.*)/test_output/iozone.log$#) {
                my $rundir = $1;
                my $profile = "$rundir/run_profile.txt";
                my $kernelname = `grep -e ^pkg_file= $profile`;
                $kernelname =~ s/^pkg_file=//;
                $kernelname =~ s/\.diff$//;

                $parser->name($kernelname);
            }

            if ($retval) {
                push @parsers, $parser;
                warn "Finished parsing record\n" if $opt_debug>1;
            } elsif (!ref($input)) {
                warn "Could not parse log file '$input'.\n";
            } else {
                warn "Could not parse input stream.\n";
            }

        }

    }

    # ------------------------------------------------------------
    # Generate report(s) from the parsed data
    # ------------------------------------------------------------

    # Generate basic per-run plots
    if ($opt_2d_plot) {
        # If multiple files were parsed, only plot the first one
        my $p = $parsers[0];
        $p->plot_2d();

    }

    # Generate 3D plots
    if ($opt_3d_plot) {
        # If multiple files were parsed, only plot the first one
        my $p = $parsers[0];
        $p->plot_3d();
    }

    # Historical performance report
    if ($opt_hist_plot) {
        # Given a selected set of data points (@opt_file_sizes, @opt_record_sizes),
        # generate graph showing the given data point from each run,
        # with X axis being the software version, and Y being Kbytes/sec

        Test::Parser::Iozone::historical_plot(\@parsers, \@opt_file_sizes, \@opt_record_sizes);
    }

    # Generate comparison report of two or more runs
    #  E.g., NFSv3 vs. NFSv4, or different filesystem types, or ...
    if ($opt_comp_plot) {
        if (@parsers < 2) {
            warn "Error:  --comparison report requires at least 2 valid runs\n";
        } elsif (@parsers != @opt_comp_names) {
            warn "Error:  Names must be specified for each run.\n";
            warn "        Use --comp-names=name1,name2,...\n"
        } else {
            Test::Parser::Iozone::comparison_plot(\@parsers, \@opt_comp_names, 
                                                  \@opt_file_sizes, \@opt_record_sizes);
        }
    }

    # Generate general summary (text) report
    if (! $opt_no_summary) {
        warn "Printing summary report\n" if $opt_debug>2;
        print Test::Parser::Iozone::summary_report(\@parsers);
    }

    return 0;
}

exit(main());

__END__

=head1 NAME

B<parse_iozone> - Generates reports and graphs from Iozone test results


=head1 SYNOPSIS

parse_iozone [options] <iozone-log>

  Options:
   -V, --version=boolean         Prints the version and exits
   -h, --help=boolean            Prints a brief help message
   -H, --helplong=boolean        Prints a long help message
       --man=boolean             Prints a manual page (detailed help)
   -D, --debug=integer           Prints debug messages

=head1 DESCRIPTION

treport - Generates reports from test results

=head1 OPTIONS

=over 8

=item B<-V>, B<--version>

Prints the version and exits

=item B<-h>, B<--help>

Prints a brief help message

=item B<-H>, B<--helplong>

Prints a long help message

=item B<--man>

Prints a manual page (detailed help)

=item B<-D> I<D>, B<--debug>=I<D>

Prints debug messages

=back

See B<treport> -h for a summary of options.


=head1 PREREQUISITES

L<Pod::Usage>,
L<Getopt::Long>,
L<Test::Parser::Iozone>




syntax highlighted by Code2HTML, v. 0.9.1