Viewing: llstat
#!/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 stats files may be in /proc/fs/lustre
# or /sys/kernel/debug/lustre. This 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 "Monitor operation count/rate of a subsystem\n";
print STDERR "Usage: $pname [-c] [-d] [-g] [-h] [-i <interval>] [-n count] <stats_file>\n";
print STDERR " stats_file : Lustre 'stats' file to watch\n";
print STDERR " -i interval: polling period in seconds\n";
print STDERR " -c : clear stats file first\n";
print STDERR " -d : debug mode\n";
print STDERR " -g : graphable output format\n";
print STDERR " -h : help, display this information\n";
print STDERR " -n count : number of samples printed\n";
print STDERR "example: $pname -i 1 ost_io (ost.OSS.ost_io.stats)\n";
print STDERR "Use CTRL + C to stop statistics printing\n";
exit 1;
}
#Globals
my $pname = $0;
my $obddev = "";
my $obdstats = "stats";
my $clear = 0;
my $graphable = 0;
my $interval = 0;
my $statspath = "None";
my $statsname = "stats";
my $anysumsquare = 0;
my $printed_once = 0;
my %cumulhash;
my %sumhash;
my $anysum = 0;
my $starttime = 0;
my $width = 120;
my $have_readkey = 0;
my $debug = 0;
my $counter = 999999999;
my $ONE_MB = 1048576;
# Command line parameter parsing
use Getopt::Std;
getopts('cdghi:n:w:') or usage();
usage() if $opt_h;
$clear = 1 if $opt_c;
$debug = $opt_d if $opt_d;
$graphable = 1 if $opt_g;
$interval = $opt_i if $opt_i;
$counter = $opt_n if $opt_n;
$width = $opt_w if $opt_w;
my $i = 0;
foreach (@ARGV) {
$obddev = $_;
$obddev =~ s/\./\//g;
$i++;
if ($i > 1) {
print "ERROR: extra argument $_\n";
usage();
}
}
if (!$obddev) {
print "ERROR: Need to specify stats_file\n";
usage();
}
# Process arguments
my $procpath = "/sys/kernel/debug/lustre";
foreach my $param ( "$obddev", "$obddev*/$obdstats", "$obddev*/*/$obdstats",
"*/$obddev*/$obdstats", "*/*/$obddev*/$obdstats" ) {
if ($debug) {
print "trying $procpath/$param\n";
}
my $st = glob("$procpath/$param");
if ($debug) {
print "glob $procpath/$param = $st\n";
}
if (-f "$st") {
$statspath = $st;
$statsname = `lctl list_param $param | head -n 1`;
if ($debug) {
print "using '$statsname' from $statspath\n"
}
last;
}
}
if ($statspath =~ /^None$/) {
# some older stats are kept in /proc, but don't look there first
$procpath = "/proc/fs/lustre";
foreach my $param ( "$obddev", "$obddev*/$obdstats", "$obddev*/*/$obdstats",
"*/$obddev*/$obdstats", "*/*/$obddev*/$obdstats" ) {
if ($debug) {
print "trying $procpath/$param\n";
}
$st = glob("$procpath/$param");
if ($debug) {
print "glob $procpath/$param = $st\n";
}
if (-f "$st") {
$statspath = $st;
$statsname = `lctl list_param $param | head -n 1`;
if ($debug) {
print "using $statspath\n"
}
last;
}
}
if ($statspath =~ /^None$/) {
die "Cannot locate stat file for: $obddev\n";
}
}
sub print_headings()
{ my $cc = $_[0];
if ($graphable) {
if (!$printed_once && $interval) {
printf "Timestamp [Name Count Rate Total Unit]...";
printf "\n--------------------------------------------------------------------";
$printed_once = 1;
}
if ($cc && !$starttime) {
$starttime = $cc
}
printf "\n%-5lu", ($cc - $starttime);
} else {
printf "$statsname @ $cc\n";
if ($width <= 90) {
printf "%-20s %-6s %-9s", "Name", "Rate", "#Events";
} else {
printf "%-23s %-6s %-6s %-10s", "Name", "Count", "Rate", "#Events";
}
if ($anysum) {
printf "%-7s %8s %6s %8s %10s", "Unit", "last", "min", "avg", "max";
}
if ($anysumsquare && $width >= 100) {
printf " %8s", "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 (<STATS>) {
chop;
($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare) =
split(/\s+/, $_);
$prevcount = $cumulhash->{$name};
if (defined($prevcount)) {
$diff = $cumulcount - $prevcount;
if ($name =~ /^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 {
if ($width <= 90) {
printf "%-20.20s %-6lu %-9lu",
$name, ($diff/$tdiff), $cumulcount;
} else {
printf "%-23.23s %-6lu %-6lu %-10lu", $name,
$diff, ($diff/$tdiff), $cumulcount;
}
}
if (defined($sum)) {
my $sum_orig = $sum;
my $sum_diff = $sum - $sumhash->{$name};
if ($graphable) {
printf " %s %lu %lu %lu %s", $name,
$diff ? ($sum_diff / $diff) : 0,
($sum_diff/$tdiff), $sum, $unit;
} else {
printf "%-7s %8lu %6lu %8lu %10lu", $unit,
$diff ? ($sum_diff / $diff) : 0, $min,
($sum/$cumulcount), $max;
}
if (defined($sumsquare) && $width >= 100) {
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 " %8lu", $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
if (!$graphable) {
printf "\n";
}
}
# check if Term::ReadKey is installed for efficient tty size, but OK if missing
eval "require Term::ReadKey" or $have_readkey = 0;
if ($debug) {
print "have_readkey=$have_readkey\n";
}
if ($have_readkey) {
eval "use Term::ReadKey";
}
# Clears stats file before printing information in intervals
if ($clear) {
open(STATS, "> $statspath") || die "Cannot clear $statspath: $!\n";
print STATS " ";
close STATS;
sleep($interval);
}
$hostname = `lctl list_nids | head -1 2> /dev/null`;
chop($hostname);
printf "%s: %s at %s on %s\n", $pname, $statsname, `date`, $hostname;
open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
do {
# find the terminal width
if (!$opt_w) {
if ($have_readkey) {
($width, $height, $wpixels, $hpixels) = GetTerminalSize();
} else {
($height, $width) = split / /, `stty size 2> /dev/null`;
#$width = 120 if ! $width
}
}
readstat();
if ($interval) {
sleep($interval);
}
} while ($interval && $counter-- > 0);
close STATS;