iostat.c revision 1.37 1 /* $NetBSD: iostat.c,v 1.37 2003/07/02 13:20:14 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.37 2003/07/02 13:20:14 simonb Exp $");
79 #endif
80 #endif /* not lint */
81
82 #include <sys/types.h>
83 #include <sys/ioctl.h>
84 #include <sys/sched.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 static int winlines = 20;
103
104 #define DEFDRIVES 3
105 #define ISSET(x, a) ((x) & (a))
106 #define SHOW_CPU 1<<0
107 #define SHOW_TTY 1<<1
108 #define SHOW_STATS_1 1<<2
109 #define SHOW_STATS_2 1<<3
110 #define SHOW_STATS_X 1<<4
111 #define SHOW_TOTALS 1<<7
112 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
113
114 static void cpustats(void);
115 static void disk_stats(double);
116 static void disk_stats2(double);
117 static void disk_statsx(double);
118 static void header(int);
119 static void usage(void);
120 static void display(void);
121 static int selectdrives(int, char *[]);
122
123 int main(int, char *[]);
124
125 int
126 main(int argc, char *argv[])
127 {
128 int ch, hdrcnt, ndrives, lines;
129 struct timespec tv;
130 struct ttysize ts;
131
132 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1)
133 switch (ch) {
134 case 'c':
135 if ((reps = atoi(optarg)) <= 0)
136 errx(1, "repetition count <= 0.");
137 break;
138 case 'C':
139 todo |= SHOW_CPU;
140 break;
141 case 'd':
142 todo &= ~SHOW_STATS_ALL;
143 todo |= SHOW_STATS_1;
144 break;
145 case 'D':
146 todo &= ~SHOW_STATS_ALL;
147 todo |= SHOW_STATS_2;
148 break;
149 case 'I':
150 todo |= SHOW_TOTALS;
151 break;
152 case 'M':
153 memf = optarg;
154 break;
155 case 'N':
156 nlistf = optarg;
157 break;
158 case 'T':
159 todo |= SHOW_TTY;
160 break;
161 case 'w':
162 if ((interval = atoi(optarg)) <= 0)
163 errx(1, "interval <= 0.");
164 break;
165 case 'x':
166 todo &= ~SHOW_STATS_ALL;
167 todo |= SHOW_STATS_X;
168 break;
169 case '?':
170 default:
171 usage();
172 }
173 argc -= optind;
174 argv += optind;
175
176 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
177 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
178 if (ISSET(todo, SHOW_STATS_X)) {
179 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
180 todo |= SHOW_STATS_X;
181 }
182
183 dkinit(0);
184 dkreadstats();
185 ndrives = selectdrives(argc, argv);
186 if (ndrives == 0) {
187 /* No drives are selected. No need to show disk stats. */
188 todo &= ~SHOW_STATS_ALL;
189 if (todo == 0)
190 errx(1, "no drives");
191 }
192 if (ISSET(todo, SHOW_STATS_X))
193 lines = ndrives;
194 else
195 lines = 1;
196
197 tv.tv_sec = interval;
198 tv.tv_nsec = 0;
199
200 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
201 if (ts.ts_lines)
202 winlines = ts.ts_lines;
203 }
204
205 /* print a new header on sigcont */
206 (void)signal(SIGCONT, header);
207
208 for (hdrcnt = 1;;) {
209 if ((hdrcnt -= lines) <= 0) {
210 header(0);
211 hdrcnt = winlines - 4;
212 }
213
214 if (!ISSET(todo, SHOW_TOTALS))
215 dkswap();
216 display();
217
218 if (reps >= 0 && --reps <= 0)
219 break;
220 nanosleep(&tv, NULL);
221 dkreadstats();
222 }
223 exit(0);
224 }
225
226 static void
227 header(int signo)
228 {
229 int i;
230
231 /* Main Headers. */
232 if (ISSET(todo, SHOW_STATS_X)) {
233 if (ISSET(todo, SHOW_TOTALS)) {
234 (void)printf(
235 "device read KB/t xfr time MB/s");
236 (void)printf(" write KB/t xfr time MB/s\n");
237 } else {
238 (void)printf(
239 "device read KB/t r/s time MB/s");
240 (void)printf(" write KB/t w/s time MB/s\n");
241 }
242 return;
243 }
244
245 if (ISSET(todo, SHOW_TTY))
246 (void)printf(" tty");
247
248 if (ISSET(todo, SHOW_STATS_1))
249 for (i = 0; i < dk_ndrive; i++)
250 if (cur.dk_select[i])
251 (void)printf(" %9.9s ", cur.dk_name[i]);
252
253 if (ISSET(todo, SHOW_STATS_2))
254 for (i = 0; i < dk_ndrive; i++)
255 if (cur.dk_select[i])
256 (void)printf(" %9.9s ", cur.dk_name[i]);
257
258 if (ISSET(todo, SHOW_CPU))
259 (void)printf(" cpu");
260
261 printf("\n");
262
263 /* Sub-Headers. */
264 if (ISSET(todo, SHOW_TTY))
265 printf(" tin tout");
266
267 if (ISSET(todo, SHOW_STATS_1)) {
268 for (i = 0; i < dk_ndrive; i++)
269 if (cur.dk_select[i]) {
270 if (ISSET(todo, SHOW_TOTALS))
271 (void)printf(" KB/t xfr MB ");
272 else
273 (void)printf(" KB/t t/s MB/s ");
274 }
275 }
276
277 if (ISSET(todo, SHOW_STATS_2))
278 for (i = 0; i < dk_ndrive; i++)
279 if (cur.dk_select[i])
280 (void)printf(" KB xfr time ");
281
282 if (ISSET(todo, SHOW_CPU))
283 (void)printf(" us ni sy in id");
284 printf("\n");
285 }
286
287 static void
288 disk_stats(double etime)
289 {
290 int dn;
291 double atime, mbps;
292
293 for (dn = 0; dn < dk_ndrive; ++dn) {
294 if (!cur.dk_select[dn])
295 continue;
296 /* average Kbytes per transfer. */
297 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
298 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
299 1024.0) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
300 else
301 mbps = 0.0;
302 (void)printf(" %5.2f", mbps);
303
304 /* average transfers per second. */
305 (void)printf(" %4.0f",
306 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
307
308 /* time busy in disk activity */
309 atime = (double)cur.dk_time[dn].tv_sec +
310 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
311
312 /* Megabytes per second. */
313 if (atime != 0.0)
314 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
315 (double)(1024 * 1024);
316 else
317 mbps = 0;
318 (void)printf(" %5.2f ", mbps / etime);
319 }
320 }
321
322 static void
323 disk_stats2(double etime)
324 {
325 int dn;
326 double atime;
327
328 for (dn = 0; dn < dk_ndrive; ++dn) {
329 if (!cur.dk_select[dn])
330 continue;
331
332 /* average kbytes per second. */
333 (void)printf(" %5.0f",
334 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0 / etime);
335
336 /* average transfers per second. */
337 (void)printf(" %5.0f",
338 (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
339
340 /* average time busy in disk activity */
341 atime = (double)cur.dk_time[dn].tv_sec +
342 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
343 (void)printf(" %4.2f ", atime / etime);
344 }
345 }
346
347 static void
348 disk_statsx(double etime)
349 {
350 int dn;
351 double atime, kbps;
352
353 for (dn = 0; dn < dk_ndrive; ++dn) {
354 if (!cur.dk_select[dn])
355 continue;
356
357 (void)printf("%-8.8s", cur.dk_name[dn]);
358
359 /* average read Kbytes per transfer */
360 if (cur.dk_rxfer[dn])
361 kbps = (cur.dk_rbytes[dn] / 1024.0) / cur.dk_rxfer[dn];
362 else
363 kbps = 0.0;
364 (void)printf(" %8.2f", kbps);
365
366 /* average read transfers
367 (per second) */
368 (void)printf(" %6.0f", cur.dk_rxfer[dn] / etime);
369
370 /* time read busy in disk activity */
371 atime = (double)cur.dk_time[dn].tv_sec +
372 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
373 (void)printf(" %6.2f", atime / etime);
374
375 /* average read megabytes
376 (per second) */
377 (void)printf(" %8.2f",
378 cur.dk_rbytes[dn] / (1024.0 * 1024) / etime);
379
380
381 /* average write Kbytes per transfer */
382 if (cur.dk_wxfer[dn])
383 kbps = (cur.dk_wbytes[dn] / 1024.0) / cur.dk_wxfer[dn];
384 else
385 kbps = 0.0;
386 (void)printf(" %8.2f", kbps);
387
388 /* average write transfers
389 (per second) */
390 (void)printf(" %6.0f", cur.dk_wxfer[dn] / etime);
391
392 /* time write busy in disk activity */
393 atime = (double)cur.dk_time[dn].tv_sec +
394 ((double)cur.dk_time[dn].tv_usec / (double)1000000);
395 (void)printf(" %6.2f", atime / etime);
396
397 /* average write megabytes
398 (per second) */
399 (void)printf(" %8.2f\n",
400 cur.dk_wbytes[dn] / (1024.0 * 1024) / etime);
401 }
402 }
403
404 static void
405 cpustats(void)
406 {
407 int state;
408 double time;
409
410 time = 0;
411 for (state = 0; state < CPUSTATES; ++state)
412 time += cur.cp_time[state];
413 if (!time)
414 time = 1.0;
415 /* States are generally never 100% and can use %3.0f. */
416 for (state = 0; state < CPUSTATES; ++state)
417 printf(" %2.0f", 100. * cur.cp_time[state] / time);
418 }
419
420 static void
421 usage(void)
422 {
423
424 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] "
425 "[-N system] [-w wait] [drives]\n");
426 exit(1);
427 }
428
429 static void
430 display(void)
431 {
432 double etime;
433
434 /* Sum up the elapsed ticks. */
435 etime = cur.cp_etime;
436
437 /*
438 * If we're showing totals only, then don't divide by the
439 * system time.
440 */
441 if (ISSET(todo, SHOW_TOTALS))
442 etime = 1.0;
443
444 if (ISSET(todo, SHOW_STATS_X)) {
445 disk_statsx(etime);
446 goto out;
447 }
448
449 if (ISSET(todo, SHOW_TTY))
450 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
451
452 if (ISSET(todo, SHOW_STATS_1))
453 disk_stats(etime);
454
455 if (ISSET(todo, SHOW_STATS_2))
456 disk_stats2(etime);
457
458 if (ISSET(todo, SHOW_CPU))
459 cpustats();
460
461 (void)printf("\n");
462
463 out:
464 (void)fflush(stdout);
465 }
466
467 static int
468 selectdrives(int argc, char *argv[])
469 {
470 int i, maxdrives, ndrives, tried;
471
472 /*
473 * Choose drives to be displayed. Priority goes to (in order) drives
474 * supplied as arguments and default drives. If everything isn't
475 * filled in and there are drives not taken care of, display the first
476 * few that fit.
477 *
478 * The backward compatibility #ifdefs permit the syntax:
479 * iostat [ drives ] [ interval [ count ] ]
480 */
481
482 #define BACKWARD_COMPATIBILITY
483 for (tried = ndrives = 0; *argv; ++argv) {
484 #ifdef BACKWARD_COMPATIBILITY
485 if (isdigit(**argv))
486 break;
487 #endif
488 tried++;
489 for (i = 0; i < dk_ndrive; i++) {
490 if (strcmp(cur.dk_name[i], *argv))
491 continue;
492 cur.dk_select[i] = 1;
493 ++ndrives;
494 }
495 }
496
497 if (ndrives == 0 && tried == 0) {
498 /*
499 * Pick up to DEFDRIVES (or all if -x is given) drives
500 * if none specified.
501 */
502 maxdrives = ISSET(todo, SHOW_STATS_X) ? dk_ndrive : DEFDRIVES;
503 for (i = 0; i < maxdrives; i++) {
504 cur.dk_select[i] = 1;
505 ++ndrives;
506 if (!ISSET(todo, SHOW_STATS_X) && ndrives == DEFDRIVES)
507 break;
508 }
509 }
510
511 #ifdef BACKWARD_COMPATIBILITY
512 if (*argv) {
513 interval = atoi(*argv);
514 if (*++argv)
515 reps = atoi(*argv);
516 }
517 #endif
518
519 if (interval) {
520 if (!reps)
521 reps = -1;
522 } else
523 if (reps)
524 interval = 1;
525
526 return (ndrives);
527 }
528