#!/usr/bin/perl
# llobdstat is a utility that parses obdfilter statistics files
# found at proc/fs/lustre/<ostname>/stats.
# It is mainly useful to watch the statistics change over time.

my $pname = $0;

my $obdstats = "stats";

sub usage()
{
    print STDERR "Usage: $pname <ost_name> [<interval> [<count>}]\n";
    print STDERR "where  ost_name  : ost name under obdfilter\n";
    print STDERR "       interval  : sample interval in seconds\n";
    print STDERR "example: $pname lustre-OST0000 2\n";
    print STDERR "Use CTRL + C to stop statistics printing\n";
    exit 1;
}

my $statspath = "None";
my $interval = 0;
my $counter = undef;

if (($#ARGV < 0) || ($#ARGV > 2)) {
    usage();
} else {
    if ( $ARGV[0] =~ /help$/ ) {
        usage();
    }
    if ( -f $ARGV[0] ) {
        $statspath = $ARGV[0];
    } elsif ( -f "$ARGV[0]/$obdstats" ) {
        $statspath = "$ARGV[0]/$obdstats";
    } else {
	my $st = glob ("/{proc,sys}/fs/lustre/obdfilter/$ARGV[0]");
	if ( -f "$st" ) {
	    $statspath = $st;
	} else {
	    my $st = glob("/{proc,sys}/fs/lustre/obdfilter/$ARGV[0]/$obdstats");
	    if ( -f "$st" ) {
		$statspath = $st;
	    }
	}
    }
    if ( $statspath =~ /^None$/ ) {
        die "Cannot locate stat file for: $ARGV[0]\n";
    }
    if ($#ARGV > 0) {
        $interval = $ARGV[1];
    }
    if ($#ARGV > 1) {
        $counter = $ARGV[2];
    }
}

print "$pname on $ARGV[0]\n";

my %cur;
my %last;
my $mhz = 0;

# Removed some statstics like open, close that obdfilter doesn't contain.
# To add statistics parameters one needs to specify parameter names in the
# below declarations in the same sequence.
my ($read_bytes, $write_bytes, $create, $destroy, $statfs, $punch,
    $snapshot_time) = ("read_bytes", "write_bytes", "create", "destroy",
                       "statfs", "punch", "snapshot_time");

my @extinfo = ($create, $destroy, $statfs, $punch);
my %shortname = ($create => "cx", $destroy => "dx", $statfs => "st", $punch => "pu");

sub get_cpumhz()
{
    my $cpu_freq;
    my $itc_freq; # On Itanium systems use this
    if (open(CPUINFO, "/proc/cpuinfo")==0) {
        return;
    }
    while (<CPUINFO>) {
        if (/^cpu MHz\s+:\s*([\d\.]+)/) { $cpu_freq=$1; }
        elsif (/^itc MHz\s+:\s*([\d\.]+)/) { $itc_freq=$1; }
    }
    if (defined($itc_freq)) {
        $mhz = $itc_freq;
    } elsif (defined($cpu_freq)) {
        $mhz = $cpu_freq;
    } else {
        $mhz = 1;
    }
    close CPUINFO;
}

get_cpumhz();
print "Processor counters run at $mhz MHz\n";

# read statistics from obdfilter stats file.
# This subroutine gets called after every interval specified by user.
sub readstat()
{
    my $prevcount;
    my @iodata;

    seek STATS, 0, 0;
    while (<STATS>) {
        chop;
#        ($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare)
        @iodata = split(/\s+/, $_);
        my $name = $iodata[0];

        $prevcount = $cur{$name};
        if (defined($prevcount)) {
            $last{$name} = $prevcount;
        }
        if ($name =~ /^read_bytes$/ || $name =~ /^write_bytes$/) {
            $cur{$name} = $iodata[6];
        }
        elsif ($name =~ /^snapshot_time$/) {
#            $cumulcount =~ /(\d+)/;
            $cur{$name} = $iodata[1];
        } else {
            $cur{$name} = $iodata[1];
        }
    }
}

# process stats information read from obdfilter stats file.
# This subroutine gets called after every interval specified by user.
sub process_stats()
{
    my $delta;
    my $data;
    my $last_time = $last{$snapshot_time};
    if (!defined($last_time)) {
        printf "Read: %-g, Write: %-g, create/destroy: %-g/%-g, stat: %-g, punch: %-g\n",
        $cur{$read_bytes}, $cur{$write_bytes},
        $cur{$create}, $cur{$destroy},
        $cur{$statfs}, $cur{$punch};
        if ($interval) {
            print "[NOTE: cx: create, dx: destroy, st: statfs, pu: punch ]\n\n";
            print "Timestamp   Read-delta  ReadRate  Write-delta  WriteRate\n";
            print "--------------------------------------------------------\n";
        }
    } else {
        my $timespan = $cur{$snapshot_time} - $last{$snapshot_time};
        my $rdelta = $cur{$read_bytes} - $last{$read_bytes};
        my $rrate = ($rdelta) / ($timespan * ( 1 << 20 ));
        my $wdelta = $cur{$write_bytes} - $last{$write_bytes};
        my $wrate = ($wdelta) / ($timespan * ( 1 << 20 ));
        $rdelta = ($rdelta) / (1024 * 1024);
        $wdelta = ($wdelta) / (1024 * 1024);
        # This print repeats after every interval.
        printf "%10lu  %6.2fMB  %6.2fMB/s   %6.2fMB  %6.2fMB/s",
               $cur{$snapshot_time}, $rdelta, $rrate, $wdelta, $wrate;

        $delta = $cur{$getattr} - $last{$getattr};
        if ( $delta != 0 ) {
            $rdelta = int ($delta/$timespan);
            print " ga:$delta,$rdelta/s";
        }

        for $data ( @extinfo ) {
            $delta = $cur{$data} - $last{$data};
            if ($delta != 0) {
                print " $shortname{$data}:$delta";
            }
        }
        print "\n";
        $| = 1;
    }
}

#Open the obdfilter stat file with STATS
open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
do {
    # read the statistics from stat file.
    readstat();
    process_stats();
    if ($interval) {
        sleep($interval);
        %last = %cur;
    }
    # Repeat the statistics printing after every "interval" specified in
    # command line, up to counter times, if specified
} while ($interval && (defined($counter) ? $counter-- > 0 : 1));
close STATS;
# llobdfilter.pl ends here.
