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