dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / stat / arcstat / arcstat.pl
blob8f13221910176671148804c13f66d205944777e8
1 #!/usr/perl5/bin/perl -w
2 # The above invocation line was changed in 0.5 to allow for
3 # interoperability with linux.
5 # Print out ZFS ARC Statistics exported via kstat(1)
6 # For a definition of fields, or usage, use arctstat.pl -v
8 # This script is a fork of the original arcstat.pl (0.1) by
9 # Neelakanth Nadgir, originally published on his Sun blog on
10 # 09/18/2007
11 # http://blogs.sun.com/realneel/entry/zfs_arc_statistics
13 # This version aims to improve upon the original by adding features
14 # and fixing bugs as needed. This version is maintained by
15 # Mike Harsch and is hosted in a public open source repository:
16 # http://github.com/mharsch/arcstat
18 # Comments, Questions, or Suggestions are always welcome.
19 # Contact the maintainer at ( mike at harschsystems dot com )
21 # CDDL HEADER START
23 # The contents of this file are subject to the terms of the
24 # Common Development and Distribution License, Version 1.0 only
25 # (the "License"). You may not use this file except in compliance
26 # with the License.
28 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
29 # or http://www.opensolaris.org/os/licensing.
30 # See the License for the specific language governing permissions
31 # and limitations under the License.
33 # When distributing Covered Code, include this CDDL HEADER in each
34 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
35 # If applicable, add the following below this CDDL HEADER, with the
36 # fields enclosed by brackets "[]" replaced with your own identifying
37 # information: Portions Copyright [yyyy] [name of copyright owner]
39 # CDDL HEADER END
42 # Fields have a fixed width. Every interval, we fill the "v"
43 # hash with its corresponding value (v[field]=value) using calculate().
44 # @hdr is the array of fields that needs to be printed, so we
45 # just iterate over this array and print the values using our pretty printer.
48 # Copyright (c) 2015 by Delphix. All rights reserved.
51 use strict;
52 use warnings;
53 use POSIX qw(strftime);
54 use Sun::Solaris::Kstat;
55 use Getopt::Long;
56 use IO::Handle;
58 my %cols = (# HDR => [Size, Scale, Description]
59 "time" =>[8, -1, "Time"],
60 "hits" =>[4, 1000, "ARC reads per second"],
61 "miss" =>[4, 1000, "ARC misses per second"],
62 "read" =>[4, 1000, "Total ARC accesses per second"],
63 "hit%" =>[4, 100, "ARC Hit percentage"],
64 "miss%" =>[5, 100, "ARC miss percentage"],
65 "dhit" =>[4, 1000, "Demand Data hits per second"],
66 "dmis" =>[4, 1000, "Demand Data misses per second"],
67 "dh%" =>[3, 100, "Demand Data hit percentage"],
68 "dm%" =>[3, 100, "Demand Data miss percentage"],
69 "phit" =>[4, 1000, "Prefetch hits per second"],
70 "pmis" =>[4, 1000, "Prefetch misses per second"],
71 "ph%" =>[3, 100, "Prefetch hits percentage"],
72 "pm%" =>[3, 100, "Prefetch miss percentage"],
73 "mhit" =>[4, 1000, "Metadata hits per second"],
74 "mmis" =>[4, 1000, "Metadata misses per second"],
75 "mread" =>[4, 1000, "Metadata accesses per second"],
76 "mh%" =>[3, 100, "Metadata hit percentage"],
77 "mm%" =>[3, 100, "Metadata miss percentage"],
78 "arcsz" =>[5, 1024, "ARC Size"],
79 "c" =>[4, 1024, "ARC Target Size"],
80 "mfu" =>[4, 1000, "MFU List hits per second"],
81 "mru" =>[4, 1000, "MRU List hits per second"],
82 "mfug" =>[4, 1000, "MFU Ghost List hits per second"],
83 "mrug" =>[4, 1000, "MRU Ghost List hits per second"],
84 "eskip" =>[5, 1000, "evict_skip per second"],
85 "mtxmis" =>[6, 1000, "mutex_miss per second"],
86 "dread" =>[5, 1000, "Demand data accesses per second"],
87 "pread" =>[5, 1000, "Prefetch accesses per second"],
88 "l2hits" =>[6, 1000, "L2ARC hits per second"],
89 "l2miss" =>[6, 1000, "L2ARC misses per second"],
90 "l2read" =>[6, 1000, "Total L2ARC accesses per second"],
91 "l2hit%" =>[6, 100, "L2ARC access hit percentage"],
92 "l2miss%" =>[7, 100, "L2ARC access miss percentage"],
93 "l2asize" =>[7, 1024, "Actual (compressed) size of the L2ARC"],
94 "l2size" =>[6, 1024, "Size of the L2ARC"],
95 "l2bytes" =>[7, 1024, "bytes read per second from the L2ARC"],
97 my %v=();
98 my @hdr = qw(time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c);
99 my @xhdr = qw(time mfu mru mfug mrug eskip mtxmis dread pread read);
100 my $int = 1; # Default interval is 1 second
101 my $count = 1; # Default count is 1
102 my $hdr_intr = 20; # Print header every 20 lines of output
103 my $opfile = "";
104 my $sep = " "; # Default separator is 2 spaces
105 my $raw_output;
106 my $version = "0.5";
107 my $l2exist = 0;
108 my $cmd = "Usage: arcstat [-hvxr] [-f fields] [-o file] [-s string] " .
109 "[interval [count]]\n";
110 my %cur;
111 my %d;
112 my $out;
113 my $kstat = Sun::Solaris::Kstat->new();
114 STDOUT->autoflush;
116 sub detailed_usage {
117 print STDERR "$cmd\n";
118 print STDERR "Field definitions are as follows:\n";
119 foreach my $hdr (keys %cols) {
120 print STDERR sprintf("%11s : %s\n", $hdr, $cols{$hdr}[2]);
122 exit(1);
125 sub usage {
126 print STDERR "$cmd\n";
127 print STDERR "\t -h : Print this help message\n";
128 print STDERR "\t -v : List all possible field headers " .
129 "and definitions\n";
130 print STDERR "\t -x : Print extended stats\n";
131 print STDERR "\t -r : Raw output mode (values not scaled)\n";
132 print STDERR "\t -f : Specify specific fields to print (see -v)\n";
133 print STDERR "\t -o : Redirect output to the specified file\n";
134 print STDERR "\t -s : Override default field separator with custom " .
135 "character or string\n";
136 print STDERR "\nExamples:\n";
137 print STDERR "\tarcstat -o /tmp/a.log 2 10\n";
138 print STDERR "\tarcstat -s \",\" -o /tmp/a.log 2 10\n";
139 print STDERR "\tarcstat -v\n";
140 print STDERR "\tarcstat -f time,hit%,dh%,ph%,mh% 1\n";
141 exit(1);
144 sub init {
145 my $desired_cols;
146 my $xflag = '';
147 my $hflag = '';
148 my $vflag;
149 my $res = GetOptions('x' => \$xflag,
150 'o=s' => \$opfile,
151 'help|h|?' => \$hflag,
152 'v' => \$vflag,
153 's=s' => \$sep,
154 'f=s' => \$desired_cols,
155 'r' => \$raw_output);
157 if (defined $ARGV[0] && defined $ARGV[1]) {
158 $int = $ARGV[0];
159 $count = $ARGV[1];
160 } elsif (defined $ARGV[0]) {
161 $int = $ARGV[0];
162 $count = 0;
165 usage() if !$res or $hflag or ($xflag and $desired_cols);
166 detailed_usage() if $vflag;
167 @hdr = @xhdr if $xflag; #reset headers to xhdr
169 # check if L2ARC exists
170 snap_stats();
171 if (defined $cur{"l2_size"}) {
172 $l2exist = 1;
175 if ($desired_cols) {
176 @hdr = split(/[ ,]+/, $desired_cols);
177 # Now check if they are valid fields
178 my @invalid = ();
179 my @incompat = ();
180 foreach my $ele (@hdr) {
181 if (not exists($cols{$ele})) {
182 push(@invalid, $ele);
183 } elsif (($l2exist == 0) && ($ele =~ /^l2/)) {
184 printf("No L2ARC here\n", $ele);
185 push(@incompat, $ele);
188 if (scalar @invalid > 0) {
189 print STDERR "Invalid column definition! -- "
190 . "@invalid\n\n";
191 usage();
194 if (scalar @incompat > 0) {
195 print STDERR "Incompatible field specified -- "
196 . "@incompat\n\n";
197 usage();
201 if ($opfile) {
202 open($out, ">$opfile") ||die "Cannot open $opfile for writing";
203 $out->autoflush;
204 select $out;
208 # Capture kstat statistics. We maintain 3 hashes, prev, cur, and
209 # d (delta). As their names imply they maintain the previous, current,
210 # and delta (cur - prev) statistics.
211 sub snap_stats {
212 my %prev = %cur;
213 if ($kstat->update()) {
214 printf("<State Changed>\n");
216 my $hashref_cur = $kstat->{"zfs"}{0}{"arcstats"};
217 %cur = %$hashref_cur;
218 foreach my $key (keys %cur) {
219 next if $key =~ /class/;
220 if (defined $prev{$key}) {
221 $d{$key} = $cur{$key} - $prev{$key};
222 } else {
223 $d{$key} = $cur{$key};
228 # Pretty print num. Arguments are width, scale, and num
229 sub prettynum {
230 my @suffix = (' ', 'K', 'M', 'G', 'T');
231 my $num = $_[2] || 0;
232 my $scale = $_[1];
233 my $sz = $_[0];
234 my $index = 0;
235 my $save = 0;
237 if ($scale == -1) { #special case for date field
238 return sprintf("%s", $num);
239 } elsif (($num > 0) && ($num < 1)) { #rounding error. return 0
240 $num = 0;
243 while ($num > $scale and $index < 5) {
244 $save = $num;
245 $num = $num/$scale;
246 $index++;
249 return sprintf("%*d", $sz, $num) if ($index == 0);
250 if (($save / $scale) < 10) {
251 return sprintf("%*.1f%s", $sz - 1, $num,$suffix[$index]);
252 } else {
253 return sprintf("%*d%s", $sz - 1, $num,$suffix[$index]);
257 sub print_values {
258 foreach my $col (@hdr) {
259 if (not $raw_output) {
260 printf("%s%s", prettynum($cols{$col}[0], $cols{$col}[1],
261 $v{$col}), $sep);
262 } else {
263 printf("%d%s", $v{$col} || 0, $sep);
266 printf("\n");
269 sub print_header {
270 if (not $raw_output) {
271 foreach my $col (@hdr) {
272 printf("%*s%s", $cols{$col}[0], $col, $sep);
274 } else {
275 # Don't try to align headers in raw mode
276 foreach my $col (@hdr) {
277 printf("%s%s", $col, $sep);
280 printf("\n");
283 sub calculate {
284 %v = ();
286 if ($raw_output) {
287 $v{"time"} = strftime("%s", localtime);
288 } else {
289 $v{"time"} = strftime("%H:%M:%S", localtime);
292 $v{"hits"} = $d{"hits"}/$int;
293 $v{"miss"} = $d{"misses"}/$int;
294 $v{"read"} = $v{"hits"} + $v{"miss"};
295 $v{"hit%"} = 100 * ($v{"hits"} / $v{"read"}) if $v{"read"} > 0;
296 $v{"miss%"} = 100 - $v{"hit%"} if $v{"read"} > 0;
298 $v{"dhit"} = ($d{"demand_data_hits"} +
299 $d{"demand_metadata_hits"})/$int;
300 $v{"dmis"} = ($d{"demand_data_misses"} +
301 $d{"demand_metadata_misses"})/$int;
303 $v{"dread"} = $v{"dhit"} + $v{"dmis"};
304 $v{"dh%"} = 100 * ($v{"dhit"} / $v{"dread"}) if $v{"dread"} > 0;
305 $v{"dm%"} = 100 - $v{"dh%"} if $v{"dread"} > 0;
307 $v{"phit"} = ($d{"prefetch_data_hits"} +
308 $d{"prefetch_metadata_hits"})/$int;
309 $v{"pmis"} = ($d{"prefetch_data_misses"} +
310 $d{"prefetch_metadata_misses"})/$int;
312 $v{"pread"} = $v{"phit"} + $v{"pmis"};
313 $v{"ph%"} = 100 * ($v{"phit"} / $v{"pread"}) if $v{"pread"} > 0;
314 $v{"pm%"} = 100 - $v{"ph%"} if $v{"pread"} > 0;
316 $v{"mhit"} = ($d{"prefetch_metadata_hits"} +
317 $d{"demand_metadata_hits"})/$int;
318 $v{"mmis"} = ($d{"prefetch_metadata_misses"} +
319 $d{"demand_metadata_misses"})/$int;
321 $v{"mread"} = $v{"mhit"} + $v{"mmis"};
322 $v{"mh%"} = 100 * ($v{"mhit"} / $v{"mread"}) if $v{"mread"} > 0;
323 $v{"mm%"} = 100 - $v{"mh%"} if $v{"mread"} > 0;
325 $v{"arcsz"} = $cur{"size"};
326 $v{"c"} = $cur{"c"};
327 $v{"mfu"} = $d{"mfu_hits"}/$int;
328 $v{"mru"} = $d{"mru_hits"}/$int;
329 $v{"mrug"} = $d{"mru_ghost_hits"}/$int;
330 $v{"mfug"} = $d{"mfu_ghost_hits"}/$int;
331 $v{"eskip"} = $d{"evict_skip"}/$int;
332 $v{"mtxmis"} = $d{"mutex_miss"}/$int;
334 if ($l2exist) {
335 $v{"l2hits"} = $d{"l2_hits"}/$int;
336 $v{"l2miss"} = $d{"l2_misses"}/$int;
337 $v{"l2read"} = $v{"l2hits"} + $v{"l2miss"};
338 $v{"l2hit%"} = 100 * ($v{"l2hits"} / $v{"l2read"})
339 if $v{"l2read"} > 0;
341 $v{"l2miss%"} = 100 - $v{"l2hit%"} if $v{"l2read"} > 0;
342 $v{"l2size"} = $cur{"l2_size"};
343 $v{"l2asize"} = $cur{"l2_asize"};
344 $v{"l2bytes"} = $d{"l2_read_bytes"}/$int;
348 sub main {
349 my $i = 0;
350 my $count_flag = 0;
352 init();
353 if ($count > 0) { $count_flag = 1; }
354 while (1) {
355 print_header() if ($i == 0);
356 snap_stats();
357 calculate();
358 print_values();
359 last if ($count_flag == 1 && $count-- <= 1);
360 $i = (($i == $hdr_intr) && (not $raw_output)) ? 0 : $i+1;
361 sleep($int);
363 close($out) if defined $out;
366 &main;