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