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