4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * rewritten from UCB 4.13 83/09/25
27 * rewritten from SunOS 4.1 SID 1.18 89/10/06
30 * Copyright (c) 2012 by Delphix. All rights reserved.
31 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
32 * Copyright 2016 James S. Blachly, MD. All rights reserved.
44 #include <sys/types.h>
47 #include <sys/sysinfo.h>
50 #include <sys/systeminfo.h>
55 #include "statcommon.h"
57 #define DISK_OLD 0x0001
58 #define DISK_NEW 0x0002
59 #define DISK_EXTENDED 0x0004
60 #define DISK_ERRORS 0x0008
61 #define DISK_EXTENDED_ERRORS 0x0010
62 #define DISK_IOPATH_LI 0x0020 /* LunInitiator */
63 #define DISK_IOPATH_LTI 0x0040 /* LunTargetInitiator */
65 #define DISK_NORMAL (DISK_OLD | DISK_NEW)
66 #define DISK_IO_MASK (DISK_OLD | DISK_NEW | DISK_EXTENDED)
67 #define DISK_ERROR_MASK (DISK_ERRORS | DISK_EXTENDED_ERRORS)
68 #define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED)
72 #define NUMBER_OF_ERR_COUNTERS 3
75 * It's really a pseudo-gigabyte. We use 1000000000 bytes so that the disk
76 * labels don't look bad. 1GB is really 1073741824 bytes.
78 #define DISK_GIGABYTE 1000000000.0
81 * Function desciptor to be called when extended
84 typedef struct formatter
{
86 struct formatter
*next
;
90 * Used to get formatting right when printing tty/cpu
91 * data to the right of disk data
99 enum show_disk_mode show_disk_mode
= SHOW_ALL
;
101 char *cmdname
= "iostat";
104 static char one_blank
[] = " ";
105 static char two_blanks
[] = " ";
108 * count for number of lines to be emitted before a header is
109 * shown again. Only used for the basic format.
111 static uint_t tohdr
= 1;
114 * If we're in raw format, have we printed a header? We only do it
115 * once for raw but we emit it every REPRINT lines in non-raw format.
116 * This applies only for the basic header. The extended header is
117 * done only once in both formats.
119 static uint_t hdr_out
;
122 * Flags representing arguments from command line
124 static uint_t do_tty
; /* show tty info (-t) */
125 static uint_t do_disk
; /* show disk info per selected */
126 /* format (-d, -D, -e, -E, -x -X -Y) */
127 static uint_t do_cpu
; /* show cpu info (-c) */
128 static uint_t do_interval
; /* do intervals (-I) */
129 static int do_partitions
; /* per-partition stats (-p) */
130 static int do_partitions_only
; /* per-partition stats only (-P) */
131 /* no per-device stats for disks */
132 static uint_t do_conversions
; /* display disks as cXtYdZ (-n) */
133 static uint_t do_megabytes
; /* display data in MB/sec (-M) */
134 static uint_t do_controller
; /* display controller info (-C) */
135 static uint_t do_raw
; /* emit raw format (-r) */
136 static uint_t timestamp_fmt
= NODATE
; /* timestamp each display (-T) */
137 static uint_t do_devid
; /* -E should show devid */
140 * Default number of disk drives to be displayed in basic format
142 #define DEFAULT_LIMIT 4
144 struct iodev_filter df
;
146 static uint_t suppress_state
; /* skip state change messages */
147 static uint_t suppress_zero
; /* skip zero valued lines */
148 static uint_t show_mountpts
; /* show mount points */
149 static int interval
; /* interval (seconds) to output */
150 static int iter
; /* iterations from command line */
152 #define SMALL_SCRATCH_BUFLEN MAXNAMELEN
154 static int iodevs_nl
; /* name field width */
155 #define IODEVS_NL_MIN 6 /* not too thin for "device" */
156 #define IODEVS_NL_MAX 24 /* but keep full width under 80 */
158 static char disk_header
[132];
159 static uint_t dh_len
; /* disk header length for centering */
160 static int lineout
; /* data waiting to be printed? */
162 static struct snapshot
*newss
;
163 static struct snapshot
*oldss
;
164 static double getime
; /* elapsed time */
165 static double percent
; /* 100 / etime */
168 * List of functions to be called which will construct the desired output
170 static format_t
*formatter_list
;
171 static format_t
*formatter_end
;
173 static u_longlong_t
ull_delta(u_longlong_t
, u_longlong_t
);
174 static uint_t
u32_delta(uint_t
, uint_t
);
175 static void setup(void (*nfunc
)(void));
176 static void print_tty_hdr1(void);
177 static void print_tty_hdr2(void);
178 static void print_cpu_hdr1(void);
179 static void print_cpu_hdr2(void);
180 static void print_tty_data(void);
181 static void print_cpu_data(void);
182 static void print_err_hdr(void);
183 static void print_disk_header(void);
184 static void hdrout(void);
185 static void disk_errors(void);
186 static void do_newline(void);
187 static void push_out(const char *, ...);
188 static void printhdr(int);
189 static void printxhdr(void);
190 static void usage(void);
191 static void do_args(int, char **);
192 static void do_format(void);
193 static void show_all_disks(void);
194 static void show_first_disk(void);
195 static void show_other_disks(void);
196 static void show_disk_errors(void *, void *, void *);
197 static void write_core_header(void);
198 static int fzero(double value
);
199 static int safe_strtoi(char const *val
, char *errmsg
);
202 main(int argc
, char **argv
)
204 enum snapshot_types types
= SNAP_SYSTEM
;
211 (void) setlocale(LC_ALL
, "");
212 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
213 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
215 (void) textdomain(TEXT_DOMAIN
);
220 * iostat historically showed CPU changes, even though
221 * it doesn't provide much useful information
226 types
|= SNAP_IODEVS
;
228 if (do_disk
&& !do_partitions_only
)
229 df
.if_allowed_types
|= IODEV_DISK
;
230 if (do_disk
& DISK_IOPATH_LI
) {
231 df
.if_allowed_types
|= IODEV_IOPATH_LTI
;
232 types
|= SNAP_IOPATHS_LI
;
234 if (do_disk
& DISK_IOPATH_LTI
) {
235 df
.if_allowed_types
|= IODEV_IOPATH_LTI
;
236 types
|= SNAP_IOPATHS_LTI
;
238 if (do_disk
& DISK_ERROR_MASK
)
239 types
|= SNAP_IODEV_ERRORS
;
240 if (do_partitions
|| do_partitions_only
)
241 df
.if_allowed_types
|= IODEV_PARTITION
;
243 types
|= SNAP_IODEV_PRETTY
;
245 types
|= SNAP_IODEV_DEVID
;
247 if (!(do_disk
& PRINT_VERTICAL
) ||
248 (do_disk
& DISK_EXTENDED_ERRORS
))
249 fail(0, "-C can only be used with -e or -x.");
250 types
|= SNAP_CONTROLLERS
;
251 df
.if_allowed_types
|= IODEV_CONTROLLER
;
254 hz
= sysconf(_SC_CLK_TCK
);
257 * Undocumented behavior - sending a SIGCONT will result
258 * in a new header being emitted. Used only if we're not
259 * doing extended headers. This is a historical
262 if (!(do_disk
& PRINT_VERTICAL
))
263 (void) signal(SIGCONT
, printhdr
);
266 period_n
= (hrtime_t
)interval
* NANOSEC
;
270 start_n
= gethrtime();
271 newss
= acquire_snapshot(kc
, types
, &df
);
273 /* compute width of "device" field */
274 iodevs_nl
= newss
->s_iodevs_is_name_maxlen
;
275 iodevs_nl
= (iodevs_nl
< IODEVS_NL_MIN
) ?
276 IODEVS_NL_MIN
: iodevs_nl
;
277 iodevs_nl
= (iodevs_nl
> IODEVS_NL_MAX
) ?
278 IODEVS_NL_MAX
: iodevs_nl
;
282 forever
= (iter
== 0);
284 if (do_conversions
&& show_mountpts
)
287 if (do_tty
|| do_cpu
) {
289 oldks
= oldss
? &oldss
->s_sys
.ss_agg_sys
: NULL
;
290 getime
= cpu_ticks_delta(oldks
,
291 &newss
->s_sys
.ss_agg_sys
);
292 percent
= (getime
> 0.0) ? 100.0 / getime
: 0.0;
293 getime
= (getime
/ nr_active_cpus(newss
)) / hz
;
295 getime
= (double)interval
;
296 if (getime
== 0.0 || do_interval
)
300 if (formatter_list
) {
302 tmp
= formatter_list
;
304 if (timestamp_fmt
!= NODATE
)
305 print_timestamp(timestamp_fmt
);
311 (void) fflush(stdout
);
314 /* only remaining/doing a single iteration, we are done */
320 sleep_until(&start_n
, period_n
, forever
, &caught_cont
);
322 free_snapshot(oldss
);
324 newss
= acquire_snapshot(kc
, types
, &df
);
325 iodevs_nl
= (newss
->s_iodevs_is_name_maxlen
> iodevs_nl
) ?
326 newss
->s_iodevs_is_name_maxlen
: iodevs_nl
;
327 iodevs_nl
= (iodevs_nl
< IODEVS_NL_MIN
) ?
328 IODEVS_NL_MIN
: iodevs_nl
;
329 iodevs_nl
= (iodevs_nl
> IODEVS_NL_MAX
) ?
330 IODEVS_NL_MAX
: iodevs_nl
;
333 snapshot_report_changes(oldss
, newss
);
335 /* if config changed, show stats from boot */
336 if (snapshot_has_changed(oldss
, newss
)) {
337 free_snapshot(oldss
);
343 free_snapshot(oldss
);
344 free_snapshot(newss
);
345 (void) kstat_close(kc
);
351 * Some magic numbers used in header formatting.
353 * DISK_LEN = length of either "kps tps serv" or "wps rps util"
354 * using 0 as the first position
356 * DISK_ERROR_LEN = length of "s/w h/w trn tot" with one space on
357 * either side. Does not use zero as first pos.
359 * DEVICE_LEN = length of "device" + 1 character.
363 #define DISK_ERROR_LEN 16
368 show_disk_name(void *v1
, void *v2
, void *data
)
370 struct iodev_snapshot
*dev
= (struct iodev_snapshot
*)v2
;
373 char fbuf
[SMALL_SCRATCH_BUFLEN
];
378 name
= do_conversions
? dev
->is_pretty
: dev
->is_name
;
379 name
= name
? name
: dev
->is_name
;
388 * which will be displayed
393 width
= (DISK_LEN
+ 1)/2 + (slen
/ 2);
394 (void) snprintf(fbuf
, sizeof (fbuf
),
397 push_out("%-13.13s ", name
);
405 show_disk_header(void *v1
, void *v2
, void *data
)
407 push_out(disk_header
);
411 * Write out a two line header. What is written out depends on the flags
412 * selected but in the worst case consists of a tty header, a disk header
413 * providing information for 4 disks and a cpu header.
415 * The tty header consists of the word "tty" on the first line above the
416 * words "tin tout" on the next line. If present the tty portion consumes
417 * the first 10 characters of each line since "tin tout" is surrounded
420 * Each of the disk sections is a 14 character "block" in which the name of
421 * the disk is centered in the first 12 characters of the first line.
423 * The cpu section is an 11 character block with "cpu" centered over the
426 * The worst case should look as follows:
428 * 0---------1--------2---------3---------4---------5---------6---------7-------
429 * tty sd0 sd1 sd2 sd3 cpu
430 * tin tout kps tps serv kps tps serv kps tps serv kps tps serv us sy dt id
431 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN
433 * When -D is specified, the disk header looks as follows (worst case):
435 * 0---------1--------2---------3---------4---------5---------6---------7-------
436 * tty sd0 sd1 sd2 sd3 cpu
437 * tin tout rps wps util rps wps util rps wps util rps wps util us sy dt id
438 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN
444 * If we're here because a signal fired, reenable the
448 (void) signal(SIGCONT
, printhdr
);
452 * Horizontal mode headers
459 if (do_disk
& DISK_NORMAL
) {
460 (void) snapshot_walk(SNAP_IODEVS
, NULL
, newss
,
461 show_disk_name
, NULL
);
474 if (do_disk
& DISK_NORMAL
) {
475 (void) snapshot_walk(SNAP_IODEVS
, NULL
, newss
,
476 show_disk_header
, NULL
);
487 * Write out the extended header centered over the core information.
490 write_core_header(void)
492 char *edev
= "extended device statistics";
493 uint_t lead_space_ct
;
494 uint_t follow_space_ct
;
499 * The things we do to look nice...
501 * Center the core output header. Make sure we have the
502 * right number of trailing spaces for follow-on headers
503 * (i.e., cpu and/or tty and/or errors).
505 edevlen
= strlen(edev
);
506 lead_space_ct
= dh_len
- edevlen
;
508 if (lead_space_ct
> 0) {
509 follow_space_ct
= dh_len
- (lead_space_ct
+ edevlen
);
510 if (do_disk
& DISK_ERRORS
)
511 follow_space_ct
-= DISK_ERROR_LEN
;
512 if ((do_disk
& DISK_EXTENDED
) && do_conversions
)
513 follow_space_ct
-= DEVICE_LEN
;
515 push_out("%1$*2$.*2$s%3$s%4$*5$.*5$s", one_blank
,
516 lead_space_ct
, edev
, one_blank
, follow_space_ct
);
518 push_out("%56s", edev
);
524 * In extended mode headers, we don't want to reprint the header on
525 * signals as they are printed every time anyways.
532 * Vertical mode headers
534 if (do_disk
& DISK_EXTENDED
)
535 setup(write_core_header
);
536 if (do_disk
& DISK_ERRORS
)
537 setup(print_err_hdr
);
539 if (do_conversions
) {
541 if (do_disk
& (DISK_EXTENDED
| DISK_ERRORS
))
542 setup(print_disk_header
);
546 setup(print_tty_hdr1
);
548 setup(print_cpu_hdr1
);
551 if (do_disk
& (DISK_EXTENDED
| DISK_ERRORS
))
552 setup(print_disk_header
);
554 setup(print_tty_hdr2
);
556 setup(print_cpu_hdr2
);
562 * Write out a line for this disk - note that show_disk writes out
563 * full lines or blocks for each selected disk.
566 show_disk(void *v1
, void *v2
, void *data
)
568 uint32_t err_counters
[NUMBER_OF_ERR_COUNTERS
];
569 boolean_t display_err_counters
= do_disk
& DISK_ERRORS
;
570 struct iodev_snapshot
*old
= (struct iodev_snapshot
*)v1
;
571 struct iodev_snapshot
*new = (struct iodev_snapshot
*)v2
;
572 int *count
= (int *)data
;
573 double rps
, wps
, tps
, mtps
, krps
, kwps
, kps
, avw
, avr
, w_pct
, r_pct
;
574 double wserv
, rserv
, serv
;
575 double iosize
; /* kb/sec or MB/sec */
576 double etime
, hr_etime
;
578 u_longlong_t ldeltas
;
590 switch (show_disk_mode
) {
591 case SHOW_FIRST_ONLY
:
592 if (count
!= NULL
&& *count
)
596 case SHOW_SECOND_ONWARDS
:
597 if (count
!= NULL
&& !*count
) {
607 disk_name
= do_conversions
? new->is_pretty
: new->is_name
;
608 disk_name
= disk_name
? disk_name
: new->is_name
;
611 * Only do if we want IO stats - Avoids errors traveling this
612 * section if that's all we want to see.
614 if (do_disk
& DISK_IO_MASK
) {
616 t_delta
= hrtime_delta(old
->is_snaptime
,
619 t_delta
= hrtime_delta(new->is_crtime
,
623 if (new->is_nr_children
) {
624 if (new->is_type
== IODEV_CONTROLLER
) {
625 t_delta
/= new->is_nr_children
;
626 } else if ((new->is_type
== IODEV_IOPATH_LT
) ||
627 (new->is_type
== IODEV_IOPATH_LI
)) {
630 t_delta
= new->is_crtime
;
632 t_delta
/= new->is_nr_children
;
636 hr_etime
= (double)t_delta
;
638 hr_etime
= (double)NANOSEC
;
639 etime
= hr_etime
/ (double)NANOSEC
;
641 /* reads per second */
642 udeltas
= u32_delta(old
? old
->is_stats
.reads
: 0,
643 new->is_stats
.reads
);
644 rps
= (double)udeltas
;
647 /* writes per second */
648 udeltas
= u32_delta(old
? old
->is_stats
.writes
: 0,
649 new->is_stats
.writes
);
650 wps
= (double)udeltas
;
654 /* transactions per second */
657 * report throughput as either kb/sec or MB/sec
665 ldeltas
= ull_delta(old
? old
->is_stats
.nread
: 0,
666 new->is_stats
.nread
);
668 krps
= (double)ldeltas
;
674 ldeltas
= ull_delta(old
? old
->is_stats
.nwritten
: 0,
675 new->is_stats
.nwritten
);
677 kwps
= (double)ldeltas
;
684 * Blocks transferred per second
689 * Average number of wait transactions waiting
691 w_delta
= hrtime_delta((u_longlong_t
)
692 (old
? old
->is_stats
.wlentime
: 0),
693 new->is_stats
.wlentime
);
695 avw
= (double)w_delta
;
701 * Average number of run transactions waiting
703 r_delta
= hrtime_delta(old
? old
->is_stats
.rlentime
: 0,
704 new->is_stats
.rlentime
);
706 avr
= (double)r_delta
;
712 * Average wait service time in milliseconds
714 if (tps
> 0.0 && (avw
!= 0.0 || avr
!= 0.0)) {
725 serv
= rserv
+ wserv
;
732 /* % of time there is a transaction waiting for service */
733 t_delta
= hrtime_delta(old
? old
->is_stats
.wtime
: 0,
734 new->is_stats
.wtime
);
736 w_pct
= (double)t_delta
;
741 * Average the wait queue utilization over the
742 * the controller's devices, if this is a controller.
744 if (new->is_type
== IODEV_CONTROLLER
)
745 w_pct
/= new->is_nr_children
;
749 /* % of time there is a transaction running */
750 t_delta
= hrtime_delta(old
? old
->is_stats
.rtime
: 0,
751 new->is_stats
.rtime
);
753 r_pct
= (double)t_delta
;
758 * Average the percent busy over the controller's
759 * devices, if this is a controller.
761 if (new->is_type
== IODEV_CONTROLLER
)
762 w_pct
/= new->is_nr_children
;
767 /* % of time there is a transaction running */
778 if (do_disk
& (DISK_EXTENDED
| DISK_ERRORS
)) {
779 if ((!do_conversions
) && ((suppress_zero
== 0) ||
780 ((do_disk
& DISK_EXTENDED
) == 0))) {
783 iodevs_nl
, iodevs_nl
, disk_name
);
791 * The error counters are read first (if asked for and if they are
794 bzero(err_counters
, sizeof (err_counters
));
796 if (display_err_counters
&& (new->is_errors
.ks_data
!= NULL
)) {
800 knp
= KSTAT_NAMED_PTR(&new->is_errors
);
801 for (i
= 0; i
< NUMBER_OF_ERR_COUNTERS
; i
++) {
802 switch (knp
[i
].data_type
) {
803 case KSTAT_DATA_ULONG
:
804 case KSTAT_DATA_ULONGLONG
:
805 err_counters
[i
] = knp
[i
].value
.ui32
;
806 toterrs
+= knp
[i
].value
.ui32
;
814 switch (do_disk
& DISK_IO_MASK
) {
817 fstr
= "%3.0f %3.0f %4.0f ";
819 fstr
= "%.0f,%.0f,%.0f";
820 push_out(fstr
, kps
, tps
, serv
);
824 fstr
= "%3.0f %3.0f %4.1f ";
826 fstr
= "%.0f,%.0f,%.1f";
827 push_out(fstr
, rps
, wps
, r_pct
);
831 if (fzero(rps
) && fzero(wps
) && fzero(krps
) &&
832 fzero(kwps
) && fzero(avw
) && fzero(avr
) &&
833 fzero(serv
) && fzero(w_pct
) && fzero(r_pct
) &&
836 display_err_counters
= B_FALSE
;
837 } else if (do_conversions
== 0) {
840 iodevs_nl
, iodevs_nl
, disk_name
);
847 if (!do_conversions
) {
849 fstr
= " %6.1f %6.1f %6.1f %6.1f "
850 "%4.1f %4.1f %6.1f %3.0f "
853 fstr
= "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
856 push_out(fstr
, rps
, wps
, krps
, kwps
, avw
, avr
,
860 fstr
= " %6.1f %6.1f %6.1f %6.1f "
861 "%4.1f %4.1f %6.1f %6.1f "
864 fstr
= "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
865 "%.1f,%.1f,%.0f,%.0f";
867 push_out(fstr
, rps
, wps
, krps
, kwps
, avw
, avr
,
868 wserv
, rserv
, w_pct
, r_pct
);
874 if (display_err_counters
) {
879 if (do_disk
== DISK_ERRORS
)
880 push_out(two_blanks
);
886 for (i
= 0; i
< NUMBER_OF_ERR_COUNTERS
; i
++)
887 push_out(efstr
, err_counters
[i
]);
889 push_out(efstr
, toterrs
);
892 if (suppress_zero
== 0 || doit
== 1) {
893 if ((do_disk
& (DISK_EXTENDED
| DISK_ERRORS
)) &&
895 push_out("%s", disk_name
);
896 if (show_mountpts
&& new->is_dname
) {
900 char lub
[SMALL_SCRATCH_BUFLEN
];
902 lu
= strrchr(new->is_dname
, '/');
904 /* only the part after a possible '/' */
905 dnlu
= strrchr(disk_name
, '/');
907 strcmp(dnlu
, lu
) == 0)
914 (void) strcat(lub
, "/");
921 mount_pt
= lookup_mntent_byname(lu
);
925 mount_pt
->mount_point
);
928 mount_pt
->mount_point
);
934 if ((do_disk
& PRINT_VERTICAL
) && show_disk_mode
!= SHOW_FIRST_ONLY
)
944 (void) fprintf(stderr
,
945 "Usage: iostat [-cCdDeEiImMnpPrstxXYz] "
946 " [-l n] [-T d|u] [disk ...] [interval [count]]\n"
947 "\t\t-c: report percentage of time system has spent\n"
948 "\t\t\tin user/system/dtrace/idle mode\n"
949 "\t\t-C: report disk statistics by controller\n"
950 "\t\t-d: display disk Kb/sec, transfers/sec, avg. \n"
951 "\t\t\tservice time in milliseconds \n"
952 "\t\t-D: display disk reads/sec, writes/sec, \n"
953 "\t\t\tpercentage disk utilization \n"
954 "\t\t-e: report device error summary statistics\n"
955 "\t\t-E: report extended device error statistics\n"
956 "\t\t-i: show device IDs for -E output\n"
957 "\t\t-I: report the counts in each interval,\n"
958 "\t\t\tinstead of rates, where applicable\n"
959 "\t\t-l n: Limit the number of disks to n\n"
960 "\t\t-m: Display mount points (most useful with -p)\n"
961 "\t\t-M: Display data throughput in MB/sec "
962 "instead of Kb/sec\n"
963 "\t\t-n: convert device names to cXdYtZ format\n"
964 "\t\t-p: report per-partition disk statistics\n"
965 "\t\t-P: report per-partition disk statistics only,\n"
966 "\t\t\tno per-device disk statistics\n"
967 "\t\t-r: Display data in comma separated format\n"
968 "\t\t-s: Suppress state change messages\n"
969 "\t\t-T d|u Display a timestamp in date (d) or unix "
971 "\t\t-t: display chars read/written to terminals\n"
972 "\t\t-x: display extended disk statistics\n"
973 "\t\t-X: display I/O path statistics\n"
974 "\t\t-Y: display I/O path (I/T/L) statistics\n"
975 "\t\t-z: Suppress entries with all zero values\n");
981 show_disk_errors(void *v1
, void *v2
, void *d
)
983 struct iodev_snapshot
*disk
= (struct iodev_snapshot
*)v2
;
989 if (disk
->is_errors
.ks_ndata
== 0)
991 if (disk
->is_type
== IODEV_CONTROLLER
)
994 dev_name
= do_conversions
? disk
->is_pretty
: disk
->is_name
;
995 dev_name
= dev_name
? dev_name
: disk
->is_name
;
997 len
= strlen(dev_name
);
999 push_out("%s ", dev_name
);
1001 push_out("%-20.20s ", dev_name
);
1004 push_out("%-16.16s ", dev_name
);
1006 push_out("%-9.9s ", dev_name
);
1010 knp
= KSTAT_NAMED_PTR(&disk
->is_errors
);
1011 for (i
= 0; i
< disk
->is_errors
.ks_ndata
; i
++) {
1012 /* skip kstats that the driver did not kstat_named_init */
1013 if (knp
[i
].name
[0] == 0)
1016 col
+= strlen(knp
[i
].name
);
1018 switch (knp
[i
].data_type
) {
1019 case KSTAT_DATA_CHAR
:
1020 case KSTAT_DATA_STRING
:
1021 if ((strcmp(knp
[i
].name
, "Serial No") == 0) &&
1023 if (disk
->is_devid
) {
1024 push_out("Device Id: %s ",
1026 col
+= strlen(disk
->is_devid
);
1028 push_out("Device Id: ");
1033 if (knp
[i
].data_type
== KSTAT_DATA_CHAR
) {
1034 push_out("%s: %-.16s ", knp
[i
].name
,
1035 &knp
[i
].value
.c
[0]);
1036 col
+= strnlen(&knp
[i
].value
.c
[0], 16);
1038 push_out("%s: %s ", knp
[i
].name
,
1039 KSTAT_NAMED_STR_PTR(&knp
[i
]));
1041 KSTAT_NAMED_STR_BUFLEN(&knp
[i
]) - 1;
1044 case KSTAT_DATA_ULONG
:
1045 push_out("%s: %u ", knp
[i
].name
,
1049 case KSTAT_DATA_ULONGLONG
:
1050 if (strcmp(knp
[i
].name
, "Size") == 0) {
1052 push_out("%s: %2.2fGB <%llu bytes>",
1054 (float)knp
[i
].value
.ui64
/
1061 push_out("%s: %u ", knp
[i
].name
,
1066 if ((col
>= 62) || (i
== 2)) {
1078 do_args(int argc
, char **argv
)
1082 extern char *optarg
;
1085 while ((c
= getopt(argc
, argv
, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF
)
1091 do_disk
|= DISK_OLD
;
1094 do_disk
|= DISK_NEW
;
1097 do_disk
|= DISK_EXTENDED
;
1100 if (do_disk
& DISK_IOPATH_LTI
)
1101 errflg
++; /* -Y already used */
1103 do_disk
|= DISK_IOPATH_LI
;
1106 if (do_disk
& DISK_IOPATH_LI
)
1107 errflg
++; /* -X already used */
1109 do_disk
|= DISK_IOPATH_LTI
;
1124 do_partitions_only
++;
1133 do_disk
|= DISK_ERRORS
;
1136 do_disk
|= DISK_EXTENDED_ERRORS
;
1153 timestamp_fmt
= UDATE
;
1154 else if (*optarg
== 'd')
1155 timestamp_fmt
= DDATE
;
1166 df
.if_max_iodevs
= safe_strtoi(optarg
, "invalid limit");
1167 if (df
.if_max_iodevs
< 1)
1174 if ((do_disk
& DISK_OLD
) && (do_disk
& DISK_NEW
)) {
1175 (void) fprintf(stderr
, "-d and -D are incompatible.\n");
1183 /* if no output classes explicity specified, use defaults */
1184 if (do_tty
== 0 && do_disk
== 0 && do_cpu
== 0)
1185 do_tty
= do_cpu
= 1, do_disk
= DISK_OLD
;
1188 * multi-path options (-X, -Y) without a specific vertical
1189 * output format (-x, -e, -E) imply extended -x format
1191 if ((do_disk
& (DISK_IOPATH_LI
| DISK_IOPATH_LTI
)) &&
1192 !(do_disk
& PRINT_VERTICAL
))
1193 do_disk
|= DISK_EXTENDED
;
1196 * If conflicting options take the preferred
1197 * -D and -x result in -x
1198 * -d or -D and -e or -E gives only whatever -d or -D was specified
1200 if ((do_disk
& DISK_EXTENDED
) && (do_disk
& DISK_NORMAL
))
1201 do_disk
&= ~DISK_NORMAL
;
1202 if ((do_disk
& DISK_NORMAL
) && (do_disk
& DISK_ERROR_MASK
))
1203 do_disk
&= ~DISK_ERROR_MASK
;
1205 /* nfs, tape, always shown */
1206 df
.if_allowed_types
= IODEV_NFS
| IODEV_TAPE
;
1209 * If limit == 0 then no command line limit was set, else if any of
1210 * the flags that cause unlimited disks were not set,
1211 * use the default of 4
1213 if (df
.if_max_iodevs
== 0) {
1214 df
.if_max_iodevs
= DEFAULT_LIMIT
;
1215 df
.if_skip_floppy
= 1;
1216 if (do_disk
& (DISK_EXTENDED
| DISK_ERRORS
|
1217 DISK_EXTENDED_ERRORS
)) {
1218 df
.if_max_iodevs
= UNLIMITED_IODEVS
;
1219 df
.if_skip_floppy
= 0;
1226 while (i
< argc
&& !isdigit(argv
[i
][0])) {
1232 * "Note: disks explicitly requested
1233 * are not subject to this disk limit"
1235 if ((count
> df
.if_max_iodevs
) ||
1236 (count
&& (df
.if_max_iodevs
== UNLIMITED_IODEVS
)))
1237 df
.if_max_iodevs
= count
;
1239 df
.if_names
= safe_alloc(count
* sizeof (char *));
1240 (void) memset(df
.if_names
, 0, count
* sizeof (char *));
1243 while (optind
< argc
&& !isdigit(argv
[optind
][0]))
1244 df
.if_names
[df
.if_nr_names
++] = argv
[optind
++];
1246 if (optind
< argc
) {
1247 interval
= safe_strtoi(argv
[optind
], "invalid interval");
1249 fail(0, "invalid interval");
1252 if (optind
< argc
) {
1253 iter
= safe_strtoi(argv
[optind
], "invalid count");
1255 fail(0, "invalid count");
1266 * Driver for doing the extended header formatting. Will produce
1267 * the function stack needed to output an extended header based
1268 * on the options selected.
1274 char header
[SMALL_SCRATCH_BUFLEN
] = {0};
1280 ch
= (do_interval
? 'i' : 's');
1281 iosz
= (do_megabytes
? 'M' : 'k');
1282 if (do_disk
& DISK_ERRORS
) {
1284 (void) sprintf(header
, "s/w h/w trn tot ");
1286 (void) sprintf(header
, "s/w,h/w,trn,tot");
1288 switch (do_disk
& DISK_IO_MASK
) {
1291 fstr
= "%cp%c tp%c serv ";
1293 fstr
= "%cp%c,tp%c,serv";
1294 (void) snprintf(disk_header
, sizeof (disk_header
),
1295 fstr
, iosz
, ch
, ch
);
1299 fstr
= "rp%c wp%c util ";
1301 fstr
= "%rp%c,wp%c,util";
1302 (void) snprintf(disk_header
, sizeof (disk_header
),
1306 /* This is -x option */
1307 if (!do_conversions
) {
1308 /* without -n option */
1310 /* without -r option */
1311 (void) snprintf(disk_header
,
1312 sizeof (disk_header
),
1314 "%cr/%c %cw/%c wait actv "
1315 "svc_t %%%%w %%%%b %s",
1316 iodevs_nl
, iodevs_nl
, "device",
1317 ch
, ch
, iosz
, ch
, iosz
, ch
, header
);
1319 /* with -r option */
1320 (void) snprintf(disk_header
,
1321 sizeof (disk_header
),
1322 "device,r/%c,w/%c,%cr/%c,%cw/%c,"
1323 "wait,actv,svc_t,%%%%w,"
1325 ch
, ch
, iosz
, ch
, iosz
, ch
,
1326 *header
== '\0' ? "" : ",",
1329 * if no -e flag, header == '\0...'
1330 * Ternary operator above is to prevent
1331 * trailing comma in full disk_header
1335 /* with -n option */
1337 fstr
= " r/%c w/%c %cr/%c "
1338 "%cw/%c wait actv wsvc_t asvc_t "
1339 "%%%%w %%%%b %sdevice";
1341 fstr
= "r/%c,w/%c,%cr/%c,%cw/%c,"
1342 "wait,actv,wsvc_t,asvc_t,"
1343 "%%%%w,%%%%b,%sdevice";
1345 * if -rnxe, "tot" (from -e) and
1346 * "device" are run together
1347 * due to lack of trailing comma
1348 * in 'header'. However, adding
1349 * trailing comma to header at
1350 * its definition leads to prob-
1351 * lems elsewhere so it's added
1352 * here in this edge case -rnxe
1354 if (*header
!= '\0')
1355 (void) strcat(header
, ",");
1357 (void) snprintf(disk_header
,
1358 sizeof (disk_header
),
1359 fstr
, ch
, ch
, iosz
, ch
, iosz
,
1367 /* do DISK_ERRORS header (already added above for DISK_EXTENDED) */
1368 if ((do_disk
& DISK_ERRORS
) &&
1369 ((do_disk
& DISK_IO_MASK
) != DISK_EXTENDED
)) {
1370 if (!do_conversions
) {
1372 (void) snprintf(disk_header
,
1373 sizeof (disk_header
), "%-*.*s %s",
1374 iodevs_nl
, iodevs_nl
, "device", header
);
1376 (void) snprintf(disk_header
,
1377 sizeof (disk_header
), "device,%s", header
);
1380 (void) snprintf(disk_header
,
1381 sizeof (disk_header
),
1382 " %sdevice", header
);
1384 (void) snprintf(disk_header
,
1385 sizeof (disk_header
),
1386 "%s,device", header
);
1391 * Need to subtract two characters for the % escape in
1394 dh_len
= strlen(disk_header
) - 2;
1398 * -n *and* (-E *or* -e *or* -x)
1400 if (do_conversions
&& (do_disk
& PRINT_VERTICAL
)) {
1402 setup(print_tty_hdr1
);
1404 setup(print_cpu_hdr1
);
1405 if (do_tty
|| do_cpu
)
1408 setup(print_tty_hdr2
);
1410 setup(print_cpu_hdr2
);
1411 if (do_tty
|| do_cpu
)
1414 setup(print_tty_data
);
1416 setup(print_cpu_data
);
1417 if (do_tty
|| do_cpu
)
1421 setup(show_all_disks
);
1424 * These unholy gymnastics are necessary to place CPU/tty
1425 * data to the right of the disks/errors for the first
1426 * line in vertical mode.
1428 if (do_disk
& PRINT_VERTICAL
) {
1431 setup(show_first_disk
);
1433 setup(print_tty_data
);
1435 setup(print_cpu_data
);
1438 setup(show_other_disks
);
1442 setup(print_tty_data
);
1443 setup(show_all_disks
);
1445 setup(print_cpu_data
);
1450 if (do_disk
& DISK_EXTENDED_ERRORS
)
1455 * Add a new function to the list of functions
1456 * for this invocation. Once on the stack the
1457 * function is never removed nor does its place
1461 setup(void (*nfunc
)(void))
1465 tmp
= safe_alloc(sizeof (format_t
));
1469 formatter_end
->next
= tmp
;
1471 formatter_list
= tmp
;
1472 formatter_end
= tmp
;
1477 * The functions after this comment are devoted to printing
1478 * various parts of the header. They are selected based on the
1479 * options provided when the program was invoked. The functions
1480 * are either directly invoked in printhdr() or are indirectly
1481 * invoked by being placed on the list of functions used when
1482 * extended headers are used.
1485 print_tty_hdr1(void)
1497 push_out(fstr
, dstr
);
1501 print_tty_hdr2(void)
1504 push_out("%-10.10s", " tin tout");
1506 push_out("tin,tout");
1510 print_cpu_hdr1(void)
1522 print_cpu_hdr2(void)
1527 dstr
= " us sy dt id";
1529 dstr
= "us,sy,dt,id";
1534 * Assumption is that tty data is always first - no need for raw mode leading
1538 print_tty_data(void)
1544 kstat_t
*oldks
= NULL
;
1547 oldks
= &oldss
->s_sys
.ss_agg_sys
;
1550 fstr
= " %3.0f %4.0f ";
1553 deltas
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
, "rawch");
1556 deltas
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
, "outch");
1559 push_out(fstr
, raw
, outch
);
1563 * Write out CPU data
1566 print_cpu_data(void)
1573 uint64_t nsec_elapsed
;
1574 kstat_t
*oldks
= NULL
;
1577 oldks
= &oldss
->s_sys
.ss_agg_sys
;
1580 fstr
= " %2.0f %2.0f %2.0f %2.0f";
1582 fstr
= "%.0f,%.0f,%.0f,%.0f";
1584 idle
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
, "cpu_ticks_idle");
1585 user
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
, "cpu_ticks_user");
1586 kern
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
, "cpu_ticks_kernel");
1587 dtrace
= kstat_delta(oldks
, &newss
->s_sys
.ss_agg_sys
,
1589 nsec_elapsed
= newss
->s_sys
.ss_agg_sys
.ks_snaptime
-
1590 (oldks
== NULL
? 0 : oldks
->ks_snaptime
);
1591 push_out(fstr
, user
* percent
, kern
* percent
,
1592 dtrace
* 100.0 / nsec_elapsed
/ newss
->s_nr_active_cpus
,
1597 * Emit the appropriate header.
1605 } else if (hdr_out
== 0) {
1612 * Write out disk errors when -E is specified.
1617 (void) snapshot_walk(SNAP_IODEVS
, oldss
, newss
, show_disk_errors
, NULL
);
1621 show_first_disk(void)
1625 show_disk_mode
= SHOW_FIRST_ONLY
;
1627 (void) snapshot_walk(SNAP_IODEVS
, oldss
, newss
, show_disk
, &count
);
1631 show_other_disks(void)
1635 show_disk_mode
= SHOW_SECOND_ONWARDS
;
1637 (void) snapshot_walk(SNAP_IODEVS
, oldss
, newss
, show_disk
, &count
);
1641 show_all_disks(void)
1645 show_disk_mode
= SHOW_ALL
;
1647 (void) snapshot_walk(SNAP_IODEVS
, oldss
, newss
, show_disk
, &count
);
1651 * Write a newline out and clear the lineout flag.
1657 (void) putchar('\n');
1663 * Generalized printf function that determines what extra
1664 * to print out if we're in raw mode. At this time we
1665 * don't care about errors.
1668 push_out(const char *message
, ...)
1672 va_start(args
, message
);
1673 if (do_raw
&& lineout
== 1)
1674 (void) putchar(',');
1675 (void) vprintf(message
, args
);
1681 * Emit the header string when -e is specified.
1686 char obuf
[SMALL_SCRATCH_BUFLEN
];
1693 if (do_conversions
== 0) {
1694 if (!(do_disk
& DISK_EXTENDED
)) {
1695 (void) snprintf(obuf
, sizeof (obuf
),
1699 } else if (do_disk
== DISK_ERRORS
)
1700 push_out(two_blanks
);
1702 push_out(one_blank
);
1703 push_out("---- errors --- ");
1707 * Emit the header string when -e is specified.
1710 print_disk_header(void)
1712 push_out(disk_header
);
1716 * No, UINTMAX_MAX isn't the right thing here since
1717 * it is #defined to be either INT32_MAX or INT64_MAX
1718 * depending on the whether _LP64 is defined.
1720 * We want to handle the odd future case of having
1721 * ulonglong_t be more than 64 bits but we have
1722 * no nice #define MAX value we can drop in place
1723 * without having to change this code in the future.
1727 ull_delta(u_longlong_t old
, u_longlong_t
new)
1732 return ((UINT64_MAX
- old
) + new + 1);
1736 * Take the difference of an unsigned 32
1737 * bit int attempting to cater for
1741 u32_delta(uint_t old
, uint_t
new)
1746 return ((UINT32_MAX
- old
) + new + 1);
1750 * This is exactly what is needed for standard iostat output,
1751 * but make sure to use it only for that
1753 #define EPSILON (0.1)
1757 return (value
>= 0.0 && value
< EPSILON
);
1761 safe_strtoi(char const *val
, char *errmsg
)
1767 tmp
= strtol(val
, &end
, 10);
1768 if (*end
!= '\0' || errno
)
1769 fail(0, "%s %s", errmsg
, val
);