Home | History | Annotate | Line # | Download | only in iostat
iostat.c revision 1.71
      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
    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
    395 sig_header(int signo)
    396 {
    397 	do_header = 1;
    398 }
    399 
    400 static void
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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