#!/usr/bin/perl # llstat 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. # Subroutine for printing usages information sub usage() { print STDERR "Usage: $pname [-c] [-g] [-i ] [-h ] \n"; print STDERR " stats_file : 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 lustre/ost/OSS/ost/stats)\n"; print STDERR "Use CTRL + C to stop statistics printing\n"; exit 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"; } } # 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() { seek STATS, 0, 0; while () { chop; ($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare) = split(/\s+/, $_); $prevcount = %cumulhash->{$name}; if (defined($prevcount)) { $diff = $cumulcount - $prevcount; if ($name eq "snapshot_time") { $tdiff = int($diff); &print_headings($cumulcount); $| = 1; if ($tdiff == 0) { $tdiff = 1; # avoid division by zero } } elsif ($cumulcount!=0) { if ($graphable) { my $myunit = $unit; my $myname = $name; if (defined($sum)) { $myunit = "[reqs]"; $myname = $myname . "_rq"; } 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; } } %cumulhash->{$name} = $cumulcount; %sumhash->{$name} = $sum; } #end of while } #Globals $pname = $0; $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("/{proc,sys}/fs/lustre/*/$obddev/$obdstats"); if ( -f "$st" ) { $statspath = $st; } else { $st = glob("/{proc,sys}/fs/lustre/*/*/$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); } } while ($interval); close STATS;