xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: iostat.c,v 1.69 2022/06/18 11:33:13 kre Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 John M. Vinopal
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed for the NetBSD Project
18  *      by John M. Vinopal.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*-
36  * Copyright (c) 1986, 1991, 1993
37  *      The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\
67  The Regents of the University of California.  All rights reserved.");
68 #endif /* not lint */
69 
70 #ifndef lint
71 #if 0
72 static char sccsid[] = "@(#)iostat.c	8.3 (Berkeley) 4/28/95";
73 #else
74 __RCSID("$NetBSD: iostat.c,v 1.69 2022/06/18 11:33:13 kre Exp $");
75 #endif
76 #endif /* not lint */
77 
78 #include <sys/types.h>
79 #include <sys/ioctl.h>
80 #include <sys/sched.h>
81 #include <sys/time.h>
82 
83 #include <err.h>
84 #include <ctype.h>
85 #include <signal.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 #include <math.h>
91 #include <fnmatch.h>
92 
93 #include "drvstats.h"
94 
95 int		hz;
96 static int	reps, interval;
97 static int	todo = 0;
98 static int	defdrives;
99 static int	winlines = 20;
100 static int	wincols = 80;
101 
102 static int *order, ordersize;
103 
104 static char Line_Marker[] = "________________________________________________";
105 
106 #define	MAX(a,b)	(((a)>(b))?(a):(b))
107 #define	MIN(a,b)	(((a)<(b))?(a):(b))
108 
109 #define	ISSET(x, a)	((x) & (a))
110 #define	SHOW_CPU	(1<<0)
111 #define	SHOW_TTY	(1<<1)
112 #define	SHOW_STATS_1	(1<<2)
113 #define	SHOW_STATS_2	(1<<3)
114 #define	SHOW_STATS_X	(1<<4)
115 #define	SHOW_STATS_Y	(1<<5)
116 #define	SHOW_UPDATES	(1<<6)
117 #define	SHOW_TOTALS	(1<<7)
118 #define	SHOW_NEW_TOTALS	(1<<8)
119 #define	SUPPRESS_ZERO	(1<<9)
120 
121 #define	SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 |	\
122 			    SHOW_STATS_X | SHOW_STATS_Y)
123 
124 /*
125  * Decide how many screen columns each output statistic is given
126  * (these are determined empirically ("looks good to me") and likely
127  * will require changes from time to time as technology advances).
128  *
129  * The odd "+ N" at the end of the summary (total width of stat) definition
130  * allows for the gaps between the columns, and is (#data cols - 1).
131  * So, tty stats have "in" and "out", 2 columns, so there is 1 extra space,
132  * whereas the cpu stats have 5 columns, so 4 extra spaces (etc).
133  */
134 #define	LAYOUT_TTY_IN	4	/* tty input in last interval */
135 #define	LAYOUT_TTY_TIN	7	/* tty input forever */
136 #define	LAYOUT_TTY_OUT	5	/* tty output in last interval */
137 #define	LAYOUT_TTY_TOUT	10	/* tty output forever */
138 #define	LAYOUT_TTY	(((todo & SHOW_TOTALS)				     \
139 				? (LAYOUT_TTY_TIN + LAYOUT_TTY_TOUT)	     \
140 				: (LAYOUT_TTY_IN + LAYOUT_TTY_OUT)) + 1)
141 #define	LAYOUT_TTY_GAP	0		/* always starts at left margin */
142 
143 #define	LAYOUT_CPU_USER	2
144 #define	LAYOUT_CPU_NICE	2
145 #define	LAYOUT_CPU_SYS	2
146 #define	LAYOUT_CPU_INT	2
147 #define	LAYOUT_CPU_IDLE	3
148 #define	LAYOUT_CPU	(LAYOUT_CPU_USER + LAYOUT_CPU_NICE + LAYOUT_CPU_SYS + \
149 			    LAYOUT_CPU_INT + LAYOUT_CPU_IDLE + 4)
150 #define	LAYOUT_CPU_GAP	2
151 
152 			/* used for:       w/o TOTALS  w TOTALS	*/
153 #define	LAYOUT_DRIVE_1_XSIZE	5	/*	KB/t	KB/t	*/
154 #define	LAYOUT_DRIVE_1_RATE	6	/*	t/s		*/
155 #define	LAYOUT_DRIVE_1_XFER	10	/*		xfr	*/
156 #define	LAYOUT_DRIVE_1_SPEED	5	/*	MB/s		*/
157 #define	LAYOUT_DRIVE_1_VOLUME	8	/*		MB	*/
158 #define	LAYOUT_DRIVE_1_INCR	5	/*		(inc)	*/
159 
160 #define	LAYOUT_DRIVE_2_XSIZE	7	/*	KB		*/
161 #define	LAYOUT_DRIVE_2_VOLUME	11	/*		KB	*/
162 #define	LAYOUT_DRIVE_2_XFR	7	/*	xfr		*/
163 #define	LAYOUT_DRIVE_2_TXFR	10	/*		xfr	*/
164 #define	LAYOUT_DRIVE_2_INCR	5	/*		(inc)	*/
165 #define	LAYOUT_DRIVE_2_TBUSY	9	/*		time	*/
166 #define	LAYOUT_DRIVE_2_BUSY	5	/*	time		*/
167 
168 #define	LAYOUT_DRIVE_1	(LAYOUT_DRIVE_1_XSIZE + ((todo & SHOW_TOTALS) ?	       \
169 			    (LAYOUT_DRIVE_1_XFER + LAYOUT_DRIVE_1_VOLUME +     \
170 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_1_INCR+2 :0)) \
171 			  : (LAYOUT_DRIVE_1_RATE + LAYOUT_DRIVE_1_SPEED)) + 3)
172 #define	LAYOUT_DRIVE_2	(((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME +      \
173 			    LAYOUT_DRIVE_2_TXFR + LAYOUT_DRIVE_2_TBUSY +       \
174 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+2 : 0))\
175 			  : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_XFR +       \
176 			     LAYOUT_DRIVE_2_BUSY)) + 3)
177 
178 #define	LAYOUT_DRIVE_GAP 0	/* Gap included in column, always present */
179 
180 /* TODO: X & Y stats layouts */
181 
182 static void cpustats(void);
183 static double drive_time(double, int);
184 static void drive_stats(int, double);
185 static void drive_stats2(int, double);
186 static void drive_statsx(int, double);
187 static void drive_statsy(int, double);
188 static void drive_statsy_io(double, double, double);
189 static void drive_statsy_q(double, double, double, double, double, double);
190 static void sig_header(int);
191 static volatile int do_header;
192 static void header(int);
193 __dead static void usage(void);
194 static void display(int);
195 static int selectdrives(int, char *[], int);
196 
197 int
198 main(int argc, char *argv[])
199 {
200 	int ch, hdrcnt, hdroffset, ndrives, lines;
201 	struct timespec	tv;
202 	struct ttysize ts;
203 	long width = -1, height = -1;
204 	char *ep;
205 
206 #if 0		/* -i and -u are not currently (sanely) implementable */
207 	while ((ch = getopt(argc, argv, "Cc:dDH:iITuw:W:xyz")) != -1)
208 #else
209 	while ((ch = getopt(argc, argv, "Cc:dDH:ITw:W:xyz")) != -1)
210 #endif
211 		switch (ch) {
212 		case 'c':
213 			if ((reps = atoi(optarg)) <= 0)
214 				errx(1, "repetition count <= 0.");
215 			break;
216 		case 'C':
217 			todo |= SHOW_CPU;
218 			break;
219 		case 'd':
220 			todo &= ~SHOW_STATS_ALL;
221 			todo |= SHOW_STATS_1;
222 			break;
223 		case 'D':
224 			todo &= ~SHOW_STATS_ALL;
225 			todo |= SHOW_STATS_2;
226 			break;
227 		case 'H':
228 			height = strtol(optarg, &ep, 10);
229 			if (height < 0 || *ep != '\0')
230 				errx(1, "bad height (-H) value.");
231 			height += 2;	/* magic, but needed to be sane */
232 			break;
233 #if 0
234 		case 'i':
235 			todo |= SHOW_TOTALS | SHOW_NEW_TOTALS;
236 			break;
237 #endif
238 		case 'I':
239 			todo |= SHOW_TOTALS;
240 			break;
241 		case 'T':
242 			todo |= SHOW_TTY;
243 			break;
244 #if 0
245 		case 'u':
246 			todo |= SHOW_UPDATES;
247 			break;
248 #endif
249 		case 'w':
250 			if ((interval = atoi(optarg)) <= 0)
251 				errx(1, "interval <= 0.");
252 			break;
253 		case 'W':
254 			width = strtol(optarg, &ep, 10);
255 			if (width < 0 || *ep != '\0')
256 				errx(1, "bad width (-W) value.");
257 			break;
258 		case 'x':
259 			todo &= ~SHOW_STATS_ALL;
260 			todo |= SHOW_STATS_X;
261 			break;
262 		case 'y':
263 			todo &= ~SHOW_STATS_ALL;
264 			todo |= SHOW_STATS_Y;
265 			break;
266 		case 'z':
267 			todo |= SUPPRESS_ZERO;
268 			break;
269 		case '?':
270 		default:
271 			usage();
272 		}
273 	argc -= optind;
274 	argv += optind;
275 
276 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
277 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
278 	if (ISSET(todo, SHOW_STATS_X)) {
279 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
280 		todo |= SHOW_STATS_X;
281 	}
282 	if (ISSET(todo, SHOW_STATS_Y)) {
283 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS);
284 		todo |= SHOW_STATS_Y;
285 	}
286 
287 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
288 		if (ts.ts_lines)
289 			winlines = ts.ts_lines;
290 		if (ts.ts_cols)
291 			wincols = ts.ts_cols;
292 	}
293 
294 	if (height == -1) {
295 		char *lns = getenv("LINES");
296 
297 		if (lns == NULL || (height = strtol(lns, &ep, 10)) < 0 ||
298 		    *ep != '\0')
299 			height = winlines;
300 	}
301 	winlines = height;
302 
303 	if (width == -1) {
304 		char *cols = getenv("COLUMNS");
305 
306 		if (cols == NULL || (width = strtol(cols, &ep, 10)) < 0 ||
307 		    *ep != '\0')
308 			width = wincols;
309 	}
310 	defdrives = width;
311 	if (defdrives == 0) {
312 		defdrives = 5000;	/* anything absurdly big */
313 	} else {
314 		if (ISSET(todo, SHOW_CPU))
315 			defdrives -= LAYOUT_CPU + LAYOUT_CPU_GAP;
316 		if (ISSET(todo, SHOW_TTY))
317 			defdrives -= LAYOUT_TTY + LAYOUT_TTY_GAP;
318 		if (ISSET(todo, SHOW_STATS_2))
319 			defdrives /= LAYOUT_DRIVE_2 + LAYOUT_DRIVE_GAP;
320 		else
321 			defdrives /= LAYOUT_DRIVE_1 + LAYOUT_DRIVE_GAP;
322 	}
323 
324 	drvinit(0);
325 	cpureadstats();
326 	drvreadstats();
327 	ordersize = 0;
328 	ndrives = selectdrives(argc, argv, 1);
329 	if (ndrives == 0) {
330 		/* No drives are selected.  No need to show drive stats. */
331 		todo &= ~SHOW_STATS_ALL;
332 		if (todo == 0)
333 			errx(1, "no drives");
334 	}
335 	tv.tv_sec = interval;
336 	tv.tv_nsec = 0;
337 
338 	/* print a new header on sigcont */
339 	(void)signal(SIGCONT, sig_header);
340 	do_header = 1;
341 
342 	for (hdrcnt = 1;;) {
343 		if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y)) {
344 			lines = ndrives;
345 			hdroffset = 3;
346 		} else {
347 			lines = 1;
348 			hdroffset = 4;
349 		}
350 
351 		if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) {
352 			do_header = 0;
353 			header(ndrives);
354 			hdrcnt = winlines - hdroffset;
355 		}
356 
357 		if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) {
358 			cpuswap();
359 			drvswap();
360 			tkswap();
361 			todo &= ~SHOW_NEW_TOTALS;
362 		}
363 
364 		display(ndrives);
365 
366 		if (reps >= 0 && --reps <= 0)
367 			break;
368 		nanosleep(&tv, NULL);
369 		cpureadstats();
370 		drvreadstats();
371 
372 		ndrives = selectdrives(argc, argv, 0);
373 	}
374 	exit(0);
375 }
376 
377 static void
378 sig_header(int signo)
379 {
380 	do_header = 1;
381 }
382 
383 static void
384 header(int ndrives)
385 {
386 	int i;
387 
388 					/* Main Headers. */
389 	if (ISSET(todo, SHOW_STATS_X)) {
390 		if (ISSET(todo, SHOW_TOTALS)) {
391 			(void)printf(
392 			    "device  read KB/t    xfr   time     MB  ");
393 			(void)printf(" write KB/t    xfr   time     MB\n");
394 		} else {
395 			(void)printf(
396 			    "device  read KB/t    r/s   time     MB/s");
397 			(void)printf(" write KB/t    w/s   time     MB/s\n");
398 		}
399 		return;
400 	}
401 
402 	if (ISSET(todo, SHOW_STATS_Y)) {
403 		(void)printf("device  read KB/t    r/s     MB/s write KB/t    w/s     MB/s");
404 		(void)printf("   wait   actv  wsvc_t  asvc_t  wtime   time");
405 		(void)printf("\n");
406 		return;
407 	}
408 
409 	if (ISSET(todo, SHOW_TTY))
410 		(void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty");
411 
412 	if (ISSET(todo, SHOW_STATS_1)) {
413 		for (i = 0; i < ndrives; i++) {
414 			char *dname = cur.name[order[i]];
415 			int dnlen = (int)strlen(dname);
416 
417 			printf(" ");	/* always a 1 column gap */
418 			if (dnlen < LAYOUT_DRIVE_1 - 6)
419 				printf("|%-*.*s ",
420 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
421 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
422 				    Line_Marker);
423 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
424 			    MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0),
425 				LAYOUT_DRIVE_1) : 0),
426 			    LAYOUT_DRIVE_1, dname);
427 			if (dnlen < LAYOUT_DRIVE_1 - 6)
428 				printf(" %*.*s|",
429 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
430 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
431 				    Line_Marker);
432 		}
433 	}
434 
435 	if (ISSET(todo, SHOW_STATS_2)) {
436 		for (i = 0; i < ndrives; i++) {
437 			char *dname = cur.name[order[i]];
438 			int dnlen = (int)strlen(dname);
439 
440 			printf(" ");	/* always a 1 column gap */
441 			if (dnlen < LAYOUT_DRIVE_2 - 6)
442 				printf("|%-*.*s ",
443 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
444 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
445 				    Line_Marker);
446 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
447 			    MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0),
448 				LAYOUT_DRIVE_2) : 0),
449 			    LAYOUT_DRIVE_1, dname);
450 			if (dnlen < LAYOUT_DRIVE_2 - 6)
451 				printf(" %*.*s|",
452 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
453 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
454 				    Line_Marker);
455 		}
456 	}
457 
458 	if (ISSET(todo, SHOW_CPU))
459 		(void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU");
460 
461 	printf("\n");
462 
463 					/* Sub-Headers. */
464 	if (ISSET(todo, SHOW_TTY)) {
465 		printf("%*s %*s",
466 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin",
467 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout");
468 	}
469 
470 	if (ISSET(todo, SHOW_STATS_1)) {
471 		for (i = 0; i < ndrives; i++) {
472 			if (ISSET(todo, SHOW_TOTALS)) {
473 				(void)printf(" %*s %*s %*s",
474 				    LAYOUT_DRIVE_1_XFER, "xfr",
475 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
476 				    LAYOUT_DRIVE_1_VOLUME, "MB");
477 			} else {
478 				(void)printf(" %*s %*s %*s",
479 				    LAYOUT_DRIVE_1_RATE, "t/s",
480 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
481 				    LAYOUT_DRIVE_1_SPEED, "MB/s");
482 			}
483 		}
484 	}
485 
486 	if (ISSET(todo, SHOW_STATS_2)) {
487 		for (i = 0; i < ndrives; i++) {
488 			if (ISSET(todo, SHOW_TOTALS)) {
489 				(void)printf(" %*s %*s %*s",
490 				    LAYOUT_DRIVE_2_TXFR, "xfr",
491 				    LAYOUT_DRIVE_2_VOLUME, "KB",
492 				    LAYOUT_DRIVE_2_TBUSY, "time");
493 			} else {
494 				(void)printf(" %*s %*s %*s",
495 				    LAYOUT_DRIVE_2_XFR, "xfr",
496 				    LAYOUT_DRIVE_2_XSIZE, "KB",
497 				    LAYOUT_DRIVE_2_BUSY, "time");
498 			}
499 		}
500 	}
501 
502 	/* should do this properly, but it is such a simple case... */
503 	if (ISSET(todo, SHOW_CPU))
504 		(void)printf("  us ni sy in  id");
505 	printf("\n");
506 }
507 
508 static double
509 drive_time(double etime, int dn)
510 {
511 	if (ISSET(todo, SHOW_TOTALS))
512 		return etime;
513 
514 	if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
515 		etime = (double)cur.timestamp[dn].tv_sec +
516 		    ((double)cur.timestamp[dn].tv_usec / (double)1000000);
517 	}
518 
519 	return etime;
520 }
521 
522 static void
523 drive_stats(int ndrives, double etime)
524 {
525 	int drive;
526 	double atime, dtime, mbps;
527 	int c1, c2, c3;
528 
529 	if (ISSET(todo, SHOW_TOTALS)) {
530 		c1 = LAYOUT_DRIVE_1_XFER;
531 		c2 = LAYOUT_DRIVE_1_XSIZE;
532 		c3 = LAYOUT_DRIVE_1_VOLUME;
533 	} else {
534 		c1 = LAYOUT_DRIVE_1_RATE;
535 		c2 = LAYOUT_DRIVE_1_XSIZE;
536 		c3 = LAYOUT_DRIVE_1_SPEED;
537 	}
538 
539 	for (drive = 0; drive < ndrives; ++drive) {
540 		int dn = order[drive];
541 
542 		if (!cur.select[dn])	/* should be impossible */
543 			continue;
544 
545 		if (todo & SUPPRESS_ZERO) {
546 			if (cur.rxfer[dn] == 0 &&
547 			    cur.wxfer[dn] == 0 &&
548 			    cur.rbytes[dn] == 0 &&
549 			    cur.wbytes[dn] == 0) {
550 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
551 				continue;
552 			}
553 		}
554 
555 		dtime = drive_time(etime, dn);
556 
557 					/* average transfers per second. */
558 		(void)printf(" %*.0f", c1,
559 		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
560 
561 					/* average Kbytes per transfer. */
562 		if (cur.rxfer[dn] + cur.wxfer[dn])
563 			mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
564 			    1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
565 		else
566 			mbps = 0.0;
567 		(void)printf(" %*.*f", c2,
568 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
569 
570 					/* time busy in drive activity */
571 		atime = (double)cur.time[dn].tv_sec +
572 		    ((double)cur.time[dn].tv_usec / (double)1000000);
573 
574 					/* Megabytes per second. */
575 		if (atime != 0.0)
576 			mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
577 			    (double)(1024 * 1024);
578 		else
579 			mbps = 0;
580 		mbps /= dtime;
581 		(void)printf(" %*.*f", c3,
582 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
583 	}
584 }
585 
586 static void
587 drive_stats2(int ndrives, double etime)
588 {
589 	int drive;
590 	double atime, dtime;
591 	int c1, c2, c3;
592 
593 	if (ISSET(todo, SHOW_TOTALS)) {
594 		c1 = LAYOUT_DRIVE_2_TXFR;
595 		c2 = LAYOUT_DRIVE_2_VOLUME;
596 		c3 = LAYOUT_DRIVE_2_TBUSY;
597 	} else {
598 		c1 = LAYOUT_DRIVE_2_XFR;
599 		c2 = LAYOUT_DRIVE_2_XSIZE;
600 		c3 = LAYOUT_DRIVE_2_BUSY;
601 	}
602 
603 	for (drive = 0; drive < ndrives; ++drive) {
604 		int dn = order[drive];
605 
606 		if (!cur.select[dn])		/* should be impossible */
607 			continue;
608 
609 		if (todo & SUPPRESS_ZERO) {
610 			if (cur.rxfer[dn] == 0 &&
611 			    cur.wxfer[dn] == 0 &&
612 			    cur.rbytes[dn] == 0 &&
613 			    cur.wbytes[dn] == 0) {
614 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
615 				continue;
616 			}
617 		}
618 
619 		dtime = drive_time(etime, dn);
620 
621 					/* average transfers per second. */
622 		(void)printf(" %*.0f", c1,
623 		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
624 
625 					/* average kbytes per second. */
626 		(void)printf(" %*.0f", c2,
627 		    (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / dtime);
628 
629 					/* average time busy in dn activity */
630 		atime = (double)cur.time[dn].tv_sec +
631 		    ((double)cur.time[dn].tv_usec / (double)1000000);
632 		(void)printf(" %*.2f", c3, atime / dtime);
633 	}
634 }
635 
636 static void
637 drive_statsx(int ndrives, double etime)
638 {
639 	int dn, drive;
640 	double atime, dtime, kbps;
641 
642 	for (drive = 0; drive < ndrives; ++drive) {
643 		dn = order[drive];
644 
645 		if (!cur.select[dn])	/* impossible */
646 			continue;
647 
648 		(void)printf("%-8.8s", cur.name[dn]);
649 
650 		if (todo & SUPPRESS_ZERO) {
651 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
652 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
653 				printf("\n");
654 				continue;
655 			}
656 		}
657 
658 		dtime = drive_time(etime, dn);
659 
660 					/* average read Kbytes per transfer */
661 		if (cur.rxfer[dn])
662 			kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
663 		else
664 			kbps = 0.0;
665 		(void)printf(" %8.2f", kbps);
666 
667 					/* average read transfers
668 					   (per second) */
669 		(void)printf(" %6.0f", cur.rxfer[dn] / dtime);
670 
671 					/* time read busy in drive activity */
672 		atime = (double)cur.time[dn].tv_sec +
673 		    ((double)cur.time[dn].tv_usec / (double)1000000);
674 		(void)printf(" %6.2f", atime / dtime);
675 
676 					/* average read megabytes
677 					   (per second) */
678 		(void)printf(" %8.2f",
679 		    cur.rbytes[dn] / (1024.0 * 1024) / dtime);
680 
681 
682 					/* average write Kbytes per transfer */
683 		if (cur.wxfer[dn])
684 			kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
685 		else
686 			kbps = 0.0;
687 		(void)printf("   %8.2f", kbps);
688 
689 					/* average write transfers
690 					   (per second) */
691 		(void)printf(" %6.0f", cur.wxfer[dn] / dtime);
692 
693 					/* time write busy in drive activity */
694 		atime = (double)cur.time[dn].tv_sec +
695 		    ((double)cur.time[dn].tv_usec / (double)1000000);
696 		(void)printf(" %6.2f", atime / dtime);
697 
698 					/* average write megabytes
699 					   (per second) */
700 		(void)printf(" %8.2f\n",
701 		    cur.wbytes[dn] / (1024.0 * 1024) / dtime);
702 	}
703 }
704 
705 static void
706 drive_statsy_io(double elapsed, double count, double volume)
707 {
708 	double kbps;
709 
710 	/* average Kbytes per transfer */
711 	if (count)
712 		kbps = (volume / 1024.0) / count;
713 	else
714 		kbps = 0.0;
715 	(void)printf(" %8.2f", kbps);
716 
717 	/* average transfers (per second) */
718 	(void)printf(" %6.0f", count / elapsed);
719 
720 	/* average megabytes (per second) */
721 	(void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
722 }
723 
724 static void
725 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
726 {
727 	/* average wait queue length */
728 	(void)printf(" %6.1f", waitsum / elapsed);
729 
730 	/* average busy queue length */
731 	(void)printf(" %6.1f", busysum / elapsed);
732 
733 	/* average wait time */
734 	(void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
735 
736 	/* average service time */
737 	(void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
738 
739 	/* time waiting for drive activity */
740 	(void)printf(" %6.2f", wait / elapsed);
741 
742 	/* time busy in drive activity */
743 	(void)printf(" %6.2f", busy / elapsed);
744 }
745 
746 static void
747 drive_statsy(int ndrives, double etime)
748 {
749 	int drive, dn;
750 	double atime, await, abusysum, awaitsum, dtime;
751 
752 	for (drive = 0; drive < ndrives; ++drive) {
753 		dn = order[drive];
754 		if (!cur.select[dn])	/* impossible */
755 			continue;
756 
757 		(void)printf("%-8.8s", cur.name[dn]);
758 
759 		if (todo & SUPPRESS_ZERO) {
760 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
761 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
762 				printf("\n");
763 				continue;
764 			}
765 		}
766 
767 		dtime = drive_time(etime, dn);
768 
769 		atime = (double)cur.time[dn].tv_sec +
770 		    ((double)cur.time[dn].tv_usec / (double)1000000);
771 		await = (double)cur.wait[dn].tv_sec +
772 		    ((double)cur.wait[dn].tv_usec / (double)1000000);
773 		abusysum = (double)cur.busysum[dn].tv_sec +
774 		    ((double)cur.busysum[dn].tv_usec / (double)1000000);
775 		awaitsum = (double)cur.waitsum[dn].tv_sec +
776 		    ((double)cur.waitsum[dn].tv_usec / (double)1000000);
777 
778 		drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]);
779 		(void)printf("  ");
780 		drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]);
781 		drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]);
782 
783 		(void)printf("\n");
784 	}
785 }
786 
787 static void
788 cpustats(void)
789 {
790 	int state;
791 	double ttime;
792 
793 	static int cwidth[CPUSTATES] = {
794 		LAYOUT_CPU_USER,
795 		LAYOUT_CPU_NICE,
796 		LAYOUT_CPU_SYS,
797 		LAYOUT_CPU_INT,
798 		LAYOUT_CPU_IDLE
799 	};
800 
801 	ttime = 0;
802 	for (state = 0; state < CPUSTATES; ++state)
803 		ttime += cur.cp_time[state];
804 	if (!ttime)
805 		ttime = 1.0;
806 
807 	printf("%*s", LAYOUT_CPU_GAP - 1, "");	/* the 1 is the next space */
808 	for (state = 0; state < CPUSTATES; ++state) {
809 		if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) {
810 			printf(" %*s", cwidth[state], "");
811 			continue;
812 		}
813 		printf(" %*.0f", cwidth[state],
814 		    100. * cur.cp_time[state] / ttime);
815 	}
816 }
817 
818 static void
819 usage(void)
820 {
821 
822 	(void)fprintf(stderr, "usage: iostat [-CdDITxyz] [-c count] "
823 	    "[-H height] [-W width] [-w wait] [drives]\n");
824 	exit(1);
825 }
826 
827 static void
828 display(int ndrives)
829 {
830 	double	etime;
831 
832 	/* Sum up the elapsed ticks. */
833 	etime = cur.cp_etime;
834 
835 	/*
836 	 * If we're showing totals only, then don't divide by the
837 	 * system time.
838 	 */
839 	if (ISSET(todo, SHOW_TOTALS))
840 		etime = 1.0;
841 
842 	if (ISSET(todo, SHOW_STATS_X)) {
843 		drive_statsx(ndrives, etime);
844 		goto out;
845 	}
846 
847 	if (ISSET(todo, SHOW_STATS_Y)) {
848 		drive_statsy(ndrives, etime);
849 		goto out;
850 	}
851 
852 	if (ISSET(todo, SHOW_TTY))
853 		printf("%*.0f %*.0f",
854 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN),
855 		    cur.tk_nin / etime,
856 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT),
857 		    cur.tk_nout / etime);
858 
859 	if (ISSET(todo, SHOW_STATS_1)) {
860 		drive_stats(ndrives, etime);
861 	}
862 
863 
864 	if (ISSET(todo, SHOW_STATS_2)) {
865 		drive_stats2(ndrives, etime);
866 	}
867 
868 
869 	if (ISSET(todo, SHOW_CPU))
870 		cpustats();
871 
872 	(void)printf("\n");
873 
874 out:
875 	(void)fflush(stdout);
876 }
877 
878 static int
879 selectdrives(int argc, char *argv[], int first)
880 {
881 	int	i, maxdrives, ndrives, tried;
882 
883 	/*
884 	 * Choose drives to be displayed.  Priority goes to (in order) drives
885 	 * supplied as arguments and default drives.  If everything isn't
886 	 * filled in and there are drives not taken care of, display the first
887 	 * few that fit.
888 	 *
889 	 * The backward compatibility #ifdefs permit the syntax:
890 	 *	iostat [ drives ] [ interval [ count ] ]
891 	 */
892 
893 #define	BACKWARD_COMPATIBILITY
894 	for (tried = ndrives = 0; *argv; ++argv) {
895 #ifdef BACKWARD_COMPATIBILITY
896 		if (isdigit((unsigned char)**argv))
897 			break;
898 #endif
899 		tried++;
900 		for (i = 0; i < (int)ndrive; i++) {
901 			if (fnmatch(*argv, cur.name[i], 0))
902 				continue;
903 			cur.select[i] = 1;
904 			if (ordersize <= ndrives) {
905 				int *new = realloc(order,
906 				    (ordersize + 8) * sizeof *order);
907 				if (new == NULL)
908 					break;
909 				ordersize += 8;
910 				order = new;
911 			}
912 			order[ndrives++] = i;
913 		}
914 
915 	}
916 
917 	if (ndrives == 0 && tried == 0) {
918 		/*
919 		 * Pick up to defdrives (or all if -x is given) drives
920 		 * if none specified.
921 		 */
922 		maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
923 			     (int)ndrive < defdrives)
924 			? (int)(ndrive) : defdrives;
925 		ordersize = maxdrives;
926 		free(order);
927 		order = calloc(ordersize, sizeof *order);
928 		if (order == NULL)
929 			errx(1, "Insufficient memory");
930 		for (i = 0; i < maxdrives; i++) {
931 			cur.select[i] = 1;
932 			order[i] = i;
933 
934 			++ndrives;
935 			if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
936 			    ndrives == defdrives)
937 				break;
938 		}
939 	}
940 
941 #ifdef BACKWARD_COMPATIBILITY
942 	if (first && *argv) {
943 		interval = atoi(*argv);
944 		if (*++argv)
945 			reps = atoi(*argv);
946 	}
947 #endif
948 
949 	if (interval) {
950 		if (!reps)
951 			reps = -1;
952 	} else
953 		if (reps)
954 			interval = 1;
955 
956 	return (ndrives);
957 }
958