#!/usr/bin/perl
+# llstat.pl is a utility that takes stats files as input with optional
+# clear-flag. The clear-flag is used to clear the stats file before
+# printing stats information. The lustre stats files generally located
+# inside proc/fs/lustre/. This program first reads the required statistics
+# information from specified stat file, process the information and prints
+# the output after every interval specified by user.
-my $pname = $0;
-
+# Subroutine for printing usages information
sub usage()
{
- print STDERR "Usage: $pname <stats_file> [<interval>]\n";
- exit 1;
-}
-
-
-my $statspath;
-my $interval = 0;
-
-if (($#ARGV < 0) || ($#ARGV > 1)) {
- usage();
-} else {
- $statspath = $ARGV[0];
- if ($#ARGV == 1) {
- $interval = $ARGV[1];
- }
+ print STDERR "Usage: $pname [-c] [-g] [-i <interval>] [-h <help>] <stats_file>\n";
+ print STDERR " stats_file : /proc/fs/lustre/.../stat\n";
+ print STDERR " -i interval: polling period\n";
+ print STDERR " -c : clear stats file first\n";
+ print STDERR " -g : graphable output format\n";
+ print STDERR " -h : help, display this information\n";
+ print STDERR "example: $pname -i 1 ost (monitors /proc/fs/lustre/ost/OSS/ost/stats)\n";
+ print STDERR "Use CTRL + C to stop statistics printing\n";
+ exit 1;
}
-
-
-my %namehash;
-my $anysum = 0;
-my $anysumsquare = 0;
-my $mhz = 0;
-
-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; }
+sub print_headings()
+{ my $cc = $_[0];
+ if ($graphable) {
+ if ( $print_once && $interval ) {
+ printf "Timestamp [Name Count Rate Total Unit]...";
+ printf "\n--------------------------------------------------------------------";
+ $print_once = 0;
+ }
+ if ($cc && !$starttime) {
+ $starttime = $cc
+ }
+ printf "\n%-5.0f", ($cc - $starttime);
+ } else {
+ printf "$statspath @ $cc\n";
+ printf "%-25s %-10s %-10s %-10s", "Name", "Cur.Count", "Cur.Rate", "#Events";
+ if ( $anysum ) {
+ printf "%-8s %10s %10s %12s %10s", "Unit", "last", "min", "avg", "max";
+ }
+ if ( $anysumsquare ) {
+ printf "%10s", "stddev";
+ }
+ printf "\n";
+ }
}
-get_cpumhz();
-print "Processor counters run at $mhz MHz\n";
-
+# known units are: reqs, bytes, usec, bufs, regs, pages
+# readstat subroutine reads and processes statistics from stats file.
+# This subroutine gets called after every interval specified by user.
sub readstat()
{
- open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
- while (<STATS>) {
+ seek STATS, 0, 0;
+ while (<STATS>) {
chop;
($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare)
- = split(/\s+/, $_);
-
- $prevcount = %namehash->{$name};
+ = split(/\s+/, $_);
+ $prevcount = %cumulhash->{$name};
if (defined($prevcount)) {
- $diff = $cumulcount - $prevcount;
- if ($name eq "snapshot_time") {
- $tdiff = $diff;
- # printf "%-25s prev=$prevcount, cumul=$cumulcount diff=$diff, tdiff=$tdiff\n", $name;
- printf "$statspath @ $cumulcount\n";
- printf "%-25s %-10s %-10s %-10s", "Name", "Cur.Count", "Cur.Rate", "#Events";
- if ($anysum) {
- printf "%-8s %10s %12s %10s", "Unit", "min", "avg", "max";
- }
- if ($anysumsquare) {
- printf "%10s", "stddev";
+ $diff = $cumulcount - $prevcount;
+ if ($name eq "snapshot_time") {
+ $tdiff = int($diff);
+ &print_headings($cumulcount);
+ $| = 1;
+ if ($tdiff == 0) {
+ $tdiff = 1; # avoid division by zero
+ }
}
- printf "\n";
- $| = 1;
- }
- elsif ($cumulcount!=0) {
- printf "%-25s %-10lu %-10lu %-10lu",
- $name, $diff, ($diff/$tdiff), $cumulcount;
-
- if (defined($sum)) {
- my $sum_orig = $sum;
- if (($unit eq "[cycles]") && ($mhz != 1)) {
- $unit = "[usecs]";
- $min = $min/$mhz;
- $sum = $sum/$mhz;
- $max = $max/$mhz;
- }
- printf "%-8s %10lu %12.2f %10lu", $unit, $min, ($sum/$cumulcount), $max;
- if (defined($sumsquare)) {
- my $s = $sumsquare - (($sum_orig*$sum_orig)/$cumulcount);
- if ($s >= 0) {
- my $cnt = ($cumulcount >= 2) ? $cumulcount : 2 ;
- my $stddev = sqrt($s/($cnt - 1));
- if (($unit eq "[usecs]") && ($mhz != 1)) {
- $stddev = $stddev/$mhz;
+ elsif ($cumulcount!=0) {
+ if ($graphable) {
+ my $myunit = $unit;
+ my $myname = $name;
+ if (defined($sum)) {
+ $myunit = "[reqs]";
+ $myname = $myname . "_rq";
}
- printf " %10.2f", $stddev;
+ printf " %s %lu %lu %lu %s",
+ $myname, $diff, ($diff/$tdiff), $cumulcount, $myunit;
+ } else {
+ printf "%-25s %-10lu %-10lu %-10lu",
+ $name, $diff, ($diff/$tdiff), $cumulcount;
+ }
+ if (defined($sum)) {
+ my $sum_orig = $sum;
+ my $sum_diff = $sum - %sumhash->{$name};
+ if ($graphable) {
+ printf " %s %lu %.2f %lu %s",
+ $name, $sum_diff, ($sum_diff/$tdiff), $sum, $unit;
+ } else {
+ printf "%-8s %10lu %10lu %12.2f %10lu", $unit,
+ $sum_diff, $min, ($sum/$cumulcount), $max;
+ }
+ if (defined($sumsquare)) {
+ my $s = $sumsquare - (($sum_orig*$sum_orig)/$cumulcount);
+ if ($s >= 0) {
+ my $cnt = ($cumulcount >= 2) ? $cumulcount : 2 ;
+ my $stddev = sqrt($s/($cnt - 1));
+ if (!$graphable) {
+ printf " %9.2f ", $stddev;
+ }
+ }
+ }
}
- }
+ if (!$graphable) {
+ printf "\n";
+ }
+ $| = 1;
+ }
+ } else {
+ if ($cumulcount!=0) {
+ # print info when interval is not specified.
+ printf "%-25s $cumulcount\n", $name
+ }
+ if (defined($sum)) {
+ $anysum = 1;
+ }
+ if (defined($sumsquare)) {
+ $anysumsquare = 1;
}
- printf "\n";
- $| = 1;
- }
- }
- else {
- if ($cumulcount!=0) {
- printf "%-25s $cumulcount\n", $name
- }
- if (defined($sum)) {
- $anysum = 1;
- }
- if (defined($sumsquare)) {
- $anysumsquare = 1;
- }
}
- %namehash->{$name} = $cumulcount;
- }
+ %cumulhash->{$name} = $cumulcount;
+ %sumhash->{$name} = $sum;
+ } #end of while
}
+#Globals
+$pname = $0;
+$defaultpath = "/proc/fs/lustre";
+$obdstats = "stats";
+$clear = 0;
+$graphable = 0;
+$interval = 0;
+$statspath = "None";
+$anysumsquare = 0;
+$mhz = 0;
+$print_once = 1;
+%cumulhash;
+%sumhash;
+$anysum = 0;
+$obddev = "";
+$starttime = 0;
+$ONE_MB = 1048576;
+
+# Command line parameter parsing
+use Getopt::Std;
+getopts('hcgi:') or usage();
+usage() if $opt_h;
+$clear = 1 if $opt_c;
+$graphable = 1 if $opt_g;
+$interval = $opt_i if $opt_i;
+
+my $i = 0;
+foreach (@ARGV) {
+ $obddev = $_;
+ $i++;
+ if ($i > 1) {
+ print "ERROR: extra argument $_\n";
+ usage();
+ }
+}
+if ( !$obddev ) {
+ print "ERROR: Need to specify stats_file\n";
+ usage();
+}
+# Process arguments
+if ( -f $obddev ) {
+ $statspath = $obddev;
+} elsif ( -f "$obddev/$obdstats" ) {
+ $statspath = "$obddev/$obdstats";
+} else {
+ my $st = glob("$defaultpath/*/$obddev/$obdstats");
+ if ( -f "$st" ) {
+ $statspath = $st;
+ } else {
+ $st = glob("$defaultpath/*/*/$obddev/$obdstats");
+ if ( -f "$st" ) {
+ $statspath = $st;
+ }
+ }
+}
+if ( $statspath =~ /^None$/ ) {
+ die "Cannot locate stat file for: $obddev\n";
+}
+# Clears stats file before printing information in intervals
+if ( $clear ) {
+ open ( STATS, "> $statspath") || die "Cannot clear $statspath: $!\n";
+ print STATS " ";
+ close STATS;
+ sleep($interval);
+}
+use POSIX qw(strftime);
+$time_v = time();
+$hostname = `lctl list_nids | head -1`;
+chop($hostname);
+print "$pname: STATS on ", strftime("%D", localtime($time_v));
+print " $statspath on $hostname\n";
+open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
do {
- readstat();
- if ($interval) {
- sleep($interval);
- }
+ readstat();
+ if ($interval) {
+ sleep($interval);
+ }
} while ($interval);
+close STATS;