iostat.c revision 1.24 1 /* $NetBSD: iostat.c,v 1.24 2000/12/01 02:10:23 simonb 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.24 2000/12/01 02:10:23 simonb Exp $");
79 #endif
80 #endif /* not lint */
81
82 #include <sys/types.h>
83 #include <sys/sched.h>
84 #include <sys/dkstat.h>
85 #include <sys/time.h>
86
87 #include <err.h>
88 #include <ctype.h>
89 #include <signal.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <unistd.h>
94
95 #include "dkstats.h"
96
97 /* Namelist and memory files. */
98 char *nlistf, *memf;
99
100 int hz, reps, interval;
101 static int todo = 0;
102
103 #define ISSET(x, a) ((x) & (a))
104 #define SHOW_CPU 1<<0
105 #define SHOW_TTY 1<<1
106 #define SHOW_STATS_1 1<<2
107 #define SHOW_STATS_2 1<<3
108 #define SHOW_STATS_X 1<<4
109 #define SHOW_TOTALS 1<<7
110 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
111
112 static void cpustats(void);
113 static void disk_stats(double);
114 static void disk_stats2(double);
115 static void disk_statsx(double);
116 static void header(int);
117 static void usage(void);
118 static void display(void);
119 static void selectdrives(int, char **);
120
121 int main(int, char **);
122
123 int
124 main(int argc, char *argv[])
125 {
126 int ch, hdrcnt;
127 struct timeval tv;
128 gid_t egid = getegid();
129 setegid(getgid());
130
131 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw: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 'M':
152 memf = optarg;
153 break;
154 case 'N':
155 nlistf = optarg;
156 break;
157 case 'T':
158 todo |= SHOW_TTY;
159 break;
160 case 'w':
161 if ((interval = atoi(optarg)) <= 0)
162 errx(1, "interval <= 0.");
163 break;
164 case 'x':
165 todo &= ~SHOW_STATS_ALL;
166 todo |= SHOW_STATS_X;
167 break;
168 case '?':
169 default:
170 usage();
171 }
172 argc -= optind;
173 argv += optind;
174
175 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
176 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
177 if (ISSET(todo, SHOW_STATS_X)) {
178 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
179 todo |= SHOW_STATS_X;
180 }
181
182 /*
183 * Discard setgid privileges if not the running kernel so that bad
184 * guys can't print interesting stuff from kernel memory.
185 */
186 if (nlistf != NULL || memf != NULL)
187 setgid(getgid());
188
189 dkinit(0, egid);
190 dkreadstats();
191 selectdrives(argc, argv);
192
193 tv.tv_sec = interval;
194 tv.tv_usec = 0;
195
196 /* print a new header on sigcont */
197 (void)signal(SIGCONT, header);
198
199 for (hdrcnt = 1;;) {
200 if (!--hdrcnt) {
201 header(0);
202 hdrcnt = 20;
203 }
204
205 if (!ISSET(todo, SHOW_TOTALS))
206 dkswap();
207 display();
208
209 if (reps >= 0 && --reps <= 0)
210 break;
211 select(0, NULL, NULL, NULL, &tv);
212 dkreadstats();
213 }
214 exit(0);
215 }
216
217 static void
218 header(int signo)
219 {
220 int i;
221
222 if (ISSET(todo, SHOW_STATS_X))
223 return;
224
225 /* Main Headers. */
226 if (ISSET(todo, SHOW_TTY))
227 (void)printf(" tty");
228
229 if (ISSET(todo, SHOW_STATS_1))
230 for (i = 0; i < dk_ndrive; i++)
231 if (cur.dk_select[i])
232 (void)printf(
233 " %7.7s ", cur.dk_name[i]);
234
235 if (ISSET(todo, SHOW_STATS_2))
236 for (i = 0; i < dk_ndrive; i++)
237 if (cur.dk_select[i])
238 (void)printf(
239 " %7.7s ", cur.dk_name[i]);
240
241 if (ISSET(todo, SHOW_CPU))
242 (void)printf(" cpu");
243
244 printf("\n");
245
246 /* Sub-Headers. */
247 if (ISSET(todo, SHOW_TTY))
248 printf(" tin tout");
249
250 if (ISSET(todo, SHOW_STATS_1)) {
251 for (i = 0; i < dk_ndrive; i++)
252 if (cur.dk_select[i]) {
253 if (ISSET(todo, SHOW_TOTALS))
254 (void)printf(" KB/t xfr MB ");
255 else
256 (void)printf(" KB/t t/s MB/s ");
257 }
258 }
259
260 if (ISSET(todo, SHOW_STATS_2))
261 for (i = 0; i < dk_ndrive; i++)
262 if (cur.dk_select[i])
263 (void)printf(" KB xfr time ");
264
265 if (ISSET(todo, SHOW_CPU))
266 (void)printf(" us ni sy in id");
267 printf("\n");
268 }
269
270 static void
271 disk_stats(double etime)
272 {
273 int dn;
274 double atime, mbps;
275
276 for (dn = 0; dn < dk_ndrive; ++dn) {
277 if (!cur.dk_select[dn])
278 continue;
279
280 /* average Kbytes per transfer. */
281 if (cur.dk_xfer[dn])
282 mbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn];
283 else
284 mbps = 0.0;
285 (void)printf(" %5.2f", mbps);
286
287 /* average transfers per second. */
288 (void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
289
290 /* time busy in disk activity */
291 atime = (double)cur.dk_time[dn].tv_sec +
292 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
293
294 /* Megabytes per second. */
295 if (atime != 0.0)
296 mbps = cur.dk_bytes[dn] / (double)(1024 * 1024);
297 else
298 mbps = 0;
299 (void)printf(" %4.2f ", mbps / etime);
300 }
301 }
302
303 static void
304 disk_stats2(double etime)
305 {
306 int dn;
307 double atime;
308
309 for (dn = 0; dn < dk_ndrive; ++dn) {
310 if (!cur.dk_select[dn])
311 continue;
312
313 /* average kbytes per second. */
314 (void)printf(" %4.0f", cur.dk_bytes[dn] / (1024.0) / etime);
315
316 /* average transfers per second. */
317 (void)printf(" %3.0f", cur.dk_xfer[dn] / etime);
318
319 /* average time busy in disk activity */
320 atime = (double)cur.dk_time[dn].tv_sec +
321 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
322 (void)printf(" %4.2f ", atime / etime);
323 }
324 }
325
326 static void
327 disk_statsx(double etime)
328 {
329 int dn;
330 double atime, kbps;
331
332 if (ISSET(todo, SHOW_TOTALS))
333 (void)printf("device KB/t xfr time MB");
334 else
335 (void)printf("device KB/t t/s time MB/s");
336
337 for (dn = 0; dn < dk_ndrive; ++dn) {
338 (void)printf("\n");
339 (void)printf("%-8.8s", cur.dk_name[dn]);
340
341 /* average Kbytes per transfer */
342 if (cur.dk_xfer[dn])
343 kbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn];
344 else
345 kbps = 0.0;
346 (void)printf(" %8.2f", kbps);
347
348 /* average transfers (per second) */
349 (void)printf(" %8.0f", cur.dk_xfer[dn] / etime);
350
351 /* time busy in disk activity */
352 atime = (double)cur.dk_time[dn].tv_sec +
353 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
354 (void)printf(" %8.2f", atime / etime);
355
356 /* average megabytes (per second) */
357 (void)printf(" %8.2f",
358 cur.dk_bytes[dn] / (1024.0 * 1024) / etime);
359
360 }
361 }
362
363 static void
364 cpustats(void)
365 {
366 int state;
367 double time;
368
369 time = 0;
370 for (state = 0; state < CPUSTATES; ++state)
371 time += cur.cp_time[state];
372 if (!time)
373 time = 1.0;
374 /* States are generally never 100% and can use %3.0f. */
375 for (state = 0; state < CPUSTATES; ++state)
376 printf(" %2.0f", 100. * cur.cp_time[state] / time);
377 }
378
379 static void
380 usage(void)
381 {
382
383 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] \
384 [-N system] [-w wait] [drives]\n");
385 exit(1);
386 }
387
388 static void
389 display(void)
390 {
391 int i;
392 double etime;
393
394 /* Sum up the elapsed ticks. */
395 etime = 0.0;
396 for (i = 0; i < CPUSTATES; i++) {
397 etime += cur.cp_time[i];
398 }
399 if (etime == 0.0)
400 etime = 1.0;
401 /* Convert to seconds. */
402 etime /= (float)hz;
403
404 /* If we're showing totals only, then don't divide by the
405 * system time.
406 */
407 if (ISSET(todo, SHOW_TOTALS))
408 etime = 1.0;
409
410 if (ISSET(todo, SHOW_TTY))
411 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
412
413 if (ISSET(todo, SHOW_STATS_1))
414 disk_stats(etime);
415
416 if (ISSET(todo, SHOW_STATS_2))
417 disk_stats2(etime);
418
419 if (ISSET(todo, SHOW_STATS_X))
420 disk_statsx(etime);
421
422 if (ISSET(todo, SHOW_CPU))
423 cpustats();
424
425 (void)printf("\n");
426 (void)fflush(stdout);
427 }
428
429 static void
430 selectdrives(int argc, char *argv[])
431 {
432 int i, ndrives;
433
434 /*
435 * Choose drives to be displayed. Priority goes to (in order) drives
436 * supplied as arguments and default drives. If everything isn't
437 * filled in and there are drives not taken care of, display the first
438 * few that fit.
439 *
440 * The backward compatibility #ifdefs permit the syntax:
441 * iostat [ drives ] [ interval [ count ] ]
442 */
443 if (ISSET(todo, SHOW_STATS_X)) {
444 for (i = 0; i < dk_ndrive; i++)
445 cur.dk_select[i] = 1;
446 }
447
448 #define BACKWARD_COMPATIBILITY
449 for (ndrives = 0; *argv; ++argv) {
450 #ifdef BACKWARD_COMPATIBILITY
451 if (isdigit(**argv))
452 break;
453 #endif
454 if (!ISSET(todo, SHOW_STATS_X))
455 for (i = 0; i < dk_ndrive; i++) {
456 if (strcmp(cur.dk_name[i], *argv))
457 continue;
458 cur.dk_select[i] = 1;
459 ++ndrives;
460 }
461 }
462 #ifdef BACKWARD_COMPATIBILITY
463 if (*argv) {
464 interval = atoi(*argv);
465 if (*++argv)
466 reps = atoi(*argv);
467 }
468 #endif
469
470 if (interval) {
471 if (!reps)
472 reps = -1;
473 } else
474 if (reps)
475 interval = 1;
476
477 /* Pick up to 4 drives if none specified. */
478 if (ndrives == 0)
479 for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
480 if (cur.dk_select[i])
481 continue;
482 cur.dk_select[i] = 1;
483 ++ndrives;
484 }
485 }
486