Viewing: llobdstat
#!/usr/bin/perl
# llobdstat is a utility that parses OST statistics files
# found at e.g. obdfilter.<ostname>.stats or osc.<ostname>*.stats.
# It is mainly useful to watch the bandwidth usage over time.
my $pname = $0;
my $obddev = "";
my $obdstats = "stats";
my $statspath = "None";
my $statsname = $obdstats;
my $interval = 0;
my $counter = 999999999;
my $debug = 0;
my $have_readkey = 1;
my $width = 120;
my $height = 25;
sub usage()
{
print STDERR "Monitor read/write bandwidth of an OST device\n";
print STDERR "Usage: $pname [-h] [-i <interval>] [-v] <ost_name> [<interval> [<count>}]\n";
print STDERR " stats_file : Lustre 'stats' file to watch\n";
print STDERR " -d : debug mode\n";
print STDERR " -h : help, display this information\n";
print STDERR " -i interval: polling period in seconds\n";
print STDERR " -n count : number of samples printed\n";
print STDERR "example: $pname -i 5 lustre-OST0000\n";
print STDERR "Use CTRL + C to stop statistics printing\n";
exit 1;
}
# Command line parameter parsing
use Getopt::Std;
getopts('dhi:n:') or usage();
usage() if $opt_h;
$debug = $opt_d if $opt_d;
$interval = $opt_i if $opt_i;
$counter = $opt_n if $opt_n;
my $i = 0;
foreach (@ARGV) {
if ($i == 0) {
$obddev = $_;
$obddev =~ s/\./\//g;
} elsif ($i == 1) {
$interval = $_;
} elsif ($i == 2) {
$counter = $_;
} else {
print "ERROR: extra argument $_\n";
usage();
}
$i++;
}
if ( !$obddev ) {
print "ERROR: Need to specify stats_file\n";
usage();
}
# Process arguments
my $procpath = "/proc/fs/lustre";
foreach my $param ( "$obddev", "$obddev*/$obdstats", "$obddev*/*/$obdstats",
"*/$obddev*/$obdstats", "*/*/$obddev*/$obdstats" ) {
if ($debug) {
printf "trying $procpath/$param\n";
}
my $st = glob("$procpath/$param");
if ( -f "$st" ) {
$statspath = $st;
$statsname = `lctl list_param $param | head -n 1`;
if ($debug) {
print "using $statspath\n"
}
last;
}
}
if ($statspath =~ /^None$/) {
# server stats are currently in /proc/fs/lustre, but may move eventually
$procpath = "/sys/kernel/debug/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";
}
}
# 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";
}
print "$pname on $statsname\n";
my %cur;
my %last;
# Removed some statstics like open, close that the OST 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, $timestamp) =
("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");
# read statistics from the 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;
@iodata = split(/\s+/, $_);
my $name = $iodata[0];
$prevcount = $cur{$name};
if (defined($prevcount)) {
$last{$name} = $prevcount;
}
if ($name =~ /^$timestamp/) {
$cur{$name} = $iodata[1];
} elsif ($name =~ /^$read_bytes$/) {
if (defined($cur{"read_ops"})) {
$last{"read_ops"} = $cur{"read_ops"};
}
$cur{"read_ops"} = $iodata[1];
$cur{$name} = $iodata[6];
} elsif ($name =~ /^$write_bytes$/) {
if (defined($cur{"write_ops"})) {
$last{"write_ops"} = $cur{"write_ops"};
}
$cur{"write_ops"} = $iodata[1];
$cur{$name} = $iodata[6];
} else {
$cur{$name} = $iodata[1];
}
}
}
# process stats information read from the stats file.
# This subroutine gets called after every interval specified by user.
sub process_stats()
{
my $delta;
my $data;
my $last_time = $last{$timestamp};
if (!defined($last_time)) {
printf "Read: %.1f GiB, Write: %.1f GiB, cr: %lu dx: %lu, st: %lu, pu: %lu\n",
$cur{$read_bytes} / (1 << 30), $cur{$write_bytes} / (1 << 30),
$cur{$create}, $cur{$destroy}, $cur{$statfs}, $cur{$punch};
print "[NOTE: cx: create, dx: destroy, st: statfs, pu: punch]\n\n";
} else {
my $timespan = $cur{$timestamp} - $last{$timestamp};
my $rtot = ($cur{$read_bytes} - $last{$read_bytes}) / (1 << 20);
my $riops = ($cur{"read_ops"} - $last{"read_ops"}) / $timespan;
my $rrate = $rtot / $timespan;
my $wtot = ($cur{$write_bytes} - $last{$write_bytes}) / (1 << 20);
my $wiops = ($cur{"write_ops"} - $last{"write_ops"}) / $timespan;
my $wrate = $wtot / $timespan;
# this is printed once per screen, like vmstat/iostat
if ($count++ % ($height - 2) == 0) {
print "Timestamp Read-MiB RdMiB/s WriteMiB WrMiB/s RdIOPS WrIOPS\n";
print "---------- -------- ------- -------- ------- ------ ------\n";
if ($have_readkey) {
($width, $height, $wpixels, $hpixels) = GetTerminalSize();
} else {
($height, $width) = split / /, `stty size 2> /dev/null`;
#$width = 120 if ! $width
}
}
# This print repeats after every interval.
printf "%10lu %8.1f %7.1f %8.1f %7.1f %6lu %6lu",
$cur{$timestamp}, $rtot, $rrate, $wtot, $wrate, $riops, $wiops;
$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 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 && $counter-- > 0);
close STATS;