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