1 /* $NetBSD: iostat.c,v 1.72 2023/10/30 19:43:33 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.72 2023/10/30 19:43:33 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 static int *order, ordersize;
103
104 static char Line_Marker[] = "________________________________________________";
105
106 #define MAX(a,b) (((a)>(b))?(a):(b))
107 #define MIN(a,b) (((a)<(b))?(a):(b))
108
109 #define ISSET(x, a) ((x) & (a))
110 #define SHOW_CPU (1u<<0)
111 #define SHOW_TTY (1u<<1)
112 #define SHOW_STATS_1 (1u<<2)
113 #define SHOW_STATS_2 (1u<<3)
114 #define SHOW_STATS_3 (1u<<4)
115 #define SHOW_STATS_X (1u<<5)
116 #define SHOW_STATS_Y (1u<<6)
117 #define SHOW_UPDATES (1u<<7)
118 #define SHOW_TOTALS (1u<<8)
119 #define SHOW_NEW_TOTALS (1u<<9)
120 #define SUPPRESS_ZERO (1u<<10)
121
122 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | \
123 SHOW_STATS_3 | SHOW_STATS_X | SHOW_STATS_Y)
124
125 /*
126 * Decide how many screen columns each output statistic is given
127 * (these are determined empirically ("looks good to me") and likely
128 * will require changes from time to time as technology advances).
129 *
130 * The odd "+ N" at the end of the summary (total width of stat) definition
131 * allows for the gaps between the columns, and is (#data cols - 1).
132 * So, tty stats have "in" and "out", 2 columns, so there is 1 extra space,
133 * whereas the cpu stats have 5 columns, so 4 extra spaces (etc).
134 */
135 #define LAYOUT_TTY_IN 4 /* tty input in last interval */
136 #define LAYOUT_TTY_TIN 7 /* tty input forever */
137 #define LAYOUT_TTY_OUT 5 /* tty output in last interval */
138 #define LAYOUT_TTY_TOUT 10 /* tty output forever */
139 #define LAYOUT_TTY (((todo & SHOW_TOTALS) \
140 ? (LAYOUT_TTY_TIN + LAYOUT_TTY_TOUT) \
141 : (LAYOUT_TTY_IN + LAYOUT_TTY_OUT)) + 1)
142 #define LAYOUT_TTY_GAP 0 /* always starts at left margin */
143
144 #define LAYOUT_CPU_USER 2
145 #define LAYOUT_CPU_NICE 2
146 #define LAYOUT_CPU_SYS 2
147 #define LAYOUT_CPU_INT 2
148 #define LAYOUT_CPU_IDLE 3
149 #define LAYOUT_CPU (LAYOUT_CPU_USER + LAYOUT_CPU_NICE + LAYOUT_CPU_SYS + \
150 LAYOUT_CPU_INT + LAYOUT_CPU_IDLE + 4)
151 #define LAYOUT_CPU_GAP 2
152
153 /* used for: w/o TOTALS w TOTALS */
154 #define LAYOUT_DRIVE_1_XSIZE 5 /* KB/t KB/t */
155 #define LAYOUT_DRIVE_1_RATE 6 /* t/s */
156 #define LAYOUT_DRIVE_1_XFER 10 /* xfr */
157 #define LAYOUT_DRIVE_1_SPEED 5 /* MB/s */
158 #define LAYOUT_DRIVE_1_VOLUME 8 /* MB */
159 #define LAYOUT_DRIVE_1_INCR 5 /* (inc) */
160
161 #define LAYOUT_DRIVE_2_XSIZE 7 /* KB */
162 #define LAYOUT_DRIVE_2_VOLUME 11 /* KB */
163 #define LAYOUT_DRIVE_2_XFR 7 /* xfr */
164 #define LAYOUT_DRIVE_2_TXFR 10 /* xfr */
165 #define LAYOUT_DRIVE_2_INCR 5 /* (inc) */
166 #define LAYOUT_DRIVE_2_TBUSY 9 /* time */
167 #define LAYOUT_DRIVE_2_BUSY 5 /* time */
168
169 /* Layout 3 uses same sizes as 2, but with MB. */
170
171 #define LAYOUT_DRIVE_1 (LAYOUT_DRIVE_1_XSIZE + ((todo & SHOW_TOTALS) ? \
172 (LAYOUT_DRIVE_1_XFER + LAYOUT_DRIVE_1_VOLUME + \
173 ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_1_INCR+2 :0)) \
174 : (LAYOUT_DRIVE_1_RATE + LAYOUT_DRIVE_1_SPEED)) + 3)
175 #define LAYOUT_DRIVE_2 (((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME + \
176 LAYOUT_DRIVE_2_TXFR + LAYOUT_DRIVE_2_TBUSY + \
177 ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+2 : 0))\
178 : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_XFR + \
179 LAYOUT_DRIVE_2_BUSY)) + 3)
180 #define LAYOUT_DRIVE_3 (((todo & SHOW_TOTALS) ? (LAYOUT_DRIVE_2_VOLUME + \
181 LAYOUT_DRIVE_2_TBUSY + \
182 ((todo&SHOW_UPDATES)? 2*LAYOUT_DRIVE_2_INCR+1 : 0))\
183 : (LAYOUT_DRIVE_2_XSIZE + LAYOUT_DRIVE_2_BUSY)) + 2)
184
185 #define LAYOUT_DRIVE_GAP 0 /* Gap included in column, always present */
186
187 /* TODO: X & Y stats layouts */
188
189 static void cpustats(void);
190 static double drive_time(double, int);
191 static void drive_stats(int, double);
192 static void drive_stats2(int, double);
193 static void drive_statsx(int, double);
194 static void drive_statsy(int, double);
195 static void drive_statsy_io(double, double, double);
196 static void drive_statsy_q(double, double, double, double, double, double);
197 static void sig_header(int);
198 static volatile int do_header;
199 static void header(int);
200 __dead static void usage(void);
201 static void display(int);
202 static int selectdrives(int, char *[], int);
203
204 int
main(int argc,char * argv[])205 main(int argc, char *argv[])
206 {
207 int ch, hdrcnt, hdroffset, ndrives, lines;
208 struct timespec tv;
209 struct ttysize ts;
210 long width = -1, height = -1;
211 char *ep;
212
213 #if 0 /* -i and -u are not currently (sanely) implementable */
214 while ((ch = getopt(argc, argv, "Cc:dDH:iITuw:W:xXyz")) != -1)
215 #else
216 while ((ch = getopt(argc, argv, "Cc:dDH:ITw:W:xXyz")) != -1)
217 #endif
218 switch (ch) {
219 case 'c':
220 if ((reps = atoi(optarg)) <= 0)
221 errx(1, "repetition count <= 0.");
222 break;
223 case 'C':
224 todo |= SHOW_CPU;
225 break;
226 case 'd':
227 todo &= ~SHOW_STATS_ALL;
228 todo |= SHOW_STATS_1;
229 break;
230 case 'D':
231 todo &= ~SHOW_STATS_ALL;
232 todo |= SHOW_STATS_2;
233 break;
234 case 'H':
235 height = strtol(optarg, &ep, 10);
236 if (height < 0 || *ep != '\0')
237 errx(1, "bad height (-H) value.");
238 height += 2; /* magic, but needed to be sane */
239 break;
240 #if 0
241 case 'i':
242 todo |= SHOW_TOTALS | SHOW_NEW_TOTALS;
243 break;
244 #endif
245 case 'I':
246 todo |= SHOW_TOTALS;
247 break;
248 case 'T':
249 todo |= SHOW_TTY;
250 break;
251 #if 0
252 case 'u':
253 todo |= SHOW_UPDATES;
254 break;
255 #endif
256 case 'w':
257 if ((interval = atoi(optarg)) <= 0)
258 errx(1, "interval <= 0.");
259 break;
260 case 'W':
261 width = strtol(optarg, &ep, 10);
262 if (width < 0 || *ep != '\0')
263 errx(1, "bad width (-W) value.");
264 break;
265 case 'x':
266 todo &= ~SHOW_STATS_ALL;
267 todo |= SHOW_STATS_X;
268 break;
269 case 'X':
270 todo &= ~SHOW_STATS_ALL;
271 todo |= SHOW_STATS_3;
272 break;
273 case 'y':
274 todo &= ~SHOW_STATS_ALL;
275 todo |= SHOW_STATS_Y;
276 break;
277 case 'z':
278 todo |= SUPPRESS_ZERO;
279 break;
280 case '?':
281 default:
282 usage();
283 }
284 argc -= optind;
285 argv += optind;
286
287 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
288 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
289 if (ISSET(todo, SHOW_STATS_X)) {
290 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
291 todo |= SHOW_STATS_X;
292 }
293 if (ISSET(todo, SHOW_STATS_3)) {
294 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
295 todo |= SHOW_STATS_3;
296 }
297 if (ISSET(todo, SHOW_STATS_Y)) {
298 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS);
299 todo |= SHOW_STATS_Y;
300 }
301
302 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
303 if (ts.ts_lines)
304 winlines = ts.ts_lines;
305 if (ts.ts_cols)
306 wincols = ts.ts_cols;
307 }
308
309 if (height == -1) {
310 char *lns = getenv("LINES");
311
312 if (lns == NULL || (height = strtol(lns, &ep, 10)) < 0 ||
313 *ep != '\0')
314 height = winlines;
315 }
316 winlines = height;
317
318 if (width == -1) {
319 char *cols = getenv("COLUMNS");
320
321 if (cols == NULL || (width = strtol(cols, &ep, 10)) < 0 ||
322 *ep != '\0')
323 width = wincols;
324 }
325 defdrives = width;
326 if (defdrives == 0) {
327 defdrives = 5000; /* anything absurdly big */
328 } else {
329 if (ISSET(todo, SHOW_CPU))
330 defdrives -= LAYOUT_CPU + LAYOUT_CPU_GAP;
331 if (ISSET(todo, SHOW_TTY))
332 defdrives -= LAYOUT_TTY + LAYOUT_TTY_GAP;
333 if (ISSET(todo, SHOW_STATS_2))
334 defdrives /= LAYOUT_DRIVE_2 + LAYOUT_DRIVE_GAP;
335 if (ISSET(todo, SHOW_STATS_3))
336 defdrives /= LAYOUT_DRIVE_3 + LAYOUT_DRIVE_GAP;
337 else
338 defdrives /= LAYOUT_DRIVE_1 + LAYOUT_DRIVE_GAP;
339 }
340
341 drvinit(0);
342 cpureadstats();
343 drvreadstats();
344 ordersize = 0;
345 ndrives = selectdrives(argc, argv, 1);
346 if (ndrives == 0) {
347 /* No drives are selected. No need to show drive stats. */
348 todo &= ~SHOW_STATS_ALL;
349 if (todo == 0)
350 errx(1, "no drives");
351 }
352 tv.tv_sec = interval;
353 tv.tv_nsec = 0;
354
355 /* print a new header on sigcont */
356 (void)signal(SIGCONT, sig_header);
357 do_header = 1;
358
359 for (hdrcnt = 1;;) {
360 if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y)) {
361 lines = ndrives;
362 hdroffset = 3;
363 } else if (ISSET(todo, SHOW_STATS_3)) {
364 lines = 1;
365 hdroffset = 3;
366 } else {
367 lines = 1;
368 hdroffset = 4;
369 }
370
371 if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) {
372 do_header = 0;
373 header(ndrives);
374 hdrcnt = winlines - hdroffset;
375 }
376
377 if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) {
378 cpuswap();
379 drvswap();
380 tkswap();
381 todo &= ~SHOW_NEW_TOTALS;
382 }
383
384 display(ndrives);
385
386 if (reps >= 0 && --reps <= 0)
387 break;
388 nanosleep(&tv, NULL);
389 cpureadstats();
390 drvreadstats();
391
392 ndrives = selectdrives(argc, argv, 0);
393 }
394 exit(0);
395 }
396
397 static void
sig_header(int signo)398 sig_header(int signo)
399 {
400 do_header = 1;
401 }
402
403 static void
header(int ndrives)404 header(int ndrives)
405 {
406 int i;
407
408 /* Main Headers. */
409 if (ISSET(todo, SHOW_STATS_X)) {
410 if (ISSET(todo, SHOW_TOTALS)) {
411 (void)printf(
412 "device read KB/t xfr time MB ");
413 (void)printf(" write KB/t xfr time MB\n");
414 } else {
415 (void)printf(
416 "device read KB/t r/s time MB/s");
417 (void)printf(" write KB/t w/s time MB/s\n");
418 }
419 return;
420 }
421
422 if (ISSET(todo, SHOW_STATS_Y)) {
423 (void)printf("device read KB/t r/s MB/s write KB/t w/s MB/s");
424 (void)printf(" wait actv wsvc_t asvc_t wtime time");
425 (void)printf("\n");
426 return;
427 }
428
429 if (ISSET(todo, SHOW_TTY))
430 (void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty");
431
432 if (ISSET(todo, SHOW_STATS_1)) {
433 for (i = 0; i < ndrives; i++) {
434 char *dname = cur.name[order[i]];
435 int dnlen = (int)strlen(dname);
436
437 printf(" "); /* always a 1 column gap */
438 if (dnlen < LAYOUT_DRIVE_1 - 6)
439 printf("|%-*.*s ",
440 (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
441 (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1,
442 Line_Marker);
443 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
444 MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0),
445 LAYOUT_DRIVE_1) : 0),
446 LAYOUT_DRIVE_1, dname);
447 if (dnlen < LAYOUT_DRIVE_1 - 6)
448 printf(" %*.*s|",
449 (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
450 (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1,
451 Line_Marker);
452 }
453 }
454
455 if (ISSET(todo, SHOW_STATS_2)) {
456 for (i = 0; i < ndrives; i++) {
457 char *dname = cur.name[order[i]];
458 int dnlen = (int)strlen(dname);
459
460 printf(" "); /* always a 1 column gap */
461 if (dnlen < LAYOUT_DRIVE_2 - 6)
462 printf("|%-*.*s ",
463 (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
464 (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1,
465 Line_Marker);
466 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
467 MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0),
468 LAYOUT_DRIVE_2) : 0),
469 LAYOUT_DRIVE_1, dname);
470 if (dnlen < LAYOUT_DRIVE_2 - 6)
471 printf(" %*.*s|",
472 (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
473 (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1,
474 Line_Marker);
475 }
476 }
477
478 if (ISSET(todo, SHOW_STATS_3)) {
479 for (i = 0; i < ndrives; i++) {
480 char *dname = cur.name[order[i]];
481 int dnlen = (int)strlen(dname);
482
483 printf(" "); /* always a 1 column gap */
484 if (dnlen < LAYOUT_DRIVE_3 - 6)
485 printf("|%-*.*s ",
486 (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
487 (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1,
488 Line_Marker);
489 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ?
490 MIN(MAX((LAYOUT_DRIVE_3 - dnlen) / 2, 0),
491 LAYOUT_DRIVE_3) : 0),
492 LAYOUT_DRIVE_1, dname);
493 if (dnlen < LAYOUT_DRIVE_3 - 6)
494 printf(" %*.*s|",
495 (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
496 (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1,
497 Line_Marker);
498 }
499 }
500
501 if (ISSET(todo, SHOW_CPU))
502 (void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU");
503
504 printf("\n");
505
506 /* Sub-Headers. */
507 if (ISSET(todo, SHOW_TTY)) {
508 printf("%*s %*s",
509 ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin",
510 ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout");
511 }
512
513 if (ISSET(todo, SHOW_STATS_1)) {
514 for (i = 0; i < ndrives; i++) {
515 if (ISSET(todo, SHOW_TOTALS)) {
516 (void)printf(" %*s %*s %*s",
517 LAYOUT_DRIVE_1_XFER, "xfr",
518 LAYOUT_DRIVE_1_XSIZE, "KB/t",
519 LAYOUT_DRIVE_1_VOLUME, "MB");
520 } else {
521 (void)printf(" %*s %*s %*s",
522 LAYOUT_DRIVE_1_RATE, "t/s",
523 LAYOUT_DRIVE_1_XSIZE, "KB/t",
524 LAYOUT_DRIVE_1_SPEED, "MB/s");
525 }
526 }
527 }
528
529 if (ISSET(todo, SHOW_STATS_2)) {
530 for (i = 0; i < ndrives; i++) {
531 if (ISSET(todo, SHOW_TOTALS)) {
532 (void)printf(" %*s %*s %*s",
533 LAYOUT_DRIVE_2_TXFR, "xfr",
534 LAYOUT_DRIVE_2_VOLUME, "KB",
535 LAYOUT_DRIVE_2_TBUSY, "time");
536 } else {
537 (void)printf(" %*s %*s %*s",
538 LAYOUT_DRIVE_2_XFR, "xfr",
539 LAYOUT_DRIVE_2_XSIZE, "KB",
540 LAYOUT_DRIVE_2_BUSY, "time");
541 }
542 }
543 }
544
545 if (ISSET(todo, SHOW_STATS_3)) {
546 for (i = 0; i < ndrives; i++) {
547 if (ISSET(todo, SHOW_TOTALS)) {
548 (void)printf(" %*s %*s",
549 LAYOUT_DRIVE_2_VOLUME, "MB/s",
550 LAYOUT_DRIVE_2_TBUSY, "time");
551 } else {
552 (void)printf(" %*s %*s",
553 LAYOUT_DRIVE_2_XSIZE, "MB/s",
554 LAYOUT_DRIVE_2_BUSY, "time");
555 }
556 }
557 }
558
559 /* should do this properly, but it is such a simple case... */
560 if (ISSET(todo, SHOW_CPU))
561 (void)printf(" us ni sy in id");
562 printf("\n");
563 }
564
565 static double
drive_time(double etime,int dn)566 drive_time(double etime, int dn)
567 {
568 if (ISSET(todo, SHOW_TOTALS))
569 return etime;
570
571 if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) {
572 etime = (double)cur.timestamp[dn].tv_sec +
573 ((double)cur.timestamp[dn].tv_usec / (double)1000000);
574 }
575
576 return etime;
577 }
578
579 static void
drive_stats(int ndrives,double etime)580 drive_stats(int ndrives, double etime)
581 {
582 int drive;
583 double atime, dtime, mbps;
584 int c1, c2, c3;
585
586 if (ISSET(todo, SHOW_TOTALS)) {
587 c1 = LAYOUT_DRIVE_1_XFER;
588 c2 = LAYOUT_DRIVE_1_XSIZE;
589 c3 = LAYOUT_DRIVE_1_VOLUME;
590 } else {
591 c1 = LAYOUT_DRIVE_1_RATE;
592 c2 = LAYOUT_DRIVE_1_XSIZE;
593 c3 = LAYOUT_DRIVE_1_SPEED;
594 }
595
596 for (drive = 0; drive < ndrives; ++drive) {
597 int dn = order[drive];
598
599 if (!cur.select[dn]) /* should be impossible */
600 continue;
601
602 if (todo & SUPPRESS_ZERO) {
603 if (cur.rxfer[dn] == 0 &&
604 cur.wxfer[dn] == 0 &&
605 cur.rbytes[dn] == 0 &&
606 cur.wbytes[dn] == 0) {
607 printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
608 continue;
609 }
610 }
611
612 dtime = drive_time(etime, dn);
613
614 /* average transfers per second. */
615 (void)printf(" %*.0f", c1,
616 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
617
618 /* average Kbytes per transfer. */
619 if (cur.rxfer[dn] + cur.wxfer[dn])
620 mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) /
621 1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]);
622 else
623 mbps = 0.0;
624 (void)printf(" %*.*f", c2,
625 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
626
627 /* time busy in drive activity */
628 atime = (double)cur.time[dn].tv_sec +
629 ((double)cur.time[dn].tv_usec / (double)1000000);
630
631 /* Megabytes per second. */
632 if (atime != 0.0)
633 mbps = (cur.rbytes[dn] + cur.wbytes[dn]) /
634 (double)(1024 * 1024);
635 else
636 mbps = 0;
637 mbps /= dtime;
638 (void)printf(" %*.*f", c3,
639 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps);
640 }
641 }
642
643 static void
drive_stats2(int ndrives,double etime)644 drive_stats2(int ndrives, double etime)
645 {
646 int drive;
647 double atime, dtime;
648 int c1, c2, c3;
649
650 if (ISSET(todo, SHOW_TOTALS)) {
651 c1 = LAYOUT_DRIVE_2_TXFR;
652 c2 = LAYOUT_DRIVE_2_VOLUME;
653 c3 = LAYOUT_DRIVE_2_TBUSY;
654 } else {
655 c1 = LAYOUT_DRIVE_2_XFR;
656 c2 = LAYOUT_DRIVE_2_XSIZE;
657 c3 = LAYOUT_DRIVE_2_BUSY;
658 }
659
660 for (drive = 0; drive < ndrives; ++drive) {
661 int dn = order[drive];
662
663 if (!cur.select[dn]) /* should be impossible */
664 continue;
665
666 if (todo & SUPPRESS_ZERO) {
667 if (cur.rxfer[dn] == 0 &&
668 cur.wxfer[dn] == 0 &&
669 cur.rbytes[dn] == 0 &&
670 cur.wbytes[dn] == 0) {
671 printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, "");
672 continue;
673 }
674 }
675
676 dtime = drive_time(etime, dn);
677
678 /* average transfers per second. */
679 if (ISSET(todo, SHOW_STATS_2)) {
680 (void)printf(" %*.0f", c1,
681 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime);
682 }
683
684 /* average mbytes per second. */
685 (void)printf(" %*.0f", c2,
686 (cur.rbytes[dn] + cur.wbytes[dn]) /
687 (double)(1024 * 1024) / dtime);
688
689 /* average time busy in dn activity */
690 atime = (double)cur.time[dn].tv_sec +
691 ((double)cur.time[dn].tv_usec / (double)1000000);
692 (void)printf(" %*.2f", c3, atime / dtime);
693 }
694 }
695
696 static void
drive_statsx(int ndrives,double etime)697 drive_statsx(int ndrives, double etime)
698 {
699 int dn, drive;
700 double atime, dtime, kbps;
701
702 for (drive = 0; drive < ndrives; ++drive) {
703 dn = order[drive];
704
705 if (!cur.select[dn]) /* impossible */
706 continue;
707
708 (void)printf("%-8.8s", cur.name[dn]);
709
710 if (todo & SUPPRESS_ZERO) {
711 if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
712 cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
713 printf("\n");
714 continue;
715 }
716 }
717
718 dtime = drive_time(etime, dn);
719
720 /* average read Kbytes per transfer */
721 if (cur.rxfer[dn])
722 kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn];
723 else
724 kbps = 0.0;
725 (void)printf(" %8.2f", kbps);
726
727 /* average read transfers
728 (per second) */
729 (void)printf(" %6.0f", cur.rxfer[dn] / dtime);
730
731 /* time read busy in drive activity */
732 atime = (double)cur.time[dn].tv_sec +
733 ((double)cur.time[dn].tv_usec / (double)1000000);
734 (void)printf(" %6.2f", atime / dtime);
735
736 /* average read megabytes
737 (per second) */
738 (void)printf(" %8.2f",
739 cur.rbytes[dn] / (1024.0 * 1024) / dtime);
740
741
742 /* average write Kbytes per transfer */
743 if (cur.wxfer[dn])
744 kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn];
745 else
746 kbps = 0.0;
747 (void)printf(" %8.2f", kbps);
748
749 /* average write transfers
750 (per second) */
751 (void)printf(" %6.0f", cur.wxfer[dn] / dtime);
752
753 /* time write busy in drive activity */
754 atime = (double)cur.time[dn].tv_sec +
755 ((double)cur.time[dn].tv_usec / (double)1000000);
756 (void)printf(" %6.2f", atime / dtime);
757
758 /* average write megabytes
759 (per second) */
760 (void)printf(" %8.2f\n",
761 cur.wbytes[dn] / (1024.0 * 1024) / dtime);
762 }
763 }
764
765 static void
drive_statsy_io(double elapsed,double count,double volume)766 drive_statsy_io(double elapsed, double count, double volume)
767 {
768 double kbps;
769
770 /* average Kbytes per transfer */
771 if (count)
772 kbps = (volume / 1024.0) / count;
773 else
774 kbps = 0.0;
775 (void)printf(" %8.2f", kbps);
776
777 /* average transfers (per second) */
778 (void)printf(" %6.0f", count / elapsed);
779
780 /* average megabytes (per second) */
781 (void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed);
782 }
783
784 static void
drive_statsy_q(double elapsed,double busy,double wait,double busysum,double waitsum,double count)785 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count)
786 {
787 /* average wait queue length */
788 (void)printf(" %6.1f", waitsum / elapsed);
789
790 /* average busy queue length */
791 (void)printf(" %6.1f", busysum / elapsed);
792
793 /* average wait time */
794 (void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0);
795
796 /* average service time */
797 (void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0);
798
799 /* time waiting for drive activity */
800 (void)printf(" %6.2f", wait / elapsed);
801
802 /* time busy in drive activity */
803 (void)printf(" %6.2f", busy / elapsed);
804 }
805
806 static void
drive_statsy(int ndrives,double etime)807 drive_statsy(int ndrives, double etime)
808 {
809 int drive, dn;
810 double atime, await, abusysum, awaitsum, dtime;
811
812 for (drive = 0; drive < ndrives; ++drive) {
813 dn = order[drive];
814 if (!cur.select[dn]) /* impossible */
815 continue;
816
817 (void)printf("%-8.8s", cur.name[dn]);
818
819 if (todo & SUPPRESS_ZERO) {
820 if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 &&
821 cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) {
822 printf("\n");
823 continue;
824 }
825 }
826
827 dtime = drive_time(etime, dn);
828
829 atime = (double)cur.time[dn].tv_sec +
830 ((double)cur.time[dn].tv_usec / (double)1000000);
831 await = (double)cur.wait[dn].tv_sec +
832 ((double)cur.wait[dn].tv_usec / (double)1000000);
833 abusysum = (double)cur.busysum[dn].tv_sec +
834 ((double)cur.busysum[dn].tv_usec / (double)1000000);
835 awaitsum = (double)cur.waitsum[dn].tv_sec +
836 ((double)cur.waitsum[dn].tv_usec / (double)1000000);
837
838 drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]);
839 (void)printf(" ");
840 drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]);
841 drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]);
842
843 (void)printf("\n");
844 }
845 }
846
847 static void
cpustats(void)848 cpustats(void)
849 {
850 int state;
851 double ttime;
852
853 static int cwidth[CPUSTATES] = {
854 LAYOUT_CPU_USER,
855 LAYOUT_CPU_NICE,
856 LAYOUT_CPU_SYS,
857 LAYOUT_CPU_INT,
858 LAYOUT_CPU_IDLE
859 };
860
861 ttime = 0;
862 for (state = 0; state < CPUSTATES; ++state)
863 ttime += cur.cp_time[state];
864 if (!ttime)
865 ttime = 1.0;
866
867 printf("%*s", LAYOUT_CPU_GAP - 1, ""); /* the 1 is the next space */
868 for (state = 0; state < CPUSTATES; ++state) {
869 if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) {
870 printf(" %*s", cwidth[state], "");
871 continue;
872 }
873 printf(" %*.0f", cwidth[state],
874 100. * cur.cp_time[state] / ttime);
875 }
876 }
877
878 static void
usage(void)879 usage(void)
880 {
881
882 (void)fprintf(stderr, "usage: iostat [-CDdITXxyz] [-c count] "
883 "[-H height] [-W width] [-w wait] [drives]\n");
884 exit(1);
885 }
886
887 static void
display(int ndrives)888 display(int ndrives)
889 {
890 double etime;
891
892 /* Sum up the elapsed ticks. */
893 etime = cur.cp_etime;
894
895 /*
896 * If we're showing totals only, then don't divide by the
897 * system time.
898 */
899 if (ISSET(todo, SHOW_TOTALS))
900 etime = 1.0;
901
902 if (ISSET(todo, SHOW_STATS_X)) {
903 drive_statsx(ndrives, etime);
904 goto out;
905 }
906
907 if (ISSET(todo, SHOW_STATS_Y)) {
908 drive_statsy(ndrives, etime);
909 goto out;
910 }
911
912 if (ISSET(todo, SHOW_TTY))
913 printf("%*.0f %*.0f",
914 ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN),
915 cur.tk_nin / etime,
916 ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT),
917 cur.tk_nout / etime);
918
919 if (ISSET(todo, SHOW_STATS_1)) {
920 drive_stats(ndrives, etime);
921 }
922
923 if (ISSET(todo, SHOW_STATS_2) || ISSET(todo, SHOW_STATS_3)) {
924 drive_stats2(ndrives, etime);
925 }
926
927 if (ISSET(todo, SHOW_CPU))
928 cpustats();
929
930 (void)printf("\n");
931
932 out:
933 (void)fflush(stdout);
934 }
935
936 static int
selectdrives(int argc,char * argv[],int first)937 selectdrives(int argc, char *argv[], int first)
938 {
939 int i, maxdrives, ndrives, tried;
940
941 /*
942 * Choose drives to be displayed. Priority goes to (in order) drives
943 * supplied as arguments and default drives. If everything isn't
944 * filled in and there are drives not taken care of, display the first
945 * few that fit.
946 *
947 * The backward compatibility #ifdefs permit the syntax:
948 * iostat [ drives ] [ interval [ count ] ]
949 */
950
951 #define BACKWARD_COMPATIBILITY
952 for (tried = ndrives = 0; *argv; ++argv) {
953 #ifdef BACKWARD_COMPATIBILITY
954 if (isdigit((unsigned char)**argv))
955 break;
956 #endif
957 tried++;
958 for (i = 0; i < (int)ndrive; i++) {
959 if (fnmatch(*argv, cur.name[i], 0))
960 continue;
961 cur.select[i] = 1;
962 if (ordersize <= ndrives) {
963 int *new = realloc(order,
964 (ordersize + 8) * sizeof *order);
965 if (new == NULL)
966 break;
967 ordersize += 8;
968 order = new;
969 }
970 order[ndrives++] = i;
971 }
972
973 }
974
975 if (ndrives == 0 && tried == 0) {
976 /*
977 * Pick up to defdrives (or all if -x is given) drives
978 * if none specified.
979 */
980 maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) ||
981 (int)ndrive < defdrives)
982 ? (int)(ndrive) : defdrives;
983 ordersize = maxdrives;
984 free(order);
985 order = calloc(ordersize, sizeof *order);
986 if (order == NULL)
987 errx(1, "Insufficient memory");
988 for (i = 0; i < maxdrives; i++) {
989 cur.select[i] = 1;
990 order[i] = i;
991
992 ++ndrives;
993 if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) &&
994 ndrives == defdrives)
995 break;
996 }
997 }
998
999 #ifdef BACKWARD_COMPATIBILITY
1000 if (first && *argv) {
1001 interval = atoi(*argv);
1002 if (*++argv)
1003 reps = atoi(*argv);
1004 }
1005 #endif
1006
1007 if (interval) {
1008 if (!reps)
1009 reps = -1;
1010 } else
1011 if (reps)
1012 interval = 1;
1013
1014 return (ndrives);
1015 }
1016