Home | History | Annotate | Line # | Download | only in iostat
iostat.c revision 1.63
      1 /*	$NetBSD: iostat.c,v 1.63 2015/10/25 02:47:17 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.63 2015/10/25 02:47:17 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 #define	MAX(a,b)	(((a)>(b))?(a):(b))
    103 
    104 #define	ISSET(x, a)	((x) & (a))
    105 #define	SHOW_CPU	(1<<0)
    106 #define	SHOW_TTY	(1<<1)
    107 #define	SHOW_STATS_1	(1<<2)
    108 #define	SHOW_STATS_2	(1<<3)
    109 #define	SHOW_STATS_X	(1<<4)
    110 #define	SHOW_TOTALS	(1<<7)
    111 #define	SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
    112 
    113 static void cpustats(void);
    114 static void drive_stats(double);
    115 static void drive_stats2(double);
    116 static void drive_statsx(double);
    117 static void sig_header(int);
    118 static volatile int do_header;
    119 static void header(void);
    120 __dead static void usage(void);
    121 static void display(void);
    122 static int selectdrives(int, char *[]);
    123 
    124 int
    125 main(int argc, char *argv[])
    126 {
    127 	int ch, hdrcnt, ndrives, lines;
    128 	struct timespec	tv;
    129 	struct ttysize ts;
    130 
    131 	while ((ch = getopt(argc, argv, "Cc:dDITw:x")) != -1)
    132 		switch (ch) {
    133 		case 'c':
    134 			if ((reps = atoi(optarg)) <= 0)
    135 				errx(1, "repetition count <= 0.");
    136 			break;
    137 		case 'C':
    138 			todo |= SHOW_CPU;
    139 			break;
    140 		case 'd':
    141 			todo &= ~SHOW_STATS_ALL;
    142 			todo |= SHOW_STATS_1;
    143 			break;
    144 		case 'D':
    145 			todo &= ~SHOW_STATS_ALL;
    146 			todo |= SHOW_STATS_2;
    147 			break;
    148 		case 'I':
    149 			todo |= SHOW_TOTALS;
    150 			break;
    151 		case 'T':
    152 			todo |= SHOW_TTY;
    153 			break;
    154 		case 'w':
    155 			if ((interval = atoi(optarg)) <= 0)
    156 				errx(1, "interval <= 0.");
    157 			break;
    158 		case 'x':
    159 			todo &= ~SHOW_STATS_ALL;
    160 			todo |= SHOW_STATS_X;
    161 			break;
    162 		case '?':
    163 		default:
    164 			usage();
    165 		}
    166 	argc -= optind;
    167 	argv += optind;
    168 
    169 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
    170 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
    171 	if (ISSET(todo, SHOW_STATS_X)) {
    172 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
    173 		todo |= SHOW_STATS_X;
    174 	}
    175 
    176 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
    177 		if (ts.ts_lines)
    178 			winlines = ts.ts_lines;
    179 		if (ts.ts_cols)
    180 			wincols = ts.ts_cols;
    181 	}
    182 
    183 	defdrives = wincols;
    184 	if (ISSET(todo, SHOW_CPU))
    185 		defdrives -= 16;	/* XXX magic number */
    186 	if (ISSET(todo, SHOW_TTY))
    187 		defdrives -= 10;	/* XXX magic number */
    188 	defdrives /= 18;		/* XXX magic number */
    189 
    190 	drvinit(0);
    191 	cpureadstats();
    192 	drvreadstats();
    193 	ndrives = selectdrives(argc, argv);
    194 	if (ndrives == 0) {
    195 		/* No drives are selected.  No need to show drive stats. */
    196 		todo &= ~SHOW_STATS_ALL;
    197 		if (todo == 0)
    198 			errx(1, "no drives");
    199 	}
    200 	if (ISSET(todo, SHOW_STATS_X))
    201 		lines = ndrives;
    202 	else
    203 		lines = 1;
    204 
    205 	tv.tv_sec = interval;
    206 	tv.tv_nsec = 0;
    207 
    208 	/* print a new header on sigcont */
    209 	(void)signal(SIGCONT, sig_header);
    210 
    211 	for (hdrcnt = 1;;) {
    212 		if (do_header || lines > 1 || (hdrcnt -= lines) <= 0) {
    213 			do_header = 0;
    214 			header();
    215 			hdrcnt = winlines - 4;
    216 		}
    217 
    218 		if (!ISSET(todo, SHOW_TOTALS)) {
    219 			cpuswap();
    220 			drvswap();
    221 			tkswap();
    222 		}
    223 
    224 		display();
    225 
    226 		if (reps >= 0 && --reps <= 0)
    227 			break;
    228 		nanosleep(&tv, NULL);
    229 		cpureadstats();
    230 		drvreadstats();
    231 	}
    232 	exit(0);
    233 }
    234 
    235 static void
    236 sig_header(int signo)
    237 {
    238 	do_header = 1;
    239 }
    240 
    241 static void
    242 header(void)
    243 {
    244 	size_t i;
    245 
    246 					/* Main Headers. */
    247 	if (ISSET(todo, SHOW_STATS_X)) {
    248 		if (ISSET(todo, SHOW_TOTALS)) {
    249 			(void)printf(
    250 			    "device  read KB/t    xfr   time     MB  ");
    251 			(void)printf(" write KB/t    xfr   time     MB\n");
    252 		} else {
    253 			(void)printf(
    254 			    "device  read KB/t    r/s   time     MB/s");
    255 			(void)printf(" write KB/t    w/s   time     MB/s\n");
    256 		}
    257 		return;
    258 	}
    259 
    260 	if (ISSET(todo, SHOW_TTY))
    261 		(void)printf("      tty");
    262 
    263 	if (ISSET(todo, SHOW_STATS_1)) {
    264 		for (i = 0; i < ndrive; i++)
    265 			if (cur.select[i])
    266 				(void)printf("        %9.9s ", cur.name[i]);
    267 	}
    268 
    269 	if (ISSET(todo, SHOW_STATS_2)) {
    270 		for (i = 0; i < ndrive; i++)
    271 			if (cur.select[i])
    272 				(void)printf("        %9.9s ", cur.name[i]);
    273 	}
    274 
    275 	if (ISSET(todo, SHOW_CPU))
    276 		(void)printf("            CPU");
    277 
    278 	printf("\n");
    279 
    280 					/* Sub-Headers. */
    281 	if (ISSET(todo, SHOW_TTY))
    282 		printf(" tin  tout");
    283 
    284 	if (ISSET(todo, SHOW_STATS_1)) {
    285 		for (i = 0; i < ndrive; i++)
    286 			if (cur.select[i]) {
    287 				if (ISSET(todo, SHOW_TOTALS))
    288 					(void)printf("  KB/t  xfr  MB   ");
    289 				else
    290 					(void)printf("  KB/t  t/s  MB/s ");
    291 			}
    292 	}
    293 
    294 	if (ISSET(todo, SHOW_STATS_2)) {
    295 		for (i = 0; i < ndrive; i++)
    296 			if (cur.select[i])
    297 				(void)printf("    KB   xfr time ");
    298 	}
    299 
    300 	if (ISSET(todo, SHOW_CPU))
    301 		(void)printf(" us ni sy in id");
    302 	printf("\n");
    303 }
    304 
    305 static void
    306 drive_stats(double etime)
    307 {
    308 	size_t dn;
    309 	double atime, mbps;
    310 
    311 	for (dn = 0; dn < ndrive; ++dn) {
    312 		if (!cur.select[dn])
    313 			continue;
    314 					/* average Kbytes per transfer. */
    315 		if (cur.rxfer[dn] + cur.wxfer[dn])
    316 			mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
    317 			    1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
    318 		else
    319 			mbps = 0.0;
    320 		(void)printf(" %5.*f",
    321 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
    322 
    323 					/* average transfers per second. */
    324 		(void)printf(" %4.0f",
    325 		    (cur.rxfer[dn] + cur.wxfer[dn]) / etime);
    326 
    327 					/* time busy in drive activity */
    328 		atime = (double)cur.time[dn].tv_sec +
    329 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    330 
    331 					/* Megabytes per second. */
    332 		if (atime != 0.0)
    333 			mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
    334 			    (double)(1024 * 1024);
    335 		else
    336 			mbps = 0;
    337 		mbps /= etime;
    338 		(void)printf(" %5.*f ",
    339 		    MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
    340 	}
    341 }
    342 
    343 static void
    344 drive_stats2(double etime)
    345 {
    346 	size_t dn;
    347 	double atime;
    348 
    349 	for (dn = 0; dn < ndrive; ++dn) {
    350 		if (!cur.select[dn])
    351 			continue;
    352 
    353 					/* average kbytes per second. */
    354 		(void)printf(" %5.0f",
    355 		    (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / etime);
    356 
    357 					/* average transfers per second. */
    358 		(void)printf(" %5.0f",
    359 		    (cur.rxfer[dn] + cur.wxfer[dn]) / etime);
    360 
    361 					/* average time busy in drive activity */
    362 		atime = (double)cur.time[dn].tv_sec +
    363 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    364 		(void)printf(" %4.2f ", atime / etime);
    365 	}
    366 }
    367 
    368 static void
    369 drive_statsx(double etime)
    370 {
    371 	size_t dn;
    372 	double atime, kbps;
    373 
    374 	for (dn = 0; dn < ndrive; ++dn) {
    375 		if (!cur.select[dn])
    376 			continue;
    377 
    378 		(void)printf("%-8.8s", cur.name[dn]);
    379 
    380 					/* average read Kbytes per transfer */
    381 		if (cur.rxfer[dn])
    382 			kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
    383 		else
    384 			kbps = 0.0;
    385 		(void)printf(" %8.2f", kbps);
    386 
    387 					/* average read transfers
    388 					   (per second) */
    389 		(void)printf(" %6.0f", cur.rxfer[dn] / etime);
    390 
    391 					/* time read busy in drive activity */
    392 		atime = (double)cur.time[dn].tv_sec +
    393 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    394 		(void)printf(" %6.2f", atime / etime);
    395 
    396 					/* average read megabytes
    397 					   (per second) */
    398 		(void)printf(" %8.2f",
    399 		    cur.rbytes[dn] / (1024.0 * 1024) / etime);
    400 
    401 
    402 					/* average write Kbytes per transfer */
    403 		if (cur.wxfer[dn])
    404 			kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
    405 		else
    406 			kbps = 0.0;
    407 		(void)printf("   %8.2f", kbps);
    408 
    409 					/* average write transfers
    410 					   (per second) */
    411 		(void)printf(" %6.0f", cur.wxfer[dn] / etime);
    412 
    413 					/* time write busy in drive activity */
    414 		atime = (double)cur.time[dn].tv_sec +
    415 		    ((double)cur.time[dn].tv_usec / (double)1000000);
    416 		(void)printf(" %6.2f", atime / etime);
    417 
    418 					/* average write megabytes
    419 					   (per second) */
    420 		(void)printf(" %8.2f\n",
    421 		    cur.wbytes[dn] / (1024.0 * 1024) / etime);
    422 	}
    423 }
    424 
    425 static void
    426 cpustats(void)
    427 {
    428 	int state;
    429 	double ttime;
    430 
    431 	ttime = 0;
    432 	for (state = 0; state < CPUSTATES; ++state)
    433 		ttime += cur.cp_time[state];
    434 	if (!ttime)
    435 		ttime = 1.0;
    436 			/* States are generally never 100% and can use %3.0f. */
    437 	for (state = 0; state < CPUSTATES; ++state)
    438 		printf(" %2.0f", 100. * cur.cp_time[state] / ttime);
    439 }
    440 
    441 static void
    442 usage(void)
    443 {
    444 
    445 	(void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] "
    446 	    "[-w wait] [drives]\n");
    447 	exit(1);
    448 }
    449 
    450 static void
    451 display(void)
    452 {
    453 	double	etime;
    454 
    455 	/* Sum up the elapsed ticks. */
    456 	etime = cur.cp_etime;
    457 
    458 	/*
    459 	 * If we're showing totals only, then don't divide by the
    460 	 * system time.
    461 	 */
    462 	if (ISSET(todo, SHOW_TOTALS))
    463 		etime = 1.0;
    464 
    465 	if (ISSET(todo, SHOW_STATS_X)) {
    466 		drive_statsx(etime);
    467 		goto out;
    468 	}
    469 
    470 	if (ISSET(todo, SHOW_TTY))
    471 		printf("%4.0f %5.0f", cur.tk_nin / etime, cur.tk_nout / etime);
    472 
    473 	if (ISSET(todo, SHOW_STATS_1)) {
    474 		drive_stats(etime);
    475 	}
    476 
    477 
    478 	if (ISSET(todo, SHOW_STATS_2)) {
    479 		drive_stats2(etime);
    480 	}
    481 
    482 
    483 	if (ISSET(todo, SHOW_CPU))
    484 		cpustats();
    485 
    486 	(void)printf("\n");
    487 
    488 out:
    489 	(void)fflush(stdout);
    490 }
    491 
    492 static int
    493 selectdrives(int argc, char *argv[])
    494 {
    495 	int	i, maxdrives, ndrives, tried;
    496 
    497 	/*
    498 	 * Choose drives to be displayed.  Priority goes to (in order) drives
    499 	 * supplied as arguments and default drives.  If everything isn't
    500 	 * filled in and there are drives not taken care of, display the first
    501 	 * few that fit.
    502 	 *
    503 	 * The backward compatibility #ifdefs permit the syntax:
    504 	 *	iostat [ drives ] [ interval [ count ] ]
    505 	 */
    506 
    507 #define	BACKWARD_COMPATIBILITY
    508 	for (tried = ndrives = 0; *argv; ++argv) {
    509 #ifdef BACKWARD_COMPATIBILITY
    510 		if (isdigit((unsigned char)**argv))
    511 			break;
    512 #endif
    513 		tried++;
    514 		for (i = 0; i < (int)ndrive; i++) {
    515 			if (fnmatch(*argv, cur.name[i], 0))
    516 				continue;
    517 			cur.select[i] = 1;
    518 			++ndrives;
    519 		}
    520 
    521 	}
    522 
    523 	if (ndrives == 0 && tried == 0) {
    524 		/*
    525 		 * Pick up to defdrives (or all if -x is given) drives
    526 		 * if none specified.
    527 		 */
    528 		maxdrives = (ISSET(todo, SHOW_STATS_X) ||
    529 			     (int)ndrive < defdrives)
    530 			? (int)(ndrive) : defdrives;
    531 		for (i = 0; i < maxdrives; i++) {
    532 			cur.select[i] = 1;
    533 
    534 			++ndrives;
    535 			if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
    536 				break;
    537 		}
    538 	}
    539 
    540 #ifdef BACKWARD_COMPATIBILITY
    541 	if (*argv) {
    542 		interval = atoi(*argv);
    543 		if (*++argv)
    544 			reps = atoi(*argv);
    545 	}
    546 #endif
    547 
    548 	if (interval) {
    549 		if (!reps)
    550 			reps = -1;
    551 	} else
    552 		if (reps)
    553 			interval = 1;
    554 
    555 	return (ndrives);
    556 }
    557