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