1 /* 2 * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 /* 31 * Parts of this program are derived from the original FreeBSD iostat 32 * program: 33 */ 34 /*- 35 * Copyright (c) 1986, 1991, 1993 36 * The Regents of the University of California. All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 4. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 /* 63 * Ideas for the new iostat statistics output modes taken from the NetBSD 64 * version of iostat: 65 */ 66 /* 67 * Copyright (c) 1996 John M. Vinopal 68 * All rights reserved. 69 * 70 * Redistribution and use in source and binary forms, with or without 71 * modification, are permitted provided that the following conditions 72 * are met: 73 * 1. Redistributions of source code must retain the above copyright 74 * notice, this list of conditions and the following disclaimer. 75 * 2. Redistributions in binary form must reproduce the above copyright 76 * notice, this list of conditions and the following disclaimer in the 77 * documentation and/or other materials provided with the distribution. 78 * 3. All advertising materials mentioning features or use of this software 79 * must display the following acknowledgement: 80 * This product includes software developed for the NetBSD Project 81 * by John M. Vinopal. 82 * 4. The name of the author may not be used to endorse or promote products 83 * derived from this software without specific prior written permission. 84 * 85 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 86 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 87 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 88 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 89 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 90 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 91 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 92 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 93 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 94 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 95 * SUCH DAMAGE. 96 */ 97 98 99 #include <sys/param.h> 100 #include <sys/errno.h> 101 #include <sys/resource.h> 102 #include <sys/sysctl.h> 103 104 #include <ctype.h> 105 #include <devstat.h> 106 #include <err.h> 107 #include <fcntl.h> 108 #include <inttypes.h> 109 #include <kvm.h> 110 #include <limits.h> 111 #include <math.h> 112 #include <nlist.h> 113 #include <stdio.h> 114 #include <stdlib.h> 115 #include <string.h> 116 #include <termios.h> 117 #include <unistd.h> 118 119 struct nlist namelist[] = { 120 #define X_TTY_NIN 0 121 { "_tty_nin" }, 122 #define X_TTY_NOUT 1 123 { "_tty_nout" }, 124 #define X_BOOTTIME 2 125 { "_boottime" }, 126 #define X_END 2 127 { NULL }, 128 }; 129 130 #define IOSTAT_DEFAULT_ROWS 20 /* Traditional default `wrows' */ 131 132 struct statinfo cur, last; 133 int num_devices; 134 struct device_selection *dev_select; 135 int maxshowdevs; 136 volatile sig_atomic_t headercount; 137 volatile sig_atomic_t wresized; /* Tty resized, when non-zero. */ 138 unsigned short wrows; /* Current number of tty rows. */ 139 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0; 140 int xflag = 0, zflag = 0; 141 142 /* local function declarations */ 143 static void usage(void); 144 static void needhdr(int signo); 145 static void needresize(int signo); 146 static void doresize(void); 147 static void phdr(void); 148 static void devstats(int perf_select, long double etime, int havelast); 149 static void cpustats(void); 150 static int readvar(kvm_t *kd, const char *name, int nlid, void *ptr, 151 size_t len); 152 153 static void 154 usage(void) 155 { 156 /* 157 * We also support the following 'traditional' syntax: 158 * iostat [drives] [wait [count]] 159 * This isn't mentioned in the man page, or the usage statement, 160 * but it is supported. 161 */ 162 fprintf(stderr, "usage: iostat [-CdhIKoTxz?] [-c count] [-M core]" 163 " [-n devs] [-N system]\n" 164 "\t [-t type,if,pass] [-w wait] [drives]\n"); 165 } 166 167 int 168 main(int argc, char **argv) 169 { 170 int c, i; 171 int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0; 172 int count = 0, waittime = 0; 173 char *memf = NULL, *nlistf = NULL; 174 struct devstat_match *matches; 175 int num_matches = 0; 176 char errbuf[_POSIX2_LINE_MAX]; 177 kvm_t *kd = NULL; 178 long generation; 179 int num_devices_specified; 180 int num_selected, num_selections; 181 long select_generation; 182 char **specified_devices; 183 devstat_select_mode select_mode; 184 float f; 185 int havelast = 0; 186 187 matches = NULL; 188 maxshowdevs = 3; 189 190 while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:xz?")) != -1) { 191 switch(c) { 192 case 'c': 193 cflag++; 194 count = atoi(optarg); 195 if (count < 1) 196 errx(1, "count %d is < 1", count); 197 break; 198 case 'C': 199 Cflag++; 200 break; 201 case 'd': 202 dflag++; 203 break; 204 case 'h': 205 hflag++; 206 break; 207 case 'I': 208 Iflag++; 209 break; 210 case 'K': 211 Kflag++; 212 break; 213 case 'M': 214 memf = optarg; 215 break; 216 case 'n': 217 nflag++; 218 maxshowdevs = atoi(optarg); 219 if (maxshowdevs < 0) 220 errx(1, "number of devices %d is < 0", 221 maxshowdevs); 222 break; 223 case 'N': 224 nlistf = optarg; 225 break; 226 case 'o': 227 oflag++; 228 break; 229 case 't': 230 tflag++; 231 if (devstat_buildmatch(optarg, &matches, 232 &num_matches) != 0) 233 errx(1, "%s", devstat_errbuf); 234 break; 235 case 'T': 236 Tflag++; 237 break; 238 case 'w': 239 wflag++; 240 f = atof(optarg); 241 waittime = f * 1000; 242 if (waittime < 1) 243 errx(1, "wait time is < 1ms"); 244 break; 245 case 'x': 246 xflag++; 247 break; 248 case 'z': 249 zflag++; 250 break; 251 default: 252 usage(); 253 exit(1); 254 break; 255 } 256 } 257 258 argc -= optind; 259 argv += optind; 260 261 if (nlistf != NULL || memf != NULL) { 262 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 263 264 if (kd == NULL) 265 errx(1, "kvm_openfiles: %s", errbuf); 266 267 if (kvm_nlist(kd, namelist) == -1) 268 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 269 } 270 271 /* 272 * Make sure that the userland devstat version matches the kernel 273 * devstat version. If not, exit and print a message informing 274 * the user of his mistake. 275 */ 276 if (devstat_checkversion(kd) < 0) 277 errx(1, "%s", devstat_errbuf); 278 279 /* 280 * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is 281 * greater than 0, they may be 0 or non-zero. 282 */ 283 if (dflag == 0 && xflag == 0) { 284 Cflag = 1; 285 Tflag = 1; 286 } 287 288 /* find out how many devices we have */ 289 if ((num_devices = devstat_getnumdevs(kd)) < 0) 290 err(1, "can't get number of devices"); 291 292 /* 293 * Figure out how many devices we should display. 294 */ 295 if (nflag == 0) { 296 if (xflag > 0) 297 maxshowdevs = num_devices; 298 else if (oflag > 0) { 299 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) 300 maxshowdevs = 5; 301 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) 302 maxshowdevs = 5; 303 else 304 maxshowdevs = 4; 305 } else { 306 if ((dflag > 0) && (Cflag == 0)) 307 maxshowdevs = 4; 308 else 309 maxshowdevs = 3; 310 } 311 } 312 313 cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 314 if (cur.dinfo == NULL) 315 err(1, "calloc failed"); 316 317 last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 318 if (last.dinfo == NULL) 319 err(1, "calloc failed"); 320 321 /* 322 * Grab all the devices. We don't look to see if the list has 323 * changed here, since it almost certainly has. We only look for 324 * errors. 325 */ 326 if (devstat_getdevs(kd, &cur) == -1) 327 errx(1, "%s", devstat_errbuf); 328 329 num_devices = cur.dinfo->numdevs; 330 generation = cur.dinfo->generation; 331 332 /* 333 * If the user specified any devices on the command line, see if 334 * they are in the list of devices we have now. 335 */ 336 specified_devices = (char **)malloc(sizeof(char *)); 337 if (specified_devices == NULL) 338 err(1, "malloc failed"); 339 340 for (num_devices_specified = 0; *argv; ++argv) { 341 if (isdigit(**argv)) 342 break; 343 num_devices_specified++; 344 specified_devices = (char **)realloc(specified_devices, 345 sizeof(char *) * 346 num_devices_specified); 347 if (specified_devices == NULL) 348 err(1, "realloc failed"); 349 350 specified_devices[num_devices_specified - 1] = *argv; 351 352 } 353 if (nflag == 0 && maxshowdevs < num_devices_specified) 354 maxshowdevs = num_devices_specified; 355 356 dev_select = NULL; 357 358 if ((num_devices_specified == 0) && (num_matches == 0)) 359 select_mode = DS_SELECT_ADD; 360 else 361 select_mode = DS_SELECT_ONLY; 362 363 /* 364 * At this point, selectdevs will almost surely indicate that the 365 * device list has changed, so we don't look for return values of 0 366 * or 1. If we get back -1, though, there is an error. 367 */ 368 if (devstat_selectdevs(&dev_select, &num_selected, 369 &num_selections, &select_generation, generation, 370 cur.dinfo->devices, num_devices, matches, 371 num_matches, specified_devices, 372 num_devices_specified, select_mode, maxshowdevs, 373 hflag) == -1) 374 errx(1, "%s", devstat_errbuf); 375 376 /* 377 * Look for the traditional wait time and count arguments. 378 */ 379 if (*argv) { 380 f = atof(*argv); 381 waittime = f * 1000; 382 383 /* Let the user know he goofed, but keep going anyway */ 384 if (wflag != 0) 385 warnx("discarding previous wait interval, using" 386 " %g instead", waittime / 1000.0); 387 wflag++; 388 389 if (*++argv) { 390 count = atoi(*argv); 391 if (cflag != 0) 392 warnx("discarding previous count, using %d" 393 " instead", count); 394 cflag++; 395 } else 396 count = -1; 397 } 398 399 /* 400 * If the user specified a count, but not an interval, we default 401 * to an interval of 1 second. 402 */ 403 if ((wflag == 0) && (cflag > 0)) 404 waittime = 1 * 1000; 405 406 /* 407 * If the user specified a wait time, but not a count, we want to 408 * go on ad infinitum. This can be redundant if the user uses the 409 * traditional method of specifying the wait, since in that case we 410 * already set count = -1 above. Oh well. 411 */ 412 if ((wflag > 0) && (cflag == 0)) 413 count = -1; 414 415 bzero(cur.cp_time, sizeof(cur.cp_time)); 416 cur.tk_nout = 0; 417 cur.tk_nin = 0; 418 419 /* 420 * Set the snap time to the system boot time (ie: zero), so the 421 * stats are calculated since system boot. 422 */ 423 cur.snap_time = 0; 424 425 /* 426 * If the user stops the program (control-Z) and then resumes it, 427 * print out the header again. 428 */ 429 (void)signal(SIGCONT, needhdr); 430 431 /* 432 * If our standard output is a tty, then install a SIGWINCH handler 433 * and set wresized so that our first iteration through the main 434 * iostat loop will peek at the terminal's current rows to find out 435 * how many lines can fit in a screenful of output. 436 */ 437 if (isatty(fileno(stdout)) != 0) { 438 wresized = 1; 439 (void)signal(SIGWINCH, needresize); 440 } else { 441 wresized = 0; 442 wrows = IOSTAT_DEFAULT_ROWS; 443 } 444 445 for (headercount = 1;;) { 446 struct devinfo *tmp_dinfo; 447 long tmp; 448 long double etime; 449 450 if (Tflag > 0) { 451 if ((readvar(kd, "kern.tty_nin", X_TTY_NIN, &cur.tk_nin, 452 sizeof(cur.tk_nin)) != 0) 453 || (readvar(kd, "kern.tty_nout", X_TTY_NOUT, 454 &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) { 455 Tflag = 0; 456 warnx("disabling TTY statistics"); 457 } 458 } 459 460 if (Cflag > 0) { 461 if (kd == NULL) { 462 if (readvar(kd, "kern.cp_time", 0, 463 &cur.cp_time, sizeof(cur.cp_time)) != 0) 464 Cflag = 0; 465 } else { 466 if (kvm_getcptime(kd, cur.cp_time) < 0) { 467 warnx("kvm_getcptime: %s", 468 kvm_geterr(kd)); 469 Cflag = 0; 470 } 471 } 472 if (Cflag == 0) 473 warnx("disabling CPU time statistics"); 474 } 475 476 if (!--headercount) { 477 phdr(); 478 if (wresized != 0) 479 doresize(); 480 headercount = wrows; 481 } 482 483 tmp_dinfo = last.dinfo; 484 last.dinfo = cur.dinfo; 485 cur.dinfo = tmp_dinfo; 486 487 last.snap_time = cur.snap_time; 488 489 /* 490 * Here what we want to do is refresh our device stats. 491 * devstat_getdevs() returns 1 when the device list has changed. 492 * If the device list has changed, we want to go through 493 * the selection process again, in case a device that we 494 * were previously displaying has gone away. 495 */ 496 switch (devstat_getdevs(kd, &cur)) { 497 case -1: 498 errx(1, "%s", devstat_errbuf); 499 break; 500 case 1: { 501 int retval; 502 503 num_devices = cur.dinfo->numdevs; 504 generation = cur.dinfo->generation; 505 retval = devstat_selectdevs(&dev_select, &num_selected, 506 &num_selections, 507 &select_generation, 508 generation, 509 cur.dinfo->devices, 510 num_devices, matches, 511 num_matches, 512 specified_devices, 513 num_devices_specified, 514 select_mode, maxshowdevs, 515 hflag); 516 switch(retval) { 517 case -1: 518 errx(1, "%s", devstat_errbuf); 519 break; 520 case 1: 521 phdr(); 522 if (wresized != 0) 523 doresize(); 524 headercount = wrows; 525 break; 526 default: 527 break; 528 } 529 break; 530 } 531 default: 532 break; 533 } 534 535 /* 536 * We only want to re-select devices if we're in 'top' 537 * mode. This is the only mode where the devices selected 538 * could actually change. 539 */ 540 if (hflag > 0) { 541 int retval; 542 retval = devstat_selectdevs(&dev_select, &num_selected, 543 &num_selections, 544 &select_generation, 545 generation, 546 cur.dinfo->devices, 547 num_devices, matches, 548 num_matches, 549 specified_devices, 550 num_devices_specified, 551 select_mode, maxshowdevs, 552 hflag); 553 switch(retval) { 554 case -1: 555 errx(1,"%s", devstat_errbuf); 556 break; 557 case 1: 558 phdr(); 559 if (wresized != 0) 560 doresize(); 561 headercount = wrows; 562 break; 563 default: 564 break; 565 } 566 } 567 568 if (Tflag > 0) { 569 tmp = cur.tk_nin; 570 cur.tk_nin -= last.tk_nin; 571 last.tk_nin = tmp; 572 tmp = cur.tk_nout; 573 cur.tk_nout -= last.tk_nout; 574 last.tk_nout = tmp; 575 } 576 577 etime = cur.snap_time - last.snap_time; 578 579 if (etime == 0.0) 580 etime = 1.0; 581 582 for (i = 0; i < CPUSTATES; i++) { 583 tmp = cur.cp_time[i]; 584 cur.cp_time[i] -= last.cp_time[i]; 585 last.cp_time[i] = tmp; 586 } 587 588 if (xflag == 0 && Tflag > 0) 589 printf("%4.0Lf %5.0Lf", cur.tk_nin / etime, 590 cur.tk_nout / etime); 591 592 devstats(hflag, etime, havelast); 593 594 if (xflag == 0) { 595 if (Cflag > 0) 596 cpustats(); 597 598 printf("\n"); 599 } 600 fflush(stdout); 601 602 if (count >= 0 && --count <= 0) 603 break; 604 605 usleep(waittime * 1000); 606 havelast = 1; 607 } 608 609 exit(0); 610 } 611 612 /* 613 * Force a header to be prepended to the next output. 614 */ 615 void 616 needhdr(int signo) 617 { 618 619 headercount = 1; 620 } 621 622 /* 623 * When the terminal is resized, force an update of the maximum number of rows 624 * printed between each header repetition. Then force a new header to be 625 * prepended to the next output. 626 */ 627 void 628 needresize(int signo) 629 { 630 631 wresized = 1; 632 headercount = 1; 633 } 634 635 /* 636 * Update the global `wrows' count of terminal rows. 637 */ 638 void 639 doresize(void) 640 { 641 int status; 642 struct winsize w; 643 644 for (;;) { 645 status = ioctl(fileno(stdout), TIOCGWINSZ, &w); 646 if (status == -1 && errno == EINTR) 647 continue; 648 else if (status == -1) 649 err(1, "ioctl"); 650 if (w.ws_row > 3) 651 wrows = w.ws_row - 3; 652 else 653 wrows = IOSTAT_DEFAULT_ROWS; 654 break; 655 } 656 657 /* 658 * Inhibit doresize() calls until we are rescheduled by SIGWINCH. 659 */ 660 wresized = 0; 661 } 662 663 static void 664 phdr(void) 665 { 666 int i, printed; 667 char devbuf[256]; 668 669 /* 670 * If xflag is set, we need a per-loop header, not a page header, so 671 * just return. We'll print the header in devstats(). 672 */ 673 if (xflag > 0) 674 return; 675 676 if (Tflag > 0) 677 (void)printf(" tty"); 678 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ 679 int di; 680 if ((dev_select[i].selected != 0) 681 && (dev_select[i].selected <= maxshowdevs)) { 682 di = dev_select[i].position; 683 snprintf(devbuf, sizeof(devbuf), "%s%d", 684 cur.dinfo->devices[di].device_name, 685 cur.dinfo->devices[di].unit_number); 686 if (oflag > 0) 687 (void)printf("%13.6s ", devbuf); 688 else 689 printf("%16.6s ", devbuf); 690 printed++; 691 } 692 } 693 if (Cflag > 0) 694 (void)printf(" cpu\n"); 695 else 696 (void)printf("\n"); 697 698 if (Tflag > 0) 699 (void)printf(" tin tout"); 700 701 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ 702 if ((dev_select[i].selected != 0) 703 && (dev_select[i].selected <= maxshowdevs)) { 704 if (oflag > 0) { 705 if (Iflag == 0) 706 (void)printf(" sps tps msps "); 707 else 708 (void)printf(" blk xfr msps "); 709 } else { 710 if (Iflag == 0) 711 printf(" KB/t tps MB/s "); 712 else 713 printf(" KB/t xfrs MB "); 714 } 715 printed++; 716 } 717 } 718 if (Cflag > 0) 719 (void)printf(" us ni sy in id\n"); 720 else 721 printf("\n"); 722 723 } 724 725 static void 726 devstats(int perf_select, long double etime, int havelast) 727 { 728 int dn; 729 long double transfers_per_second, transfers_per_second_read, transfers_per_second_write; 730 long double kb_per_transfer, mb_per_second, mb_per_second_read, mb_per_second_write; 731 u_int64_t total_bytes, total_transfers, total_blocks; 732 u_int64_t total_bytes_read, total_transfers_read; 733 u_int64_t total_bytes_write, total_transfers_write; 734 long double busy_pct, busy_time; 735 u_int64_t queue_len; 736 long double total_mb; 737 long double blocks_per_second, ms_per_transaction, total_duration; 738 int firstline = 1; 739 char *devname; 740 741 if (xflag > 0) { 742 printf(" extended device statistics "); 743 if (Tflag > 0) 744 printf(" tty "); 745 if (Cflag > 0) 746 printf(" cpu "); 747 printf("\n"); 748 if (Iflag == 0) { 749 printf("device r/s w/s kr/s kw/s qlen " 750 "svc_t %%b "); 751 } else { 752 printf("device r/i w/i kr/i" 753 " kw/i qlen tsvc_t/i sb/i "); 754 } 755 if (Tflag > 0) 756 printf("tin tout "); 757 if (Cflag > 0) 758 printf("us ni sy in id "); 759 printf("\n"); 760 } 761 762 for (dn = 0; dn < num_devices; dn++) { 763 int di; 764 765 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 766 || (dev_select[dn].selected > maxshowdevs)) 767 continue; 768 769 di = dev_select[dn].position; 770 771 if (devstat_compute_statistics(&cur.dinfo->devices[di], 772 havelast ? &last.dinfo->devices[di] : NULL, etime, 773 DSM_TOTAL_BYTES, &total_bytes, 774 DSM_TOTAL_BYTES_READ, &total_bytes_read, 775 DSM_TOTAL_BYTES_WRITE, &total_bytes_write, 776 DSM_TOTAL_TRANSFERS, &total_transfers, 777 DSM_TOTAL_TRANSFERS_READ, &total_transfers_read, 778 DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write, 779 DSM_TOTAL_BLOCKS, &total_blocks, 780 DSM_KB_PER_TRANSFER, &kb_per_transfer, 781 DSM_TRANSFERS_PER_SECOND, &transfers_per_second, 782 DSM_TRANSFERS_PER_SECOND_READ, &transfers_per_second_read, 783 DSM_TRANSFERS_PER_SECOND_WRITE, &transfers_per_second_write, 784 DSM_MB_PER_SECOND, &mb_per_second, 785 DSM_MB_PER_SECOND_READ, &mb_per_second_read, 786 DSM_MB_PER_SECOND_WRITE, &mb_per_second_write, 787 DSM_BLOCKS_PER_SECOND, &blocks_per_second, 788 DSM_MS_PER_TRANSACTION, &ms_per_transaction, 789 DSM_BUSY_PCT, &busy_pct, 790 DSM_QUEUE_LENGTH, &queue_len, 791 DSM_TOTAL_DURATION, &total_duration, 792 DSM_TOTAL_BUSY_TIME, &busy_time, 793 DSM_NONE) != 0) 794 errx(1, "%s", devstat_errbuf); 795 796 if (perf_select != 0) { 797 dev_select[dn].bytes = total_bytes; 798 if ((dev_select[dn].selected == 0) 799 || (dev_select[dn].selected > maxshowdevs)) 800 continue; 801 } 802 803 if (Kflag > 0 || xflag > 0) { 804 int block_size = cur.dinfo->devices[di].block_size; 805 total_blocks = total_blocks * (block_size ? 806 block_size : 512) / 1024; 807 } 808 809 if (xflag > 0) { 810 if (asprintf(&devname, "%s%d", 811 cur.dinfo->devices[di].device_name, 812 cur.dinfo->devices[di].unit_number) == -1) 813 err(1, "asprintf"); 814 /* 815 * If zflag is set, skip any devices with zero I/O. 816 */ 817 if (zflag == 0 || transfers_per_second_read > 0.05 || 818 transfers_per_second_write > 0.05 || 819 mb_per_second_read > ((long double).0005)/1024 || 820 mb_per_second_write > ((long double).0005)/1024 || 821 busy_pct > 0.5) { 822 if (Iflag == 0) 823 printf("%-8.8s %5.1Lf %5.1Lf %7.1Lf %7.1Lf %4" PRIu64 " %5.1Lf %3.0Lf ", 824 devname, transfers_per_second_read, 825 transfers_per_second_write, 826 mb_per_second_read * 1024, 827 mb_per_second_write * 1024, 828 queue_len, 829 ms_per_transaction, busy_pct); 830 else 831 printf("%-8.8s %11.1Lf %11.1Lf " 832 "%12.1Lf %12.1Lf %4" PRIu64 833 " %10.1Lf %9.1Lf ", 834 devname, 835 (long double)total_transfers_read, 836 (long double)total_transfers_write, 837 (long double) 838 total_bytes_read / 1024, 839 (long double) 840 total_bytes_write / 1024, 841 queue_len, 842 total_duration, busy_time); 843 if (firstline) { 844 /* 845 * If this is the first device 846 * we're printing, also print 847 * CPU or TTY stats if requested. 848 */ 849 firstline = 0; 850 if (Tflag > 0) 851 printf("%4.0Lf%5.0Lf", 852 cur.tk_nin / etime, 853 cur.tk_nout / etime); 854 if (Cflag > 0) 855 cpustats(); 856 } 857 printf("\n"); 858 } 859 free(devname); 860 } else if (oflag > 0) { 861 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 862 863 if (Iflag == 0) 864 printf("%4.0Lf%4.0Lf%5.*Lf ", 865 blocks_per_second, 866 transfers_per_second, 867 msdig, 868 ms_per_transaction); 869 else 870 printf("%4.1" PRIu64 "%4.1" PRIu64 "%5.*Lf ", 871 total_blocks, 872 total_transfers, 873 msdig, 874 ms_per_transaction); 875 } else { 876 if (Iflag == 0) 877 printf(" %5.2Lf %3.0Lf %5.2Lf ", 878 kb_per_transfer, 879 transfers_per_second, 880 mb_per_second); 881 else { 882 total_mb = total_bytes; 883 total_mb /= 1024 * 1024; 884 885 printf(" %5.2Lf %3.1" PRIu64 " %5.2Lf ", 886 kb_per_transfer, 887 total_transfers, 888 total_mb); 889 } 890 } 891 } 892 if (xflag > 0 && zflag > 0 && firstline == 1 && 893 (Tflag > 0 || Cflag > 0)) { 894 /* 895 * If zflag is set and we did not print any device 896 * lines I/O because they were all zero, 897 * print TTY/CPU stats. 898 */ 899 printf("%52s",""); 900 if (Tflag > 0) 901 printf("%4.0Lf %5.0Lf", cur.tk_nin / etime, 902 cur.tk_nout / etime); 903 if (Cflag > 0) 904 cpustats(); 905 printf("\n"); 906 } 907 } 908 909 static void 910 cpustats(void) 911 { 912 int state; 913 double time; 914 915 time = 0.0; 916 917 for (state = 0; state < CPUSTATES; ++state) 918 time += cur.cp_time[state]; 919 for (state = 0; state < CPUSTATES; ++state) 920 printf(" %2.0f", 921 rint(100. * cur.cp_time[state] / (time ? time : 1))); 922 } 923 924 static int 925 readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len) 926 { 927 if (kd != NULL) { 928 ssize_t nbytes; 929 930 nbytes = kvm_read(kd, namelist[nlid].n_value, ptr, len); 931 932 if (nbytes < 0) { 933 warnx("kvm_read(%s): %s", namelist[nlid].n_name, 934 kvm_geterr(kd)); 935 return (1); 936 } 937 if (nbytes != len) { 938 warnx("kvm_read(%s): expected %zu bytes, got %zd bytes", 939 namelist[nlid].n_name, len, nbytes); 940 return (1); 941 } 942 } else { 943 size_t nlen = len; 944 945 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { 946 warn("sysctl(%s...) failed", name); 947 return (1); 948 } 949 if (nlen != len) { 950 warnx("sysctl(%s...): expected %lu, got %lu", name, 951 (unsigned long)len, (unsigned long)nlen); 952 return (1); 953 } 954 } 955 return (0); 956 } 957