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