xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: iostat.c,v 1.17 1998/08/27 03:17:49 lukem 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. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *      This product includes software developed by the University of
50  *      California, Berkeley and its contributors.
51  * 4. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 
68 #include <sys/cdefs.h>
69 #ifndef lint
70 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\n\
71         The Regents of the University of California.  All rights reserved.\n");
72 #endif /* not lint */
73 
74 #ifndef lint
75 #if 0
76 static char sccsid[] = "@(#)iostat.c	8.3 (Berkeley) 4/28/95";
77 #else
78 __RCSID("$NetBSD: iostat.c,v 1.17 1998/08/27 03:17:49 lukem Exp $");
79 #endif
80 #endif /* not lint */
81 
82 #include <sys/types.h>
83 #include <sys/dkstat.h>
84 #include <sys/time.h>
85 
86 #include <err.h>
87 #include <ctype.h>
88 #include <signal.h>
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <string.h>
92 #include <unistd.h>
93 
94 #include "dkstats.h"
95 
96 /* Defined in dkstats.c */
97 extern struct _disk cur;
98 extern int  	dk_ndrive;
99 
100 /* Namelist and memory files. */
101 char	*nlistf, *memf;
102 
103 int		hz, reps, interval;
104 static int	todo = 0;
105 
106 #define ISSET(x, a)	((x) & (a))
107 #define SHOW_CPU	1<<0
108 #define SHOW_TTY	1<<1
109 #define SHOW_STATS_1	1<<2
110 #define SHOW_STATS_2	1<<3
111 #define SHOW_STATS_X	1<<4
112 #define SHOW_TOTALS	1<<7
113 #define SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
114 
115 static void cpustats __P((void));
116 static void disk_stats __P((double));
117 static void disk_stats2 __P((double));
118 static void disk_statsx __P((double));
119 static void header __P((int));
120 static void usage __P((void));
121 static void display __P((void));
122 static void selectdrives __P((int, char **));
123 
124 void dkswap __P((void));
125 void dkreadstats __P((void));
126 int dkinit __P((int, gid_t));
127 int main __P((int, char **));
128 
129 int
130 main(argc, argv)
131 	int argc;
132 	char *argv[];
133 {
134 	int ch, hdrcnt;
135 	struct timeval	tv;
136 	gid_t egid = getegid();
137 	setegid(getgid());
138 
139 	while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1)
140 		switch(ch) {
141 		case 'c':
142 			if ((reps = atoi(optarg)) <= 0)
143 				errx(1, "repetition count <= 0.");
144 			break;
145 		case 'C':
146 			todo |= SHOW_CPU;
147 			break;
148 		case 'd':
149 			todo &= ~SHOW_STATS_ALL;
150 			todo |= SHOW_STATS_1;
151 			break;
152 		case 'D':
153 			todo &= ~SHOW_STATS_ALL;
154 			todo |= SHOW_STATS_2;
155 			break;
156 		case 'I':
157 			todo |= SHOW_TOTALS;
158 			break;
159 		case 'M':
160 			memf = optarg;
161 			break;
162 		case 'N':
163 			nlistf = optarg;
164 			break;
165 		case 'T':
166 			todo |= SHOW_TTY;
167 			break;
168 		case 'w':
169 			if ((interval = atoi(optarg)) <= 0)
170 				errx(1, "interval <= 0.");
171 			break;
172 		case 'x':
173 			todo &= ~SHOW_STATS_ALL;
174 			todo |= SHOW_STATS_X;
175 			break;
176 		case '?':
177 		default:
178 			usage();
179 		}
180 	argc -= optind;
181 	argv += optind;
182 
183 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
184 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
185 	if (ISSET(todo, SHOW_STATS_X)) {
186 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
187 		todo |= SHOW_STATS_X;
188 	}
189 
190 	/*
191 	 * Discard setgid privileges if not the running kernel so that bad
192 	 * guys can't print interesting stuff from kernel memory.
193 	 */
194 	if (nlistf != NULL || memf != NULL)
195 		setgid(getgid());
196 
197 	dkinit(0, egid);
198 	dkreadstats();
199 	selectdrives(argc, argv);
200 
201 	tv.tv_sec = interval;
202 	tv.tv_usec = 0;
203 
204 	/* print a new header on sigcont */
205 	(void)signal(SIGCONT, header);
206 
207 	for (hdrcnt = 1;;) {
208 		if (!--hdrcnt) {
209 			header(0);
210 			hdrcnt = 20;
211 		}
212 
213 		if (!ISSET(todo, SHOW_TOTALS))
214 			dkswap();
215 		display();
216 
217 		if (reps >= 0 && --reps <= 0)
218 			break;
219 		select(0, NULL, NULL, NULL, &tv);
220 		dkreadstats();
221 	}
222 	exit(0);
223 }
224 
225 static void
226 header(signo)
227 	int signo;
228 {
229 	int i;
230 
231 	if (ISSET(todo, SHOW_STATS_X))
232 		return;
233 
234 					/* Main Headers. */
235 	if (ISSET(todo, SHOW_TTY))
236 		(void)printf("      tty");
237 
238 	if (ISSET(todo, SHOW_STATS_1))
239 		for (i = 0; i < dk_ndrive; i++)
240 			if (cur.dk_select[i])
241 				(void)printf(
242 				    "            %3.3s ", cur.dk_name[i]);
243 
244 	if (ISSET(todo, SHOW_STATS_2))
245 		for (i = 0; i < dk_ndrive; i++)
246 			if (cur.dk_select[i])
247 				(void)printf(
248 				    "           %3.3s ", cur.dk_name[i]);
249 
250 	if (ISSET(todo, SHOW_CPU))
251 		(void)printf("            cpu");
252 
253 	printf("\n");
254 
255 					/* Sub-Headers. */
256 	if (ISSET(todo, SHOW_TTY))
257 		printf(" tin tout");
258 
259 	if (ISSET(todo, SHOW_STATS_1)) {
260 		for (i = 0; i < dk_ndrive; i++)
261 			if (cur.dk_select[i]) {
262 				if (ISSET(todo, SHOW_TOTALS))
263 					(void)printf("  KB/t xfr MB   ");
264 				else
265 					(void)printf("  KB/t t/s MB/s ");
266 			}
267 	}
268 
269 	if (ISSET(todo, SHOW_STATS_2))
270 		for (i = 0; i < dk_ndrive; i++)
271 			if (cur.dk_select[i])
272 				(void)printf("   KB xfr time ");
273 
274 	if (ISSET(todo, SHOW_CPU))
275 		(void)printf(" us ni sy in id");
276 	printf("\n");
277 }
278 
279 static void
280 disk_stats(etime)
281 	double etime;
282 {
283 	int dn;
284 	double atime, mbps;
285 
286 	for (dn = 0; dn < dk_ndrive; ++dn) {
287 		if (!cur.dk_select[dn])
288 			continue;
289 
290 					/* average Kbytes per transfer. */
291 		if (cur.dk_xfer[dn])
292 			mbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn];
293 		else
294 			mbps = 0.0;
295 		(void)printf(" %5.2f", mbps);
296 
297 					/* average transfers per second. */
298 		(void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
299 
300 					/* time busy in disk activity */
301 		atime = (double)cur.dk_time[dn].tv_sec +
302 			((double)cur.dk_time[dn].tv_usec / (double)1000000);
303 
304 					/* Megabytes per second. */
305 		if (atime != 0.0)
306 			mbps = cur.dk_bytes[dn] / (double)(1024 * 1024);
307 		else
308 			mbps = 0;
309 		(void)printf(" %4.2f ", mbps / etime);
310 	}
311 }
312 
313 static void
314 disk_stats2(etime)
315 	double etime;
316 {
317 	int dn;
318 	double atime;
319 
320 	for (dn = 0; dn < dk_ndrive; ++dn) {
321 		if (!cur.dk_select[dn])
322 			continue;
323 
324 					/* average kbytes per second. */
325 		(void)printf(" %4.0f", cur.dk_bytes[dn] / (1024.0) / etime);
326 
327 					/* average transfers per second. */
328 		(void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
329 
330 					/* average time busy in disk activity */
331 		atime = (double)cur.dk_time[dn].tv_sec +
332 			((double)cur.dk_time[dn].tv_usec / (double)1000000);
333 		(void)printf(" %4.2f ", atime / etime);
334 	}
335 }
336 
337 static void
338 disk_statsx(etime)
339 	double etime;
340 {
341 	int dn;
342 	double atime, kbps;
343 
344 	if (ISSET(todo, SHOW_TOTALS))
345 		(void)printf("device       KB/t      xfr     time       MB");
346 	else
347 		(void)printf("device       KB/t      t/s     time     MB/s");
348 
349 	for (dn = 0; dn < dk_ndrive; ++dn) {
350 		(void)printf("\n");
351 		(void)printf("%-8.8s", cur.dk_name[dn]);
352 
353 					/* average Kbytes per transfer */
354 		if (cur.dk_xfer[dn])
355 			kbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn];
356 		else
357 			kbps = 0.0;
358 		(void)printf(" %8.2f", kbps);
359 
360 					/* average transfers (per second) */
361 		(void)printf(" %8.0f", cur.dk_xfer[dn] / etime);
362 
363 					/* time busy in disk activity */
364 		atime = (double)cur.dk_time[dn].tv_sec +
365 			((double)cur.dk_time[dn].tv_usec / (double)1000000);
366 		(void)printf(" %8.2f", atime / etime);
367 
368 					/* average megabytes (per second) */
369 		(void)printf(" %8.2f",
370 		    cur.dk_bytes[dn] / (1024.0 * 1024) / etime);
371 
372 	}
373 }
374 
375 static void
376 cpustats()
377 {
378 	int state;
379 	double time;
380 
381 	time = 0;
382 	for (state = 0; state < CPUSTATES; ++state)
383 		time += cur.cp_time[state];
384 	if (!time)
385 		time = 1.0;
386 			/* States are generally never 100% and can use %3.0f. */
387 	for (state = 0; state < CPUSTATES; ++state)
388 		printf("%3.0f", 100. * cur.cp_time[state] / time);
389 }
390 
391 static void
392 usage()
393 {
394 
395 	(void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] \
396 [-N system] [-w wait] [drives]\n");
397 	exit(1);
398 }
399 
400 static void
401 display()
402 {
403 	int	i;
404 	double	etime;
405 
406 	/* Sum up the elapsed ticks. */
407 	etime = 0.0;
408 	for (i = 0; i < CPUSTATES; i++) {
409 		etime += cur.cp_time[i];
410 	}
411 	if (etime == 0.0)
412 		etime = 1.0;
413 					/* Convert to seconds. */
414 	etime /= (float)hz;
415 
416 	/* If we're showing totals only, then don't divide by the
417 	 * system time.
418 	 */
419 	if (ISSET(todo, SHOW_TOTALS))
420 		etime = 1.0;
421 
422 	if (ISSET(todo, SHOW_TTY))
423 		printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
424 
425 	if (ISSET(todo, SHOW_STATS_1))
426 		disk_stats(etime);
427 
428 	if (ISSET(todo, SHOW_STATS_2))
429 		disk_stats2(etime);
430 
431 	if (ISSET(todo, SHOW_STATS_X))
432 		disk_statsx(etime);
433 
434 	if (ISSET(todo, SHOW_CPU))
435 		cpustats();
436 
437 	(void)printf("\n");
438 	(void)fflush(stdout);
439 }
440 
441 static void
442 selectdrives(argc, argv)
443 	int	argc;
444 	char	*argv[];
445 {
446 	int	i, ndrives;
447 
448 	/*
449 	 * Choose drives to be displayed.  Priority goes to (in order) drives
450 	 * supplied as arguments and default drives.  If everything isn't
451 	 * filled in and there are drives not taken care of, display the first
452 	 * few that fit.
453 	 *
454 	 * The backward compatibility #ifdefs permit the syntax:
455 	 *	iostat [ drives ] [ interval [ count ] ]
456 	 */
457 #define	BACKWARD_COMPATIBILITY
458 	for (ndrives = 0; *argv; ++argv) {
459 #ifdef	BACKWARD_COMPATIBILITY
460 		if (isdigit(**argv))
461 			break;
462 #endif
463 		for (i = 0; i < dk_ndrive; i++) {
464 			if (strcmp(cur.dk_name[i], *argv))
465 				continue;
466 			cur.dk_select[i] = 1;
467 			++ndrives;
468 		}
469 	}
470 #ifdef	BACKWARD_COMPATIBILITY
471 	if (*argv) {
472 		interval = atoi(*argv);
473 		if (*++argv)
474 			reps = atoi(*argv);
475 	}
476 #endif
477 
478 	if (interval) {
479 		if (!reps)
480 			reps = -1;
481 	} else
482 		if (reps)
483 			interval = 1;
484 
485 	/* Pick up to 4 drives if none specified. */
486 	if (ndrives == 0)
487 		for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
488 			if (cur.dk_select[i])
489 				continue;
490 			cur.dk_select[i] = 1;
491 			++ndrives;
492 		}
493 }
494