xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision a0065a57e41ffa4aad6781fb220f205ca1c31c4d)
1 /*	$NetBSD: iostat.c,v 1.72 2023/10/30 19:43:33 mrg 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.72 2023/10/30 19:43:33 mrg 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	(1u<<0)
111 #define	SHOW_TTY	(1u<<1)
112 #define	SHOW_STATS_1	(1u<<2)
113 #define	SHOW_STATS_2	(1u<<3)
114 #define	SHOW_STATS_3	(1u<<4)
115 #define	SHOW_STATS_X	(1u<<5)
116 #define	SHOW_STATS_Y	(1u<<6)
117 #define	SHOW_UPDATES	(1u<<7)
118 #define	SHOW_TOTALS	(1u<<8)
119 #define	SHOW_NEW_TOTALS	(1u<<9)
120 #define	SUPPRESS_ZERO	(1u<<10)
121 
122 #define	SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 |	\
123 			 SHOW_STATS_3 | SHOW_STATS_X | SHOW_STATS_Y)
124 
125 /*
126  * Decide how many screen columns each output statistic is given
127  * (these are determined empirically ("looks good to me") and likely
128  * will require changes from time to time as technology advances).
129  *
130  * The odd "+ N" at the end of the summary (total width of stat) definition
131  * allows for the gaps between the columns, and is (#data cols - 1).
132  * So, tty stats have "in" and "out", 2 columns, so there is 1 extra space,
133  * whereas the cpu stats have 5 columns, so 4 extra spaces (etc).
134  */
135 #define	LAYOUT_TTY_IN	4	/* tty input in last interval */
136 #define	LAYOUT_TTY_TIN	7	/* tty input forever */
137 #define	LAYOUT_TTY_OUT	5	/* tty output in last interval */
138 #define	LAYOUT_TTY_TOUT	10	/* tty output forever */
139 #define	LAYOUT_TTY	(((todo & SHOW_TOTALS)				     \
140 				? (LAYOUT_TTY_TIN + LAYOUT_TTY_TOUT)	     \
141 				: (LAYOUT_TTY_IN + LAYOUT_TTY_OUT)) + 1)
142 #define	LAYOUT_TTY_GAP	0		/* always starts at left margin */
143 
144 #define	LAYOUT_CPU_USER	2
145 #define	LAYOUT_CPU_NICE	2
146 #define	LAYOUT_CPU_SYS	2
147 #define	LAYOUT_CPU_INT	2
148 #define	LAYOUT_CPU_IDLE	3
149 #define	LAYOUT_CPU	(LAYOUT_CPU_USER + LAYOUT_CPU_NICE + LAYOUT_CPU_SYS + \
150 			    LAYOUT_CPU_INT + LAYOUT_CPU_IDLE + 4)
151 #define	LAYOUT_CPU_GAP	2
152 
153 			/* used for:       w/o TOTALS  w TOTALS	*/
154 #define	LAYOUT_DRIVE_1_XSIZE	5	/*	KB/t	KB/t	*/
155 #define	LAYOUT_DRIVE_1_RATE	6	/*	t/s		*/
156 #define	LAYOUT_DRIVE_1_XFER	10	/*		xfr	*/
157 #define	LAYOUT_DRIVE_1_SPEED	5	/*	MB/s		*/
158 #define	LAYOUT_DRIVE_1_VOLUME	8	/*		MB	*/
159 #define	LAYOUT_DRIVE_1_INCR	5	/*		(inc)	*/
160 
161 #define	LAYOUT_DRIVE_2_XSIZE	7	/*	KB		*/
162 #define	LAYOUT_DRIVE_2_VOLUME	11	/*		KB	*/
163 #define	LAYOUT_DRIVE_2_XFR	7	/*	xfr		*/
164 #define	LAYOUT_DRIVE_2_TXFR	10	/*		xfr	*/
165 #define	LAYOUT_DRIVE_2_INCR	5	/*		(inc)	*/
166 #define	LAYOUT_DRIVE_2_TBUSY	9	/*		time	*/
167 #define	LAYOUT_DRIVE_2_BUSY	5	/*	time		*/
168 
169 /* Layout 3 uses same sizes as 2, but with MB. */
170 
171 #define	LAYOUT_DRIVE_1	(LAYOUT_DRIVE_1_XSIZE + ((todo & SHOW_TOTALS) ?	       \
172 			    (LAYOUT_DRIVE_1_XFER + LAYOUT_DRIVE_1_VOLUME +     \
173 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_1_INCR+2 :0)) \
174 			  : (LAYOUT_DRIVE_1_RATE + LAYOUT_DRIVE_1_SPEED)) + 3)
175 #define	LAYOUT_DRIVE_2	(((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME +      \
176 			    LAYOUT_DRIVE_2_TXFR + LAYOUT_DRIVE_2_TBUSY +       \
177 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+2 : 0))\
178 			  : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_XFR +       \
179 			     LAYOUT_DRIVE_2_BUSY)) + 3)
180 #define	LAYOUT_DRIVE_3	(((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME +      \
181 			    LAYOUT_DRIVE_2_TBUSY +			       \
182 			    ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+1 : 0))\
183 			  : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_BUSY)) + 2)
184 
185 #define	LAYOUT_DRIVE_GAP 0	/* Gap included in column, always present */
186 
187 /* TODO: X & Y stats layouts */
188 
189 static void cpustats(void);
190 static double drive_time(double, int);
191 static void drive_stats(int, double);
192 static void drive_stats2(int, double);
193 static void drive_statsx(int, double);
194 static void drive_statsy(int, double);
195 static void drive_statsy_io(double, double, double);
196 static void drive_statsy_q(double, double, double, double, double, double);
197 static void sig_header(int);
198 static volatile int do_header;
199 static void header(int);
200 __dead static void usage(void);
201 static void display(int);
202 static int selectdrives(int, char *[], int);
203 
204 int
main(int argc,char * argv[])205 main(int argc, char *argv[])
206 {
207 	int ch, hdrcnt, hdroffset, ndrives, lines;
208 	struct timespec	tv;
209 	struct ttysize ts;
210 	long width = -1, height = -1;
211 	char *ep;
212 
213 #if 0		/* -i and -u are not currently (sanely) implementable */
214 	while ((ch = getopt(argc, argv, "Cc:dDH:iITuw:W:xXyz")) != -1)
215 #else
216 	while ((ch = getopt(argc, argv, "Cc:dDH:ITw:W:xXyz")) != -1)
217 #endif
218 		switch (ch) {
219 		case 'c':
220 			if ((reps = atoi(optarg)) <= 0)
221 				errx(1, "repetition count <= 0.");
222 			break;
223 		case 'C':
224 			todo |= SHOW_CPU;
225 			break;
226 		case 'd':
227 			todo &= ~SHOW_STATS_ALL;
228 			todo |= SHOW_STATS_1;
229 			break;
230 		case 'D':
231 			todo &= ~SHOW_STATS_ALL;
232 			todo |= SHOW_STATS_2;
233 			break;
234 		case 'H':
235 			height = strtol(optarg, &ep, 10);
236 			if (height < 0 || *ep != '\0')
237 				errx(1, "bad height (-H) value.");
238 			height += 2;	/* magic, but needed to be sane */
239 			break;
240 #if 0
241 		case 'i':
242 			todo |= SHOW_TOTALS | SHOW_NEW_TOTALS;
243 			break;
244 #endif
245 		case 'I':
246 			todo |= SHOW_TOTALS;
247 			break;
248 		case 'T':
249 			todo |= SHOW_TTY;
250 			break;
251 #if 0
252 		case 'u':
253 			todo |= SHOW_UPDATES;
254 			break;
255 #endif
256 		case 'w':
257 			if ((interval = atoi(optarg)) <= 0)
258 				errx(1, "interval <= 0.");
259 			break;
260 		case 'W':
261 			width = strtol(optarg, &ep, 10);
262 			if (width < 0 || *ep != '\0')
263 				errx(1, "bad width (-W) value.");
264 			break;
265 		case 'x':
266 			todo &= ~SHOW_STATS_ALL;
267 			todo |= SHOW_STATS_X;
268 			break;
269 		case 'X':
270 			todo &= ~SHOW_STATS_ALL;
271 			todo |= SHOW_STATS_3;
272 			break;
273 		case 'y':
274 			todo &= ~SHOW_STATS_ALL;
275 			todo |= SHOW_STATS_Y;
276 			break;
277 		case 'z':
278 			todo |= SUPPRESS_ZERO;
279 			break;
280 		case '?':
281 		default:
282 			usage();
283 		}
284 	argc -= optind;
285 	argv += optind;
286 
287 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
288 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
289 	if (ISSET(todo, SHOW_STATS_X)) {
290 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
291 		todo |= SHOW_STATS_X;
292 	}
293 	if (ISSET(todo, SHOW_STATS_3)) {
294 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
295 		todo |= SHOW_STATS_3;
296 	}
297 	if (ISSET(todo, SHOW_STATS_Y)) {
298 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS);
299 		todo |= SHOW_STATS_Y;
300 	}
301 
302 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
303 		if (ts.ts_lines)
304 			winlines = ts.ts_lines;
305 		if (ts.ts_cols)
306 			wincols = ts.ts_cols;
307 	}
308 
309 	if (height == -1) {
310 		char *lns = getenv("LINES");
311 
312 		if (lns == NULL || (height = strtol(lns, &ep, 10)) < 0 ||
313 		    *ep != '\0')
314 			height = winlines;
315 	}
316 	winlines = height;
317 
318 	if (width == -1) {
319 		char *cols = getenv("COLUMNS");
320 
321 		if (cols == NULL || (width = strtol(cols, &ep, 10)) < 0 ||
322 		    *ep != '\0')
323 			width = wincols;
324 	}
325 	defdrives = width;
326 	if (defdrives == 0) {
327 		defdrives = 5000;	/* anything absurdly big */
328 	} else {
329 		if (ISSET(todo, SHOW_CPU))
330 			defdrives -= LAYOUT_CPU + LAYOUT_CPU_GAP;
331 		if (ISSET(todo, SHOW_TTY))
332 			defdrives -= LAYOUT_TTY + LAYOUT_TTY_GAP;
333 		if (ISSET(todo, SHOW_STATS_2))
334 			defdrives /= LAYOUT_DRIVE_2 + LAYOUT_DRIVE_GAP;
335 		if (ISSET(todo, SHOW_STATS_3))
336 			defdrives /= LAYOUT_DRIVE_3 + LAYOUT_DRIVE_GAP;
337 		else
338 			defdrives /= LAYOUT_DRIVE_1 + LAYOUT_DRIVE_GAP;
339 	}
340 
341 	drvinit(0);
342 	cpureadstats();
343 	drvreadstats();
344 	ordersize = 0;
345 	ndrives = selectdrives(argc, argv, 1);
346 	if (ndrives == 0) {
347 		/* No drives are selected.  No need to show drive stats. */
348 		todo &= ~SHOW_STATS_ALL;
349 		if (todo == 0)
350 			errx(1, "no drives");
351 	}
352 	tv.tv_sec = interval;
353 	tv.tv_nsec = 0;
354 
355 	/* print a new header on sigcont */
356 	(void)signal(SIGCONT, sig_header);
357 	do_header = 1;
358 
359 	for (hdrcnt = 1;;) {
360 		if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y)) {
361 			lines = ndrives;
362 			hdroffset = 3;
363 		} else if (ISSET(todo, SHOW_STATS_3)) {
364 			lines = 1;
365 			hdroffset = 3;
366 		} else {
367 			lines = 1;
368 			hdroffset = 4;
369 		}
370 
371 		if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) {
372 			do_header = 0;
373 			header(ndrives);
374 			hdrcnt = winlines - hdroffset;
375 		}
376 
377 		if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) {
378 			cpuswap();
379 			drvswap();
380 			tkswap();
381 			todo &= ~SHOW_NEW_TOTALS;
382 		}
383 
384 		display(ndrives);
385 
386 		if (reps >= 0 && --reps <= 0)
387 			break;
388 		nanosleep(&tv, NULL);
389 		cpureadstats();
390 		drvreadstats();
391 
392 		ndrives = selectdrives(argc, argv, 0);
393 	}
394 	exit(0);
395 }
396 
397 static void
sig_header(int signo)398 sig_header(int signo)
399 {
400 	do_header = 1;
401 }
402 
403 static void
header(int ndrives)404 header(int ndrives)
405 {
406 	int i;
407 
408 					/* Main Headers. */
409 	if (ISSET(todo, SHOW_STATS_X)) {
410 		if (ISSET(todo, SHOW_TOTALS)) {
411 			(void)printf(
412 			    "device  read KB/t    xfr   time     MB  ");
413 			(void)printf(" write KB/t    xfr   time     MB\n");
414 		} else {
415 			(void)printf(
416 			    "device  read KB/t    r/s   time     MB/s");
417 			(void)printf(" write KB/t    w/s   time     MB/s\n");
418 		}
419 		return;
420 	}
421 
422 	if (ISSET(todo, SHOW_STATS_Y)) {
423 		(void)printf("device  read KB/t    r/s     MB/s write KB/t    w/s     MB/s");
424 		(void)printf("   wait   actv  wsvc_t  asvc_t  wtime   time");
425 		(void)printf("\n");
426 		return;
427 	}
428 
429 	if (ISSET(todo, SHOW_TTY))
430 		(void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty");
431 
432 	if (ISSET(todo, SHOW_STATS_1)) {
433 		for (i = 0; i < ndrives; i++) {
434 			char *dname = cur.name[order[i]];
435 			int dnlen = (int)strlen(dname);
436 
437 			printf(" ");	/* always a 1 column gap */
438 			if (dnlen < LAYOUT_DRIVE_1 - 6)
439 				printf("|%-*.*s ",
440 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
441 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
442 				    Line_Marker);
443 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
444 			    MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0),
445 				LAYOUT_DRIVE_1) : 0),
446 			    LAYOUT_DRIVE_1, dname);
447 			if (dnlen < LAYOUT_DRIVE_1 - 6)
448 				printf(" %*.*s|",
449 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
450 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
451 				    Line_Marker);
452 		}
453 	}
454 
455 	if (ISSET(todo, SHOW_STATS_2)) {
456 		for (i = 0; i < ndrives; i++) {
457 			char *dname = cur.name[order[i]];
458 			int dnlen = (int)strlen(dname);
459 
460 			printf(" ");	/* always a 1 column gap */
461 			if (dnlen < LAYOUT_DRIVE_2 - 6)
462 				printf("|%-*.*s ",
463 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
464 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
465 				    Line_Marker);
466 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
467 			    MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0),
468 				LAYOUT_DRIVE_2) : 0),
469 			    LAYOUT_DRIVE_1, dname);
470 			if (dnlen < LAYOUT_DRIVE_2 - 6)
471 				printf(" %*.*s|",
472 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
473 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
474 				    Line_Marker);
475 		}
476 	}
477 
478 	if (ISSET(todo, SHOW_STATS_3)) {
479 		for (i = 0; i < ndrives; i++) {
480 			char *dname = cur.name[order[i]];
481 			int dnlen = (int)strlen(dname);
482 
483 			printf(" ");	/* always a 1 column gap */
484 			if (dnlen < LAYOUT_DRIVE_3 - 6)
485 				printf("|%-*.*s ",
486 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
487 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
488 				    Line_Marker);
489 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
490 			    MIN(MAX((LAYOUT_DRIVE_3 - dnlen) / 2, 0),
491 				LAYOUT_DRIVE_3) : 0),
492 			    LAYOUT_DRIVE_1, dname);
493 			if (dnlen < LAYOUT_DRIVE_3 - 6)
494 				printf(" %*.*s|",
495 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
496 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
497 				    Line_Marker);
498 		}
499 	}
500 
501 	if (ISSET(todo, SHOW_CPU))
502 		(void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU");
503 
504 	printf("\n");
505 
506 					/* Sub-Headers. */
507 	if (ISSET(todo, SHOW_TTY)) {
508 		printf("%*s %*s",
509 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin",
510 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout");
511 	}
512 
513 	if (ISSET(todo, SHOW_STATS_1)) {
514 		for (i = 0; i < ndrives; i++) {
515 			if (ISSET(todo, SHOW_TOTALS)) {
516 				(void)printf(" %*s %*s %*s",
517 				    LAYOUT_DRIVE_1_XFER, "xfr",
518 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
519 				    LAYOUT_DRIVE_1_VOLUME, "MB");
520 			} else {
521 				(void)printf(" %*s %*s %*s",
522 				    LAYOUT_DRIVE_1_RATE, "t/s",
523 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
524 				    LAYOUT_DRIVE_1_SPEED, "MB/s");
525 			}
526 		}
527 	}
528 
529 	if (ISSET(todo, SHOW_STATS_2)) {
530 		for (i = 0; i < ndrives; i++) {
531 			if (ISSET(todo, SHOW_TOTALS)) {
532 				(void)printf(" %*s %*s %*s",
533 				    LAYOUT_DRIVE_2_TXFR, "xfr",
534 				    LAYOUT_DRIVE_2_VOLUME, "KB",
535 				    LAYOUT_DRIVE_2_TBUSY, "time");
536 			} else {
537 				(void)printf(" %*s %*s %*s",
538 				    LAYOUT_DRIVE_2_XFR, "xfr",
539 				    LAYOUT_DRIVE_2_XSIZE, "KB",
540 				    LAYOUT_DRIVE_2_BUSY, "time");
541 			}
542 		}
543 	}
544 
545 	if (ISSET(todo, SHOW_STATS_3)) {
546 		for (i = 0; i < ndrives; i++) {
547 			if (ISSET(todo, SHOW_TOTALS)) {
548 				(void)printf(" %*s %*s",
549 				    LAYOUT_DRIVE_2_VOLUME, "MB/s",
550 				    LAYOUT_DRIVE_2_TBUSY, "time");
551 			} else {
552 				(void)printf(" %*s %*s",
553 				    LAYOUT_DRIVE_2_XSIZE, "MB/s",
554 				    LAYOUT_DRIVE_2_BUSY, "time");
555 			}
556 		}
557 	}
558 
559 	/* should do this properly, but it is such a simple case... */
560 	if (ISSET(todo, SHOW_CPU))
561 		(void)printf("  us ni sy in  id");
562 	printf("\n");
563 }
564 
565 static double
drive_time(double etime,int dn)566 drive_time(double etime, int dn)
567 {
568 	if (ISSET(todo, SHOW_TOTALS))
569 		return etime;
570 
571 	if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
572 		etime = (double)cur.timestamp[dn].tv_sec +
573 		    ((double)cur.timestamp[dn].tv_usec / (double)1000000);
574 	}
575 
576 	return etime;
577 }
578 
579 static void
drive_stats(int ndrives,double etime)580 drive_stats(int ndrives, double etime)
581 {
582 	int drive;
583 	double atime, dtime, mbps;
584 	int c1, c2, c3;
585 
586 	if (ISSET(todo, SHOW_TOTALS)) {
587 		c1 = LAYOUT_DRIVE_1_XFER;
588 		c2 = LAYOUT_DRIVE_1_XSIZE;
589 		c3 = LAYOUT_DRIVE_1_VOLUME;
590 	} else {
591 		c1 = LAYOUT_DRIVE_1_RATE;
592 		c2 = LAYOUT_DRIVE_1_XSIZE;
593 		c3 = LAYOUT_DRIVE_1_SPEED;
594 	}
595 
596 	for (drive = 0; drive < ndrives; ++drive) {
597 		int dn = order[drive];
598 
599 		if (!cur.select[dn])	/* should be impossible */
600 			continue;
601 
602 		if (todo & SUPPRESS_ZERO) {
603 			if (cur.rxfer[dn] == 0 &&
604 			    cur.wxfer[dn] == 0 &&
605 			    cur.rbytes[dn] == 0 &&
606 			    cur.wbytes[dn] == 0) {
607 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
608 				continue;
609 			}
610 		}
611 
612 		dtime = drive_time(etime, dn);
613 
614 					/* average transfers per second. */
615 		(void)printf(" %*.0f", c1,
616 		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
617 
618 					/* average Kbytes per transfer. */
619 		if (cur.rxfer[dn] + cur.wxfer[dn])
620 			mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
621 			    1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
622 		else
623 			mbps = 0.0;
624 		(void)printf(" %*.*f", c2,
625 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
626 
627 					/* time busy in drive activity */
628 		atime = (double)cur.time[dn].tv_sec +
629 		    ((double)cur.time[dn].tv_usec / (double)1000000);
630 
631 					/* Megabytes per second. */
632 		if (atime != 0.0)
633 			mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
634 			    (double)(1024 * 1024);
635 		else
636 			mbps = 0;
637 		mbps /= dtime;
638 		(void)printf(" %*.*f", c3,
639 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
640 	}
641 }
642 
643 static void
drive_stats2(int ndrives,double etime)644 drive_stats2(int ndrives, double etime)
645 {
646 	int drive;
647 	double atime, dtime;
648 	int c1, c2, c3;
649 
650 	if (ISSET(todo, SHOW_TOTALS)) {
651 		c1 = LAYOUT_DRIVE_2_TXFR;
652 		c2 = LAYOUT_DRIVE_2_VOLUME;
653 		c3 = LAYOUT_DRIVE_2_TBUSY;
654 	} else {
655 		c1 = LAYOUT_DRIVE_2_XFR;
656 		c2 = LAYOUT_DRIVE_2_XSIZE;
657 		c3 = LAYOUT_DRIVE_2_BUSY;
658 	}
659 
660 	for (drive = 0; drive < ndrives; ++drive) {
661 		int dn = order[drive];
662 
663 		if (!cur.select[dn])		/* should be impossible */
664 			continue;
665 
666 		if (todo & SUPPRESS_ZERO) {
667 			if (cur.rxfer[dn] == 0 &&
668 			    cur.wxfer[dn] == 0 &&
669 			    cur.rbytes[dn] == 0 &&
670 			    cur.wbytes[dn] == 0) {
671 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
672 				continue;
673 			}
674 		}
675 
676 		dtime = drive_time(etime, dn);
677 
678 					/* average transfers per second. */
679 		if (ISSET(todo, SHOW_STATS_2)) {
680 			(void)printf(" %*.0f", c1,
681 			    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
682 		}
683 
684 					/* average mbytes per second. */
685 		(void)printf(" %*.0f", c2,
686 		    (cur.rbytes[dn] + cur.wbytes[dn]) /
687 		    (double)(1024 * 1024) / dtime);
688 
689 					/* average time busy in dn activity */
690 		atime = (double)cur.time[dn].tv_sec +
691 		    ((double)cur.time[dn].tv_usec / (double)1000000);
692 		(void)printf(" %*.2f", c3, atime / dtime);
693 	}
694 }
695 
696 static void
drive_statsx(int ndrives,double etime)697 drive_statsx(int ndrives, double etime)
698 {
699 	int dn, drive;
700 	double atime, dtime, kbps;
701 
702 	for (drive = 0; drive < ndrives; ++drive) {
703 		dn = order[drive];
704 
705 		if (!cur.select[dn])	/* impossible */
706 			continue;
707 
708 		(void)printf("%-8.8s", cur.name[dn]);
709 
710 		if (todo & SUPPRESS_ZERO) {
711 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
712 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
713 				printf("\n");
714 				continue;
715 			}
716 		}
717 
718 		dtime = drive_time(etime, dn);
719 
720 					/* average read Kbytes per transfer */
721 		if (cur.rxfer[dn])
722 			kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
723 		else
724 			kbps = 0.0;
725 		(void)printf(" %8.2f", kbps);
726 
727 					/* average read transfers
728 					   (per second) */
729 		(void)printf(" %6.0f", cur.rxfer[dn] / dtime);
730 
731 					/* time read busy in drive activity */
732 		atime = (double)cur.time[dn].tv_sec +
733 		    ((double)cur.time[dn].tv_usec / (double)1000000);
734 		(void)printf(" %6.2f", atime / dtime);
735 
736 					/* average read megabytes
737 					   (per second) */
738 		(void)printf(" %8.2f",
739 		    cur.rbytes[dn] / (1024.0 * 1024) / dtime);
740 
741 
742 					/* average write Kbytes per transfer */
743 		if (cur.wxfer[dn])
744 			kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
745 		else
746 			kbps = 0.0;
747 		(void)printf("   %8.2f", kbps);
748 
749 					/* average write transfers
750 					   (per second) */
751 		(void)printf(" %6.0f", cur.wxfer[dn] / dtime);
752 
753 					/* time write busy in drive activity */
754 		atime = (double)cur.time[dn].tv_sec +
755 		    ((double)cur.time[dn].tv_usec / (double)1000000);
756 		(void)printf(" %6.2f", atime / dtime);
757 
758 					/* average write megabytes
759 					   (per second) */
760 		(void)printf(" %8.2f\n",
761 		    cur.wbytes[dn] / (1024.0 * 1024) / dtime);
762 	}
763 }
764 
765 static void
drive_statsy_io(double elapsed,double count,double volume)766 drive_statsy_io(double elapsed, double count, double volume)
767 {
768 	double kbps;
769 
770 	/* average Kbytes per transfer */
771 	if (count)
772 		kbps = (volume / 1024.0) / count;
773 	else
774 		kbps = 0.0;
775 	(void)printf(" %8.2f", kbps);
776 
777 	/* average transfers (per second) */
778 	(void)printf(" %6.0f", count / elapsed);
779 
780 	/* average megabytes (per second) */
781 	(void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
782 }
783 
784 static void
drive_statsy_q(double elapsed,double busy,double wait,double busysum,double waitsum,double count)785 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
786 {
787 	/* average wait queue length */
788 	(void)printf(" %6.1f", waitsum / elapsed);
789 
790 	/* average busy queue length */
791 	(void)printf(" %6.1f", busysum / elapsed);
792 
793 	/* average wait time */
794 	(void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
795 
796 	/* average service time */
797 	(void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
798 
799 	/* time waiting for drive activity */
800 	(void)printf(" %6.2f", wait / elapsed);
801 
802 	/* time busy in drive activity */
803 	(void)printf(" %6.2f", busy / elapsed);
804 }
805 
806 static void
drive_statsy(int ndrives,double etime)807 drive_statsy(int ndrives, double etime)
808 {
809 	int drive, dn;
810 	double atime, await, abusysum, awaitsum, dtime;
811 
812 	for (drive = 0; drive < ndrives; ++drive) {
813 		dn = order[drive];
814 		if (!cur.select[dn])	/* impossible */
815 			continue;
816 
817 		(void)printf("%-8.8s", cur.name[dn]);
818 
819 		if (todo & SUPPRESS_ZERO) {
820 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
821 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
822 				printf("\n");
823 				continue;
824 			}
825 		}
826 
827 		dtime = drive_time(etime, dn);
828 
829 		atime = (double)cur.time[dn].tv_sec +
830 		    ((double)cur.time[dn].tv_usec / (double)1000000);
831 		await = (double)cur.wait[dn].tv_sec +
832 		    ((double)cur.wait[dn].tv_usec / (double)1000000);
833 		abusysum = (double)cur.busysum[dn].tv_sec +
834 		    ((double)cur.busysum[dn].tv_usec / (double)1000000);
835 		awaitsum = (double)cur.waitsum[dn].tv_sec +
836 		    ((double)cur.waitsum[dn].tv_usec / (double)1000000);
837 
838 		drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]);
839 		(void)printf("  ");
840 		drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]);
841 		drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]);
842 
843 		(void)printf("\n");
844 	}
845 }
846 
847 static void
cpustats(void)848 cpustats(void)
849 {
850 	int state;
851 	double ttime;
852 
853 	static int cwidth[CPUSTATES] = {
854 		LAYOUT_CPU_USER,
855 		LAYOUT_CPU_NICE,
856 		LAYOUT_CPU_SYS,
857 		LAYOUT_CPU_INT,
858 		LAYOUT_CPU_IDLE
859 	};
860 
861 	ttime = 0;
862 	for (state = 0; state < CPUSTATES; ++state)
863 		ttime += cur.cp_time[state];
864 	if (!ttime)
865 		ttime = 1.0;
866 
867 	printf("%*s", LAYOUT_CPU_GAP - 1, "");	/* the 1 is the next space */
868 	for (state = 0; state < CPUSTATES; ++state) {
869 		if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) {
870 			printf(" %*s", cwidth[state], "");
871 			continue;
872 		}
873 		printf(" %*.0f", cwidth[state],
874 		    100. * cur.cp_time[state] / ttime);
875 	}
876 }
877 
878 static void
usage(void)879 usage(void)
880 {
881 
882 	(void)fprintf(stderr, "usage: iostat [-CDdITXxyz] [-c count] "
883 	    "[-H height] [-W width] [-w wait] [drives]\n");
884 	exit(1);
885 }
886 
887 static void
display(int ndrives)888 display(int ndrives)
889 {
890 	double	etime;
891 
892 	/* Sum up the elapsed ticks. */
893 	etime = cur.cp_etime;
894 
895 	/*
896 	 * If we're showing totals only, then don't divide by the
897 	 * system time.
898 	 */
899 	if (ISSET(todo, SHOW_TOTALS))
900 		etime = 1.0;
901 
902 	if (ISSET(todo, SHOW_STATS_X)) {
903 		drive_statsx(ndrives, etime);
904 		goto out;
905 	}
906 
907 	if (ISSET(todo, SHOW_STATS_Y)) {
908 		drive_statsy(ndrives, etime);
909 		goto out;
910 	}
911 
912 	if (ISSET(todo, SHOW_TTY))
913 		printf("%*.0f %*.0f",
914 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN),
915 		    cur.tk_nin / etime,
916 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT),
917 		    cur.tk_nout / etime);
918 
919 	if (ISSET(todo, SHOW_STATS_1)) {
920 		drive_stats(ndrives, etime);
921 	}
922 
923 	if (ISSET(todo, SHOW_STATS_2) || ISSET(todo, SHOW_STATS_3)) {
924 		drive_stats2(ndrives, etime);
925 	}
926 
927 	if (ISSET(todo, SHOW_CPU))
928 		cpustats();
929 
930 	(void)printf("\n");
931 
932 out:
933 	(void)fflush(stdout);
934 }
935 
936 static int
selectdrives(int argc,char * argv[],int first)937 selectdrives(int argc, char *argv[], int first)
938 {
939 	int	i, maxdrives, ndrives, tried;
940 
941 	/*
942 	 * Choose drives to be displayed.  Priority goes to (in order) drives
943 	 * supplied as arguments and default drives.  If everything isn't
944 	 * filled in and there are drives not taken care of, display the first
945 	 * few that fit.
946 	 *
947 	 * The backward compatibility #ifdefs permit the syntax:
948 	 *	iostat [ drives ] [ interval [ count ] ]
949 	 */
950 
951 #define	BACKWARD_COMPATIBILITY
952 	for (tried = ndrives = 0; *argv; ++argv) {
953 #ifdef BACKWARD_COMPATIBILITY
954 		if (isdigit((unsigned char)**argv))
955 			break;
956 #endif
957 		tried++;
958 		for (i = 0; i < (int)ndrive; i++) {
959 			if (fnmatch(*argv, cur.name[i], 0))
960 				continue;
961 			cur.select[i] = 1;
962 			if (ordersize <= ndrives) {
963 				int *new = realloc(order,
964 				    (ordersize + 8) * sizeof *order);
965 				if (new == NULL)
966 					break;
967 				ordersize += 8;
968 				order = new;
969 			}
970 			order[ndrives++] = i;
971 		}
972 
973 	}
974 
975 	if (ndrives == 0 && tried == 0) {
976 		/*
977 		 * Pick up to defdrives (or all if -x is given) drives
978 		 * if none specified.
979 		 */
980 		maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
981 			     (int)ndrive < defdrives)
982 			? (int)(ndrive) : defdrives;
983 		ordersize = maxdrives;
984 		free(order);
985 		order = calloc(ordersize, sizeof *order);
986 		if (order == NULL)
987 			errx(1, "Insufficient memory");
988 		for (i = 0; i < maxdrives; i++) {
989 			cur.select[i] = 1;
990 			order[i] = i;
991 
992 			++ndrives;
993 			if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
994 			    ndrives == defdrives)
995 				break;
996 		}
997 	}
998 
999 #ifdef BACKWARD_COMPATIBILITY
1000 	if (first && *argv) {
1001 		interval = atoi(*argv);
1002 		if (*++argv)
1003 			reps = atoi(*argv);
1004 	}
1005 #endif
1006 
1007 	if (interval) {
1008 		if (!reps)
1009 			reps = -1;
1010 	} else
1011 		if (reps)
1012 			interval = 1;
1013 
1014 	return (ndrives);
1015 }
1016