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 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 398 sig_header(int signo) 399 { 400 do_header = 1; 401 } 402 403 static void 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 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 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 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 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 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 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 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 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 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 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 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