Fetching contributors…
Cannot retrieve contributors at this time
executable file 395 lines (366 sloc) 14.9 KB
#!/usr/bin/perl -w
####################################################################################
# creator: NinGoo
# Description: a tool for flashcache status per seconds
# created: 2012-01-04
# version: 0.3
# modified:
# 2012-01-10 NinGoo version 0.2 add --nocolor option
# 2012-01-11 NinGoo version 0.3 parse /proc/../flashcache_stats instead of dmsetup status
#
#####################################################################################
use POSIX qw(strftime);
use strict;
use Getopt::Long;
use Term::ANSIColor;
use File::Basename;
Getopt::Long::Configure qw(no_ignore_case);
$SIG{TERM} = $SIG{INT} = \&reset_color;
sub reset_color {
print YELLOW(),"\nExit Now...\n\n", RESET();
exit;
}
my %opt; # option parameters
my %result; # result for status per seconds
my %hit; # hit percent
my %sysctl; # sysctl parameters
my %dmsetup_table; # dmsetup table info
my %status; # flashcache status info
my $dev = "/dev/mapper/cachedev";
my $flashcache_stats_old_version = "/proc/flashcache_stats"; # for old version, this path is right
my $flashcache_stats_new_version; # for new version, get path in get_sysctl()
my $format_title = "%14s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %6s %6s %6s\n";
my $interval = 1;
$interval = 5 if(-f $flashcache_stats_old_version); # for old version ,using dmsetup status, default interval set to 5s
my $count = 0;
get_options();
get_dmsetup_table();
get_sysctl();
get_status();
print_header();
my $n = 0;
while(1){
if($n % 20 == 0){ # print title every 20 lines
print YELLOW(), BOLD();
printf $format_title, "time", "read/s", "write/s", "diskr/s", "diskw/s", "ssdr/s", "ssdw/s", "uread/s", "uwrit/s", "metaw/s", "clean/s","repl/s","wrepl/s", "hit%", "whit%", "dwhit%";
print RESET();
}
my %status_old = %status;
sleep($interval);
get_status();
# calculate status per second
foreach (keys(%status)){
$result{$_} = ($status{$_} - $status_old{$_}) / ($interval + 0.00001);
}
# calculate hit percent
$result{read_hit_percent} = sprintf "%d", ($result{read_hits} * 100) / ($result{reads} + 0.0001);
$result{write_hit_percent} = sprintf "%d", ($result{write_hits} * 100)/ ($result{writes} + 0.0001);
$result{dirty_write_hit_percent} = sprintf "%d", ($result{dirty_write_hits} * 100) / ($result{writes} + 0.0001);
# print value
print YELLOW();
printf "%14s ", get_current_time();
print RESET();
$result{reads} > 10000 ? print RED() : print WHITE();
printf "%7d ", $result{reads} and print RESET();
$result{writes} > 10000 ? print RED() : print WHITE();
printf "%7d ", $result{writes} and print RESET();
$result{disk_reads} > 1000 ? print RED() : print GREEN();
printf "%7d ", $result{disk_reads} and print RESET();
$result{disk_writes} > 1000 ? print RED() : print GREEN();
printf "%7d ", $result{disk_writes} and print RESET();
$result{ssd_reads} > 10000 ? print RED() : print WHITE();
printf "%7d ", $result{ssd_reads} and print RESET();
$result{ssd_writes} > 10000 ? print RED() : print WHITE();
printf "%7d ", $result{ssd_writes} and print RESET();
$result{uncached_reads} > 100 ? print RED() : print GREEN();
printf "%7d ", $result{uncached_reads} and print RESET();
$result{uncached_writes} > 100 ? print RED() : print GREEN();
printf "%7d ", $result{uncached_writes} and print RESET();
$result{metadata_ssd_writes} > 100 ? print RED() : print WHITE();
printf "%7d ", $result{metadata_ssd_writes} and print RESET();
$result{cleanings} > 100 ? print RED() : print WHITE();
printf "%7d ", $result{cleanings} and print RESET();
$result{replacement} > 100 ? print RED() : print WHITE();
printf "%7d ", $result{replacement} and print RESET();
$result{write_replacement} > 100 ? print RED() : print WHITE();
printf "%7d ", $result{write_replacement} and print RESET();
$result{read_hit_percent} < 90 ? print RED() : print GREEN();
printf "%6s ", $result{read_hit_percent}."|".$hit{read_hit_percent} and print RESET();
$result{write_hit_percent} < 90 ? print RED() : print GREEN();
printf "%6s ", $result{write_hit_percent}."|".$hit{write_hit_percent} and print RESET();
$result{dirty_write_hit_percent} < 90 ? print RED() : print GREEN();
printf "%6s ", $result{dirty_write_hit_percent}."|".$hit{dirty_write_hit_percent} and print RESET();
print "\n";
print RESET();
$n++;
exit if($count > 0 && $n >= $count);
}
##############################################################
# get sysctl parameter of flashcache
##############################################################
sub get_sysctl{
chomp(my $tmp = `sudo /sbin/sysctl -a | grep flashcache`);
my @lines = split(/\n/, $tmp);
foreach my $line (@lines){
if($line =~ /\+/){ # for new version of flashcache sysctl has per ssd+disk dev parameter
my $dev_device = basename($dmsetup_table{ssd_dev})."+".basename($dmsetup_table{disk_dev});
$dev_device =~ s/\/dev\///g;
$flashcache_stats_new_version = "/proc/flashcache/".$dev_device."/flashcache_stats";
next if($line !~ /\Q$dev_device\E/);
}
if($line =~ /cache_all/){
$sysctl{cache_all} = (split(/=/, $line))[1];
$sysctl{cache_all} =~ s/^\s+//;
}
elsif($line =~ /reclaim_policy/){
my $policy = (split(/=/, $line))[1];
$policy =~ s/\s+//;
$sysctl{reclaim_policy} = $policy eq '0'? 'FIFO' : 'LRU';
}
elsif($line =~ /dirty_thresh_pct/){
$sysctl{dirty_thresh_pct} = (split(/=/, $line))[1];
$sysctl{dirty_thresh_pct} =~ s/^\s+//;
}
elsif($line =~ /max_clean_ios_set/){
$sysctl{max_clean_ios_set} = (split(/=/, $line))[1];
$sysctl{max_clean_ios_set} =~ s/^\s+//;
}
elsif($line =~ /max_clean_ios_total/){
$sysctl{max_clean_ios_total} = (split(/=/, $line))[1];
$sysctl{max_clean_ios_total} =~ s/^\s+//;
}
}
}
##############################################################
# get status for flashcache device, using /proc/../flashcache instead of dmsetup status
###############################################################
sub get_status{
if(-f $flashcache_stats_old_version){
get_dmsetup_status();
return;
}
# new version using /proc/flashcache/dev+dev/flashcache_stats
if(defined($dmsetup_table{ssd_dev}) && defined($dmsetup_table{disk_dev})){
my @stats = split('\s+', `cat $flashcache_stats_new_version`);
foreach (@stats){
my @kv = split('=', $_);
$status{$kv[0]} = $kv[1];
}
$hit{read_hit_percent} = $status{read_hit_percent} if(defined($status{read_hit_percent}));
$hit{write_hit_percent} = $status{write_hit_percent} if(defined($status{write_hit_percent}));
$hit{dirty_write_hit_percent} = $status{dirty_write_hit_percent} if(defined($status{dirty_write_hit_percent}));
}
}
##############################################################
# get dmsetup status for flashcache device, for old version use only
###############################################################
sub get_dmsetup_status{
my $flag = 0;
chomp(my $tmp = `sudo dmsetup status $dev`);
my @lines = split(/\n/,$tmp);
foreach my $line(@lines){
$line =~ s/^\s+//g;
if($line =~ m/^reads\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{reads} = $1;
$status{writes} = $2;
}
elsif($line =~ m/^disk reads\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+).*\((\d+).*\((\d+)/g;
$status{disk_reads} = $1;
$status{disk_writes} = $2;
$status{ssd_reads} = $3;
$status{ssd_writes} = $4;
}
elsif($line =~ m/^uncached reads\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+).*\((\d+)/g;
$status{uncached_reads} = $1;
$status{uncached_writes} = $2;
$status{uncached_requeue} = $3;
}
elsif($line =~ m/^metadata batch\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{metadata_ssd_writes} = $2;
}
elsif($line =~ m/^cleanings\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{cleanings} = $1;
}
elsif($line =~ m/^replacement\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{replacement} = $1;
$status{write_replacement} = $2;
}
elsif($line =~ m/^read hits\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{read_hits} = $1;
$hit{read_hit_percent} = $2;
}
elsif($line =~ m/^write hits\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{write_hits} = $1;
$hit{write_hit_percent} = $2;
}
elsif($line =~ m/^dirty write hits\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/g;
$status{dirty_write_hits} = $1;
$hit{dirty_write_hit_percent} = $2;
}
}
exit if($flag == 0);
}
##############################################################
## get dmsetup table for flashcache device
###############################################################
sub get_dmsetup_table{
my $flag = 0;
chomp(my $tmp = `sudo dmsetup table $dev`);
my @lines = split(/\n/, $tmp);
foreach my $line (@lines){
$line =~ s/^\s+//g;
if($line =~ m/^ssd dev \(.*\)/){
$flag = 1;
if($line =~ /cache mode/){ # for new version of flashcache, get cache mode
$line =~ m/cache mode\((\w+)/;
$dmsetup_table{cache_mode} = $1;
}
#$line =~ m/(\/\w+\/\w+).*\((\/\w+\/\w+)/; # bugfix for Issue #1 of https://github.com/NinGoo/flashstat
$line =~ m/(\/[^\s]{1,})\).*\((\/[^\s]{1,})\)/;
$dmsetup_table{ssd_dev} = $1;
$dmsetup_table{disk_dev} = $2;
}
elsif($line =~ m/^capacity\(.*\)/){
$flag = 1;
if($line =~ /metadata block size\(.*\)/){
$line =~ m/(\d+\w).*\((\d+).*\((\d+\w).*\((\d+\w)/;
$dmsetup_table{capacity} = $1;
$dmsetup_table{associativity} = $2;
$dmsetup_table{block_size} = $3;
$dmsetup_table{metadata_block_size} = $4;
}
else{
$line =~ m/(\d+\w).*\((\d+).*\((\d+\w)/;
$dmsetup_table{capacity} = $1;
$dmsetup_table{associativity} = $2;
$dmsetup_table{block_size} = $3;
}
}
elsif($line =~ m/^total blocks\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+).*\((\d+)/;
$dmsetup_table{total_blocks} = $1;
$dmsetup_table{cached_blocks} = $2;
$dmsetup_table{cached_percent} = $3;
}
elsif($line =~ m/^dirty blocks\(.*\)/){
$flag = 1;
$line =~ m/(\d+).*\((\d+)/;
$dmsetup_table{dirty_blocks} = $1;
$dmsetup_table{dirty_percent} = $2;
}
elsif($line =~ /skip sequential thresh\(.*\)/){
$flag = 1;
$line =~ /(\d+\w)/;
$dmsetup_table{skip_sequential_thresh} = $1;
}
}
exit if($flag == 0);
}
##############################################################
# get current time
###############################################################
sub get_current_time{
return strftime("%m-%d %H:%M:%S",localtime);
}
##############################################################
## get option
###############################################################
sub get_options{
GetOptions(\%opt,
'h|help',
'i|interval=i',
'c|count=i',
'd|device=s',
'n|nocolor',
);
$opt{'h'} and print_usage();
$opt{'i'} and $interval = $opt{'i'};
$opt{'c'} and $count = $opt{'c'};
$opt{'d'} and $dev = $opt{'d'};
if(!defined($opt{'n'})){
import Term::ANSIColor ':constants';
}
else{
*RESET = sub { };
*YELLOW = sub { };
*RED = sub { };
*GREEN = sub { };
*WHITE = sub { };
*BOLD = sub { };
}
}
##############################################################
# print help information
###############################################################
sub print_usage{
print <<EOF;
==========================================================================================
Flashstat: a tool for flashcache status per second
Author : NinGoo(seaman.ning\@gmail.com)
Version : 0.3
==========================================================================================
Usage : flashstat [options]
Command line options :
-h,--help Print Help Info.
-i,--interval Time(second) Interval.
-C,--count Times.
-d,--device Flashcache device.
-n,--nocolor No color mode.
EOF
exit;
}
sub print_header{
print GREEN();
print "======================================================================================================\n";
print "Flashstat: a tool for flashcache status per second\n";
print "Author : NinGoo(seaman.ning\@gmail.com)\n";
print "Version : 0.3\n";
print "======================================================================================================\n";
if(defined($dmsetup_table{ssd_dev})){
printf "%20s: %10s", "SSD Device", $dmsetup_table{ssd_dev};
printf "%20s: %10s", " Disk Device", $dmsetup_table{disk_dev} if(defined($dmsetup_table{disk_dev}));
printf "%20s: %10s", " Cache Mode", $dmsetup_table{cache_mode} if(defined($dmsetup_table{cache_mode}));
print "\n";
printf "%20s: %10s", "Capacity", $dmsetup_table{capacity} if(defined($dmsetup_table{capacity}));
printf "%20s: %10s", " Block Size", $dmsetup_table{block_size} if(defined($dmsetup_table{block_size}));
printf "%20s: %10s", " Meta Block Size", $dmsetup_table{metadata_block_size} if(defined($dmsetup_table{metadata_block_size}));
print "\n";
printf "%20s: %10d", "Total Blocks", $dmsetup_table{total_blocks} if(defined($dmsetup_table{total_blocks}));
printf "%20s: %10d", " Cached Blocks", $dmsetup_table{cached_blocks} if(defined($dmsetup_table{cached_blocks}));
printf "%20s: %10d", " Cached Percent", $dmsetup_table{cached_percent} if(defined($dmsetup_table{cached_percent}));
print "\n";
printf "%20s: %10d", "Set Numbers", $dmsetup_table{associativity} if(defined($dmsetup_table{associativity}));
printf "%20s: %10d", " Dirty Blocks", $dmsetup_table{dirty_blocks} if(defined($dmsetup_table{dirty_blocks}));
printf "%20s: %10d", " Dirty Percent", $dmsetup_table{dirty_percent} if(defined($dmsetup_table{dirty_percent}));
print "\n";
printf "%20s: %10d", "cache_all", $sysctl{cache_all} if(defined($sysctl{cache_all}));
printf "%20s: %10s", " reclaim_policy", $sysctl{reclaim_policy} if(defined($sysctl{reclaim_policy}));
printf "%20s: %10d", " dirty_thresh_pct", $sysctl{dirty_thresh_pct} if(defined($sysctl{dirty_thresh_pct}));
print "\n";
printf "%20s: %10d", "max_clean_ios_set", $sysctl{max_clean_ios_set} if(defined($sysctl{max_clean_ios_set}));
printf "%20s: %10d", " max_clean_ios_total", $sysctl{max_clean_ios_total} if(defined($sysctl{max_clean_ios_total}));
printf "%20s: %10s", " skip_seq_thresh", $dmsetup_table{skip_sequential_thresh} if(defined($dmsetup_table{skip_sequential_thresh}));
print "\n";
print "======================================================================================================\n";
}
print RESET();
}