Whamcloud - gitweb
LU-14475 log: Rewrite some log messages
[fs/lustre-release.git] / lustre / utils / llstat
1 #!/usr/bin/perl
2 # llstat is a utility that takes stats files as input with optional
3 # clear-flag. The clear-flag is used to clear the stats file before
4 # printing stats information. The stats files may be in /proc/fs/lustre
5 # or /sys/kernel/debug/lustre. This first reads the required statistics
6 # information from specified stat file, process the information and prints
7 # the output after every interval specified by user.
8
9 # Subroutine for printing usages information
10 sub usage()
11 {
12         print STDERR "Monitor operation count/rate of a subsystem\n";
13         print STDERR "Usage: $pname [-c] [-d] [-g] [-h] [-i <interval>] [-n count] <stats_file>\n";
14         print STDERR "       stats_file : Lustre 'stats' file to watch\n";
15         print STDERR "       -i interval: polling period in seconds\n";
16         print STDERR "       -c         : clear stats file first\n";
17         print STDERR "       -d         : debug mode\n";
18         print STDERR "       -g         : graphable output format\n";
19         print STDERR "       -h         : help, display this information\n";
20         print STDERR "       -n count   : number of samples printed\n";
21         print STDERR "example: $pname -i 1 ost_io (ost.OSS.ost_io.stats)\n";
22         print STDERR "Use CTRL + C to stop statistics printing\n";
23         exit 1;
24 }
25
26 #Globals
27 my $pname = $0;
28 my $obddev = "";
29 my $obdstats = "stats";
30 my $clear = 0;
31 my $graphable = 0;
32 my $interval = 0;
33 my $statspath = "None";
34 my $statsname = "stats";
35 my $anysumsquare = 0;
36 my $printed_once = 0;
37 my %cumulhash;
38 my %sumhash;
39 my $anysum = 0;
40 my $starttime = 0;
41 my $width = 120;
42 my $have_readkey = 0;
43 my $debug = 0;
44 my $counter = 999999999;
45 my $ONE_MB = 1048576;
46
47 # Command line parameter parsing
48 use Getopt::Std;
49 getopts('cdghi:w:') or usage();
50 usage() if $opt_h;
51 $clear = 1 if $opt_c;
52 $debug = $opt_d if $opt_d;
53 $graphable = 1 if $opt_g;
54 $interval = $opt_i if $opt_i;
55 $counter = $opt_n if $opt_n;
56 $width = $opt_w if $opt_w;
57
58 my $i = 0;
59 foreach (@ARGV) {
60         $obddev = $_;
61         $obddev =~ s/\./\//g;
62         $i++;
63         if ($i > 1) {
64                 print "ERROR: extra argument $_\n";
65                 usage();
66         }
67 }
68 if (!$obddev) {
69         print "ERROR: Need to specify stats_file\n";
70         usage();
71 }
72
73 # Process arguments
74 my $procpath = "/sys/kernel/debug/lustre";
75 foreach my $param ( "$obddev", "$obddev*/$obdstats", "$obddev*/*/$obdstats",
76                     "*/$obddev*/$obdstats", "*/*/$obddev*/$obdstats" ) {
77         if ($debug) {
78                 print "trying $procpath/$param\n";
79         }
80         my $st = glob("$procpath/$param");
81         if ($debug) {
82                 print "glob $procpath/$param = $st\n";
83         }
84         if (-f "$st") {
85                 $statspath = $st;
86                 $statsname = `lctl list_param $param | head -n 1`;
87                 if ($debug) {
88                         print "using '$statsname' from $statspath\n"
89                 }
90                 last;
91         }
92 }
93 if ($statspath =~ /^None$/) {
94         # some older stats are kept in /proc, but don't look there first
95         $procpath = "/proc/fs/lustre";
96
97         foreach my $param ( "$obddev", "$obddev*/$obdstats", "$obddev*/*/$obdstats",
98                             "*/$obddev*/$obdstats", "*/*/$obddev*/$obdstats" ) {
99                 if ($debug) {
100                         print "trying $procpath/$param\n";
101                 }
102                 $st = glob("$procpath/$param");
103                 if ($debug) {
104                         print "glob $procpath/$param = $st\n";
105                 }
106                 if (-f "$st") {
107                         $statspath = $st;
108                         $statsname = `lctl list_param $param | head -n 1`;
109                         if ($debug) {
110                                 print "using $statspath\n"
111                         }
112                         last;
113                 }
114         }
115         if ($statspath =~ /^None$/) {
116                 die "Cannot locate stat file for: $obddev\n";
117         }
118 }
119
120 sub print_headings()
121 {       my $cc = $_[0];
122         if ($graphable) {
123                 if (!$printed_once && $interval) {
124                         printf "Timestamp [Name Count Rate Total Unit]...";
125                         printf "\n--------------------------------------------------------------------";
126                         $printed_once = 1;
127                 }
128                 if ($cc && !$starttime) {
129                         $starttime = $cc
130                 }
131                 printf "\n%-5lu", ($cc - $starttime);
132         } else {
133                 printf "$statsname @ $cc\n";
134                 if ($width <= 90) {
135                         printf "%-20s %-6s %-9s", "Name", "Rate", "#Events";
136                 } else {
137                         printf "%-23s %-6s %-6s %-10s", "Name", "Count", "Rate", "#Events";
138                 }
139                 if ($anysum) {
140                         printf "%-7s %8s %6s %8s %10s", "Unit", "last", "min", "avg", "max";
141                 }
142                 if ($anysumsquare && $width >= 100) {
143                         printf " %8s", "stddev";
144                 }
145                 printf "\n";
146         }
147 }
148
149 # known units are: reqs, bytes, usec, bufs, regs, pages
150 # readstat subroutine reads and processes statistics from stats file.
151 # This subroutine gets called after every interval specified by user.
152 sub readstat()
153 {
154         seek STATS, 0, 0;
155         while (<STATS>) {
156         chop;
157         ($name, $cumulcount, $samples, $unit, $min, $max, $sum, $sumsquare) =
158                 split(/\s+/, $_);
159         $prevcount = $cumulhash->{$name};
160         if (defined($prevcount)) {
161                 $diff = $cumulcount - $prevcount;
162                 if ($name eq "snapshot_time") {
163                         $tdiff = int($diff);
164                         &print_headings($cumulcount);
165                         $| = 1;
166                         if ($tdiff == 0) {
167                                 $tdiff = 1; # avoid division by zero
168                         }
169                 }
170                 elsif ($cumulcount!=0) {
171                         if ($graphable) {
172                                 my $myunit = $unit;
173                                 my $myname = $name;
174                                 if (defined($sum)) {
175                                         $myunit = "[reqs]";
176                                         $myname = $myname . "_rq";
177                                 }
178                                 printf "   %s %lu %lu %lu %s", $myname, $diff,
179                                         ($diff/$tdiff), $cumulcount, $myunit;
180                         } else {
181                                 if ($width <= 90) {
182                                         printf "%-20.20s %-6lu %-9lu",
183                                                 $name, ($diff/$tdiff), $cumulcount;
184                                 } else {
185                                         printf "%-23.23s %-6lu %-6lu %-10lu", $name,
186                                                 $diff, ($diff/$tdiff), $cumulcount;
187                                 }
188                         }
189                         if (defined($sum)) {
190                                 my $sum_orig = $sum;
191                                 my $sum_diff = $sum - $sumhash->{$name};
192                                 if ($graphable) {
193                                         printf "   %s %lu %lu %lu %s", $name,
194                                                 $diff ? ($sum_diff / $diff) : 0,
195                                                 ($sum_diff/$tdiff), $sum, $unit;
196                                 } else {
197                                         printf "%-7s %8lu %6lu %8lu %10lu", $unit,
198                                                 $diff ? ($sum_diff / $diff) : 0, $min,
199                                                 ($sum/$cumulcount), $max;
200                                 }
201                                 if (defined($sumsquare) && $width >= 100) {
202                                         my $s = $sumsquare - (($sum_orig * $sum_orig) /
203                                                               $cumulcount);
204                                         if ($s >= 0) {
205                                                 my $cnt = ($cumulcount >= 2) ?
206                                                           $cumulcount : 2 ;
207                                                 my $stddev = sqrt($s/($cnt - 1));
208                                                 if (!$graphable) {
209                                                         printf " %8lu", $stddev;
210                                                 }
211                                         }
212                                 }
213                         }
214                         if (!$graphable) {
215                                 printf "\n";
216                         }
217                         $| = 1;
218                 }
219         } else {
220                 if ($cumulcount != 0) {
221                         # print info when interval is not specified.
222                         printf "%-25s $cumulcount\n", $name
223                 }
224                 if (defined($sum)) {
225                         $anysum = 1;
226                 }
227                 if (defined($sumsquare)) {
228                         $anysumsquare = 1;
229                 }
230         }
231         $cumulhash->{$name} = $cumulcount;
232         $sumhash->{$name} = $sum;
233         } #end of while
234
235         if (!$graphable) {
236                 printf "\n";
237         }
238 }
239
240 # check if Term::ReadKey is installed for efficient tty size, but OK if missing
241 eval "require Term::ReadKey" or $have_readkey = 0;
242 if ($debug) {
243         print "have_readkey=$have_readkey\n";
244 }
245 if ($have_readkey) {
246         eval "use Term::ReadKey";
247 }
248
249 # Clears stats file before printing information in intervals
250 if ($clear) {
251         open(STATS, "> $statspath") || die "Cannot clear $statspath: $!\n";
252         print STATS " ";
253         close STATS;
254         sleep($interval);
255 }
256
257 $hostname = `lctl list_nids | head -1 2> /dev/null`;
258 chop($hostname);
259 printf "%s: %s at %s on %s\n", $pname, $statsname, `date`, $hostname;
260 open(STATS, $statspath) || die "Cannot open $statspath: $!\n";
261 do {
262         # find the terminal width
263         if (!$opt_w) {
264                 if ($have_readkey) {
265                         ($width, $height, $wpixels, $hpixels) = GetTerminalSize();
266                 } else {
267                         ($height, $width) = split / /, `stty size 2> /dev/null`;
268                         #$width = 120 if ! $width
269                 }
270         }
271
272         readstat();
273         if ($interval) {
274                 sleep($interval);
275         }
276 } while ($interval && $counter-- > 0);
277 close STATS;