6396 remove SVM
[unleashed.git] / usr / src / cmd / stat / iostat / iostat.c
blob50cb3258893a64b63a1393bbab468185ccac8b27
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <memory.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <signal.h>
44 #include <sys/types.h>
45 #include <time.h>
46 #include <sys/time.h>
47 #include <sys/sysinfo.h>
48 #include <inttypes.h>
49 #include <strings.h>
50 #include <sys/systeminfo.h>
51 #include <kstat.h>
52 #include <locale.h>
54 #include "dsr.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)
70 #define REPRINT 19
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
82 * headers are used.
84 typedef struct formatter {
85 void (*nfunc)(void);
86 struct formatter *next;
87 } format_t;
90 * Used to get formatting right when printing tty/cpu
91 * data to the right of disk data
93 enum show_disk_mode {
94 SHOW_FIRST_ONLY,
95 SHOW_SECOND_ONWARDS,
96 SHOW_ALL
99 enum show_disk_mode show_disk_mode = SHOW_ALL;
101 char *cmdname = "iostat";
102 int caught_cont = 0;
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;
205 kstat_ctl_t *kc;
206 long hz;
207 int forever;
208 hrtime_t start_n;
209 hrtime_t period_n;
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 */
214 #endif
215 (void) textdomain(TEXT_DOMAIN);
217 do_args(argc, argv);
220 * iostat historically showed CPU changes, even though
221 * it doesn't provide much useful information
223 types |= SNAP_CPUS;
225 if (do_disk)
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;
242 if (do_conversions)
243 types |= SNAP_IODEV_PRETTY;
244 if (do_devid)
245 types |= SNAP_IODEV_DEVID;
246 if (do_controller) {
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
260 * artifact.
262 if (!(do_disk & PRINT_VERTICAL))
263 (void) signal(SIGCONT, printhdr);
265 if (interval)
266 period_n = (hrtime_t)interval * NANOSEC;
268 kc = open_kstat();
269 if (interval)
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;
280 do_format();
282 forever = (iter == 0);
283 do {
284 if (do_conversions && show_mountpts)
285 do_mnttab();
287 if (do_tty || do_cpu) {
288 kstat_t *oldks;
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;
294 if (getime == 0.0)
295 getime = (double)interval;
296 if (getime == 0.0 || do_interval)
297 getime = 1.0;
300 if (formatter_list) {
301 format_t *tmp;
302 tmp = formatter_list;
304 if (timestamp_fmt != NODATE)
305 print_timestamp(timestamp_fmt);
307 while (tmp) {
308 (tmp->nfunc)();
309 tmp = tmp->next;
311 (void) fflush(stdout);
314 /* only remaining/doing a single iteration, we are done */
315 if (iter == 1)
316 continue;
318 if (interval > 0)
319 /* Have a kip */
320 sleep_until(&start_n, period_n, forever, &caught_cont);
322 free_snapshot(oldss);
323 oldss = newss;
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;
332 if (!suppress_state)
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);
338 oldss = NULL;
341 } while (--iter);
343 free_snapshot(oldss);
344 free_snapshot(newss);
345 (void) kstat_close(kc);
346 free(df.if_names);
347 return (0);
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.
362 #define DISK_LEN 11
363 #define DISK_ERROR_LEN 16
364 #define DEVICE_LEN 7
366 /*ARGSUSED*/
367 static void
368 show_disk_name(void *v1, void *v2, void *data)
370 struct iodev_snapshot *dev = (struct iodev_snapshot *)v2;
371 size_t slen;
372 char *name;
373 char fbuf[SMALL_SCRATCH_BUFLEN];
375 if (dev == NULL)
376 return;
378 name = do_conversions ? dev->is_pretty : dev->is_name;
379 name = name ? name : dev->is_name;
381 if (!do_raw) {
382 uint_t width;
384 slen = strlen(name);
386 * The length is less
387 * than the section
388 * which will be displayed
389 * on the next line.
390 * Center the entry.
393 width = (DISK_LEN + 1)/2 + (slen / 2);
394 (void) snprintf(fbuf, sizeof (fbuf),
395 "%*s", width, name);
396 name = fbuf;
397 push_out("%-13.13s ", name);
398 } else {
399 push_out(name);
403 /*ARGSUSED*/
404 static void
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
418 * by single spaces.
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
424 * section.
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
440 static void
441 printhdr(int sig)
444 * If we're here because a signal fired, reenable the
445 * signal.
447 if (sig)
448 (void) signal(SIGCONT, printhdr);
449 if (sig == SIGCONT)
450 caught_cont = 1;
452 * Horizontal mode headers
454 * First line
456 if (do_tty)
457 print_tty_hdr1();
459 if (do_disk & DISK_NORMAL) {
460 (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
461 show_disk_name, NULL);
464 if (do_cpu)
465 print_cpu_hdr1();
466 do_newline();
469 * Second line
471 if (do_tty)
472 print_tty_hdr2();
474 if (do_disk & DISK_NORMAL) {
475 (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
476 show_disk_header, NULL);
479 if (do_cpu)
480 print_cpu_hdr2();
481 do_newline();
483 tohdr = REPRINT;
487 * Write out the extended header centered over the core information.
489 static void
490 write_core_header(void)
492 char *edev = "extended device statistics";
493 uint_t lead_space_ct;
494 uint_t follow_space_ct;
495 size_t edevlen;
497 if (do_raw == 0) {
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;
507 lead_space_ct /= 2;
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);
517 } else
518 push_out("%56s", edev);
519 } else
520 push_out(edev);
524 * In extended mode headers, we don't want to reprint the header on
525 * signals as they are printed every time anyways.
527 static void
528 printxhdr(void)
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) {
540 setup(do_newline);
541 if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
542 setup(print_disk_header);
543 setup(do_newline);
544 } else {
545 if (do_tty)
546 setup(print_tty_hdr1);
547 if (do_cpu)
548 setup(print_cpu_hdr1);
549 setup(do_newline);
551 if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
552 setup(print_disk_header);
553 if (do_tty)
554 setup(print_tty_hdr2);
555 if (do_cpu)
556 setup(print_cpu_hdr2);
557 setup(do_newline);
562 * Write out a line for this disk - note that show_disk writes out
563 * full lines or blocks for each selected disk.
565 static void
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;
577 char *disk_name;
578 u_longlong_t ldeltas;
579 uint_t udeltas;
580 uint64_t t_delta;
581 uint64_t w_delta;
582 uint64_t r_delta;
583 int doit = 1;
584 uint_t toterrs;
585 char *fstr;
587 if (new == NULL)
588 return;
590 switch (show_disk_mode) {
591 case SHOW_FIRST_ONLY:
592 if (count != NULL && *count)
593 return;
594 break;
596 case SHOW_SECOND_ONWARDS:
597 if (count != NULL && !*count) {
598 (*count)++;
599 return;
601 break;
603 default:
604 break;
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) {
615 if (old) {
616 t_delta = hrtime_delta(old->is_snaptime,
617 new->is_snaptime);
618 } else {
619 t_delta = hrtime_delta(new->is_crtime,
620 new->is_snaptime);
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)) {
628 /* synthetic path */
629 if (!old) {
630 t_delta = new->is_crtime;
632 t_delta /= new->is_nr_children;
636 hr_etime = (double)t_delta;
637 if (hr_etime == 0.0)
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;
645 rps /= etime;
647 /* writes per second */
648 udeltas = u32_delta(old ? old->is_stats.writes : 0,
649 new->is_stats.writes);
650 wps = (double)udeltas;
651 wps /= etime;
653 tps = rps + wps;
654 /* transactions per second */
657 * report throughput as either kb/sec or MB/sec
660 if (!do_megabytes)
661 iosize = 1024.0;
662 else
663 iosize = 1048576.0;
665 ldeltas = ull_delta(old ? old->is_stats.nread : 0,
666 new->is_stats.nread);
667 if (ldeltas) {
668 krps = (double)ldeltas;
669 krps /= etime;
670 krps /= iosize;
671 } else
672 krps = 0.0;
674 ldeltas = ull_delta(old ? old->is_stats.nwritten : 0,
675 new->is_stats.nwritten);
676 if (ldeltas) {
677 kwps = (double)ldeltas;
678 kwps /= etime;
679 kwps /= iosize;
680 } else
681 kwps = 0.0;
684 * Blocks transferred per second
686 kps = krps + kwps;
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);
694 if (w_delta) {
695 avw = (double)w_delta;
696 avw /= hr_etime;
697 } else
698 avw = 0.0;
701 * Average number of run transactions waiting
703 r_delta = hrtime_delta(old ? old->is_stats.rlentime : 0,
704 new->is_stats.rlentime);
705 if (r_delta) {
706 avr = (double)r_delta;
707 avr /= hr_etime;
708 } else
709 avr = 0.0;
712 * Average wait service time in milliseconds
714 if (tps > 0.0 && (avw != 0.0 || avr != 0.0)) {
715 mtps = 1000.0 / tps;
716 if (avw != 0.0)
717 wserv = avw * mtps;
718 else
719 wserv = 0.0;
721 if (avr != 0.0)
722 rserv = avr * mtps;
723 else
724 rserv = 0.0;
725 serv = rserv + wserv;
726 } else {
727 rserv = 0.0;
728 wserv = 0.0;
729 serv = 0.0;
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);
735 if (t_delta) {
736 w_pct = (double)t_delta;
737 w_pct /= hr_etime;
738 w_pct *= 100.0;
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;
746 } else
747 w_pct = 0.0;
749 /* % of time there is a transaction running */
750 t_delta = hrtime_delta(old ? old->is_stats.rtime : 0,
751 new->is_stats.rtime);
752 if (t_delta) {
753 r_pct = (double)t_delta;
754 r_pct /= hr_etime;
755 r_pct *= 100.0;
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;
763 } else {
764 r_pct = 0.0;
767 /* % of time there is a transaction running */
768 if (do_interval) {
769 rps *= etime;
770 wps *= etime;
771 tps *= etime;
772 krps *= etime;
773 kwps *= etime;
774 kps *= etime;
778 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) {
779 if ((!do_conversions) && ((suppress_zero == 0) ||
780 ((do_disk & DISK_EXTENDED) == 0))) {
781 if (do_raw == 0) {
782 push_out("%-*.*s",
783 iodevs_nl, iodevs_nl, disk_name);
784 } else {
785 push_out(disk_name);
791 * The error counters are read first (if asked for and if they are
792 * available).
794 bzero(err_counters, sizeof (err_counters));
795 toterrs = 0;
796 if (display_err_counters && (new->is_errors.ks_data != NULL)) {
797 kstat_named_t *knp;
798 int i;
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;
807 break;
808 default:
809 break;
814 switch (do_disk & DISK_IO_MASK) {
815 case DISK_OLD:
816 if (do_raw == 0)
817 fstr = "%3.0f %3.0f %4.0f ";
818 else
819 fstr = "%.0f,%.0f,%.0f";
820 push_out(fstr, kps, tps, serv);
821 break;
822 case DISK_NEW:
823 if (do_raw == 0)
824 fstr = "%3.0f %3.0f %4.1f ";
825 else
826 fstr = "%.0f,%.0f,%.1f";
827 push_out(fstr, rps, wps, r_pct);
828 break;
829 case DISK_EXTENDED:
830 if (suppress_zero) {
831 if (fzero(rps) && fzero(wps) && fzero(krps) &&
832 fzero(kwps) && fzero(avw) && fzero(avr) &&
833 fzero(serv) && fzero(w_pct) && fzero(r_pct) &&
834 (toterrs == 0)) {
835 doit = 0;
836 display_err_counters = B_FALSE;
837 } else if (do_conversions == 0) {
838 if (do_raw == 0) {
839 push_out("%-*.*s",
840 iodevs_nl, iodevs_nl, disk_name);
841 } else {
842 push_out(disk_name);
846 if (doit) {
847 if (!do_conversions) {
848 if (do_raw == 0) {
849 fstr = " %6.1f %6.1f %6.1f %6.1f "
850 "%4.1f %4.1f %6.1f %3.0f "
851 "%3.0f ";
852 } else {
853 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
854 "%.1f,%.0f,%.0f";
856 push_out(fstr, rps, wps, krps, kwps, avw, avr,
857 serv, w_pct, r_pct);
858 } else {
859 if (do_raw == 0) {
860 fstr = " %6.1f %6.1f %6.1f %6.1f "
861 "%4.1f %4.1f %6.1f %6.1f "
862 "%3.0f %3.0f ";
863 } else {
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);
871 break;
874 if (display_err_counters) {
875 char *efstr;
876 int i;
878 if (do_raw == 0) {
879 if (do_disk == DISK_ERRORS)
880 push_out(two_blanks);
881 efstr = "%3u ";
882 } else {
883 efstr = "%u";
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)) &&
894 do_conversions) {
895 push_out("%s", disk_name);
896 if (show_mountpts && new->is_dname) {
897 mnt_t *mount_pt;
898 char *lu;
899 char *dnlu;
900 char lub[SMALL_SCRATCH_BUFLEN];
902 lu = strrchr(new->is_dname, '/');
903 if (lu) {
904 /* only the part after a possible '/' */
905 dnlu = strrchr(disk_name, '/');
906 if (dnlu != NULL &&
907 strcmp(dnlu, lu) == 0)
908 lu = new->is_dname;
909 else {
910 *lu = 0;
911 (void) strcpy(lub,
912 new->is_dname);
913 *lu = '/';
914 (void) strcat(lub, "/");
915 (void) strcat(lub,
916 disk_name);
917 lu = lub;
919 } else
920 lu = disk_name;
921 mount_pt = lookup_mntent_byname(lu);
922 if (mount_pt) {
923 if (do_raw == 0)
924 push_out(" (%s)",
925 mount_pt->mount_point);
926 else
927 push_out("(%s)",
928 mount_pt->mount_point);
934 if ((do_disk & PRINT_VERTICAL) && show_disk_mode != SHOW_FIRST_ONLY)
935 do_newline();
937 if (count != NULL)
938 (*count)++;
941 static void
942 usage(void)
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 "
970 "time_t (u)\n"
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");
976 exit(1);
979 /*ARGSUSED*/
980 static void
981 show_disk_errors(void *v1, void *v2, void *d)
983 struct iodev_snapshot *disk = (struct iodev_snapshot *)v2;
984 kstat_named_t *knp;
985 size_t col;
986 int i, len;
987 char *dev_name;
989 if (disk->is_errors.ks_ndata == 0)
990 return;
991 if (disk->is_type == IODEV_CONTROLLER)
992 return;
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);
998 if (len > 20)
999 push_out("%s ", dev_name);
1000 else if (len > 16)
1001 push_out("%-20.20s ", dev_name);
1002 else {
1003 if (do_conversions)
1004 push_out("%-16.16s ", dev_name);
1005 else
1006 push_out("%-9.9s ", dev_name);
1008 col = 0;
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)
1014 continue;
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) &&
1022 do_devid) {
1023 if (disk->is_devid) {
1024 push_out("Device Id: %s ",
1025 disk->is_devid);
1026 col += strlen(disk->is_devid);
1027 } else {
1028 push_out("Device Id: ");
1031 break;
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);
1037 } else {
1038 push_out("%s: %s ", knp[i].name,
1039 KSTAT_NAMED_STR_PTR(&knp[i]));
1040 col +=
1041 KSTAT_NAMED_STR_BUFLEN(&knp[i]) - 1;
1043 break;
1044 case KSTAT_DATA_ULONG:
1045 push_out("%s: %u ", knp[i].name,
1046 knp[i].value.ui32);
1047 col += 4;
1048 break;
1049 case KSTAT_DATA_ULONGLONG:
1050 if (strcmp(knp[i].name, "Size") == 0) {
1051 do_newline();
1052 push_out("%s: %2.2fGB <%llu bytes>",
1053 knp[i].name,
1054 (float)knp[i].value.ui64 /
1055 DISK_GIGABYTE,
1056 knp[i].value.ui64);
1057 do_newline();
1058 col = 0;
1059 break;
1061 push_out("%s: %u ", knp[i].name,
1062 knp[i].value.ui32);
1063 col += 4;
1064 break;
1066 if ((col >= 62) || (i == 2)) {
1067 do_newline();
1068 col = 0;
1071 if (col > 0) {
1072 do_newline();
1074 do_newline();
1077 void
1078 do_args(int argc, char **argv)
1080 int c;
1081 int errflg = 0;
1082 extern char *optarg;
1083 extern int optind;
1085 while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF)
1086 switch (c) {
1087 case 't':
1088 do_tty++;
1089 break;
1090 case 'd':
1091 do_disk |= DISK_OLD;
1092 break;
1093 case 'D':
1094 do_disk |= DISK_NEW;
1095 break;
1096 case 'x':
1097 do_disk |= DISK_EXTENDED;
1098 break;
1099 case 'X':
1100 if (do_disk & DISK_IOPATH_LTI)
1101 errflg++; /* -Y already used */
1102 else
1103 do_disk |= DISK_IOPATH_LI;
1104 break;
1105 case 'Y':
1106 if (do_disk & DISK_IOPATH_LI)
1107 errflg++; /* -X already used */
1108 else
1109 do_disk |= DISK_IOPATH_LTI;
1110 break;
1111 case 'C':
1112 do_controller++;
1113 break;
1114 case 'c':
1115 do_cpu++;
1116 break;
1117 case 'I':
1118 do_interval++;
1119 break;
1120 case 'p':
1121 do_partitions++;
1122 break;
1123 case 'P':
1124 do_partitions_only++;
1125 break;
1126 case 'n':
1127 do_conversions++;
1128 break;
1129 case 'M':
1130 do_megabytes++;
1131 break;
1132 case 'e':
1133 do_disk |= DISK_ERRORS;
1134 break;
1135 case 'E':
1136 do_disk |= DISK_EXTENDED_ERRORS;
1137 break;
1138 case 'i':
1139 do_devid = 1;
1140 break;
1141 case 's':
1142 suppress_state = 1;
1143 break;
1144 case 'z':
1145 suppress_zero = 1;
1146 break;
1147 case 'm':
1148 show_mountpts = 1;
1149 break;
1150 case 'T':
1151 if (optarg) {
1152 if (*optarg == 'u')
1153 timestamp_fmt = UDATE;
1154 else if (*optarg == 'd')
1155 timestamp_fmt = DDATE;
1156 else
1157 errflg++;
1158 } else {
1159 errflg++;
1161 break;
1162 case 'r':
1163 do_raw = 1;
1164 break;
1165 case 'l':
1166 df.if_max_iodevs = safe_strtoi(optarg, "invalid limit");
1167 if (df.if_max_iodevs < 1)
1168 usage();
1169 break;
1170 case '?':
1171 errflg++;
1174 if ((do_disk & DISK_OLD) && (do_disk & DISK_NEW)) {
1175 (void) fprintf(stderr, "-d and -D are incompatible.\n");
1176 usage();
1179 if (errflg) {
1180 usage();
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;
1222 if (do_disk) {
1223 size_t count = 0;
1224 size_t i = optind;
1226 while (i < argc && !isdigit(argv[i][0])) {
1227 count++;
1228 i++;
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 *));
1242 df.if_nr_names = 0;
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");
1248 if (interval < 1)
1249 fail(0, "invalid interval");
1250 optind++;
1252 if (optind < argc) {
1253 iter = safe_strtoi(argv[optind], "invalid count");
1254 if (iter < 1)
1255 fail(0, "invalid count");
1256 optind++;
1259 if (interval == 0)
1260 iter = 1;
1261 if (optind < argc)
1262 usage();
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.
1271 void
1272 do_format(void)
1274 char header[SMALL_SCRATCH_BUFLEN] = {0};
1275 char ch;
1276 char iosz;
1277 const char *fstr;
1279 disk_header[0] = 0;
1280 ch = (do_interval ? 'i' : 's');
1281 iosz = (do_megabytes ? 'M' : 'k');
1282 if (do_disk & DISK_ERRORS) {
1283 if (do_raw == 0) {
1284 (void) sprintf(header, "s/w h/w trn tot ");
1285 } else
1286 (void) sprintf(header, "s/w,h/w,trn,tot");
1288 switch (do_disk & DISK_IO_MASK) {
1289 case DISK_OLD:
1290 if (do_raw == 0)
1291 fstr = "%cp%c tp%c serv ";
1292 else
1293 fstr = "%cp%c,tp%c,serv";
1294 (void) snprintf(disk_header, sizeof (disk_header),
1295 fstr, iosz, ch, ch);
1296 break;
1297 case DISK_NEW:
1298 if (do_raw == 0)
1299 fstr = "rp%c wp%c util ";
1300 else
1301 fstr = "%rp%c,wp%c,util";
1302 (void) snprintf(disk_header, sizeof (disk_header),
1303 fstr, ch, ch);
1304 break;
1305 case DISK_EXTENDED:
1306 /* This is -x option */
1307 if (!do_conversions) {
1308 /* without -n option */
1309 if (do_raw == 0) {
1310 /* without -r option */
1311 (void) snprintf(disk_header,
1312 sizeof (disk_header),
1313 "%-*.*s r/%c w/%c "
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);
1318 } else {
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,"
1324 "%%%%b%s%s",
1325 ch, ch, iosz, ch, iosz, ch,
1326 *header == '\0' ? "" : ",",
1327 header);
1329 * if no -e flag, header == '\0...'
1330 * Ternary operator above is to prevent
1331 * trailing comma in full disk_header
1334 } else {
1335 /* with -n option */
1336 if (do_raw == 0) {
1337 fstr = " r/%c w/%c %cr/%c "
1338 "%cw/%c wait actv wsvc_t asvc_t "
1339 "%%%%w %%%%b %sdevice";
1340 } else {
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,
1360 ch, header);
1362 break;
1363 default:
1364 break;
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) {
1371 if (do_raw == 0)
1372 (void) snprintf(disk_header,
1373 sizeof (disk_header), "%-*.*s %s",
1374 iodevs_nl, iodevs_nl, "device", header);
1375 else
1376 (void) snprintf(disk_header,
1377 sizeof (disk_header), "device,%s", header);
1378 } else {
1379 if (do_raw == 0) {
1380 (void) snprintf(disk_header,
1381 sizeof (disk_header),
1382 " %sdevice", header);
1383 } else {
1384 (void) snprintf(disk_header,
1385 sizeof (disk_header),
1386 "%s,device", header);
1389 } else {
1391 * Need to subtract two characters for the % escape in
1392 * the string.
1394 dh_len = strlen(disk_header) - 2;
1398 * -n *and* (-E *or* -e *or* -x)
1400 if (do_conversions && (do_disk & PRINT_VERTICAL)) {
1401 if (do_tty)
1402 setup(print_tty_hdr1);
1403 if (do_cpu)
1404 setup(print_cpu_hdr1);
1405 if (do_tty || do_cpu)
1406 setup(do_newline);
1407 if (do_tty)
1408 setup(print_tty_hdr2);
1409 if (do_cpu)
1410 setup(print_cpu_hdr2);
1411 if (do_tty || do_cpu)
1412 setup(do_newline);
1413 if (do_tty)
1414 setup(print_tty_data);
1415 if (do_cpu)
1416 setup(print_cpu_data);
1417 if (do_tty || do_cpu)
1418 setup(do_newline);
1419 printxhdr();
1421 setup(show_all_disks);
1422 } else {
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) {
1429 printxhdr();
1431 setup(show_first_disk);
1432 if (do_tty)
1433 setup(print_tty_data);
1434 if (do_cpu)
1435 setup(print_cpu_data);
1436 setup(do_newline);
1438 setup(show_other_disks);
1439 } else {
1440 setup(hdrout);
1441 if (do_tty)
1442 setup(print_tty_data);
1443 setup(show_all_disks);
1444 if (do_cpu)
1445 setup(print_cpu_data);
1448 setup(do_newline);
1450 if (do_disk & DISK_EXTENDED_ERRORS)
1451 setup(disk_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
1458 * change.
1460 void
1461 setup(void (*nfunc)(void))
1463 format_t *tmp;
1465 tmp = safe_alloc(sizeof (format_t));
1466 tmp->nfunc = nfunc;
1467 tmp->next = 0;
1468 if (formatter_end)
1469 formatter_end->next = tmp;
1470 else
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.
1484 void
1485 print_tty_hdr1(void)
1487 char *fstr;
1488 char *dstr;
1490 if (do_raw == 0) {
1491 fstr = "%10.10s";
1492 dstr = "tty ";
1493 } else {
1494 fstr = "%s";
1495 dstr = "tty";
1497 push_out(fstr, dstr);
1500 void
1501 print_tty_hdr2(void)
1503 if (do_raw == 0)
1504 push_out("%-10.10s", " tin tout");
1505 else
1506 push_out("tin,tout");
1509 void
1510 print_cpu_hdr1(void)
1512 char *dstr;
1514 if (do_raw == 0)
1515 dstr = " cpu";
1516 else
1517 dstr = "cpu";
1518 push_out(dstr);
1521 void
1522 print_cpu_hdr2(void)
1524 char *dstr;
1526 if (do_raw == 0)
1527 dstr = " us sy dt id";
1528 else
1529 dstr = "us,sy,dt,id";
1530 push_out(dstr);
1534 * Assumption is that tty data is always first - no need for raw mode leading
1535 * comma.
1537 void
1538 print_tty_data(void)
1540 char *fstr;
1541 uint64_t deltas;
1542 double raw;
1543 double outch;
1544 kstat_t *oldks = NULL;
1546 if (oldss)
1547 oldks = &oldss->s_sys.ss_agg_sys;
1549 if (do_raw == 0)
1550 fstr = " %3.0f %4.0f ";
1551 else
1552 fstr = "%.0f,%.0f";
1553 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "rawch");
1554 raw = deltas;
1555 raw /= getime;
1556 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "outch");
1557 outch = deltas;
1558 outch /= getime;
1559 push_out(fstr, raw, outch);
1563 * Write out CPU data
1565 void
1566 print_cpu_data(void)
1568 char *fstr;
1569 uint64_t idle;
1570 uint64_t user;
1571 uint64_t kern;
1572 uint64_t dtrace;
1573 uint64_t nsec_elapsed;
1574 kstat_t *oldks = NULL;
1576 if (oldss)
1577 oldks = &oldss->s_sys.ss_agg_sys;
1579 if (do_raw == 0)
1580 fstr = " %2.0f %2.0f %2.0f %2.0f";
1581 else
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,
1588 "cpu_nsec_dtrace");
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,
1593 idle * percent);
1597 * Emit the appropriate header.
1599 void
1600 hdrout(void)
1602 if (do_raw == 0) {
1603 if (--tohdr == 0)
1604 printhdr(0);
1605 } else if (hdr_out == 0) {
1606 printhdr(0);
1607 hdr_out = 1;
1612 * Write out disk errors when -E is specified.
1614 void
1615 disk_errors(void)
1617 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk_errors, NULL);
1620 void
1621 show_first_disk(void)
1623 int count = 0;
1625 show_disk_mode = SHOW_FIRST_ONLY;
1627 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1630 void
1631 show_other_disks(void)
1633 int count = 0;
1635 show_disk_mode = SHOW_SECOND_ONWARDS;
1637 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1640 void
1641 show_all_disks(void)
1643 int count = 0;
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.
1653 static void
1654 do_newline(void)
1656 if (lineout) {
1657 (void) putchar('\n');
1658 lineout = 0;
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.
1667 static void
1668 push_out(const char *message, ...)
1670 va_list args;
1672 va_start(args, message);
1673 if (do_raw && lineout == 1)
1674 (void) putchar(',');
1675 (void) vprintf(message, args);
1676 va_end(args);
1677 lineout = 1;
1681 * Emit the header string when -e is specified.
1683 static void
1684 print_err_hdr(void)
1686 char obuf[SMALL_SCRATCH_BUFLEN];
1688 if (do_raw) {
1689 push_out("errors");
1690 return;
1693 if (do_conversions == 0) {
1694 if (!(do_disk & DISK_EXTENDED)) {
1695 (void) snprintf(obuf, sizeof (obuf),
1696 "%11s", one_blank);
1697 push_out(obuf);
1699 } else if (do_disk == DISK_ERRORS)
1700 push_out(two_blanks);
1701 else
1702 push_out(one_blank);
1703 push_out("---- errors --- ");
1707 * Emit the header string when -e is specified.
1709 static void
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.
1726 u_longlong_t
1727 ull_delta(u_longlong_t old, u_longlong_t new)
1729 if (new >= old)
1730 return (new - old);
1731 else
1732 return ((UINT64_MAX - old) + new + 1);
1736 * Take the difference of an unsigned 32
1737 * bit int attempting to cater for
1738 * overflow.
1740 uint_t
1741 u32_delta(uint_t old, uint_t new)
1743 if (new >= old)
1744 return (new - old);
1745 else
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)
1754 static int
1755 fzero(double value)
1757 return (value >= 0.0 && value < EPSILON);
1760 static int
1761 safe_strtoi(char const *val, char *errmsg)
1763 char *end;
1764 long tmp;
1766 errno = 0;
1767 tmp = strtol(val, &end, 10);
1768 if (*end != '\0' || errno)
1769 fail(0, "%s %s", errmsg, val);
1770 return ((int)tmp);