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 int havelast = 0; 188 189 matches = NULL; 190 maxshowdevs = 3; 191 192 while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:xz?")) != -1) { 193 switch(c) { 194 case 'c': 195 cflag++; 196 count = atoi(optarg); 197 if (count < 1) 198 errx(1, "count %d is < 1", count); 199 break; 200 case 'C': 201 Cflag++; 202 break; 203 case 'd': 204 dflag++; 205 break; 206 case 'h': 207 hflag++; 208 break; 209 case 'I': 210 Iflag++; 211 break; 212 case 'K': 213 Kflag++; 214 break; 215 case 'M': 216 memf = optarg; 217 break; 218 case 'n': 219 nflag++; 220 maxshowdevs = atoi(optarg); 221 if (maxshowdevs < 0) 222 errx(1, "number of devices %d is < 0", 223 maxshowdevs); 224 break; 225 case 'N': 226 nlistf = optarg; 227 break; 228 case 'o': 229 oflag++; 230 break; 231 case 't': 232 tflag++; 233 if (devstat_buildmatch(optarg, &matches, 234 &num_matches) != 0) 235 errx(1, "%s", devstat_errbuf); 236 break; 237 case 'T': 238 Tflag++; 239 break; 240 case 'w': 241 wflag++; 242 waittime = atoi(optarg); 243 if (waittime < 1) 244 errx(1, "wait time is < 1"); 245 break; 246 case 'x': 247 xflag++; 248 break; 249 case 'z': 250 zflag++; 251 break; 252 default: 253 usage(); 254 exit(1); 255 break; 256 } 257 } 258 259 argc -= optind; 260 argv += optind; 261 262 if (nlistf != NULL || memf != NULL) { 263 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 264 265 if (kd == NULL) 266 errx(1, "kvm_openfiles: %s", errbuf); 267 268 if (kvm_nlist(kd, namelist) == -1) 269 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 270 } 271 272 /* 273 * Make sure that the userland devstat version matches the kernel 274 * devstat version. If not, exit and print a message informing 275 * the user of his mistake. 276 */ 277 if (devstat_checkversion(kd) < 0) 278 errx(1, "%s", devstat_errbuf); 279 280 /* 281 * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is 282 * greater than 0, they may be 0 or non-zero. 283 */ 284 if (dflag == 0 && xflag == 0) { 285 Cflag = 1; 286 Tflag = 1; 287 } 288 289 /* find out how many devices we have */ 290 if ((num_devices = devstat_getnumdevs(kd)) < 0) 291 err(1, "can't get number of devices"); 292 293 /* 294 * Figure out how many devices we should display. 295 */ 296 if (nflag == 0) { 297 if (xflag > 0) 298 maxshowdevs = num_devices; 299 else if (oflag > 0) { 300 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) 301 maxshowdevs = 5; 302 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) 303 maxshowdevs = 5; 304 else 305 maxshowdevs = 4; 306 } else { 307 if ((dflag > 0) && (Cflag == 0)) 308 maxshowdevs = 4; 309 else 310 maxshowdevs = 3; 311 } 312 } 313 314 cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 315 if (cur.dinfo == NULL) 316 err(1, "calloc failed"); 317 318 last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); 319 if (last.dinfo == NULL) 320 err(1, "calloc failed"); 321 322 /* 323 * Grab all the devices. We don't look to see if the list has 324 * changed here, since it almost certainly has. We only look for 325 * errors. 326 */ 327 if (devstat_getdevs(kd, &cur) == -1) 328 errx(1, "%s", devstat_errbuf); 329 330 num_devices = cur.dinfo->numdevs; 331 generation = cur.dinfo->generation; 332 333 /* 334 * If the user specified any devices on the command line, see if 335 * they are in the list of devices we have now. 336 */ 337 specified_devices = (char **)malloc(sizeof(char *)); 338 if (specified_devices == NULL) 339 err(1, "malloc failed"); 340 341 for (num_devices_specified = 0; *argv; ++argv) { 342 if (isdigit(**argv)) 343 break; 344 num_devices_specified++; 345 specified_devices = (char **)realloc(specified_devices, 346 sizeof(char *) * 347 num_devices_specified); 348 if (specified_devices == NULL) 349 err(1, "realloc failed"); 350 351 specified_devices[num_devices_specified - 1] = *argv; 352 353 } 354 if (nflag == 0 && maxshowdevs < num_devices_specified) 355 maxshowdevs = num_devices_specified; 356 357 dev_select = NULL; 358 359 if ((num_devices_specified == 0) && (num_matches == 0)) 360 select_mode = DS_SELECT_ADD; 361 else 362 select_mode = DS_SELECT_ONLY; 363 364 /* 365 * At this point, selectdevs will almost surely indicate that the 366 * device list has changed, so we don't look for return values of 0 367 * or 1. If we get back -1, though, there is an error. 368 */ 369 if (devstat_selectdevs(&dev_select, &num_selected, 370 &num_selections, &select_generation, generation, 371 cur.dinfo->devices, num_devices, matches, 372 num_matches, specified_devices, 373 num_devices_specified, select_mode, maxshowdevs, 374 hflag) == -1) 375 errx(1, "%s", devstat_errbuf); 376 377 /* 378 * Look for the traditional wait time and count arguments. 379 */ 380 if (*argv) { 381 waittime = atoi(*argv); 382 383 /* Let the user know he goofed, but keep going anyway */ 384 if (wflag != 0) 385 warnx("discarding previous wait interval, using" 386 " %d instead", waittime); 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; 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_TK_NIN, &cur.tk_nin, 452 sizeof(cur.tk_nin)) != 0) 453 || (readvar(kd, "kern.tty_nout", X_TK_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 sleep(waittime); 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; 735 u_int64_t queue_len; 736 long double total_mb; 737 long double blocks_per_second, ms_per_transaction; 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( 750 "device r/s w/s kr/s kw/s wait svc_t %%b " 751 ); 752 else 753 printf( 754 "device r/i w/i kr/i kw/i wait svc_t %%b " 755 ); 756 if (Tflag > 0) 757 printf("tin tout "); 758 if (Cflag > 0) 759 printf("us ni sy in id "); 760 printf("\n"); 761 } 762 763 for (dn = 0; dn < num_devices; dn++) { 764 int di; 765 766 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 767 || (dev_select[dn].selected > maxshowdevs)) 768 continue; 769 770 di = dev_select[dn].position; 771 772 if (devstat_compute_statistics(&cur.dinfo->devices[di], 773 havelast ? &last.dinfo->devices[di] : NULL, etime, 774 DSM_TOTAL_BYTES, &total_bytes, 775 DSM_TOTAL_BYTES_READ, &total_bytes_read, 776 DSM_TOTAL_BYTES_WRITE, &total_bytes_write, 777 DSM_TOTAL_TRANSFERS, &total_transfers, 778 DSM_TOTAL_TRANSFERS_READ, &total_transfers_read, 779 DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write, 780 DSM_TOTAL_BLOCKS, &total_blocks, 781 DSM_KB_PER_TRANSFER, &kb_per_transfer, 782 DSM_TRANSFERS_PER_SECOND, &transfers_per_second, 783 DSM_TRANSFERS_PER_SECOND_READ, &transfers_per_second_read, 784 DSM_TRANSFERS_PER_SECOND_WRITE, &transfers_per_second_write, 785 DSM_MB_PER_SECOND, &mb_per_second, 786 DSM_MB_PER_SECOND_READ, &mb_per_second_read, 787 DSM_MB_PER_SECOND_WRITE, &mb_per_second_write, 788 DSM_BLOCKS_PER_SECOND, &blocks_per_second, 789 DSM_MS_PER_TRANSACTION, &ms_per_transaction, 790 DSM_BUSY_PCT, &busy_pct, 791 DSM_QUEUE_LENGTH, &queue_len, 792 DSM_NONE) != 0) 793 errx(1, "%s", devstat_errbuf); 794 795 if (perf_select != 0) { 796 dev_select[dn].bytes = total_bytes; 797 if ((dev_select[dn].selected == 0) 798 || (dev_select[dn].selected > maxshowdevs)) 799 continue; 800 } 801 802 if (Kflag > 0 || xflag > 0) { 803 int block_size = cur.dinfo->devices[di].block_size; 804 total_blocks = total_blocks * (block_size ? 805 block_size : 512) / 1024; 806 } 807 808 if (xflag > 0) { 809 if (asprintf(&devname, "%s%d", 810 cur.dinfo->devices[di].device_name, 811 cur.dinfo->devices[di].unit_number) == -1) 812 err(1, "asprintf"); 813 /* 814 * If zflag is set, skip any devices with zero I/O. 815 */ 816 if (zflag == 0 || transfers_per_second_read > 0.05 || 817 transfers_per_second_write > 0.05 || 818 mb_per_second_read > ((long double).0005)/1024 || 819 mb_per_second_write > ((long double).0005)/1024 || 820 busy_pct > 0.5) { 821 if (Iflag == 0) 822 printf("%-8.8s %5.1Lf %5.1Lf %7.1Lf %7.1Lf %4qu %5.1Lf %3.0Lf ", 823 devname, transfers_per_second_read, 824 transfers_per_second_write, 825 mb_per_second_read * 1024, 826 mb_per_second_write * 1024, 827 queue_len, 828 ms_per_transaction, busy_pct); 829 else 830 printf("%-8.8s %5.1Lf %5.1Lf %7.1Lf %7.1Lf %4qu %5.1Lf %3.0Lf ", 831 devname, 832 (long double)total_transfers_read, 833 (long double)total_transfers_write, 834 (long double) 835 total_bytes_read / 1024, 836 (long double) 837 total_bytes_write / 1024, 838 queue_len, 839 ms_per_transaction, busy_pct); 840 if (firstline) { 841 /* 842 * If this is the first device 843 * we're printing, also print 844 * CPU or TTY stats if requested. 845 */ 846 firstline = 0; 847 if (Tflag > 0) 848 printf("%4.0Lf%5.0Lf", 849 cur.tk_nin / etime, 850 cur.tk_nout / etime); 851 if (Cflag > 0) 852 cpustats(); 853 } 854 printf("\n"); 855 } 856 free(devname); 857 } else if (oflag > 0) { 858 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 859 860 if (Iflag == 0) 861 printf("%4.0Lf%4.0Lf%5.*Lf ", 862 blocks_per_second, 863 transfers_per_second, 864 msdig, 865 ms_per_transaction); 866 else 867 printf("%4.1qu%4.1qu%5.*Lf ", 868 total_blocks, 869 total_transfers, 870 msdig, 871 ms_per_transaction); 872 } else { 873 if (Iflag == 0) 874 printf(" %5.2Lf %3.0Lf %5.2Lf ", 875 kb_per_transfer, 876 transfers_per_second, 877 mb_per_second); 878 else { 879 total_mb = total_bytes; 880 total_mb /= 1024 * 1024; 881 882 printf(" %5.2Lf %3.1qu %5.2Lf ", 883 kb_per_transfer, 884 total_transfers, 885 total_mb); 886 } 887 } 888 } 889 if (xflag > 0 && zflag > 0 && firstline == 1 && 890 (Tflag > 0 || Cflag > 0)) { 891 /* 892 * If zflag is set and we did not print any device 893 * lines I/O because they were all zero, 894 * print TTY/CPU stats. 895 */ 896 printf("%52s",""); 897 if (Tflag > 0) 898 printf("%4.0Lf%5.0Lf", cur.tk_nin / etime, 899 cur.tk_nout / etime); 900 if (Cflag > 0) 901 cpustats(); 902 printf("\n"); 903 } 904 } 905 906 static void 907 cpustats(void) 908 { 909 int state; 910 double time; 911 912 time = 0.0; 913 914 for (state = 0; state < CPUSTATES; ++state) 915 time += cur.cp_time[state]; 916 for (state = 0; state < CPUSTATES; ++state) 917 printf(" %2.0f", 918 rint(100. * cur.cp_time[state] / (time ? time : 1))); 919 } 920 921 static int 922 readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len) 923 { 924 if (kd != NULL) { 925 ssize_t nbytes; 926 927 nbytes = kvm_read(kd, namelist[nlid].n_value, ptr, len); 928 929 if (nbytes < 0) { 930 warnx("kvm_read(%s): %s", namelist[nlid].n_name, 931 kvm_geterr(kd)); 932 return (1); 933 } 934 if (nbytes != len) { 935 warnx("kvm_read(%s): expected %zu bytes, got %zd bytes", 936 namelist[nlid].n_name, len, nbytes); 937 return (1); 938 } 939 } else { 940 size_t nlen = len; 941 942 if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { 943 warn("sysctl(%s...) failed", name); 944 return (1); 945 } 946 if (nlen != len) { 947 warnx("sysctl(%s...): expected %lu, got %lu", name, 948 (unsigned long)len, (unsigned long)nlen); 949 return (1); 950 } 951 } 952 return (0); 953 } 954