1 /* $NetBSD: iostat.c,v 1.71 2023/07/28 12:03:33 wiz 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.71 2023/07/28 12:03:33 wiz 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 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_3 | SHOW_STATS_Y)) { 361 lines = ndrives; 362 hdroffset = 3; 363 } else { 364 lines = 1; 365 hdroffset = 4; 366 } 367 368 if (do_header || (winlines != 0 && (hdrcnt -= lines) <= 0)) { 369 do_header = 0; 370 header(ndrives); 371 hdrcnt = winlines - hdroffset; 372 } 373 374 if (!ISSET(todo, SHOW_TOTALS) || ISSET(todo, SHOW_NEW_TOTALS)) { 375 cpuswap(); 376 drvswap(); 377 tkswap(); 378 todo &= ~SHOW_NEW_TOTALS; 379 } 380 381 display(ndrives); 382 383 if (reps >= 0 && --reps <= 0) 384 break; 385 nanosleep(&tv, NULL); 386 cpureadstats(); 387 drvreadstats(); 388 389 ndrives = selectdrives(argc, argv, 0); 390 } 391 exit(0); 392 } 393 394 static void 395 sig_header(int signo) 396 { 397 do_header = 1; 398 } 399 400 static void 401 header(int ndrives) 402 { 403 int i; 404 405 /* Main Headers. */ 406 if (ISSET(todo, SHOW_STATS_X)) { 407 if (ISSET(todo, SHOW_TOTALS)) { 408 (void)printf( 409 "device read KB/t xfr time MB "); 410 (void)printf(" write KB/t xfr time MB\n"); 411 } else { 412 (void)printf( 413 "device read KB/t r/s time MB/s"); 414 (void)printf(" write KB/t w/s time MB/s\n"); 415 } 416 return; 417 } 418 419 if (ISSET(todo, SHOW_STATS_Y)) { 420 (void)printf("device read KB/t r/s MB/s write KB/t w/s MB/s"); 421 (void)printf(" wait actv wsvc_t asvc_t wtime time"); 422 (void)printf("\n"); 423 return; 424 } 425 426 if (ISSET(todo, SHOW_TTY)) 427 (void)printf("%*s", LAYOUT_TTY_GAP + LAYOUT_TTY, "tty"); 428 429 if (ISSET(todo, SHOW_STATS_1)) { 430 for (i = 0; i < ndrives; i++) { 431 char *dname = cur.name[order[i]]; 432 int dnlen = (int)strlen(dname); 433 434 printf(" "); /* always a 1 column gap */ 435 if (dnlen < LAYOUT_DRIVE_1 - 6) 436 printf("|%-*.*s ", 437 (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1, 438 (LAYOUT_DRIVE_1 - 1 - dnlen - 1) / 2 - 1, 439 Line_Marker); 440 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ? 441 MIN(MAX((LAYOUT_DRIVE_1 - dnlen) / 2, 0), 442 LAYOUT_DRIVE_1) : 0), 443 LAYOUT_DRIVE_1, dname); 444 if (dnlen < LAYOUT_DRIVE_1 - 6) 445 printf(" %*.*s|", 446 (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1, 447 (LAYOUT_DRIVE_1 - 1 - dnlen - 2) / 2 - 1, 448 Line_Marker); 449 } 450 } 451 452 if (ISSET(todo, SHOW_STATS_2)) { 453 for (i = 0; i < ndrives; i++) { 454 char *dname = cur.name[order[i]]; 455 int dnlen = (int)strlen(dname); 456 457 printf(" "); /* always a 1 column gap */ 458 if (dnlen < LAYOUT_DRIVE_2 - 6) 459 printf("|%-*.*s ", 460 (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1, 461 (LAYOUT_DRIVE_2 - 1 - dnlen - 1) / 2 - 1, 462 Line_Marker); 463 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ? 464 MIN(MAX((LAYOUT_DRIVE_2 - dnlen) / 2, 0), 465 LAYOUT_DRIVE_2) : 0), 466 LAYOUT_DRIVE_1, dname); 467 if (dnlen < LAYOUT_DRIVE_2 - 6) 468 printf(" %*.*s|", 469 (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1, 470 (LAYOUT_DRIVE_2 - 1 - dnlen - 2) / 2 - 1, 471 Line_Marker); 472 } 473 } 474 475 if (ISSET(todo, SHOW_STATS_3)) { 476 for (i = 0; i < ndrives; i++) { 477 char *dname = cur.name[order[i]]; 478 int dnlen = (int)strlen(dname); 479 480 printf(" "); /* always a 1 column gap */ 481 if (dnlen < LAYOUT_DRIVE_3 - 6) 482 printf("|%-*.*s ", 483 (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1, 484 (LAYOUT_DRIVE_3 - 1 - dnlen - 1) / 2 - 1, 485 Line_Marker); 486 printf("%*.*s", ((dnlen >= LAYOUT_DRIVE_1 - 6) ? 487 MIN(MAX((LAYOUT_DRIVE_3 - dnlen) / 2, 0), 488 LAYOUT_DRIVE_3) : 0), 489 LAYOUT_DRIVE_1, dname); 490 if (dnlen < LAYOUT_DRIVE_3 - 6) 491 printf(" %*.*s|", 492 (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1, 493 (LAYOUT_DRIVE_3 - 1 - dnlen - 2) / 2 - 1, 494 Line_Marker); 495 } 496 } 497 498 if (ISSET(todo, SHOW_CPU)) 499 (void)printf("%*s", LAYOUT_CPU + LAYOUT_CPU_GAP, "CPU"); 500 501 printf("\n"); 502 503 /* Sub-Headers. */ 504 if (ISSET(todo, SHOW_TTY)) { 505 printf("%*s %*s", 506 ((todo&SHOW_TOTALS)?LAYOUT_TTY_TIN:LAYOUT_TTY_IN), "tin", 507 ((todo&SHOW_TOTALS)?LAYOUT_TTY_TOUT:LAYOUT_TTY_OUT), "tout"); 508 } 509 510 if (ISSET(todo, SHOW_STATS_1)) { 511 for (i = 0; i < ndrives; i++) { 512 if (ISSET(todo, SHOW_TOTALS)) { 513 (void)printf(" %*s %*s %*s", 514 LAYOUT_DRIVE_1_XFER, "xfr", 515 LAYOUT_DRIVE_1_XSIZE, "KB/t", 516 LAYOUT_DRIVE_1_VOLUME, "MB"); 517 } else { 518 (void)printf(" %*s %*s %*s", 519 LAYOUT_DRIVE_1_RATE, "t/s", 520 LAYOUT_DRIVE_1_XSIZE, "KB/t", 521 LAYOUT_DRIVE_1_SPEED, "MB/s"); 522 } 523 } 524 } 525 526 if (ISSET(todo, SHOW_STATS_2)) { 527 for (i = 0; i < ndrives; i++) { 528 if (ISSET(todo, SHOW_TOTALS)) { 529 (void)printf(" %*s %*s %*s", 530 LAYOUT_DRIVE_2_TXFR, "xfr", 531 LAYOUT_DRIVE_2_VOLUME, "KB", 532 LAYOUT_DRIVE_2_TBUSY, "time"); 533 } else { 534 (void)printf(" %*s %*s %*s", 535 LAYOUT_DRIVE_2_XFR, "xfr", 536 LAYOUT_DRIVE_2_XSIZE, "KB", 537 LAYOUT_DRIVE_2_BUSY, "time"); 538 } 539 } 540 } 541 542 if (ISSET(todo, SHOW_STATS_3)) { 543 for (i = 0; i < ndrives; i++) { 544 if (ISSET(todo, SHOW_TOTALS)) { 545 (void)printf(" %*s %*s", 546 LAYOUT_DRIVE_2_VOLUME, "MB/s", 547 LAYOUT_DRIVE_2_TBUSY, "time"); 548 } else { 549 (void)printf(" %*s %*s", 550 LAYOUT_DRIVE_2_XSIZE, "MB/s", 551 LAYOUT_DRIVE_2_BUSY, "time"); 552 } 553 } 554 } 555 556 /* should do this properly, but it is such a simple case... */ 557 if (ISSET(todo, SHOW_CPU)) 558 (void)printf(" us ni sy in id"); 559 printf("\n"); 560 } 561 562 static double 563 drive_time(double etime, int dn) 564 { 565 if (ISSET(todo, SHOW_TOTALS)) 566 return etime; 567 568 if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) { 569 etime = (double)cur.timestamp[dn].tv_sec + 570 ((double)cur.timestamp[dn].tv_usec / (double)1000000); 571 } 572 573 return etime; 574 } 575 576 static void 577 drive_stats(int ndrives, double etime) 578 { 579 int drive; 580 double atime, dtime, mbps; 581 int c1, c2, c3; 582 583 if (ISSET(todo, SHOW_TOTALS)) { 584 c1 = LAYOUT_DRIVE_1_XFER; 585 c2 = LAYOUT_DRIVE_1_XSIZE; 586 c3 = LAYOUT_DRIVE_1_VOLUME; 587 } else { 588 c1 = LAYOUT_DRIVE_1_RATE; 589 c2 = LAYOUT_DRIVE_1_XSIZE; 590 c3 = LAYOUT_DRIVE_1_SPEED; 591 } 592 593 for (drive = 0; drive < ndrives; ++drive) { 594 int dn = order[drive]; 595 596 if (!cur.select[dn]) /* should be impossible */ 597 continue; 598 599 if (todo & SUPPRESS_ZERO) { 600 if (cur.rxfer[dn] == 0 && 601 cur.wxfer[dn] == 0 && 602 cur.rbytes[dn] == 0 && 603 cur.wbytes[dn] == 0) { 604 printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, ""); 605 continue; 606 } 607 } 608 609 dtime = drive_time(etime, dn); 610 611 /* average transfers per second. */ 612 (void)printf(" %*.0f", c1, 613 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); 614 615 /* average Kbytes per transfer. */ 616 if (cur.rxfer[dn] + cur.wxfer[dn]) 617 mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) / 618 1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]); 619 else 620 mbps = 0.0; 621 (void)printf(" %*.*f", c2, 622 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 623 624 /* time busy in drive activity */ 625 atime = (double)cur.time[dn].tv_sec + 626 ((double)cur.time[dn].tv_usec / (double)1000000); 627 628 /* Megabytes per second. */ 629 if (atime != 0.0) 630 mbps = (cur.rbytes[dn] + cur.wbytes[dn]) / 631 (double)(1024 * 1024); 632 else 633 mbps = 0; 634 mbps /= dtime; 635 (void)printf(" %*.*f", c3, 636 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 637 } 638 } 639 640 static void 641 drive_stats2(int ndrives, double etime) 642 { 643 int drive; 644 double atime, dtime; 645 int c1, c2, c3; 646 647 if (ISSET(todo, SHOW_TOTALS)) { 648 c1 = LAYOUT_DRIVE_2_TXFR; 649 c2 = LAYOUT_DRIVE_2_VOLUME; 650 c3 = LAYOUT_DRIVE_2_TBUSY; 651 } else { 652 c1 = LAYOUT_DRIVE_2_XFR; 653 c2 = LAYOUT_DRIVE_2_XSIZE; 654 c3 = LAYOUT_DRIVE_2_BUSY; 655 } 656 657 for (drive = 0; drive < ndrives; ++drive) { 658 int dn = order[drive]; 659 660 if (!cur.select[dn]) /* should be impossible */ 661 continue; 662 663 if (todo & SUPPRESS_ZERO) { 664 if (cur.rxfer[dn] == 0 && 665 cur.wxfer[dn] == 0 && 666 cur.rbytes[dn] == 0 && 667 cur.wbytes[dn] == 0) { 668 printf("%*s", c1 + 1 + c2 + 1 + c3 + 1, ""); 669 continue; 670 } 671 } 672 673 dtime = drive_time(etime, dn); 674 675 /* average transfers per second. */ 676 if (ISSET(todo, SHOW_STATS_2)) { 677 (void)printf(" %*.0f", c1, 678 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); 679 } 680 681 /* average mbytes per second. */ 682 (void)printf(" %*.0f", c2, 683 (cur.rbytes[dn] + cur.wbytes[dn]) / 684 (double)(1024 * 1024) / dtime); 685 686 /* average time busy in dn activity */ 687 atime = (double)cur.time[dn].tv_sec + 688 ((double)cur.time[dn].tv_usec / (double)1000000); 689 (void)printf(" %*.2f", c3, atime / dtime); 690 } 691 } 692 693 static void 694 drive_statsx(int ndrives, double etime) 695 { 696 int dn, drive; 697 double atime, dtime, kbps; 698 699 for (drive = 0; drive < ndrives; ++drive) { 700 dn = order[drive]; 701 702 if (!cur.select[dn]) /* impossible */ 703 continue; 704 705 (void)printf("%-8.8s", cur.name[dn]); 706 707 if (todo & SUPPRESS_ZERO) { 708 if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 && 709 cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) { 710 printf("\n"); 711 continue; 712 } 713 } 714 715 dtime = drive_time(etime, dn); 716 717 /* average read Kbytes per transfer */ 718 if (cur.rxfer[dn]) 719 kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn]; 720 else 721 kbps = 0.0; 722 (void)printf(" %8.2f", kbps); 723 724 /* average read transfers 725 (per second) */ 726 (void)printf(" %6.0f", cur.rxfer[dn] / dtime); 727 728 /* time read busy in drive activity */ 729 atime = (double)cur.time[dn].tv_sec + 730 ((double)cur.time[dn].tv_usec / (double)1000000); 731 (void)printf(" %6.2f", atime / dtime); 732 733 /* average read megabytes 734 (per second) */ 735 (void)printf(" %8.2f", 736 cur.rbytes[dn] / (1024.0 * 1024) / dtime); 737 738 739 /* average write Kbytes per transfer */ 740 if (cur.wxfer[dn]) 741 kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn]; 742 else 743 kbps = 0.0; 744 (void)printf(" %8.2f", kbps); 745 746 /* average write transfers 747 (per second) */ 748 (void)printf(" %6.0f", cur.wxfer[dn] / dtime); 749 750 /* time write busy in drive activity */ 751 atime = (double)cur.time[dn].tv_sec + 752 ((double)cur.time[dn].tv_usec / (double)1000000); 753 (void)printf(" %6.2f", atime / dtime); 754 755 /* average write megabytes 756 (per second) */ 757 (void)printf(" %8.2f\n", 758 cur.wbytes[dn] / (1024.0 * 1024) / dtime); 759 } 760 } 761 762 static void 763 drive_statsy_io(double elapsed, double count, double volume) 764 { 765 double kbps; 766 767 /* average Kbytes per transfer */ 768 if (count) 769 kbps = (volume / 1024.0) / count; 770 else 771 kbps = 0.0; 772 (void)printf(" %8.2f", kbps); 773 774 /* average transfers (per second) */ 775 (void)printf(" %6.0f", count / elapsed); 776 777 /* average megabytes (per second) */ 778 (void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed); 779 } 780 781 static void 782 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count) 783 { 784 /* average wait queue length */ 785 (void)printf(" %6.1f", waitsum / elapsed); 786 787 /* average busy queue length */ 788 (void)printf(" %6.1f", busysum / elapsed); 789 790 /* average wait time */ 791 (void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0); 792 793 /* average service time */ 794 (void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0); 795 796 /* time waiting for drive activity */ 797 (void)printf(" %6.2f", wait / elapsed); 798 799 /* time busy in drive activity */ 800 (void)printf(" %6.2f", busy / elapsed); 801 } 802 803 static void 804 drive_statsy(int ndrives, double etime) 805 { 806 int drive, dn; 807 double atime, await, abusysum, awaitsum, dtime; 808 809 for (drive = 0; drive < ndrives; ++drive) { 810 dn = order[drive]; 811 if (!cur.select[dn]) /* impossible */ 812 continue; 813 814 (void)printf("%-8.8s", cur.name[dn]); 815 816 if (todo & SUPPRESS_ZERO) { 817 if (cur.rbytes[dn] == 0 && cur.rxfer[dn] == 0 && 818 cur.wbytes[dn] == 0 && cur.wxfer[dn] == 0) { 819 printf("\n"); 820 continue; 821 } 822 } 823 824 dtime = drive_time(etime, dn); 825 826 atime = (double)cur.time[dn].tv_sec + 827 ((double)cur.time[dn].tv_usec / (double)1000000); 828 await = (double)cur.wait[dn].tv_sec + 829 ((double)cur.wait[dn].tv_usec / (double)1000000); 830 abusysum = (double)cur.busysum[dn].tv_sec + 831 ((double)cur.busysum[dn].tv_usec / (double)1000000); 832 awaitsum = (double)cur.waitsum[dn].tv_sec + 833 ((double)cur.waitsum[dn].tv_usec / (double)1000000); 834 835 drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]); 836 (void)printf(" "); 837 drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]); 838 drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]); 839 840 (void)printf("\n"); 841 } 842 } 843 844 static void 845 cpustats(void) 846 { 847 int state; 848 double ttime; 849 850 static int cwidth[CPUSTATES] = { 851 LAYOUT_CPU_USER, 852 LAYOUT_CPU_NICE, 853 LAYOUT_CPU_SYS, 854 LAYOUT_CPU_INT, 855 LAYOUT_CPU_IDLE 856 }; 857 858 ttime = 0; 859 for (state = 0; state < CPUSTATES; ++state) 860 ttime += cur.cp_time[state]; 861 if (!ttime) 862 ttime = 1.0; 863 864 printf("%*s", LAYOUT_CPU_GAP - 1, ""); /* the 1 is the next space */ 865 for (state = 0; state < CPUSTATES; ++state) { 866 if ((todo & SUPPRESS_ZERO) && cur.cp_time[state] == 0) { 867 printf(" %*s", cwidth[state], ""); 868 continue; 869 } 870 printf(" %*.0f", cwidth[state], 871 100. * cur.cp_time[state] / ttime); 872 } 873 } 874 875 static void 876 usage(void) 877 { 878 879 (void)fprintf(stderr, "usage: iostat [-CDdITXxyz] [-c count] " 880 "[-H height] [-W width] [-w wait] [drives]\n"); 881 exit(1); 882 } 883 884 static void 885 display(int ndrives) 886 { 887 double etime; 888 889 /* Sum up the elapsed ticks. */ 890 etime = cur.cp_etime; 891 892 /* 893 * If we're showing totals only, then don't divide by the 894 * system time. 895 */ 896 if (ISSET(todo, SHOW_TOTALS)) 897 etime = 1.0; 898 899 if (ISSET(todo, SHOW_STATS_X)) { 900 drive_statsx(ndrives, etime); 901 goto out; 902 } 903 904 if (ISSET(todo, SHOW_STATS_Y)) { 905 drive_statsy(ndrives, etime); 906 goto out; 907 } 908 909 if (ISSET(todo, SHOW_TTY)) 910 printf("%*.0f %*.0f", 911 ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TIN : LAYOUT_TTY_IN), 912 cur.tk_nin / etime, 913 ((todo & SHOW_TOTALS) ? LAYOUT_TTY_TOUT : LAYOUT_TTY_OUT), 914 cur.tk_nout / etime); 915 916 if (ISSET(todo, SHOW_STATS_1)) { 917 drive_stats(ndrives, etime); 918 } 919 920 if (ISSET(todo, SHOW_STATS_2) || ISSET(todo, SHOW_STATS_3)) { 921 drive_stats2(ndrives, etime); 922 } 923 924 if (ISSET(todo, SHOW_CPU)) 925 cpustats(); 926 927 (void)printf("\n"); 928 929 out: 930 (void)fflush(stdout); 931 } 932 933 static int 934 selectdrives(int argc, char *argv[], int first) 935 { 936 int i, maxdrives, ndrives, tried; 937 938 /* 939 * Choose drives to be displayed. Priority goes to (in order) drives 940 * supplied as arguments and default drives. If everything isn't 941 * filled in and there are drives not taken care of, display the first 942 * few that fit. 943 * 944 * The backward compatibility #ifdefs permit the syntax: 945 * iostat [ drives ] [ interval [ count ] ] 946 */ 947 948 #define BACKWARD_COMPATIBILITY 949 for (tried = ndrives = 0; *argv; ++argv) { 950 #ifdef BACKWARD_COMPATIBILITY 951 if (isdigit((unsigned char)**argv)) 952 break; 953 #endif 954 tried++; 955 for (i = 0; i < (int)ndrive; i++) { 956 if (fnmatch(*argv, cur.name[i], 0)) 957 continue; 958 cur.select[i] = 1; 959 if (ordersize <= ndrives) { 960 int *new = realloc(order, 961 (ordersize + 8) * sizeof *order); 962 if (new == NULL) 963 break; 964 ordersize += 8; 965 order = new; 966 } 967 order[ndrives++] = i; 968 } 969 970 } 971 972 if (ndrives == 0 && tried == 0) { 973 /* 974 * Pick up to defdrives (or all if -x is given) drives 975 * if none specified. 976 */ 977 maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) || 978 (int)ndrive < defdrives) 979 ? (int)(ndrive) : defdrives; 980 ordersize = maxdrives; 981 free(order); 982 order = calloc(ordersize, sizeof *order); 983 if (order == NULL) 984 errx(1, "Insufficient memory"); 985 for (i = 0; i < maxdrives; i++) { 986 cur.select[i] = 1; 987 order[i] = i; 988 989 ++ndrives; 990 if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) && 991 ndrives == defdrives) 992 break; 993 } 994 } 995 996 #ifdef BACKWARD_COMPATIBILITY 997 if (first && *argv) { 998 interval = atoi(*argv); 999 if (*++argv) 1000 reps = atoi(*argv); 1001 } 1002 #endif 1003 1004 if (interval) { 1005 if (!reps) 1006 reps = -1; 1007 } else 1008 if (reps) 1009 interval = 1; 1010 1011 return (ndrives); 1012 } 1013