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