1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * rewritten from UCB 4.13 83/09/25
27 * rewritten from SunOS 4.1 SID 1.18 89/10/06
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <memory.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <sys/types.h>
40 #include <time.h>
41 #include <sys/time.h>
42 #include <sys/sysinfo.h>
43 #include <inttypes.h>
44 #include <strings.h>
45 #include <sys/systeminfo.h>
46 #include <kstat.h>
47 #include <locale.h>
48
49 #include "dsr.h"
50 #include "statcommon.h"
51
52 #define DISK_OLD 0x0001
53 #define DISK_NEW 0x0002
54 #define DISK_EXTENDED 0x0004
55 #define DISK_ERRORS 0x0008
56 #define DISK_EXTENDED_ERRORS 0x0010
57 #define DISK_IOPATH_LI 0x0020 /* LunInitiator */
58 #define DISK_IOPATH_LTI 0x0040 /* LunTargetInitiator */
59
60 #define DISK_NORMAL (DISK_OLD | DISK_NEW)
61 #define DISK_IO_MASK (DISK_OLD | DISK_NEW | DISK_EXTENDED)
62 #define DISK_ERROR_MASK (DISK_ERRORS | DISK_EXTENDED_ERRORS)
63 #define PRINT_VERTICAL (DISK_ERROR_MASK | DISK_EXTENDED)
64
65 #define REPRINT 19
66
67 #define NUMBER_OF_ERR_COUNTERS 3
68
69 /*
70 * It's really a pseudo-gigabyte. We use 1000000000 bytes so that the disk
71 * labels don't look bad. 1GB is really 1073741824 bytes.
72 */
73 #define DISK_GIGABYTE 1000000000.0
74
75 /*
76 * Function desciptor to be called when extended
77 * headers are used.
78 */
79 typedef struct formatter {
80 void (*nfunc)(void);
81 struct formatter *next;
82 } format_t;
83
84 /*
85 * Used to get formatting right when printing tty/cpu
86 * data to the right of disk data
87 */
88 enum show_disk_mode {
89 SHOW_FIRST_ONLY,
90 SHOW_SECOND_ONWARDS,
91 SHOW_ALL
92 };
93
94 enum show_disk_mode show_disk_mode = SHOW_ALL;
95
96 char *cmdname = "iostat";
97 int caught_cont = 0;
98
99 static char one_blank[] = " ";
100 static char two_blanks[] = " ";
101
102 /*
103 * count for number of lines to be emitted before a header is
104 * shown again. Only used for the basic format.
105 */
106 static uint_t tohdr = 1;
107
108 /*
109 * If we're in raw format, have we printed a header? We only do it
110 * once for raw but we emit it every REPRINT lines in non-raw format.
111 * This applies only for the basic header. The extended header is
112 * done only once in both formats.
113 */
114 static uint_t hdr_out;
115
116 /*
117 * Flags representing arguments from command line
118 */
119 static uint_t do_tty; /* show tty info (-t) */
120 static uint_t do_disk; /* show disk info per selected */
121 /* format (-d, -D, -e, -E, -x -X -Y) */
122 static uint_t do_cpu; /* show cpu info (-c) */
123 static uint_t do_interval; /* do intervals (-I) */
124 static int do_partitions; /* per-partition stats (-p) */
125 static int do_partitions_only; /* per-partition stats only (-P) */
126 /* no per-device stats for disks */
127 static uint_t do_conversions; /* display disks as cXtYdZ (-n) */
128 static uint_t do_megabytes; /* display data in MB/sec (-M) */
129 static uint_t do_controller; /* display controller info (-C) */
130 static uint_t do_raw; /* emit raw format (-r) */
131 static uint_t timestamp_fmt = NODATE; /* timestamp each display (-T) */
132 static uint_t do_devid; /* -E should show devid */
133
134 /*
135 * Default number of disk drives to be displayed in basic format
136 */
137 #define DEFAULT_LIMIT 4
138
139 struct iodev_filter df;
140
141 static uint_t suppress_state; /* skip state change messages */
142 static uint_t suppress_zero; /* skip zero valued lines */
143 static uint_t show_mountpts; /* show mount points */
144 static int interval; /* interval (seconds) to output */
145 static int iter; /* iterations from command line */
146
147 #define SMALL_SCRATCH_BUFLEN MAXNAMELEN
148
149 static int iodevs_nl; /* name field width */
150 #define IODEVS_NL_MIN 6 /* not too thin for "device" */
151 #define IODEVS_NL_MAX 24 /* but keep full width under 80 */
152
153 static char disk_header[132];
154 static uint_t dh_len; /* disk header length for centering */
155 static int lineout; /* data waiting to be printed? */
156
157 static struct snapshot *newss;
158 static struct snapshot *oldss;
159 static double getime; /* elapsed time */
160 static double percent; /* 100 / etime */
161
162 /*
163 * List of functions to be called which will construct the desired output
164 */
165 static format_t *formatter_list;
166 static format_t *formatter_end;
167
168 static u_longlong_t ull_delta(u_longlong_t, u_longlong_t);
169 static uint_t u32_delta(uint_t, uint_t);
170 static void setup(void (*nfunc)(void));
171 static void print_tty_hdr1(void);
172 static void print_tty_hdr2(void);
173 static void print_cpu_hdr1(void);
174 static void print_cpu_hdr2(void);
175 static void print_tty_data(void);
176 static void print_cpu_data(void);
177 static void print_err_hdr(void);
178 static void print_disk_header(void);
179 static void hdrout(void);
180 static void disk_errors(void);
181 static void do_newline(void);
182 static void push_out(const char *, ...);
183 static void printhdr(int);
184 static void printxhdr(void);
185 static void usage(void);
186 static void do_args(int, char **);
187 static void do_format(void);
188 static void show_all_disks(void);
189 static void show_first_disk(void);
190 static void show_other_disks(void);
191 static void show_disk_errors(void *, void *, void *);
192 static void write_core_header(void);
193 static int fzero(double value);
194 static int safe_strtoi(char const *val, char *errmsg);
195
196 int
main(int argc,char ** argv)197 main(int argc, char **argv)
198 {
199 enum snapshot_types types = SNAP_SYSTEM;
200 kstat_ctl_t *kc;
201 long hz;
202 int forever;
203 hrtime_t start_n;
204 hrtime_t period_n;
205
206 (void) setlocale(LC_ALL, "");
207 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
208 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
209 #endif
210 (void) textdomain(TEXT_DOMAIN);
211
212 do_args(argc, argv);
213
214 /*
215 * iostat historically showed CPU changes, even though
216 * it doesn't provide much useful information
217 */
218 types |= SNAP_CPUS;
219
220 if (do_disk)
221 types |= SNAP_IODEVS;
222
223 if (do_disk && !do_partitions_only)
224 df.if_allowed_types |= IODEV_DISK;
225 if (do_disk & DISK_IOPATH_LI) {
226 df.if_allowed_types |= IODEV_IOPATH_LTI;
227 types |= SNAP_IOPATHS_LI;
228 }
229 if (do_disk & DISK_IOPATH_LTI) {
230 df.if_allowed_types |= IODEV_IOPATH_LTI;
231 types |= SNAP_IOPATHS_LTI;
232 }
233 if (do_disk & DISK_ERROR_MASK)
234 types |= SNAP_IODEV_ERRORS;
235 if (do_partitions || do_partitions_only)
236 df.if_allowed_types |= IODEV_PARTITION;
237 if (do_conversions)
238 types |= SNAP_IODEV_PRETTY;
239 if (do_devid)
240 types |= SNAP_IODEV_DEVID;
241 if (do_controller) {
242 if (!(do_disk & PRINT_VERTICAL) ||
243 (do_disk & DISK_EXTENDED_ERRORS))
244 fail(0, "-C can only be used with -e or -x.");
245 types |= SNAP_CONTROLLERS;
246 df.if_allowed_types |= IODEV_CONTROLLER;
247 }
248
249 hz = sysconf(_SC_CLK_TCK);
250
251 /*
252 * Undocumented behavior - sending a SIGCONT will result
253 * in a new header being emitted. Used only if we're not
254 * doing extended headers. This is a historical
255 * artifact.
256 */
257 if (!(do_disk & PRINT_VERTICAL))
258 (void) signal(SIGCONT, printhdr);
259
260 if (interval)
261 period_n = (hrtime_t)interval * NANOSEC;
262
263 kc = open_kstat();
264 if (interval)
265 start_n = gethrtime();
266 newss = acquire_snapshot(kc, types, &df);
267
268 /* compute width of "device" field */
269 iodevs_nl = newss->s_iodevs_is_name_maxlen;
270 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
271 IODEVS_NL_MIN : iodevs_nl;
272 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
273 IODEVS_NL_MAX : iodevs_nl;
274
275 do_format();
276
277 forever = (iter == 0);
278 do {
279 if (do_conversions && show_mountpts)
280 do_mnttab();
281
282 if (do_tty || do_cpu) {
283 kstat_t *oldks;
284 oldks = oldss ? &oldss->s_sys.ss_agg_sys : NULL;
285 getime = cpu_ticks_delta(oldks,
286 &newss->s_sys.ss_agg_sys);
287 percent = (getime > 0.0) ? 100.0 / getime : 0.0;
288 getime = (getime / nr_active_cpus(newss)) / hz;
289 if (getime == 0.0)
290 getime = (double)interval;
291 if (getime == 0.0 || do_interval)
292 getime = 1.0;
293 }
294
295 if (formatter_list) {
296 format_t *tmp;
297 tmp = formatter_list;
298
299 if (timestamp_fmt != NODATE)
300 print_timestamp(timestamp_fmt);
301
302 while (tmp) {
303 (tmp->nfunc)();
304 tmp = tmp->next;
305 }
306 (void) fflush(stdout);
307 }
308
309 /* only remaining/doing a single iteration, we are done */
310 if (iter == 1)
311 continue;
312
313 if (interval > 0)
314 /* Have a kip */
315 sleep_until(&start_n, period_n, forever, &caught_cont);
316
317 free_snapshot(oldss);
318 oldss = newss;
319 newss = acquire_snapshot(kc, types, &df);
320 iodevs_nl = (newss->s_iodevs_is_name_maxlen > iodevs_nl) ?
321 newss->s_iodevs_is_name_maxlen : iodevs_nl;
322 iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
323 IODEVS_NL_MIN : iodevs_nl;
324 iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
325 IODEVS_NL_MAX : iodevs_nl;
326
327 if (!suppress_state)
328 snapshot_report_changes(oldss, newss);
329
330 /* if config changed, show stats from boot */
331 if (snapshot_has_changed(oldss, newss)) {
332 free_snapshot(oldss);
333 oldss = NULL;
334 }
335
336 } while (--iter);
337
338 free_snapshot(oldss);
339 free_snapshot(newss);
340 (void) kstat_close(kc);
341 free(df.if_names);
342 return (0);
343 }
344
345 /*
346 * Some magic numbers used in header formatting.
347 *
348 * DISK_LEN = length of either "kps tps serv" or "wps rps util"
349 * using 0 as the first position
350 *
351 * DISK_ERROR_LEN = length of "s/w h/w trn tot" with one space on
352 * either side. Does not use zero as first pos.
353 *
354 * DEVICE_LEN = length of "device" + 1 character.
355 */
356
357 #define DISK_LEN 11
358 #define DISK_ERROR_LEN 16
359 #define DEVICE_LEN 7
360
361 /*ARGSUSED*/
362 static void
show_disk_name(void * v1,void * v2,void * data)363 show_disk_name(void *v1, void *v2, void *data)
364 {
365 struct iodev_snapshot *dev = (struct iodev_snapshot *)v2;
366 size_t slen;
367 char *name;
368 char fbuf[SMALL_SCRATCH_BUFLEN];
369
370 if (dev == NULL)
371 return;
372
373 name = do_conversions ? dev->is_pretty : dev->is_name;
374 name = name ? name : dev->is_name;
375
376 if (!do_raw) {
377 uint_t width;
378
379 slen = strlen(name);
380 /*
381 * The length is less
382 * than the section
383 * which will be displayed
384 * on the next line.
385 * Center the entry.
386 */
387
388 width = (DISK_LEN + 1)/2 + (slen / 2);
389 (void) snprintf(fbuf, sizeof (fbuf),
390 "%*s", width, name);
391 name = fbuf;
392 push_out("%-13.13s ", name);
393 } else {
394 push_out(name);
395 }
396 }
397
398 /*ARGSUSED*/
399 static void
show_disk_header(void * v1,void * v2,void * data)400 show_disk_header(void *v1, void *v2, void *data)
401 {
402 push_out(disk_header);
403 }
404
405 /*
406 * Write out a two line header. What is written out depends on the flags
407 * selected but in the worst case consists of a tty header, a disk header
408 * providing information for 4 disks and a cpu header.
409 *
410 * The tty header consists of the word "tty" on the first line above the
411 * words "tin tout" on the next line. If present the tty portion consumes
412 * the first 10 characters of each line since "tin tout" is surrounded
413 * by single spaces.
414 *
415 * Each of the disk sections is a 14 character "block" in which the name of
416 * the disk is centered in the first 12 characters of the first line.
417 *
418 * The cpu section is an 11 character block with "cpu" centered over the
419 * section.
420 *
421 * The worst case should look as follows:
422 *
423 * 0---------1--------2---------3---------4---------5---------6---------7-------
424 * tty sd0 sd1 sd2 sd3 cpu
425 * tin tout kps tps serv kps tps serv kps tps serv kps tps serv us sy wt id
426 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN
427 *
428 * When -D is specified, the disk header looks as follows (worst case):
429 *
430 * 0---------1--------2---------3---------4---------5---------6---------7-------
431 * tty sd0 sd1 sd2 sd3 cpu
432 * tin tout rps wps util rps wps util rps wps util rps wps util us sy wt id
433 * NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NNN NNN NNNN NN NN NN NN
434 */
435 static void
printhdr(int sig)436 printhdr(int sig)
437 {
438 /*
439 * If we're here because a signal fired, reenable the
440 * signal.
441 */
442 if (sig)
443 (void) signal(SIGCONT, printhdr);
444 if (sig == SIGCONT)
445 caught_cont = 1;
446 /*
447 * Horizontal mode headers
448 *
449 * First line
450 */
451 if (do_tty)
452 print_tty_hdr1();
453
454 if (do_disk & DISK_NORMAL) {
455 (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
456 show_disk_name, NULL);
457 }
458
459 if (do_cpu)
460 print_cpu_hdr1();
461 do_newline();
462
463 /*
464 * Second line
465 */
466 if (do_tty)
467 print_tty_hdr2();
468
469 if (do_disk & DISK_NORMAL) {
470 (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
471 show_disk_header, NULL);
472 }
473
474 if (do_cpu)
475 print_cpu_hdr2();
476 do_newline();
477
478 tohdr = REPRINT;
479 }
480
481 /*
482 * Write out the extended header centered over the core information.
483 */
484 static void
write_core_header(void)485 write_core_header(void)
486 {
487 char *edev = "extended device statistics";
488 uint_t lead_space_ct;
489 uint_t follow_space_ct;
490 size_t edevlen;
491
492 if (do_raw == 0) {
493 /*
494 * The things we do to look nice...
495 *
496 * Center the core output header. Make sure we have the
497 * right number of trailing spaces for follow-on headers
498 * (i.e., cpu and/or tty and/or errors).
499 */
500 edevlen = strlen(edev);
501 lead_space_ct = dh_len - edevlen;
502 lead_space_ct /= 2;
503 if (lead_space_ct > 0) {
504 follow_space_ct = dh_len - (lead_space_ct + edevlen);
505 if (do_disk & DISK_ERRORS)
506 follow_space_ct -= DISK_ERROR_LEN;
507 if ((do_disk & DISK_EXTENDED) && do_conversions)
508 follow_space_ct -= DEVICE_LEN;
509
510 push_out("%1$*2$.*2$s%3$s%4$*5$.*5$s", one_blank,
511 lead_space_ct, edev, one_blank, follow_space_ct);
512 } else
513 push_out("%56s", edev);
514 } else
515 push_out(edev);
516 }
517
518 /*
519 * In extended mode headers, we don't want to reprint the header on
520 * signals as they are printed every time anyways.
521 */
522 static void
printxhdr(void)523 printxhdr(void)
524 {
525
526 /*
527 * Vertical mode headers
528 */
529 if (do_disk & DISK_EXTENDED)
530 setup(write_core_header);
531 if (do_disk & DISK_ERRORS)
532 setup(print_err_hdr);
533
534 if (do_conversions) {
535 setup(do_newline);
536 if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
537 setup(print_disk_header);
538 setup(do_newline);
539 } else {
540 if (do_tty)
541 setup(print_tty_hdr1);
542 if (do_cpu)
543 setup(print_cpu_hdr1);
544 setup(do_newline);
545
546 if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
547 setup(print_disk_header);
548 if (do_tty)
549 setup(print_tty_hdr2);
550 if (do_cpu)
551 setup(print_cpu_hdr2);
552 setup(do_newline);
553 }
554 }
555
556 /*
557 * Write out a line for this disk - note that show_disk writes out
558 * full lines or blocks for each selected disk.
559 */
560 static void
show_disk(void * v1,void * v2,void * data)561 show_disk(void *v1, void *v2, void *data)
562 {
563 uint32_t err_counters[NUMBER_OF_ERR_COUNTERS];
564 boolean_t display_err_counters = do_disk & DISK_ERRORS;
565 struct iodev_snapshot *old = (struct iodev_snapshot *)v1;
566 struct iodev_snapshot *new = (struct iodev_snapshot *)v2;
567 int *count = (int *)data;
568 double rps, wps, tps, mtps, krps, kwps, kps, avw, avr, w_pct, r_pct;
569 double wserv, rserv, serv;
570 double iosize; /* kb/sec or MB/sec */
571 double etime, hr_etime;
572 char *disk_name;
573 u_longlong_t ldeltas;
574 uint_t udeltas;
575 uint64_t t_delta;
576 uint64_t w_delta;
577 uint64_t r_delta;
578 int doit = 1;
579 uint_t toterrs;
580 char *fstr;
581
582 if (new == NULL)
583 return;
584
585 switch (show_disk_mode) {
586 case SHOW_FIRST_ONLY:
587 if (count != NULL && *count)
588 return;
589 break;
590
591 case SHOW_SECOND_ONWARDS:
592 if (count != NULL && !*count) {
593 (*count)++;
594 return;
595 }
596 break;
597
598 default:
599 break;
600 }
601
602 disk_name = do_conversions ? new->is_pretty : new->is_name;
603 disk_name = disk_name ? disk_name : new->is_name;
604
605 /*
606 * Only do if we want IO stats - Avoids errors traveling this
607 * section if that's all we want to see.
608 */
609 if (do_disk & DISK_IO_MASK) {
610 if (old) {
611 t_delta = hrtime_delta(old->is_snaptime,
612 new->is_snaptime);
613 } else {
614 t_delta = hrtime_delta(new->is_crtime,
615 new->is_snaptime);
616 }
617
618 if (new->is_nr_children) {
619 if (new->is_type == IODEV_CONTROLLER) {
620 t_delta /= new->is_nr_children;
621 } else if ((new->is_type == IODEV_IOPATH_LT) ||
622 (new->is_type == IODEV_IOPATH_LI)) {
623 /* synthetic path */
624 if (!old) {
625 t_delta = new->is_crtime;
626 }
627 t_delta /= new->is_nr_children;
628 }
629 }
630
631 hr_etime = (double)t_delta;
632 if (hr_etime == 0.0)
633 hr_etime = (double)NANOSEC;
634 etime = hr_etime / (double)NANOSEC;
635
636 /* reads per second */
637 udeltas = u32_delta(old ? old->is_stats.reads : 0,
638 new->is_stats.reads);
639 rps = (double)udeltas;
640 rps /= etime;
641
642 /* writes per second */
643 udeltas = u32_delta(old ? old->is_stats.writes : 0,
644 new->is_stats.writes);
645 wps = (double)udeltas;
646 wps /= etime;
647
648 tps = rps + wps;
649 /* transactions per second */
650
651 /*
652 * report throughput as either kb/sec or MB/sec
653 */
654
655 if (!do_megabytes)
656 iosize = 1024.0;
657 else
658 iosize = 1048576.0;
659
660 ldeltas = ull_delta(old ? old->is_stats.nread : 0,
661 new->is_stats.nread);
662 if (ldeltas) {
663 krps = (double)ldeltas;
664 krps /= etime;
665 krps /= iosize;
666 } else
667 krps = 0.0;
668
669 ldeltas = ull_delta(old ? old->is_stats.nwritten : 0,
670 new->is_stats.nwritten);
671 if (ldeltas) {
672 kwps = (double)ldeltas;
673 kwps /= etime;
674 kwps /= iosize;
675 } else
676 kwps = 0.0;
677
678 /*
679 * Blocks transferred per second
680 */
681 kps = krps + kwps;
682
683 /*
684 * Average number of wait transactions waiting
685 */
686 w_delta = hrtime_delta((u_longlong_t)
687 (old ? old->is_stats.wlentime : 0),
688 new->is_stats.wlentime);
689 if (w_delta) {
690 avw = (double)w_delta;
691 avw /= hr_etime;
692 } else
693 avw = 0.0;
694
695 /*
696 * Average number of run transactions waiting
697 */
698 r_delta = hrtime_delta(old ? old->is_stats.rlentime : 0,
699 new->is_stats.rlentime);
700 if (r_delta) {
701 avr = (double)r_delta;
702 avr /= hr_etime;
703 } else
704 avr = 0.0;
705
706 /*
707 * Average wait service time in milliseconds
708 */
709 if (tps > 0.0 && (avw != 0.0 || avr != 0.0)) {
710 mtps = 1000.0 / tps;
711 if (avw != 0.0)
712 wserv = avw * mtps;
713 else
714 wserv = 0.0;
715
716 if (avr != 0.0)
717 rserv = avr * mtps;
718 else
719 rserv = 0.0;
720 serv = rserv + wserv;
721 } else {
722 rserv = 0.0;
723 wserv = 0.0;
724 serv = 0.0;
725 }
726
727 /* % of time there is a transaction waiting for service */
728 t_delta = hrtime_delta(old ? old->is_stats.wtime : 0,
729 new->is_stats.wtime);
730 if (t_delta) {
731 w_pct = (double)t_delta;
732 w_pct /= hr_etime;
733 w_pct *= 100.0;
734
735 /*
736 * Average the wait queue utilization over the
737 * the controller's devices, if this is a controller.
738 */
739 if (new->is_type == IODEV_CONTROLLER)
740 w_pct /= new->is_nr_children;
741 } else
742 w_pct = 0.0;
743
744 /* % of time there is a transaction running */
745 t_delta = hrtime_delta(old ? old->is_stats.rtime : 0,
746 new->is_stats.rtime);
747 if (t_delta) {
748 r_pct = (double)t_delta;
749 r_pct /= hr_etime;
750 r_pct *= 100.0;
751
752 /*
753 * Average the percent busy over the controller's
754 * devices, if this is a controller.
755 */
756 if (new->is_type == IODEV_CONTROLLER)
757 w_pct /= new->is_nr_children;
758 } else {
759 r_pct = 0.0;
760 }
761
762 /* % of time there is a transaction running */
763 if (do_interval) {
764 rps *= etime;
765 wps *= etime;
766 tps *= etime;
767 krps *= etime;
768 kwps *= etime;
769 kps *= etime;
770 }
771 }
772
773 if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) {
774 if ((!do_conversions) && ((suppress_zero == 0) ||
775 ((do_disk & DISK_EXTENDED) == 0))) {
776 if (do_raw == 0) {
777 push_out("%-*.*s",
778 iodevs_nl, iodevs_nl, disk_name);
779 } else {
780 push_out(disk_name);
781 }
782 }
783 }
784
785 /*
786 * The error counters are read first (if asked for and if they are
787 * available).
788 */
789 bzero(err_counters, sizeof (err_counters));
790 toterrs = 0;
791 if (display_err_counters && (new->is_errors.ks_data != NULL)) {
792 kstat_named_t *knp;
793 int i;
794
795 knp = KSTAT_NAMED_PTR(&new->is_errors);
796 for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++) {
797 switch (knp[i].data_type) {
798 case KSTAT_DATA_ULONG:
799 case KSTAT_DATA_ULONGLONG:
800 err_counters[i] = knp[i].value.ui32;
801 toterrs += knp[i].value.ui32;
802 break;
803 default:
804 break;
805 }
806 }
807 }
808
809 switch (do_disk & DISK_IO_MASK) {
810 case DISK_OLD:
811 if (do_raw == 0)
812 fstr = "%3.0f %3.0f %4.0f ";
813 else
814 fstr = "%.0f,%.0f,%.0f";
815 push_out(fstr, kps, tps, serv);
816 break;
817 case DISK_NEW:
818 if (do_raw == 0)
819 fstr = "%3.0f %3.0f %4.1f ";
820 else
821 fstr = "%.0f,%.0f,%.1f";
822 push_out(fstr, rps, wps, r_pct);
823 break;
824 case DISK_EXTENDED:
825 if (suppress_zero) {
826 if (fzero(rps) && fzero(wps) && fzero(krps) &&
827 fzero(kwps) && fzero(avw) && fzero(avr) &&
828 fzero(serv) && fzero(w_pct) && fzero(r_pct) &&
829 (toterrs == 0)) {
830 doit = 0;
831 display_err_counters = B_FALSE;
832 } else if (do_conversions == 0) {
833 if (do_raw == 0) {
834 push_out("%-*.*s",
835 iodevs_nl, iodevs_nl, disk_name);
836 } else {
837 push_out(disk_name);
838 }
839 }
840 }
841 if (doit) {
842 if (!do_conversions) {
843 if (do_raw == 0) {
844 fstr = " %6.1f %6.1f %6.1f %6.1f "
845 "%4.1f %4.1f %6.1f %3.0f "
846 "%3.0f ";
847 } else {
848 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
849 "%.1f,%.0f,%.0f";
850 }
851 push_out(fstr, rps, wps, krps, kwps, avw, avr,
852 serv, w_pct, r_pct);
853 } else {
854 if (do_raw == 0) {
855 fstr = " %6.1f %6.1f %6.1f %6.1f "
856 "%4.1f %4.1f %6.1f %6.1f "
857 "%3.0f %3.0f ";
858 } else {
859 fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
860 "%.1f,%.1f,%.0f,%.0f";
861 }
862 push_out(fstr, rps, wps, krps, kwps, avw, avr,
863 wserv, rserv, w_pct, r_pct);
864 }
865 }
866 break;
867 }
868
869 if (display_err_counters) {
870 char *efstr;
871 int i;
872
873 if (do_raw == 0) {
874 if (do_disk == DISK_ERRORS)
875 push_out(two_blanks);
876 efstr = "%3u ";
877 } else {
878 efstr = "%u";
879 }
880
881 for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++)
882 push_out(efstr, err_counters[i]);
883
884 push_out(efstr, toterrs);
885 }
886
887 if (suppress_zero == 0 || doit == 1) {
888 if ((do_disk & (DISK_EXTENDED | DISK_ERRORS)) &&
889 do_conversions) {
890 push_out("%s", disk_name);
891 if (show_mountpts && new->is_dname) {
892 mnt_t *mount_pt;
893 char *lu;
894 char *dnlu;
895 char lub[SMALL_SCRATCH_BUFLEN];
896
897 lu = strrchr(new->is_dname, '/');
898 if (lu) {
899 /* only the part after a possible '/' */
900 dnlu = strrchr(disk_name, '/');
901 if (dnlu != NULL &&
902 strcmp(dnlu, lu) == 0)
903 lu = new->is_dname;
904 else {
905 *lu = 0;
906 (void) strcpy(lub,
907 new->is_dname);
908 *lu = '/';
909 (void) strcat(lub, "/");
910 (void) strcat(lub,
911 disk_name);
912 lu = lub;
913 }
914 } else
915 lu = disk_name;
916 mount_pt = lookup_mntent_byname(lu);
917 if (mount_pt) {
918 if (do_raw == 0)
919 push_out(" (%s)",
920 mount_pt->mount_point);
921 else
922 push_out("(%s)",
923 mount_pt->mount_point);
924 }
925 }
926 }
927 }
928
929 if ((do_disk & PRINT_VERTICAL) && show_disk_mode != SHOW_FIRST_ONLY)
930 do_newline();
931
932 if (count != NULL)
933 (*count)++;
934 }
935
936 static void
usage(void)937 usage(void)
938 {
939 (void) fprintf(stderr,
940 "Usage: iostat [-cCdDeEiImMnpPrstxXYz] "
941 " [-l n] [-T d|u] [disk ...] [interval [count]]\n"
942 "\t\t-c: report percentage of time system has spent\n"
943 "\t\t\tin user/system/wait/idle mode\n"
944 "\t\t-C: report disk statistics by controller\n"
945 "\t\t-d: display disk Kb/sec, transfers/sec, avg. \n"
946 "\t\t\tservice time in milliseconds \n"
947 "\t\t-D: display disk reads/sec, writes/sec, \n"
948 "\t\t\tpercentage disk utilization \n"
949 "\t\t-e: report device error summary statistics\n"
950 "\t\t-E: report extended device error statistics\n"
951 "\t\t-i: show device IDs for -E output\n"
952 "\t\t-I: report the counts in each interval,\n"
953 "\t\t\tinstead of rates, where applicable\n"
954 "\t\t-l n: Limit the number of disks to n\n"
955 "\t\t-m: Display mount points (most useful with -p)\n"
956 "\t\t-M: Display data throughput in MB/sec "
957 "instead of Kb/sec\n"
958 "\t\t-n: convert device names to cXdYtZ format\n"
959 "\t\t-p: report per-partition disk statistics\n"
960 "\t\t-P: report per-partition disk statistics only,\n"
961 "\t\t\tno per-device disk statistics\n"
962 "\t\t-r: Display data in comma separated format\n"
963 "\t\t-s: Suppress state change messages\n"
964 "\t\t-T d|u Display a timestamp in date (d) or unix "
965 "time_t (u)\n"
966 "\t\t-t: display chars read/written to terminals\n"
967 "\t\t-x: display extended disk statistics\n"
968 "\t\t-X: display I/O path statistics\n"
969 "\t\t-Y: display I/O path (I/T/L) statistics\n"
970 "\t\t-z: Suppress entries with all zero values\n");
971 exit(1);
972 }
973
974 /*ARGSUSED*/
975 static void
show_disk_errors(void * v1,void * v2,void * d)976 show_disk_errors(void *v1, void *v2, void *d)
977 {
978 struct iodev_snapshot *disk = (struct iodev_snapshot *)v2;
979 kstat_named_t *knp;
980 size_t col;
981 int i, len;
982 char *dev_name;
983
984 if (disk->is_errors.ks_ndata == 0)
985 return;
986 if (disk->is_type == IODEV_CONTROLLER)
987 return;
988
989 dev_name = do_conversions ? disk->is_pretty : disk->is_name;
990 dev_name = dev_name ? dev_name : disk->is_name;
991
992 len = strlen(dev_name);
993 if (len > 20)
994 push_out("%s ", dev_name);
995 else if (len > 16)
996 push_out("%-20.20s ", dev_name);
997 else {
998 if (do_conversions)
999 push_out("%-16.16s ", dev_name);
1000 else
1001 push_out("%-9.9s ", dev_name);
1002 }
1003 col = 0;
1004
1005 knp = KSTAT_NAMED_PTR(&disk->is_errors);
1006 for (i = 0; i < disk->is_errors.ks_ndata; i++) {
1007 /* skip kstats that the driver did not kstat_named_init */
1008 if (knp[i].name[0] == 0)
1009 continue;
1010
1011 col += strlen(knp[i].name);
1012
1013 switch (knp[i].data_type) {
1014 case KSTAT_DATA_CHAR:
1015 if ((strcmp(knp[i].name, "Serial No") == 0) &&
1016 do_devid) {
1017 if (disk->is_devid) {
1018 push_out("Device Id: %s ",
1019 disk->is_devid);
1020 col += strlen(disk->is_devid);
1021 } else
1022 push_out("Device Id: ");
1023 } else {
1024 push_out("%s: %-.16s ", knp[i].name,
1025 &knp[i].value.c[0]);
1026 col += strlen(&knp[i].value.c[0]);
1027 }
1028 break;
1029 case KSTAT_DATA_ULONG:
1030 push_out("%s: %u ", knp[i].name,
1031 knp[i].value.ui32);
1032 col += 4;
1033 break;
1034 case KSTAT_DATA_ULONGLONG:
1035 if (strcmp(knp[i].name, "Size") == 0) {
1036 push_out("%s: %2.2fGB <%llu bytes>\n",
1037 knp[i].name,
1038 (float)knp[i].value.ui64 /
1039 DISK_GIGABYTE,
1040 knp[i].value.ui64);
1041 col = 0;
1042 break;
1043 }
1044 push_out("%s: %u ", knp[i].name,
1045 knp[i].value.ui32);
1046 col += 4;
1047 break;
1048 }
1049 if ((col >= 62) || (i == 2)) {
1050 do_newline();
1051 col = 0;
1052 }
1053 }
1054 if (col > 0) {
1055 do_newline();
1056 }
1057 do_newline();
1058 }
1059
1060 void
do_args(int argc,char ** argv)1061 do_args(int argc, char **argv)
1062 {
1063 int c;
1064 int errflg = 0;
1065 extern char *optarg;
1066 extern int optind;
1067
1068 while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF)
1069 switch (c) {
1070 case 't':
1071 do_tty++;
1072 break;
1073 case 'd':
1074 do_disk |= DISK_OLD;
1075 break;
1076 case 'D':
1077 do_disk |= DISK_NEW;
1078 break;
1079 case 'x':
1080 do_disk |= DISK_EXTENDED;
1081 break;
1082 case 'X':
1083 if (do_disk & DISK_IOPATH_LTI)
1084 errflg++; /* -Y already used */
1085 else
1086 do_disk |= DISK_IOPATH_LI;
1087 break;
1088 case 'Y':
1089 if (do_disk & DISK_IOPATH_LI)
1090 errflg++; /* -X already used */
1091 else
1092 do_disk |= DISK_IOPATH_LTI;
1093 break;
1094 case 'C':
1095 do_controller++;
1096 break;
1097 case 'c':
1098 do_cpu++;
1099 break;
1100 case 'I':
1101 do_interval++;
1102 break;
1103 case 'p':
1104 do_partitions++;
1105 break;
1106 case 'P':
1107 do_partitions_only++;
1108 break;
1109 case 'n':
1110 do_conversions++;
1111 break;
1112 case 'M':
1113 do_megabytes++;
1114 break;
1115 case 'e':
1116 do_disk |= DISK_ERRORS;
1117 break;
1118 case 'E':
1119 do_disk |= DISK_EXTENDED_ERRORS;
1120 break;
1121 case 'i':
1122 do_devid = 1;
1123 break;
1124 case 's':
1125 suppress_state = 1;
1126 break;
1127 case 'z':
1128 suppress_zero = 1;
1129 break;
1130 case 'm':
1131 show_mountpts = 1;
1132 break;
1133 case 'T':
1134 if (optarg) {
1135 if (*optarg == 'u')
1136 timestamp_fmt = UDATE;
1137 else if (*optarg == 'd')
1138 timestamp_fmt = DDATE;
1139 else
1140 errflg++;
1141 } else {
1142 errflg++;
1143 }
1144 break;
1145 case 'r':
1146 do_raw = 1;
1147 break;
1148 case 'l':
1149 df.if_max_iodevs = safe_strtoi(optarg, "invalid limit");
1150 if (df.if_max_iodevs < 1)
1151 usage();
1152 break;
1153 case '?':
1154 errflg++;
1155 }
1156
1157 if ((do_disk & DISK_OLD) && (do_disk & DISK_NEW)) {
1158 (void) fprintf(stderr, "-d and -D are incompatible.\n");
1159 usage();
1160 }
1161
1162 if (errflg) {
1163 usage();
1164 }
1165
1166 /* if no output classes explicity specified, use defaults */
1167 if (do_tty == 0 && do_disk == 0 && do_cpu == 0)
1168 do_tty = do_cpu = 1, do_disk = DISK_OLD;
1169
1170 /*
1171 * multi-path options (-X, -Y) without a specific vertical
1172 * output format (-x, -e, -E) imply extended -x format
1173 */
1174 if ((do_disk & (DISK_IOPATH_LI | DISK_IOPATH_LTI)) &&
1175 !(do_disk & PRINT_VERTICAL))
1176 do_disk |= DISK_EXTENDED;
1177
1178 /*
1179 * If conflicting options take the preferred
1180 * -D and -x result in -x
1181 * -d or -D and -e or -E gives only whatever -d or -D was specified
1182 */
1183 if ((do_disk & DISK_EXTENDED) && (do_disk & DISK_NORMAL))
1184 do_disk &= ~DISK_NORMAL;
1185 if ((do_disk & DISK_NORMAL) && (do_disk & DISK_ERROR_MASK))
1186 do_disk &= ~DISK_ERROR_MASK;
1187
1188 /* nfs, tape, always shown */
1189 df.if_allowed_types = IODEV_NFS | IODEV_TAPE;
1190
1191 /*
1192 * If limit == 0 then no command line limit was set, else if any of
1193 * the flags that cause unlimited disks were not set,
1194 * use the default of 4
1195 */
1196 if (df.if_max_iodevs == 0) {
1197 df.if_max_iodevs = DEFAULT_LIMIT;
1198 df.if_skip_floppy = 1;
1199 if (do_disk & (DISK_EXTENDED | DISK_ERRORS |
1200 DISK_EXTENDED_ERRORS)) {
1201 df.if_max_iodevs = UNLIMITED_IODEVS;
1202 df.if_skip_floppy = 0;
1203 }
1204 }
1205 if (do_disk) {
1206 size_t count = 0;
1207 size_t i = optind;
1208
1209 while (i < argc && !isdigit(argv[i][0])) {
1210 count++;
1211 i++;
1212 }
1213
1214 /*
1215 * "Note: disks explicitly requested
1216 * are not subject to this disk limit"
1217 */
1218 if ((count > df.if_max_iodevs) ||
1219 (count && (df.if_max_iodevs == UNLIMITED_IODEVS)))
1220 df.if_max_iodevs = count;
1221
1222 df.if_names = safe_alloc(count * sizeof (char *));
1223 (void) memset(df.if_names, 0, count * sizeof (char *));
1224
1225 df.if_nr_names = 0;
1226 while (optind < argc && !isdigit(argv[optind][0]))
1227 df.if_names[df.if_nr_names++] = argv[optind++];
1228 }
1229 if (optind < argc) {
1230 interval = safe_strtoi(argv[optind], "invalid interval");
1231 if (interval < 1)
1232 fail(0, "invalid interval");
1233 optind++;
1234
1235 if (optind < argc) {
1236 iter = safe_strtoi(argv[optind], "invalid count");
1237 if (iter < 1)
1238 fail(0, "invalid count");
1239 optind++;
1240 }
1241 }
1242 if (interval == 0)
1243 iter = 1;
1244 if (optind < argc)
1245 usage();
1246 }
1247
1248 /*
1249 * Driver for doing the extended header formatting. Will produce
1250 * the function stack needed to output an extended header based
1251 * on the options selected.
1252 */
1253
1254 void
do_format(void)1255 do_format(void)
1256 {
1257 char header[SMALL_SCRATCH_BUFLEN];
1258 char ch;
1259 char iosz;
1260 const char *fstr;
1261
1262 disk_header[0] = 0;
1263 ch = (do_interval ? 'i' : 's');
1264 iosz = (do_megabytes ? 'M' : 'k');
1265 if (do_disk & DISK_ERRORS) {
1266 if (do_raw == 0) {
1267 (void) sprintf(header, "s/w h/w trn tot ");
1268 } else
1269 (void) sprintf(header, "s/w,h/w,trn,tot");
1270 } else
1271 *header = NULL;
1272 switch (do_disk & DISK_IO_MASK) {
1273 case DISK_OLD:
1274 if (do_raw == 0)
1275 fstr = "%cp%c tp%c serv ";
1276 else
1277 fstr = "%cp%c,tp%c,serv";
1278 (void) snprintf(disk_header, sizeof (disk_header),
1279 fstr, iosz, ch, ch);
1280 break;
1281 case DISK_NEW:
1282 if (do_raw == 0)
1283 fstr = "rp%c wp%c util ";
1284 else
1285 fstr = "%rp%c,wp%c,util";
1286 (void) snprintf(disk_header, sizeof (disk_header),
1287 fstr, ch, ch);
1288 break;
1289 case DISK_EXTENDED:
1290 /* This is -x option */
1291 if (!do_conversions) {
1292 /* without -n option */
1293 if (do_raw == 0) {
1294 /* without -r option */
1295 (void) snprintf(disk_header,
1296 sizeof (disk_header),
1297 "%-*.*s r/%c w/%c "
1298 "%cr/%c %cw/%c wait actv "
1299 "svc_t %%%%w %%%%b %s",
1300 iodevs_nl, iodevs_nl, "device",
1301 ch, ch, iosz, ch, iosz, ch, header);
1302 } else {
1303 /* with -r option */
1304 (void) snprintf(disk_header,
1305 sizeof (disk_header),
1306 "device,r/%c,w/%c,%cr/%c,%cw/%c,"
1307 "wait,actv,svc_t,%%%%w,"
1308 "%%%%b,%s",
1309 ch, ch, iosz, ch, iosz, ch, header);
1310 }
1311 } else {
1312 /* with -n option */
1313 if (do_raw == 0) {
1314 fstr = " r/%c w/%c %cr/%c "
1315 "%cw/%c wait actv wsvc_t asvc_t "
1316 "%%%%w %%%%b %sdevice";
1317 } else {
1318 fstr = "r/%c,w/%c,%cr/%c,%cw/%c,"
1319 "wait,actv,wsvc_t,asvc_t,"
1320 "%%%%w,%%%%b,%sdevice";
1321 }
1322 (void) snprintf(disk_header,
1323 sizeof (disk_header),
1324 fstr, ch, ch, iosz, ch, iosz,
1325 ch, header);
1326 }
1327 break;
1328 default:
1329 break;
1330 }
1331
1332 /* do DISK_ERRORS header (already added above for DISK_EXTENDED) */
1333 if ((do_disk & DISK_ERRORS) &&
1334 ((do_disk & DISK_IO_MASK) != DISK_EXTENDED)) {
1335 if (!do_conversions) {
1336 if (do_raw == 0)
1337 (void) snprintf(disk_header,
1338 sizeof (disk_header), "%-*.*s %s",
1339 iodevs_nl, iodevs_nl, "device", header);
1340 else
1341 (void) snprintf(disk_header,
1342 sizeof (disk_header), "device,%s", header);
1343 } else {
1344 if (do_raw == 0) {
1345 (void) snprintf(disk_header,
1346 sizeof (disk_header),
1347 " %sdevice", header);
1348 } else {
1349 (void) snprintf(disk_header,
1350 sizeof (disk_header),
1351 "%s,device", header);
1352 }
1353 }
1354 } else {
1355 /*
1356 * Need to subtract two characters for the % escape in
1357 * the string.
1358 */
1359 dh_len = strlen(disk_header) - 2;
1360 }
1361
1362 /*
1363 * -n *and* (-E *or* -e *or* -x)
1364 */
1365 if (do_conversions && (do_disk & PRINT_VERTICAL)) {
1366 if (do_tty)
1367 setup(print_tty_hdr1);
1368 if (do_cpu)
1369 setup(print_cpu_hdr1);
1370 if (do_tty || do_cpu)
1371 setup(do_newline);
1372 if (do_tty)
1373 setup(print_tty_hdr2);
1374 if (do_cpu)
1375 setup(print_cpu_hdr2);
1376 if (do_tty || do_cpu)
1377 setup(do_newline);
1378 if (do_tty)
1379 setup(print_tty_data);
1380 if (do_cpu)
1381 setup(print_cpu_data);
1382 if (do_tty || do_cpu)
1383 setup(do_newline);
1384 printxhdr();
1385
1386 setup(show_all_disks);
1387 } else {
1388 /*
1389 * These unholy gymnastics are necessary to place CPU/tty
1390 * data to the right of the disks/errors for the first
1391 * line in vertical mode.
1392 */
1393 if (do_disk & PRINT_VERTICAL) {
1394 printxhdr();
1395
1396 setup(show_first_disk);
1397 if (do_tty)
1398 setup(print_tty_data);
1399 if (do_cpu)
1400 setup(print_cpu_data);
1401 setup(do_newline);
1402
1403 setup(show_other_disks);
1404 } else {
1405 setup(hdrout);
1406 if (do_tty)
1407 setup(print_tty_data);
1408 setup(show_all_disks);
1409 if (do_cpu)
1410 setup(print_cpu_data);
1411 }
1412
1413 setup(do_newline);
1414 }
1415 if (do_disk & DISK_EXTENDED_ERRORS)
1416 setup(disk_errors);
1417 }
1418
1419 /*
1420 * Add a new function to the list of functions
1421 * for this invocation. Once on the stack the
1422 * function is never removed nor does its place
1423 * change.
1424 */
1425 void
setup(void (* nfunc)(void))1426 setup(void (*nfunc)(void))
1427 {
1428 format_t *tmp;
1429
1430 tmp = safe_alloc(sizeof (format_t));
1431 tmp->nfunc = nfunc;
1432 tmp->next = 0;
1433 if (formatter_end)
1434 formatter_end->next = tmp;
1435 else
1436 formatter_list = tmp;
1437 formatter_end = tmp;
1438
1439 }
1440
1441 /*
1442 * The functions after this comment are devoted to printing
1443 * various parts of the header. They are selected based on the
1444 * options provided when the program was invoked. The functions
1445 * are either directly invoked in printhdr() or are indirectly
1446 * invoked by being placed on the list of functions used when
1447 * extended headers are used.
1448 */
1449 void
print_tty_hdr1(void)1450 print_tty_hdr1(void)
1451 {
1452 char *fstr;
1453 char *dstr;
1454
1455 if (do_raw == 0) {
1456 fstr = "%10.10s";
1457 dstr = "tty ";
1458 } else {
1459 fstr = "%s";
1460 dstr = "tty";
1461 }
1462 push_out(fstr, dstr);
1463 }
1464
1465 void
print_tty_hdr2(void)1466 print_tty_hdr2(void)
1467 {
1468 if (do_raw == 0)
1469 push_out("%-10.10s", " tin tout");
1470 else
1471 push_out("tin,tout");
1472 }
1473
1474 void
print_cpu_hdr1(void)1475 print_cpu_hdr1(void)
1476 {
1477 char *dstr;
1478
1479 if (do_raw == 0)
1480 dstr = " cpu";
1481 else
1482 dstr = "cpu";
1483 push_out(dstr);
1484 }
1485
1486 void
print_cpu_hdr2(void)1487 print_cpu_hdr2(void)
1488 {
1489 char *dstr;
1490
1491 if (do_raw == 0)
1492 dstr = " us sy wt id";
1493 else
1494 dstr = "us,sy,wt,id";
1495 push_out(dstr);
1496 }
1497
1498 /*
1499 * Assumption is that tty data is always first - no need for raw mode leading
1500 * comma.
1501 */
1502 void
print_tty_data(void)1503 print_tty_data(void)
1504 {
1505 char *fstr;
1506 uint64_t deltas;
1507 double raw;
1508 double outch;
1509 kstat_t *oldks = NULL;
1510
1511 if (oldss)
1512 oldks = &oldss->s_sys.ss_agg_sys;
1513
1514 if (do_raw == 0)
1515 fstr = " %3.0f %4.0f ";
1516 else
1517 fstr = "%.0f,%.0f";
1518 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "rawch");
1519 raw = deltas;
1520 raw /= getime;
1521 deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "outch");
1522 outch = deltas;
1523 outch /= getime;
1524 push_out(fstr, raw, outch);
1525 }
1526
1527 /*
1528 * Write out CPU data
1529 */
1530 void
print_cpu_data(void)1531 print_cpu_data(void)
1532 {
1533 char *fstr;
1534 uint64_t idle;
1535 uint64_t user;
1536 uint64_t kern;
1537 uint64_t wait;
1538 kstat_t *oldks = NULL;
1539
1540 if (oldss)
1541 oldks = &oldss->s_sys.ss_agg_sys;
1542
1543 if (do_raw == 0)
1544 fstr = " %2.0f %2.0f %2.0f %2.0f";
1545 else
1546 fstr = "%.0f,%.0f,%.0f,%.0f";
1547
1548 idle = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_idle");
1549 user = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_user");
1550 kern = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_kernel");
1551 wait = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_wait");
1552 push_out(fstr, user * percent, kern * percent,
1553 wait * percent, idle * percent);
1554 }
1555
1556 /*
1557 * Emit the appropriate header.
1558 */
1559 void
hdrout(void)1560 hdrout(void)
1561 {
1562 if (do_raw == 0) {
1563 if (--tohdr == 0)
1564 printhdr(0);
1565 } else if (hdr_out == 0) {
1566 printhdr(0);
1567 hdr_out = 1;
1568 }
1569 }
1570
1571 /*
1572 * Write out disk errors when -E is specified.
1573 */
1574 void
disk_errors(void)1575 disk_errors(void)
1576 {
1577 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk_errors, NULL);
1578 }
1579
1580 void
show_first_disk(void)1581 show_first_disk(void)
1582 {
1583 int count = 0;
1584
1585 show_disk_mode = SHOW_FIRST_ONLY;
1586
1587 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1588 }
1589
1590 void
show_other_disks(void)1591 show_other_disks(void)
1592 {
1593 int count = 0;
1594
1595 show_disk_mode = SHOW_SECOND_ONWARDS;
1596
1597 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1598 }
1599
1600 void
show_all_disks(void)1601 show_all_disks(void)
1602 {
1603 int count = 0;
1604
1605 show_disk_mode = SHOW_ALL;
1606
1607 (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1608 }
1609
1610 /*
1611 * Write a newline out and clear the lineout flag.
1612 */
1613 static void
do_newline(void)1614 do_newline(void)
1615 {
1616 if (lineout) {
1617 (void) putchar('\n');
1618 lineout = 0;
1619 }
1620 }
1621
1622 /*
1623 * Generalized printf function that determines what extra
1624 * to print out if we're in raw mode. At this time we
1625 * don't care about errors.
1626 */
1627 static void
push_out(const char * message,...)1628 push_out(const char *message, ...)
1629 {
1630 va_list args;
1631
1632 va_start(args, message);
1633 if (do_raw && lineout == 1)
1634 (void) putchar(',');
1635 (void) vprintf(message, args);
1636 va_end(args);
1637 lineout = 1;
1638 }
1639
1640 /*
1641 * Emit the header string when -e is specified.
1642 */
1643 static void
print_err_hdr(void)1644 print_err_hdr(void)
1645 {
1646 char obuf[SMALL_SCRATCH_BUFLEN];
1647
1648 if (do_raw) {
1649 push_out("errors");
1650 return;
1651 }
1652
1653 if (do_conversions == 0) {
1654 if (!(do_disk & DISK_EXTENDED)) {
1655 (void) snprintf(obuf, sizeof (obuf),
1656 "%11s", one_blank);
1657 push_out(obuf);
1658 }
1659 } else if (do_disk == DISK_ERRORS)
1660 push_out(two_blanks);
1661 else
1662 push_out(one_blank);
1663 push_out("---- errors --- ");
1664 }
1665
1666 /*
1667 * Emit the header string when -e is specified.
1668 */
1669 static void
print_disk_header(void)1670 print_disk_header(void)
1671 {
1672 push_out(disk_header);
1673 }
1674
1675 /*
1676 * No, UINTMAX_MAX isn't the right thing here since
1677 * it is #defined to be either INT32_MAX or INT64_MAX
1678 * depending on the whether _LP64 is defined.
1679 *
1680 * We want to handle the odd future case of having
1681 * ulonglong_t be more than 64 bits but we have
1682 * no nice #define MAX value we can drop in place
1683 * without having to change this code in the future.
1684 */
1685
1686 u_longlong_t
ull_delta(u_longlong_t old,u_longlong_t new)1687 ull_delta(u_longlong_t old, u_longlong_t new)
1688 {
1689 if (new >= old)
1690 return (new - old);
1691 else
1692 return ((UINT64_MAX - old) + new + 1);
1693 }
1694
1695 /*
1696 * Take the difference of an unsigned 32
1697 * bit int attempting to cater for
1698 * overflow.
1699 */
1700 uint_t
u32_delta(uint_t old,uint_t new)1701 u32_delta(uint_t old, uint_t new)
1702 {
1703 if (new >= old)
1704 return (new - old);
1705 else
1706 return ((UINT32_MAX - old) + new + 1);
1707 }
1708
1709 /*
1710 * This is exactly what is needed for standard iostat output,
1711 * but make sure to use it only for that
1712 */
1713 #define EPSILON (0.1)
1714 static int
fzero(double value)1715 fzero(double value)
1716 {
1717 return (value >= 0.0 && value < EPSILON);
1718 }
1719
1720 static int
safe_strtoi(char const * val,char * errmsg)1721 safe_strtoi(char const *val, char *errmsg)
1722 {
1723 char *end;
1724 long tmp;
1725
1726 errno = 0;
1727 tmp = strtol(val, &end, 10);
1728 if (*end != '\0' || errno)
1729 fail(0, "%s %s", errmsg, val);
1730 return ((int)tmp);
1731 }
1732