xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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