EXPORT_SYMBOL(ll_stats_ops_tally);
static const char *const ra_stat_string[] = {
- [RA_STAT_HIT] = "hits",
- [RA_STAT_MISS] = "misses",
- [RA_STAT_DISTANT_READPAGE] = "readpage not consecutive",
- [RA_STAT_MISS_IN_WINDOW] = "miss inside window",
- [RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page",
- [RA_STAT_FAILED_MATCH] = "failed lock match",
- [RA_STAT_DISCARDED] = "read but discarded",
- [RA_STAT_ZERO_LEN] = "zero length file",
- [RA_STAT_ZERO_WINDOW] = "zero size window",
- [RA_STAT_EOF] = "read-ahead to EOF",
- [RA_STAT_MAX_IN_FLIGHT] = "hit max r-a issue",
- [RA_STAT_WRONG_GRAB_PAGE] = "wrong page from grab_cache_page",
- [RA_STAT_FAILED_REACH_END] = "failed to reach end",
- [RA_STAT_ASYNC] = "async readahead",
- [RA_STAT_FAILED_FAST_READ] = "failed to fast read",
- [RA_STAT_MMAP_RANGE_READ] = "mmap range read",
+ [RA_STAT_HIT] = "hits",
+ [RA_STAT_MISS] = "misses",
+ [RA_STAT_DISTANT_READPAGE] = "readpage_not_consecutive",
+ [RA_STAT_MISS_IN_WINDOW] = "miss_inside_window",
+ [RA_STAT_FAILED_GRAB_PAGE] = "failed_grab_cache_page",
+ [RA_STAT_FAILED_MATCH] = "failed_lock_match",
+ [RA_STAT_DISCARDED] = "read_but_discarded",
+ [RA_STAT_ZERO_LEN] = "zero_length_file",
+ [RA_STAT_ZERO_WINDOW] = "zero_size_window",
+ [RA_STAT_EOF] = "readahead_to_eof",
+ [RA_STAT_MAX_IN_FLIGHT] = "hit_max_readahead_issue",
+ [RA_STAT_WRONG_GRAB_PAGE] = "wrong_page_from_grab_cache_page",
+ [RA_STAT_FAILED_REACH_END] = "failed_to_reach_end",
+ [RA_STAT_ASYNC] = "async_readahead",
+ [RA_STAT_FAILED_FAST_READ] = "failed_to_fast_read",
+ [RA_STAT_MMAP_RANGE_READ] = "mmap_range_read",
};
int ll_debugfs_register_super(struct super_block *sb, const char *name)
local write_bytes
local all_zeros
- all_zeros=:
- rm -fr $DIR/$tdir
+ all_zeros=true
test_mkdir $DIR/$tdir
# Read: 0, Write: 4, create/destroy: 2/0, stat: 1, punch: 0
- sync
- for ostnum in $(seq $OSTCOUNT); do
- # test-framework's OST numbering is one-based, while Lustre's
- # is zero-based
- ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
- # Parsing llobdstat's output sucks; we could grep the /proc
- # path, but that's likely to not be as portable as using the
- # llobdstat utility. So we parse lctl output instead.
- write_bytes=$(do_facet ost$ostnum lctl get_param -n \
- obdfilter/$ostname/stats |
- awk '/^write_bytes/ {print $7}' )
- echo "baseline_write_bytes@$OSTnum/$ostname=$write_bytes"
- if (( ${write_bytes:-0} > 0 ))
- then
- all_zeros=false
- break;
- fi
- done
+ sync
+ for ostnum in $(seq $OSTCOUNT); do
+ # test-framework's OST numbering is one-based, while Lustre's
+ # is zero-based
+ ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
+ # check if at least some write_bytes stats are counted
+ write_bytes=$(do_facet ost$ostnum lctl get_param -n \
+ obdfilter.$ostname.stats |
+ awk '/^write_bytes/ {print $7}' )
+ echo "baseline_write_bytes@ost$ostnum/$ostname=$write_bytes"
+ if (( ${write_bytes:-0} > 0 )); then
+ all_zeros=false
+ break
+ fi
+ done
- $all_zeros || return 0
+ $all_zeros || return 0
# Write four bytes
echo foo > $DIR/$tdir/bar
# Really write them
sync
- # Total up write_bytes after writing. We'd better find non-zeros.
- for ostnum in $(seq $OSTCOUNT); do
- ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
- write_bytes=$(do_facet ost$ostnum lctl get_param -n \
- obdfilter/$ostname/stats |
- awk '/^write_bytes/ {print $7}' )
- echo "write_bytes@$OSTnum/$ostname=$write_bytes"
- if (( ${write_bytes:-0} > 0 ))
- then
- all_zeros=false
- break;
- fi
- done
+ # Total up write_bytes after writing. We'd better find non-zeros.
+ for ostnum in $(seq $OSTCOUNT); do
+ ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
+ write_bytes=$(do_facet ost$ostnum lctl get_param -n \
+ obdfilter/$ostname/stats |
+ awk '/^write_bytes/ {print $7}' )
+ echo "write_bytes@ost$ostnum/$ostname=$write_bytes"
+ if (( ${write_bytes:-0} > 0 )); then
+ all_zeros=false
+ break
+ fi
+ done
- if $all_zeros
- then
- for ostnum in $(seq $OSTCOUNT); do
- ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
- echo "Check that write_bytes is present in obdfilter/*/stats:"
- do_facet ost$ostnum lctl get_param -n \
- obdfilter/$ostname/stats
- done
- error "OST not keeping write_bytes stats (b22312)"
- fi
+ if $all_zeros; then
+ for ostnum in $(seq $OSTCOUNT); do
+ ostname=$(printf "$FSNAME-OST%.4x" $((ostnum - 1)))
+ echo "Check write_bytes is in obdfilter.*.stats:"
+ do_facet ost$ostnum lctl get_param -n \
+ obdfilter.$ostname.stats
+ done
+ error "OST not keeping write_bytes stats (b=22312)"
+ fi
}
-run_test 33c "test llobdstat and write_bytes"
+run_test 33c "test write_bytes stats"
test_33d() {
[[ $MDSCOUNT -lt 2 ]] && skip_env "needs >= 2 MDTs"
function get_named_value()
{
- local tag
-
- tag=$1
- while read ;do
- line=$REPLY
- case $line in
- $tag*)
- echo $line | sed "s/^$tag[ ]*//"
- break
- ;;
- esac
- done
+ local tag=$1
+
+ grep -w "$tag" | sed "s/^$tag *\([0-9]*\) *.*/\1/"
}
export CACHE_MAX=$($LCTL get_param -n llite.*.max_cached_mb |
local nreads=10000
local cache_limit=32
- $LCTL set_param -n osc.*-osc*.rpc_stats 0
+ $LCTL set_param -n osc.*-osc*.rpc_stats=0
trap cleanup_101a EXIT
- $LCTL set_param -n llite.*.read_ahead_stats 0
- $LCTL set_param -n llite.*.max_cached_mb $cache_limit
+ $LCTL set_param -n llite.*.read_ahead_stats=0
+ $LCTL set_param -n llite.*.max_cached_mb=$cache_limit
#
# randomly read 10000 of 64K chunks from file 3x 32MB in size
discard=0
for s in $($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'read but discarded' | cut -d" " -f1); do
+ get_named_value 'read.but.discarded'); do
discard=$(($discard + $s))
done
cleanup_101a
local discard_limit=$((((STRIDE_LENGTH - 1)*3/(STRIDE_LENGTH*OSTCOUNT))* \
(STRIDE_LENGTH*OSTCOUNT - STRIDE_LENGTH)))
DISCARD=$($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'read but discarded' |
- cut -d" " -f1 | calc_total)
+ get_named_value 'read.but.discarded' | calc_total)
if [[ $DISCARD -gt $discard_limit ]]; then
$LCTL get_param llite.*.read_ahead_stats
error "Too many ($DISCARD) discarded pages with size (${READ_SIZE})"
local READ_COUNT=$((STRIPE_SIZE/BSIZE))
local STRIDE_LENGTH=$((STRIDE_SIZE/BSIZE))
local OFFSET=$((STRIPE_SIZE/BSIZE*(OSTCOUNT - 1)))
- $LCTL set_param -n llite.*.read_ahead_stats 0
+ $LCTL set_param -n llite.*.read_ahead_stats=0
$READS -f $DIR/$tfile -l $STRIDE_LENGTH -o $OFFSET \
-s $FILE_LENGTH -b $STRIPE_SIZE -a $READ_COUNT -n $ITERATION
cancel_lru_locks osc
setup_test101bc $STRIPE_SIZE $FILE_LENGTH
cancel_lru_locks osc
- $LCTL set_param osc.*.rpc_stats 0
+ $LCTL set_param osc.*.rpc_stats=0
$READS -f $DIR/$tfile -s$FILE_LENGTH -b$rsize -n$nreads -t 180
$LCTL get_param osc.*.rpc_stats
for osc_rpc_stats in $($LCTL get_param -N osc.*.rpc_stats); do
cleanup_test101bc
true
}
-run_test 101c "check stripe_size aligned read-ahead ================="
+run_test 101c "check stripe_size aligned read-ahead"
test_101d() {
[ $PARALLEL == "yes" ] && skip "skip parallel run"
echo Disable read-ahead
local old_RA=$($LCTL get_param -n llite.*.max_read_ahead_mb | head -n 1)
$LCTL set_param -n llite.*.max_read_ahead_mb=0
- stack_trap "$LCTL set_param -n llite.*.max_read_ahead_mb $old_RA" EXIT
+ stack_trap "$LCTL set_param -n llite.*.max_read_ahead_mb=$old_RA" EXIT
$LCTL get_param -n llite.*.max_read_ahead_mb
echo "Reading the test file $file with read-ahead disabled"
cancel_lru_locks $OSC
echo "Reset readahead stats"
- $LCTL set_param -n llite.*.read_ahead_stats 0
+ $LCTL set_param -n llite.*.read_ahead_stats=0
for ((i = 0; i < $count; i++)); do
dd if=$file.$i of=/dev/null bs=$bsize count=$size_KB 2>/dev/null
done
local miss=$($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'misses' | cut -d" " -f1 | calc_total)
+ get_named_value 'misses' | calc_total)
for ((i = 0; i < $count; i++)); do
rm -rf $file.$i 2>/dev/null
cancel_lru_locks osc
echo Reset readahead stats
- $LCTL set_param -n llite.*.read_ahead_stats 0
+ $LCTL set_param -n llite.*.read_ahead_stats=0
echo mmap read the file with small block size
iozone -i 1 -u 1 -l 1 -+n -r 32k -s 128m -B -f $DIR/$tfile \
echo checking missing pages
$LCTL get_param llite.*.read_ahead_stats
local miss=$($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'misses' | cut -d" " -f1 | calc_total)
+ get_named_value 'misses' | calc_total)
$LCTL set_param debug="$old_debug"
[ $miss -lt 3 ] || error "misses too much pages ('$miss')!"
echo "Read 10M of data but cross 64M bundary"
dd if=$DIR/$tfile of=/dev/null bs=10M skip=6 count=1
local miss=$($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'misses' | cut -d" " -f1 | calc_total)
+ get_named_value 'misses' | calc_total)
[ $miss -eq 1 ] || error "expected miss 1 but got $miss"
rm -f $p $DIR/$tfile
}
local count=$(($file_size / $blk))
dd if=$DIR/$tfile bs=$blk count=$count of=/dev/null
local miss=$($LCTL get_param -n llite.*.read_ahead_stats |
- get_named_value 'failed to fast read' |
- cut -d" " -f1 | calc_total)
+ get_named_value 'failed.to.fast.read' | calc_total)
$LCTL get_param -n llite.*.read_ahead_stats
[ $miss -eq $count ] || error "expected $count got $miss"
done
#!/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.
+# 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 "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;
+ 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;
}
-my $statspath = "None";
-my $interval = 0;
-my $counter = undef;
+# 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();
+}
-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]");
+# 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;
- } else {
- my $st = glob("/{proc,sys}/fs/lustre/obdfilter/$ARGV[0]/$obdstats");
- 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";
}
- }
- 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";
+# 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;
-my $mhz = 0;
-# Removed some statstics like open, close that obdfilter doesn't contain.
+# 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,
- $snapshot_time) = ("read_bytes", "write_bytes", "create", "destroy",
- "statfs", "punch", "snapshot_time");
+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");
-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.
+# read statistics from the 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)
@iodata = split(/\s+/, $_);
my $name = $iodata[0];
if (defined($prevcount)) {
$last{$name} = $prevcount;
}
- if ($name =~ /^read_bytes$/ || $name =~ /^write_bytes$/) {
- $cur{$name} = $iodata[6];
- }
- elsif ($name =~ /^snapshot_time$/) {
-# $cumulcount =~ /(\d+)/;
+ 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 obdfilter stats file.
+# 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{$snapshot_time};
+ my $last_time = $last{$timestamp};
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";
- }
+ 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{$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);
+ 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 %6.2fMB %6.2fMB/s %6.2fMB %6.2fMB/s",
- $cur{$snapshot_time}, $rdelta, $rrate, $wdelta, $wrate;
+ 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 ) {
}
}
-#Open the obdfilter stat file with STATS
+#Open the stat file with STATS
open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
do {
# read the statistics from stat file.
}
# Repeat the statistics printing after every "interval" specified in
# command line, up to counter times, if specified
-} while ($interval && (defined($counter) ? $counter-- > 0 : 1));
+} while ($interval && $counter-- > 0);
close STATS;
-# llobdfilter.pl ends here.
#!/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
+# 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 "Usage: $pname [-c] [-g] [-i <interval>] [-h <help>] <stats_file>\n";
- print STDERR " stats_file : lustre/.../stat\n";
- print STDERR " -i interval: polling period\n";
+ 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 "example: $pname -i 1 ost (monitors lustre/ost/OSS/ost/stats)\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: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 ( $print_once && $interval ) {
+ if (!$printed_once && $interval) {
printf "Timestamp [Name Count Rate Total Unit]...";
printf "\n--------------------------------------------------------------------";
- $print_once = 0;
+ $printed_once = 1;
}
if ($cc && !$starttime) {
$starttime = $cc
}
- printf "\n%-5.0f", ($cc - $starttime);
+ printf "\n%-5lu", ($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";
+ 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 ( $anysumsquare ) {
- printf "%10s", "stddev";
+ if ($anysum) {
+ printf "%-7s %8s %6s %8s %10s", "Unit", "last", "min", "avg", "max";
+ }
+ if ($anysumsquare && $width >= 100) {
+ printf " %8s", "stddev";
}
printf "\n";
}
seek STATS, 0, 0;
while (<STATS>) {
chop;
- ($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare)
- = split(/\s+/, $_);
+ ($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare) =
+ split(/\s+/, $_);
$prevcount = $cumulhash->{$name};
if (defined($prevcount)) {
$diff = $cumulcount - $prevcount;
&print_headings($cumulcount);
$| = 1;
if ($tdiff == 0) {
- $tdiff = 1; # avoid division by zero
+ $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;
+ 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 ($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 %.2f %lu %s",
- $name, $sum_diff, ($sum_diff/$tdiff), $sum, $unit;
+ printf " %s %lu %lu %lu %s", $name,
+ $diff ? ($sum_diff / $diff) : 0,
+ ($sum_diff/$tdiff), $sum, $unit;
} else {
- printf "%-8s %10lu %10lu %12.2f %10lu", $unit,
- $sum_diff, $min, ($sum/$cumulcount), $max;
+ printf "%-7s %8lu %6lu %8lu %10lu", $unit,
+ $diff ? ($sum_diff / $diff) : 0, $min,
+ ($sum/$cumulcount), $max;
}
- if (defined($sumsquare)) {
- my $s = $sumsquare - (($sum_orig*$sum_orig)/$cumulcount);
+ if (defined($sumsquare) && $width >= 100) {
+ my $s = $sumsquare - (($sum_orig * $sum_orig) /
+ $cumulcount);
if ($s >= 0) {
- my $cnt = ($cumulcount >= 2) ? $cumulcount : 2 ;
+ my $cnt = ($cumulcount >= 2) ?
+ $cumulcount : 2 ;
my $stddev = sqrt($s/($cnt - 1));
if (!$graphable) {
- printf " %9.2f ", $stddev;
+ printf " %8lu", $stddev;
}
}
}
$| = 1;
}
} else {
- if ($cumulcount!=0) {
+ if ($cumulcount != 0) {
# print info when interval is not specified.
printf "%-25s $cumulcount\n", $name
}
$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 (!$graphable) {
+ printf "\n";
}
}
-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";
}
+
# Clears stats file before printing information in intervals
-if ( $clear ) {
- open ( STATS, "> $statspath") || die "Cannot clear $statspath: $!\n";
+if ($clear) {
+ open(STATS, "> $statspath") || die "Cannot clear $statspath: $!\n";
print STATS " ";
close STATS;
- sleep($interval);
+ sleep($interval);
}
-use POSIX qw(strftime);
-$time_v = time();
-$hostname = `lctl list_nids | head -1`;
+
+$hostname = `lctl list_nids | head -1 2> /dev/null`;
chop($hostname);
-print "$pname: STATS on ", strftime("%D", localtime($time_v));
-print " $statspath on $hostname\n";
+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) {
+ if ($interval) {
sleep($interval);
}
-} while ($interval);
+} while ($interval && $counter-- > 0);
close STATS;