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