1 /* $NetBSD: iostat.c,v 1.67 2018/04/08 11:37:31 mlelstv 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.67 2018/04/08 11:37:31 mlelstv 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 #define MAX(a,b) (((a)>(b))?(a):(b)) 103 104 #define ISSET(x, a) ((x) & (a)) 105 #define SHOW_CPU (1<<0) 106 #define SHOW_TTY (1<<1) 107 #define SHOW_STATS_1 (1<<2) 108 #define SHOW_STATS_2 (1<<3) 109 #define SHOW_STATS_X (1<<4) 110 #define SHOW_STATS_Y (1<<5) 111 #define SHOW_TOTALS (1<<7) 112 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X | SHOW_STATS_Y) 113 114 static void cpustats(void); 115 static double drive_time(double, int); 116 static void drive_stats(double); 117 static void drive_stats2(double); 118 static void drive_statsx(double); 119 static void drive_statsy(double); 120 static void drive_statsy_io(double, double, double); 121 static void drive_statsy_q(double, double, double, double, double, double); 122 static void sig_header(int); 123 static volatile int do_header; 124 static void header(void); 125 __dead static void usage(void); 126 static void display(void); 127 static int selectdrives(int, char *[]); 128 129 int 130 main(int argc, char *argv[]) 131 { 132 int ch, hdrcnt, hdroffset, ndrives, lines; 133 struct timespec tv; 134 struct ttysize ts; 135 136 while ((ch = getopt(argc, argv, "Cc:dDITw:xy")) != -1) 137 switch (ch) { 138 case 'c': 139 if ((reps = atoi(optarg)) <= 0) 140 errx(1, "repetition count <= 0."); 141 break; 142 case 'C': 143 todo |= SHOW_CPU; 144 break; 145 case 'd': 146 todo &= ~SHOW_STATS_ALL; 147 todo |= SHOW_STATS_1; 148 break; 149 case 'D': 150 todo &= ~SHOW_STATS_ALL; 151 todo |= SHOW_STATS_2; 152 break; 153 case 'I': 154 todo |= SHOW_TOTALS; 155 break; 156 case 'T': 157 todo |= SHOW_TTY; 158 break; 159 case 'w': 160 if ((interval = atoi(optarg)) <= 0) 161 errx(1, "interval <= 0."); 162 break; 163 case 'x': 164 todo &= ~SHOW_STATS_ALL; 165 todo |= SHOW_STATS_X; 166 break; 167 case 'y': 168 todo &= ~SHOW_STATS_ALL; 169 todo |= SHOW_STATS_Y; 170 break; 171 case '?': 172 default: 173 usage(); 174 } 175 argc -= optind; 176 argv += optind; 177 178 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL)) 179 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 180 if (ISSET(todo, SHOW_STATS_X)) { 181 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL); 182 todo |= SHOW_STATS_X; 183 } 184 if (ISSET(todo, SHOW_STATS_Y)) { 185 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL | SHOW_TOTALS); 186 todo |= SHOW_STATS_Y; 187 } 188 189 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) { 190 if (ts.ts_lines) 191 winlines = ts.ts_lines; 192 if (ts.ts_cols) 193 wincols = ts.ts_cols; 194 } 195 196 defdrives = wincols; 197 if (ISSET(todo, SHOW_CPU)) 198 defdrives -= 16; /* XXX magic number */ 199 if (ISSET(todo, SHOW_TTY)) 200 defdrives -= 10; /* XXX magic number */ 201 defdrives /= 18; /* XXX magic number */ 202 203 drvinit(0); 204 cpureadstats(); 205 drvreadstats(); 206 ndrives = selectdrives(argc, argv); 207 if (ndrives == 0) { 208 /* No drives are selected. No need to show drive stats. */ 209 todo &= ~SHOW_STATS_ALL; 210 if (todo == 0) 211 errx(1, "no drives"); 212 } 213 tv.tv_sec = interval; 214 tv.tv_nsec = 0; 215 216 /* print a new header on sigcont */ 217 (void)signal(SIGCONT, sig_header); 218 219 for (hdrcnt = 1;;) { 220 if (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y)) { 221 lines = ndrives; 222 hdroffset = 3; 223 } else { 224 lines = 1; 225 hdroffset = 4; 226 } 227 228 if (do_header || (hdrcnt -= lines) <= 0) { 229 do_header = 0; 230 header(); 231 hdrcnt = winlines - hdroffset; 232 } 233 234 if (!ISSET(todo, SHOW_TOTALS)) { 235 cpuswap(); 236 drvswap(); 237 tkswap(); 238 } 239 240 display(); 241 242 if (reps >= 0 && --reps <= 0) 243 break; 244 nanosleep(&tv, NULL); 245 cpureadstats(); 246 drvreadstats(); 247 248 ndrives = selectdrives(argc, argv); 249 } 250 exit(0); 251 } 252 253 static void 254 sig_header(int signo) 255 { 256 do_header = 1; 257 } 258 259 static void 260 header(void) 261 { 262 size_t i; 263 264 /* Main Headers. */ 265 if (ISSET(todo, SHOW_STATS_X)) { 266 if (ISSET(todo, SHOW_TOTALS)) { 267 (void)printf( 268 "device read KB/t xfr time MB "); 269 (void)printf(" write KB/t xfr time MB\n"); 270 } else { 271 (void)printf( 272 "device read KB/t r/s time MB/s"); 273 (void)printf(" write KB/t w/s time MB/s\n"); 274 } 275 return; 276 } 277 278 if (ISSET(todo, SHOW_STATS_Y)) { 279 (void)printf("device read KB/t r/s MB/s write KB/t w/s MB/s"); 280 (void)printf(" wait actv wsvc_t asvc_t wtime time"); 281 (void)printf("\n"); 282 return; 283 } 284 285 if (ISSET(todo, SHOW_TTY)) 286 (void)printf(" tty"); 287 288 if (ISSET(todo, SHOW_STATS_1)) { 289 for (i = 0; i < ndrive; i++) 290 if (cur.select[i]) 291 (void)printf(" %9.9s ", cur.name[i]); 292 } 293 294 if (ISSET(todo, SHOW_STATS_2)) { 295 for (i = 0; i < ndrive; i++) 296 if (cur.select[i]) 297 (void)printf(" %9.9s ", cur.name[i]); 298 } 299 300 if (ISSET(todo, SHOW_CPU)) 301 (void)printf(" CPU"); 302 303 printf("\n"); 304 305 /* Sub-Headers. */ 306 if (ISSET(todo, SHOW_TTY)) 307 printf(" tin tout"); 308 309 if (ISSET(todo, SHOW_STATS_1)) { 310 for (i = 0; i < ndrive; i++) 311 if (cur.select[i]) { 312 if (ISSET(todo, SHOW_TOTALS)) 313 (void)printf(" KB/t xfr MB "); 314 else 315 (void)printf(" KB/t t/s MB/s "); 316 } 317 } 318 319 if (ISSET(todo, SHOW_STATS_2)) { 320 for (i = 0; i < ndrive; i++) 321 if (cur.select[i]) 322 (void)printf(" KB xfr time "); 323 } 324 325 if (ISSET(todo, SHOW_CPU)) 326 (void)printf(" us ni sy in id"); 327 printf("\n"); 328 } 329 330 static double 331 drive_time(double etime, int dn) 332 { 333 if (ISSET(todo, SHOW_TOTALS)) 334 return etime; 335 336 if (cur.timestamp[dn].tv_sec || cur.timestamp[dn].tv_usec) { 337 etime = (double)cur.timestamp[dn].tv_sec + 338 ((double)cur.timestamp[dn].tv_usec / (double)1000000); 339 } 340 341 return etime; 342 } 343 344 static void 345 drive_stats(double etime) 346 { 347 size_t dn; 348 double atime, dtime, mbps; 349 350 for (dn = 0; dn < ndrive; ++dn) { 351 if (!cur.select[dn]) 352 continue; 353 354 dtime = drive_time(etime, dn); 355 356 /* average Kbytes per transfer. */ 357 if (cur.rxfer[dn] + cur.wxfer[dn]) 358 mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) / 359 1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]); 360 else 361 mbps = 0.0; 362 (void)printf(" %5.*f", 363 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 364 365 /* average transfers per second. */ 366 (void)printf(" %4.0f", 367 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); 368 369 /* time busy in drive activity */ 370 atime = (double)cur.time[dn].tv_sec + 371 ((double)cur.time[dn].tv_usec / (double)1000000); 372 373 /* Megabytes per second. */ 374 if (atime != 0.0) 375 mbps = (cur.rbytes[dn] + cur.wbytes[dn]) / 376 (double)(1024 * 1024); 377 else 378 mbps = 0; 379 mbps /= dtime; 380 (void)printf(" %5.*f ", 381 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 382 } 383 } 384 385 static void 386 drive_stats2(double etime) 387 { 388 size_t dn; 389 double atime, dtime; 390 391 for (dn = 0; dn < ndrive; ++dn) { 392 if (!cur.select[dn]) 393 continue; 394 395 dtime = drive_time(etime, dn); 396 397 /* average kbytes per second. */ 398 (void)printf(" %5.0f", 399 (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / dtime); 400 401 /* average transfers per second. */ 402 (void)printf(" %5.0f", 403 (cur.rxfer[dn] + cur.wxfer[dn]) / dtime); 404 405 /* average time busy in drive activity */ 406 atime = (double)cur.time[dn].tv_sec + 407 ((double)cur.time[dn].tv_usec / (double)1000000); 408 (void)printf(" %4.2f ", atime / dtime); 409 } 410 } 411 412 static void 413 drive_statsx(double etime) 414 { 415 size_t dn; 416 double atime, dtime, kbps; 417 418 for (dn = 0; dn < ndrive; ++dn) { 419 if (!cur.select[dn]) 420 continue; 421 422 dtime = drive_time(etime, dn); 423 424 (void)printf("%-8.8s", cur.name[dn]); 425 426 /* average read Kbytes per transfer */ 427 if (cur.rxfer[dn]) 428 kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn]; 429 else 430 kbps = 0.0; 431 (void)printf(" %8.2f", kbps); 432 433 /* average read transfers 434 (per second) */ 435 (void)printf(" %6.0f", cur.rxfer[dn] / dtime); 436 437 /* time read busy in drive activity */ 438 atime = (double)cur.time[dn].tv_sec + 439 ((double)cur.time[dn].tv_usec / (double)1000000); 440 (void)printf(" %6.2f", atime / dtime); 441 442 /* average read megabytes 443 (per second) */ 444 (void)printf(" %8.2f", 445 cur.rbytes[dn] / (1024.0 * 1024) / dtime); 446 447 448 /* average write Kbytes per transfer */ 449 if (cur.wxfer[dn]) 450 kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn]; 451 else 452 kbps = 0.0; 453 (void)printf(" %8.2f", kbps); 454 455 /* average write transfers 456 (per second) */ 457 (void)printf(" %6.0f", cur.wxfer[dn] / dtime); 458 459 /* time write busy in drive activity */ 460 atime = (double)cur.time[dn].tv_sec + 461 ((double)cur.time[dn].tv_usec / (double)1000000); 462 (void)printf(" %6.2f", atime / dtime); 463 464 /* average write megabytes 465 (per second) */ 466 (void)printf(" %8.2f\n", 467 cur.wbytes[dn] / (1024.0 * 1024) / dtime); 468 } 469 } 470 471 static void 472 drive_statsy_io(double elapsed, double count, double volume) 473 { 474 double kbps; 475 476 /* average Kbytes per transfer */ 477 if (count) 478 kbps = (volume / 1024.0) / count; 479 else 480 kbps = 0.0; 481 (void)printf(" %8.2f", kbps); 482 483 /* average transfers (per second) */ 484 (void)printf(" %6.0f", count / elapsed); 485 486 /* average megabytes (per second) */ 487 (void)printf(" %8.2f", volume / (1024.0 * 1024) / elapsed); 488 } 489 490 static void 491 drive_statsy_q(double elapsed, double busy, double wait, double busysum, double waitsum, double count) 492 { 493 /* average wait queue length */ 494 (void)printf(" %6.1f", waitsum / elapsed); 495 496 /* average busy queue length */ 497 (void)printf(" %6.1f", busysum / elapsed); 498 499 /* average wait time */ 500 (void)printf(" %7.2f", count > 0 ? waitsum / count * 1000.0 : 0.0); 501 502 /* average service time */ 503 (void)printf(" %7.2f", count > 0 ? busysum / count * 1000.0 : 0.0); 504 505 /* time waiting for drive activity */ 506 (void)printf(" %6.2f", wait / elapsed); 507 508 /* time busy in drive activity */ 509 (void)printf(" %6.2f", busy / elapsed); 510 } 511 512 static void 513 drive_statsy(double etime) 514 { 515 size_t dn; 516 double atime, await, abusysum, awaitsum, dtime; 517 518 for (dn = 0; dn < ndrive; ++dn) { 519 if (!cur.select[dn]) 520 continue; 521 522 dtime = drive_time(etime, dn); 523 524 (void)printf("%-8.8s", cur.name[dn]); 525 526 atime = (double)cur.time[dn].tv_sec + 527 ((double)cur.time[dn].tv_usec / (double)1000000); 528 await = (double)cur.wait[dn].tv_sec + 529 ((double)cur.wait[dn].tv_usec / (double)1000000); 530 abusysum = (double)cur.busysum[dn].tv_sec + 531 ((double)cur.busysum[dn].tv_usec / (double)1000000); 532 awaitsum = (double)cur.waitsum[dn].tv_sec + 533 ((double)cur.waitsum[dn].tv_usec / (double)1000000); 534 535 drive_statsy_io(dtime, cur.rxfer[dn], cur.rbytes[dn]); 536 (void)printf(" "); 537 drive_statsy_io(dtime, cur.wxfer[dn], cur.wbytes[dn]); 538 drive_statsy_q(dtime, atime, await, abusysum, awaitsum, cur.rxfer[dn]+cur.wxfer[dn]); 539 540 (void)printf("\n"); 541 } 542 } 543 544 static void 545 cpustats(void) 546 { 547 int state; 548 double ttime; 549 550 ttime = 0; 551 for (state = 0; state < CPUSTATES; ++state) 552 ttime += cur.cp_time[state]; 553 if (!ttime) 554 ttime = 1.0; 555 /* States are generally never 100% and can use %3.0f. */ 556 for (state = 0; state < CPUSTATES; ++state) 557 printf(" %2.0f", 100. * cur.cp_time[state] / ttime); 558 } 559 560 static void 561 usage(void) 562 { 563 564 (void)fprintf(stderr, "usage: iostat [-CdDITxy] [-c count] " 565 "[-w wait] [drives]\n"); 566 exit(1); 567 } 568 569 static void 570 display(void) 571 { 572 double etime; 573 574 /* Sum up the elapsed ticks. */ 575 etime = cur.cp_etime; 576 577 /* 578 * If we're showing totals only, then don't divide by the 579 * system time. 580 */ 581 if (ISSET(todo, SHOW_TOTALS)) 582 etime = 1.0; 583 584 if (ISSET(todo, SHOW_STATS_X)) { 585 drive_statsx(etime); 586 goto out; 587 } 588 589 if (ISSET(todo, SHOW_STATS_Y)) { 590 drive_statsy(etime); 591 goto out; 592 } 593 594 if (ISSET(todo, SHOW_TTY)) 595 printf("%4.0f %5.0f", cur.tk_nin / etime, cur.tk_nout / etime); 596 597 if (ISSET(todo, SHOW_STATS_1)) { 598 drive_stats(etime); 599 } 600 601 602 if (ISSET(todo, SHOW_STATS_2)) { 603 drive_stats2(etime); 604 } 605 606 607 if (ISSET(todo, SHOW_CPU)) 608 cpustats(); 609 610 (void)printf("\n"); 611 612 out: 613 (void)fflush(stdout); 614 } 615 616 static int 617 selectdrives(int argc, char *argv[]) 618 { 619 int i, maxdrives, ndrives, tried; 620 621 /* 622 * Choose drives to be displayed. Priority goes to (in order) drives 623 * supplied as arguments and default drives. If everything isn't 624 * filled in and there are drives not taken care of, display the first 625 * few that fit. 626 * 627 * The backward compatibility #ifdefs permit the syntax: 628 * iostat [ drives ] [ interval [ count ] ] 629 */ 630 631 #define BACKWARD_COMPATIBILITY 632 for (tried = ndrives = 0; *argv; ++argv) { 633 #ifdef BACKWARD_COMPATIBILITY 634 if (isdigit((unsigned char)**argv)) 635 break; 636 #endif 637 tried++; 638 for (i = 0; i < (int)ndrive; i++) { 639 if (fnmatch(*argv, cur.name[i], 0)) 640 continue; 641 cur.select[i] = 1; 642 ++ndrives; 643 } 644 645 } 646 647 if (ndrives == 0 && tried == 0) { 648 /* 649 * Pick up to defdrives (or all if -x is given) drives 650 * if none specified. 651 */ 652 maxdrives = (ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) || 653 (int)ndrive < defdrives) 654 ? (int)(ndrive) : defdrives; 655 for (i = 0; i < maxdrives; i++) { 656 cur.select[i] = 1; 657 658 ++ndrives; 659 if (!ISSET(todo, SHOW_STATS_X | SHOW_STATS_Y) && 660 ndrives == defdrives) 661 break; 662 } 663 } 664 665 #ifdef BACKWARD_COMPATIBILITY 666 if (*argv) { 667 interval = atoi(*argv); 668 if (*++argv) 669 reps = atoi(*argv); 670 } 671 #endif 672 673 if (interval) { 674 if (!reps) 675 reps = -1; 676 } else 677 if (reps) 678 interval = 1; 679 680 return (ndrives); 681 } 682