iostat.c revision 1.63 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