xref: /netbsd/usr.sbin/iostat/iostat.c (revision 7c4c1927)
1 /*	$NetBSD: iostat.c,v 1.71 2023/07/28 12:03:33 wiz 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.71 2023/07/28 12:03:33 wiz 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_3 | SHOW_STATS_Y)) {
361 			lines = ndrives;
362 			hdroffset = 3;
363 		} else {
364 			lines = 1;
365 			hdroffset = 4;
366 		}
367 
368 		if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) {
369 			do_header = 0;
370 			header(ndrives);
371 			hdrcnt = winlines - hdroffset;
372 		}
373 
374 		if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) {
375 			cpuswap();
376 			drvswap();
377 			tkswap();
378 			todo &= ~SHOW_NEW_TOTALS;
379 		}
380 
381 		display(ndrives);
382 
383 		if (reps >= 0 && --reps <= 0)
384 			break;
385 		nanosleep(&tv, NULL);
386 		cpureadstats();
387 		drvreadstats();
388 
389 		ndrives = selectdrives(argc, argv, 0);
390 	}
391 	exit(0);
392 }
393 
394 static void
sig_header(int signo)395 sig_header(int signo)
396 {
397 	do_header = 1;
398 }
399 
400 static void
header(int ndrives)401 header(int ndrives)
402 {
403 	int i;
404 
405 					/* Main Headers. */
406 	if (ISSET(todo, SHOW_STATS_X)) {
407 		if (ISSET(todo, SHOW_TOTALS)) {
408 			(void)printf(
409 			    "device  read KB/t    xfr   time     MB  ");
410 			(void)printf(" write KB/t    xfr   time     MB\n");
411 		} else {
412 			(void)printf(
413 			    "device  read KB/t    r/s   time     MB/s");
414 			(void)printf(" write KB/t    w/s   time     MB/s\n");
415 		}
416 		return;
417 	}
418 
419 	if (ISSET(todo, SHOW_STATS_Y)) {
420 		(void)printf("device  read KB/t    r/s     MB/s write KB/t    w/s     MB/s");
421 		(void)printf("   wait   actv  wsvc_t  asvc_t  wtime   time");
422 		(void)printf("\n");
423 		return;
424 	}
425 
426 	if (ISSET(todo, SHOW_TTY))
427 		(void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty");
428 
429 	if (ISSET(todo, SHOW_STATS_1)) {
430 		for (i = 0; i < ndrives; i++) {
431 			char *dname = cur.name[order[i]];
432 			int dnlen = (int)strlen(dname);
433 
434 			printf(" ");	/* always a 1 column gap */
435 			if (dnlen < LAYOUT_DRIVE_1 - 6)
436 				printf("|%-*.*s ",
437 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
438 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
439 				    Line_Marker);
440 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
441 			    MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0),
442 				LAYOUT_DRIVE_1) : 0),
443 			    LAYOUT_DRIVE_1, dname);
444 			if (dnlen < LAYOUT_DRIVE_1 - 6)
445 				printf(" %*.*s|",
446 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
447 				    (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
448 				    Line_Marker);
449 		}
450 	}
451 
452 	if (ISSET(todo, SHOW_STATS_2)) {
453 		for (i = 0; i < ndrives; i++) {
454 			char *dname = cur.name[order[i]];
455 			int dnlen = (int)strlen(dname);
456 
457 			printf(" ");	/* always a 1 column gap */
458 			if (dnlen < LAYOUT_DRIVE_2 - 6)
459 				printf("|%-*.*s ",
460 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
461 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
462 				    Line_Marker);
463 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
464 			    MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0),
465 				LAYOUT_DRIVE_2) : 0),
466 			    LAYOUT_DRIVE_1, dname);
467 			if (dnlen < LAYOUT_DRIVE_2 - 6)
468 				printf(" %*.*s|",
469 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
470 				    (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
471 				    Line_Marker);
472 		}
473 	}
474 
475 	if (ISSET(todo, SHOW_STATS_3)) {
476 		for (i = 0; i < ndrives; i++) {
477 			char *dname = cur.name[order[i]];
478 			int dnlen = (int)strlen(dname);
479 
480 			printf(" ");	/* always a 1 column gap */
481 			if (dnlen < LAYOUT_DRIVE_3 - 6)
482 				printf("|%-*.*s ",
483 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
484 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
485 				    Line_Marker);
486 			printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
487 			    MIN(MAX((LAYOUT_DRIVE_3 - dnlen) / 2, 0),
488 				LAYOUT_DRIVE_3) : 0),
489 			    LAYOUT_DRIVE_1, dname);
490 			if (dnlen < LAYOUT_DRIVE_3 - 6)
491 				printf(" %*.*s|",
492 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
493 				    (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
494 				    Line_Marker);
495 		}
496 	}
497 
498 	if (ISSET(todo, SHOW_CPU))
499 		(void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU");
500 
501 	printf("\n");
502 
503 					/* Sub-Headers. */
504 	if (ISSET(todo, SHOW_TTY)) {
505 		printf("%*s %*s",
506 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin",
507 		   ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout");
508 	}
509 
510 	if (ISSET(todo, SHOW_STATS_1)) {
511 		for (i = 0; i < ndrives; i++) {
512 			if (ISSET(todo, SHOW_TOTALS)) {
513 				(void)printf(" %*s %*s %*s",
514 				    LAYOUT_DRIVE_1_XFER, "xfr",
515 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
516 				    LAYOUT_DRIVE_1_VOLUME, "MB");
517 			} else {
518 				(void)printf(" %*s %*s %*s",
519 				    LAYOUT_DRIVE_1_RATE, "t/s",
520 				    LAYOUT_DRIVE_1_XSIZE, "KB/t",
521 				    LAYOUT_DRIVE_1_SPEED, "MB/s");
522 			}
523 		}
524 	}
525 
526 	if (ISSET(todo, SHOW_STATS_2)) {
527 		for (i = 0; i < ndrives; i++) {
528 			if (ISSET(todo, SHOW_TOTALS)) {
529 				(void)printf(" %*s %*s %*s",
530 				    LAYOUT_DRIVE_2_TXFR, "xfr",
531 				    LAYOUT_DRIVE_2_VOLUME, "KB",
532 				    LAYOUT_DRIVE_2_TBUSY, "time");
533 			} else {
534 				(void)printf(" %*s %*s %*s",
535 				    LAYOUT_DRIVE_2_XFR, "xfr",
536 				    LAYOUT_DRIVE_2_XSIZE, "KB",
537 				    LAYOUT_DRIVE_2_BUSY, "time");
538 			}
539 		}
540 	}
541 
542 	if (ISSET(todo, SHOW_STATS_3)) {
543 		for (i = 0; i < ndrives; i++) {
544 			if (ISSET(todo, SHOW_TOTALS)) {
545 				(void)printf(" %*s %*s",
546 				    LAYOUT_DRIVE_2_VOLUME, "MB/s",
547 				    LAYOUT_DRIVE_2_TBUSY, "time");
548 			} else {
549 				(void)printf(" %*s %*s",
550 				    LAYOUT_DRIVE_2_XSIZE, "MB/s",
551 				    LAYOUT_DRIVE_2_BUSY, "time");
552 			}
553 		}
554 	}
555 
556 	/* should do this properly, but it is such a simple case... */
557 	if (ISSET(todo, SHOW_CPU))
558 		(void)printf("  us ni sy in  id");
559 	printf("\n");
560 }
561 
562 static double
drive_time(double etime,int dn)563 drive_time(double etime, int dn)
564 {
565 	if (ISSET(todo, SHOW_TOTALS))
566 		return etime;
567 
568 	if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
569 		etime = (double)cur.timestamp[dn].tv_sec +
570 		    ((double)cur.timestamp[dn].tv_usec / (double)1000000);
571 	}
572 
573 	return etime;
574 }
575 
576 static void
drive_stats(int ndrives,double etime)577 drive_stats(int ndrives, double etime)
578 {
579 	int drive;
580 	double atime, dtime, mbps;
581 	int c1, c2, c3;
582 
583 	if (ISSET(todo, SHOW_TOTALS)) {
584 		c1 = LAYOUT_DRIVE_1_XFER;
585 		c2 = LAYOUT_DRIVE_1_XSIZE;
586 		c3 = LAYOUT_DRIVE_1_VOLUME;
587 	} else {
588 		c1 = LAYOUT_DRIVE_1_RATE;
589 		c2 = LAYOUT_DRIVE_1_XSIZE;
590 		c3 = LAYOUT_DRIVE_1_SPEED;
591 	}
592 
593 	for (drive = 0; drive < ndrives; ++drive) {
594 		int dn = order[drive];
595 
596 		if (!cur.select[dn])	/* should be impossible */
597 			continue;
598 
599 		if (todo & SUPPRESS_ZERO) {
600 			if (cur.rxfer[dn] == 0 &&
601 			    cur.wxfer[dn] == 0 &&
602 			    cur.rbytes[dn] == 0 &&
603 			    cur.wbytes[dn] == 0) {
604 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
605 				continue;
606 			}
607 		}
608 
609 		dtime = drive_time(etime, dn);
610 
611 					/* average transfers per second. */
612 		(void)printf(" %*.0f", c1,
613 		    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
614 
615 					/* average Kbytes per transfer. */
616 		if (cur.rxfer[dn] + cur.wxfer[dn])
617 			mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
618 			    1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
619 		else
620 			mbps = 0.0;
621 		(void)printf(" %*.*f", c2,
622 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
623 
624 					/* time busy in drive activity */
625 		atime = (double)cur.time[dn].tv_sec +
626 		    ((double)cur.time[dn].tv_usec / (double)1000000);
627 
628 					/* Megabytes per second. */
629 		if (atime != 0.0)
630 			mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
631 			    (double)(1024 * 1024);
632 		else
633 			mbps = 0;
634 		mbps /= dtime;
635 		(void)printf(" %*.*f", c3,
636 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
637 	}
638 }
639 
640 static void
drive_stats2(int ndrives,double etime)641 drive_stats2(int ndrives, double etime)
642 {
643 	int drive;
644 	double atime, dtime;
645 	int c1, c2, c3;
646 
647 	if (ISSET(todo, SHOW_TOTALS)) {
648 		c1 = LAYOUT_DRIVE_2_TXFR;
649 		c2 = LAYOUT_DRIVE_2_VOLUME;
650 		c3 = LAYOUT_DRIVE_2_TBUSY;
651 	} else {
652 		c1 = LAYOUT_DRIVE_2_XFR;
653 		c2 = LAYOUT_DRIVE_2_XSIZE;
654 		c3 = LAYOUT_DRIVE_2_BUSY;
655 	}
656 
657 	for (drive = 0; drive < ndrives; ++drive) {
658 		int dn = order[drive];
659 
660 		if (!cur.select[dn])		/* should be impossible */
661 			continue;
662 
663 		if (todo & SUPPRESS_ZERO) {
664 			if (cur.rxfer[dn] == 0 &&
665 			    cur.wxfer[dn] == 0 &&
666 			    cur.rbytes[dn] == 0 &&
667 			    cur.wbytes[dn] == 0) {
668 				printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
669 				continue;
670 			}
671 		}
672 
673 		dtime = drive_time(etime, dn);
674 
675 					/* average transfers per second. */
676 		if (ISSET(todo, SHOW_STATS_2)) {
677 			(void)printf(" %*.0f", c1,
678 			    (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
679 		}
680 
681 					/* average mbytes per second. */
682 		(void)printf(" %*.0f", c2,
683 		    (cur.rbytes[dn] + cur.wbytes[dn]) /
684 		    (double)(1024 * 1024) / dtime);
685 
686 					/* average time busy in dn activity */
687 		atime = (double)cur.time[dn].tv_sec +
688 		    ((double)cur.time[dn].tv_usec / (double)1000000);
689 		(void)printf(" %*.2f", c3, atime / dtime);
690 	}
691 }
692 
693 static void
drive_statsx(int ndrives,double etime)694 drive_statsx(int ndrives, double etime)
695 {
696 	int dn, drive;
697 	double atime, dtime, kbps;
698 
699 	for (drive = 0; drive < ndrives; ++drive) {
700 		dn = order[drive];
701 
702 		if (!cur.select[dn])	/* impossible */
703 			continue;
704 
705 		(void)printf("%-8.8s", cur.name[dn]);
706 
707 		if (todo & SUPPRESS_ZERO) {
708 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
709 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
710 				printf("\n");
711 				continue;
712 			}
713 		}
714 
715 		dtime = drive_time(etime, dn);
716 
717 					/* average read Kbytes per transfer */
718 		if (cur.rxfer[dn])
719 			kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
720 		else
721 			kbps = 0.0;
722 		(void)printf(" %8.2f", kbps);
723 
724 					/* average read transfers
725 					   (per second) */
726 		(void)printf(" %6.0f", cur.rxfer[dn] / dtime);
727 
728 					/* time read busy in drive activity */
729 		atime = (double)cur.time[dn].tv_sec +
730 		    ((double)cur.time[dn].tv_usec / (double)1000000);
731 		(void)printf(" %6.2f", atime / dtime);
732 
733 					/* average read megabytes
734 					   (per second) */
735 		(void)printf(" %8.2f",
736 		    cur.rbytes[dn] / (1024.0 * 1024) / dtime);
737 
738 
739 					/* average write Kbytes per transfer */
740 		if (cur.wxfer[dn])
741 			kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
742 		else
743 			kbps = 0.0;
744 		(void)printf("   %8.2f", kbps);
745 
746 					/* average write transfers
747 					   (per second) */
748 		(void)printf(" %6.0f", cur.wxfer[dn] / dtime);
749 
750 					/* time write busy in drive activity */
751 		atime = (double)cur.time[dn].tv_sec +
752 		    ((double)cur.time[dn].tv_usec / (double)1000000);
753 		(void)printf(" %6.2f", atime / dtime);
754 
755 					/* average write megabytes
756 					   (per second) */
757 		(void)printf(" %8.2f\n",
758 		    cur.wbytes[dn] / (1024.0 * 1024) / dtime);
759 	}
760 }
761 
762 static void
drive_statsy_io(double elapsed,double count,double volume)763 drive_statsy_io(double elapsed, double count, double volume)
764 {
765 	double kbps;
766 
767 	/* average Kbytes per transfer */
768 	if (count)
769 		kbps = (volume / 1024.0) / count;
770 	else
771 		kbps = 0.0;
772 	(void)printf(" %8.2f", kbps);
773 
774 	/* average transfers (per second) */
775 	(void)printf(" %6.0f", count / elapsed);
776 
777 	/* average megabytes (per second) */
778 	(void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
779 }
780 
781 static void
drive_statsy_q(double elapsed,double busy,double wait,double busysum,double waitsum,double count)782 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
783 {
784 	/* average wait queue length */
785 	(void)printf(" %6.1f", waitsum / elapsed);
786 
787 	/* average busy queue length */
788 	(void)printf(" %6.1f", busysum / elapsed);
789 
790 	/* average wait time */
791 	(void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
792 
793 	/* average service time */
794 	(void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
795 
796 	/* time waiting for drive activity */
797 	(void)printf(" %6.2f", wait / elapsed);
798 
799 	/* time busy in drive activity */
800 	(void)printf(" %6.2f", busy / elapsed);
801 }
802 
803 static void
drive_statsy(int ndrives,double etime)804 drive_statsy(int ndrives, double etime)
805 {
806 	int drive, dn;
807 	double atime, await, abusysum, awaitsum, dtime;
808 
809 	for (drive = 0; drive < ndrives; ++drive) {
810 		dn = order[drive];
811 		if (!cur.select[dn])	/* impossible */
812 			continue;
813 
814 		(void)printf("%-8.8s", cur.name[dn]);
815 
816 		if (todo & SUPPRESS_ZERO) {
817 			if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
818 			    cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
819 				printf("\n");
820 				continue;
821 			}
822 		}
823 
824 		dtime = drive_time(etime, dn);
825 
826 		atime = (double)cur.time[dn].tv_sec +
827 		    ((double)cur.time[dn].tv_usec / (double)1000000);
828 		await = (double)cur.wait[dn].tv_sec +
829 		    ((double)cur.wait[dn].tv_usec / (double)1000000);
830 		abusysum = (double)cur.busysum[dn].tv_sec +
831 		    ((double)cur.busysum[dn].tv_usec / (double)1000000);
832 		awaitsum = (double)cur.waitsum[dn].tv_sec +
833 		    ((double)cur.waitsum[dn].tv_usec / (double)1000000);
834 
835 		drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]);
836 		(void)printf("  ");
837 		drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]);
838 		drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]);
839 
840 		(void)printf("\n");
841 	}
842 }
843 
844 static void
cpustats(void)845 cpustats(void)
846 {
847 	int state;
848 	double ttime;
849 
850 	static int cwidth[CPUSTATES] = {
851 		LAYOUT_CPU_USER,
852 		LAYOUT_CPU_NICE,
853 		LAYOUT_CPU_SYS,
854 		LAYOUT_CPU_INT,
855 		LAYOUT_CPU_IDLE
856 	};
857 
858 	ttime = 0;
859 	for (state = 0; state < CPUSTATES; ++state)
860 		ttime += cur.cp_time[state];
861 	if (!ttime)
862 		ttime = 1.0;
863 
864 	printf("%*s", LAYOUT_CPU_GAP - 1, "");	/* the 1 is the next space */
865 	for (state = 0; state < CPUSTATES; ++state) {
866 		if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) {
867 			printf(" %*s", cwidth[state], "");
868 			continue;
869 		}
870 		printf(" %*.0f", cwidth[state],
871 		    100. * cur.cp_time[state] / ttime);
872 	}
873 }
874 
875 static void
usage(void)876 usage(void)
877 {
878 
879 	(void)fprintf(stderr, "usage: iostat [-CDdITXxyz] [-c count] "
880 	    "[-H height] [-W width] [-w wait] [drives]\n");
881 	exit(1);
882 }
883 
884 static void
display(int ndrives)885 display(int ndrives)
886 {
887 	double	etime;
888 
889 	/* Sum up the elapsed ticks. */
890 	etime = cur.cp_etime;
891 
892 	/*
893 	 * If we're showing totals only, then don't divide by the
894 	 * system time.
895 	 */
896 	if (ISSET(todo, SHOW_TOTALS))
897 		etime = 1.0;
898 
899 	if (ISSET(todo, SHOW_STATS_X)) {
900 		drive_statsx(ndrives, etime);
901 		goto out;
902 	}
903 
904 	if (ISSET(todo, SHOW_STATS_Y)) {
905 		drive_statsy(ndrives, etime);
906 		goto out;
907 	}
908 
909 	if (ISSET(todo, SHOW_TTY))
910 		printf("%*.0f %*.0f",
911 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN),
912 		    cur.tk_nin / etime,
913 		    ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT),
914 		    cur.tk_nout / etime);
915 
916 	if (ISSET(todo, SHOW_STATS_1)) {
917 		drive_stats(ndrives, etime);
918 	}
919 
920 	if (ISSET(todo, SHOW_STATS_2) || ISSET(todo, SHOW_STATS_3)) {
921 		drive_stats2(ndrives, etime);
922 	}
923 
924 	if (ISSET(todo, SHOW_CPU))
925 		cpustats();
926 
927 	(void)printf("\n");
928 
929 out:
930 	(void)fflush(stdout);
931 }
932 
933 static int
selectdrives(int argc,char * argv[],int first)934 selectdrives(int argc, char *argv[], int first)
935 {
936 	int	i, maxdrives, ndrives, tried;
937 
938 	/*
939 	 * Choose drives to be displayed.  Priority goes to (in order) drives
940 	 * supplied as arguments and default drives.  If everything isn't
941 	 * filled in and there are drives not taken care of, display the first
942 	 * few that fit.
943 	 *
944 	 * The backward compatibility #ifdefs permit the syntax:
945 	 *	iostat [ drives ] [ interval [ count ] ]
946 	 */
947 
948 #define	BACKWARD_COMPATIBILITY
949 	for (tried = ndrives = 0; *argv; ++argv) {
950 #ifdef BACKWARD_COMPATIBILITY
951 		if (isdigit((unsigned char)**argv))
952 			break;
953 #endif
954 		tried++;
955 		for (i = 0; i < (int)ndrive; i++) {
956 			if (fnmatch(*argv, cur.name[i], 0))
957 				continue;
958 			cur.select[i] = 1;
959 			if (ordersize <= ndrives) {
960 				int *new = realloc(order,
961 				    (ordersize + 8) * sizeof *order);
962 				if (new == NULL)
963 					break;
964 				ordersize += 8;
965 				order = new;
966 			}
967 			order[ndrives++] = i;
968 		}
969 
970 	}
971 
972 	if (ndrives == 0 && tried == 0) {
973 		/*
974 		 * Pick up to defdrives (or all if -x is given) drives
975 		 * if none specified.
976 		 */
977 		maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
978 			     (int)ndrive < defdrives)
979 			? (int)(ndrive) : defdrives;
980 		ordersize = maxdrives;
981 		free(order);
982 		order = calloc(ordersize, sizeof *order);
983 		if (order == NULL)
984 			errx(1, "Insufficient memory");
985 		for (i = 0; i < maxdrives; i++) {
986 			cur.select[i] = 1;
987 			order[i] = i;
988 
989 			++ndrives;
990 			if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
991 			    ndrives == defdrives)
992 				break;
993 		}
994 	}
995 
996 #ifdef BACKWARD_COMPATIBILITY
997 	if (first && *argv) {
998 		interval = atoi(*argv);
999 		if (*++argv)
1000 			reps = atoi(*argv);
1001 	}
1002 #endif
1003 
1004 	if (interval) {
1005 		if (!reps)
1006 			reps = -1;
1007 	} else
1008 		if (reps)
1009 			interval = 1;
1010 
1011 	return (ndrives);
1012 }
1013