iostat.c revision 1.18 1 /* $NetBSD: iostat.c,v 1.18 1999/06/27 12:32:13 mjl 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.18 1999/06/27 12:32:13 mjl 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 " %7.7s ", 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 " %7.7s ", 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