1 /* 2 * Copyright (c) 1997, 1998 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/dkstat.h> 106 107 #include <err.h> 108 #include <ctype.h> 109 #include <fcntl.h> 110 #include <kvm.h> 111 #include <stdio.h> 112 #include <stdlib.h> 113 #include <string.h> 114 #include <unistd.h> 115 #include <limits.h> 116 #include <devstat.h> 117 118 struct nlist namelist[] = { 119 #define X_TK_NIN 0 120 { "_tk_nin" }, 121 #define X_TK_NOUT 1 122 { "_tk_nout" }, 123 #define X_CP_TIME 2 124 { "_cp_time" }, 125 #define X_HZ 3 126 { "_hz" }, 127 #define X_STATHZ 4 128 { "_stathz" }, 129 #define X_END 4 130 { NULL }, 131 }; 132 133 struct statinfo cur, last; 134 int num_devices; 135 struct device_selection *dev_select; 136 int maxshowdevs; 137 int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0; 138 139 #define nlread(x, v) \ 140 kvm_read(kd, namelist[x].n_value, &(v), sizeof(v)) 141 142 /* local function declarations */ 143 static void usage(void); 144 static void phdr(int signo); 145 static void devstats(int perf_select); 146 static void cpustats(void); 147 148 static void 149 usage(void) 150 { 151 /* 152 * We also support the following 'traditional' syntax: 153 * iostat [drives] [wait [count]] 154 * This isn't mentioned in the man page, or the usage statement, 155 * but it is supported. 156 */ 157 fprintf(stderr, "usage: iostat [-CdhIKoT?] [-c count] [-M core]" 158 " [-n devs] [-N system]\n" 159 "\t [-t type,if,pass] [-w wait] [drives]\n"); 160 } 161 162 int 163 main(int argc, char **argv) 164 { 165 int c; 166 register int i; 167 int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0; 168 int count = 0, waittime = 0; 169 char *memf = NULL, *nlistf = NULL; 170 struct devstat_match *matches; 171 int num_matches = 0; 172 char errbuf[_POSIX2_LINE_MAX]; 173 kvm_t *kd; 174 int hz, stathz; 175 int headercount; 176 long generation; 177 int num_devices_specified; 178 int num_selected, num_selections; 179 long select_generation; 180 char **specified_devices; 181 devstat_select_mode select_mode; 182 183 matches = NULL; 184 maxshowdevs = 3; 185 186 while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:?")) != -1) { 187 switch(c) { 188 case 'c': 189 cflag++; 190 count = atoi(optarg); 191 if (count < 1) 192 errx(1, "count %d is < 1", count); 193 break; 194 case 'C': 195 Cflag++; 196 break; 197 case 'd': 198 dflag++; 199 break; 200 case 'h': 201 hflag++; 202 break; 203 case 'I': 204 Iflag++; 205 break; 206 case 'K': 207 Kflag++; 208 break; 209 case 'M': 210 memf = optarg; 211 break; 212 case 'n': 213 nflag++; 214 maxshowdevs = atoi(optarg); 215 if (maxshowdevs < 0) 216 errx(1, "number of devices %d is < 0", 217 maxshowdevs); 218 break; 219 case 'N': 220 nlistf = optarg; 221 break; 222 case 'o': 223 oflag++; 224 break; 225 case 't': 226 tflag++; 227 if (buildmatch(optarg, &matches, 228 &num_matches) != 0) 229 errx(1, "%s", devstat_errbuf); 230 break; 231 case 'T': 232 Tflag++; 233 break; 234 case 'w': 235 wflag++; 236 waittime = atoi(optarg); 237 if (waittime < 1) 238 errx(1, "wait time is < 1"); 239 break; 240 default: 241 usage(); 242 exit(1); 243 break; 244 } 245 } 246 247 argc -= optind; 248 argv += optind; 249 250 /* 251 * Discard setgid privileges if not the running kernel so that bad 252 * guys can't print interesting stuff from kernel memory. 253 */ 254 if (nlistf != NULL || memf != NULL) 255 setgid(getgid()); 256 257 /* 258 * Make sure that the userland devstat version matches the kernel 259 * devstat version. If not, exit and print a message informing 260 * the user of his mistake. 261 */ 262 if (checkversion() < 0) 263 errx(1, "%s", devstat_errbuf); 264 265 /* 266 * Figure out how many devices we should display. 267 */ 268 if (nflag == 0) { 269 if (oflag > 0) { 270 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) 271 maxshowdevs = 5; 272 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) 273 maxshowdevs = 5; 274 else 275 maxshowdevs = 4; 276 } else { 277 if ((dflag > 0) && (Cflag == 0)) 278 maxshowdevs = 4; 279 else 280 maxshowdevs = 3; 281 } 282 } 283 284 /* find out how many devices we have */ 285 if ((num_devices = getnumdevs()) < 0) 286 err(1, "can't get number of devices"); 287 288 if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 289 NULL) 290 err(1, "devinfo malloc failed"); 291 if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 292 NULL) 293 err(1, "devinfo malloc failed"); 294 bzero(cur.dinfo, sizeof(struct devinfo)); 295 bzero(last.dinfo, sizeof(struct devinfo)); 296 297 /* 298 * Grab all the devices. We don't look to see if the list has 299 * changed here, since it almost certainly has. We only look for 300 * errors. 301 */ 302 if (getdevs(&cur) == -1) 303 errx(1, "%s", devstat_errbuf); 304 305 num_devices = cur.dinfo->numdevs; 306 generation = cur.dinfo->generation; 307 308 /* 309 * If the user specified any devices on the command line, see if 310 * they are in the list of devices we have now. 311 */ 312 if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL) 313 err(1, "specified_devices malloc failed"); 314 for (num_devices_specified = 0; *argv; ++argv) { 315 if (isdigit(**argv)) 316 break; 317 num_devices_specified++; 318 specified_devices = (char **)realloc(specified_devices, 319 sizeof(char *) * 320 num_devices_specified); 321 specified_devices[num_devices_specified - 1] = *argv; 322 323 } 324 if (nflag == 0 && maxshowdevs < num_devices_specified) 325 maxshowdevs = num_devices_specified; 326 327 dev_select = NULL; 328 329 if ((num_devices_specified == 0) && (num_matches == 0)) 330 select_mode = DS_SELECT_ADD; 331 else 332 select_mode = DS_SELECT_ONLY; 333 334 /* 335 * At this point, selectdevs will almost surely indicate that the 336 * device list has changed, so we don't look for return values of 0 337 * or 1. If we get back -1, though, there is an error. 338 */ 339 if (selectdevs(&dev_select, &num_selected, 340 &num_selections, &select_generation, 341 generation, cur.dinfo->devices, num_devices, 342 matches, num_matches, 343 specified_devices, num_devices_specified, 344 select_mode, maxshowdevs, hflag) == -1) 345 errx(1, "%s", devstat_errbuf); 346 347 /* 348 * Look for the traditional wait time and count arguments. 349 */ 350 if (*argv) { 351 waittime = atoi(*argv); 352 353 /* Let the user know he goofed, but keep going anyway */ 354 if (wflag != 0) 355 warnx("discarding previous wait interval, using" 356 " %d instead", waittime); 357 wflag++; 358 359 if (*++argv) { 360 count = atoi(*argv); 361 if (cflag != 0) 362 warnx("discarding previous count, using %d" 363 " instead", count); 364 cflag++; 365 } else 366 count = -1; 367 } 368 369 /* 370 * If the user specified a count, but not an interval, we default 371 * to an interval of 1 second. 372 */ 373 if ((wflag == 0) && (cflag > 0)) 374 waittime = 1; 375 376 /* 377 * If the user specified a wait time, but not a count, we want to 378 * go on ad infinitum. This can be redundant if the user uses the 379 * traditional method of specifying the wait, since in that case we 380 * already set count = -1 above. Oh well. 381 */ 382 if ((wflag > 0) && (cflag == 0)) 383 count = -1; 384 385 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 386 387 if (kd == 0) 388 errx(1, "kvm_openfiles: %s", errbuf); 389 390 if (kvm_nlist(kd, namelist) == -1) 391 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 392 393 (void)nlread(X_HZ, hz); 394 (void)nlread(X_STATHZ, stathz); 395 if (stathz) 396 hz = stathz; 397 398 /* 399 * If the user stops the program (control-Z) and then resumes it, 400 * print out the header again. 401 */ 402 (void)signal(SIGCONT, phdr); 403 404 for (headercount = 1;;) { 405 struct devinfo *tmp_dinfo; 406 long tmp; 407 double etime; 408 409 if (!--headercount) { 410 phdr(0); 411 headercount = 20; 412 } 413 (void)kvm_read(kd, namelist[X_TK_NIN].n_value, 414 &cur.tk_nin, sizeof(cur.tk_nin)); 415 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, 416 &cur.tk_nout, sizeof(cur.tk_nout)); 417 (void)kvm_read(kd, namelist[X_CP_TIME].n_value, 418 cur.cp_time, sizeof(cur.cp_time)); 419 420 tmp_dinfo = last.dinfo; 421 last.dinfo = cur.dinfo; 422 cur.dinfo = tmp_dinfo; 423 424 last.busy_time = cur.busy_time; 425 426 /* 427 * Here what we want to do is refresh our device stats. 428 * getdevs() returns 1 when the device list has changed. 429 * If the device list has changed, we want to go through 430 * the selection process again, in case a device that we 431 * were previously displaying has gone away. 432 */ 433 switch (getdevs(&cur)) { 434 case -1: 435 errx(1, "%s", devstat_errbuf); 436 break; 437 case 1: { 438 int retval; 439 440 num_devices = cur.dinfo->numdevs; 441 generation = cur.dinfo->generation; 442 retval = selectdevs(&dev_select, &num_selected, 443 &num_selections, &select_generation, 444 generation, cur.dinfo->devices, 445 num_devices, matches, num_matches, 446 specified_devices, 447 num_devices_specified, 448 select_mode, maxshowdevs, hflag); 449 switch(retval) { 450 case -1: 451 errx(1, "%s", devstat_errbuf); 452 break; 453 case 1: 454 phdr(0); 455 headercount = 20; 456 break; 457 default: 458 break; 459 } 460 break; 461 } 462 default: 463 break; 464 } 465 466 /* 467 * We only want to re-select devices if we're in 'top' 468 * mode. This is the only mode where the devices selected 469 * could actually change. 470 */ 471 if (hflag > 0) { 472 int retval; 473 retval = selectdevs(&dev_select, &num_selected, 474 &num_selections, &select_generation, 475 generation, cur.dinfo->devices, 476 num_devices, matches, num_matches, 477 specified_devices, 478 num_devices_specified, 479 select_mode, maxshowdevs, hflag); 480 switch(retval) { 481 case -1: 482 errx(1,"%s", devstat_errbuf); 483 break; 484 case 1: 485 phdr(0); 486 headercount = 20; 487 break; 488 default: 489 break; 490 } 491 } 492 493 tmp = cur.tk_nin; 494 cur.tk_nin -= last.tk_nin; 495 last.tk_nin = tmp; 496 tmp = cur.tk_nout; 497 cur.tk_nout -= last.tk_nout; 498 last.tk_nout = tmp; 499 500 etime = 0.0; 501 502 #define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 503 504 for (i = 0; i < CPUSTATES; i++) { 505 X(cp_time); 506 etime += cur.cp_time[i]; 507 } 508 if (etime == 0.0) 509 etime = 1.0; 510 etime /= (float)hz; 511 if ((dflag == 0) || (Tflag > 0)) 512 printf("%4.0f%5.0f", cur.tk_nin / etime, 513 cur.tk_nout/etime); 514 devstats(hflag); 515 if ((dflag == 0) || (Cflag > 0)) 516 cpustats(); 517 printf("\n"); 518 fflush(stdout); 519 520 if (count >= 0 && --count <= 0) 521 break; 522 523 sleep(waittime); 524 } 525 526 exit(0); 527 } 528 529 static void 530 phdr(int signo) 531 { 532 register int i; 533 int printed; 534 535 if ((dflag == 0) || (Tflag > 0)) 536 (void)printf(" tty"); 537 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ 538 int di; 539 if ((dev_select[i].selected != 0) 540 && (dev_select[i].selected <= maxshowdevs)) { 541 di = dev_select[i].position; 542 if (oflag > 0) 543 (void)printf("%12.6s%d ", 544 cur.dinfo->devices[di].device_name, 545 cur.dinfo->devices[di].unit_number); 546 else 547 printf("%15.6s%d ", 548 cur.dinfo->devices[di].device_name, 549 cur.dinfo->devices[di].unit_number); 550 printed++; 551 } 552 } 553 if ((dflag == 0) || (Cflag > 0)) 554 (void)printf(" cpu\n"); 555 else 556 (void)printf("\n"); 557 558 if ((dflag == 0) || (Tflag > 0)) 559 (void)printf(" tin tout"); 560 561 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ 562 if ((dev_select[i].selected != 0) 563 && (dev_select[i].selected <= maxshowdevs)) { 564 if (oflag > 0) { 565 if (Iflag == 0) 566 (void)printf(" sps tps msps "); 567 else 568 (void)printf(" blk xfr msps "); 569 } else { 570 if (Iflag == 0) 571 printf(" KB/t tps MB/s "); 572 else 573 printf(" KB/t xfrs MB "); 574 } 575 printed++; 576 } 577 } 578 if ((dflag == 0) || (Cflag > 0)) 579 (void)printf(" us ni sy in id\n"); 580 else 581 printf("\n"); 582 583 } 584 585 static void 586 devstats(int perf_select) 587 { 588 register int dn; 589 long double transfers_per_second; 590 long double kb_per_transfer, mb_per_second; 591 u_int64_t total_bytes, total_transfers, total_blocks; 592 long double busy_seconds; 593 long double total_mb; 594 long double blocks_per_second, ms_per_transaction; 595 596 /* 597 * Calculate elapsed time up front, since it's the same for all 598 * devices. 599 */ 600 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 601 602 for (dn = 0; dn < num_devices; dn++) { 603 int di; 604 605 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 606 || (dev_select[dn].selected > maxshowdevs)) 607 continue; 608 609 di = dev_select[dn].position; 610 611 if (compute_stats(&cur.dinfo->devices[di], 612 &last.dinfo->devices[di], busy_seconds, 613 &total_bytes, &total_transfers, 614 &total_blocks, &kb_per_transfer, 615 &transfers_per_second, &mb_per_second, 616 &blocks_per_second, &ms_per_transaction)!= 0) 617 errx(1, "%s", devstat_errbuf); 618 619 if (perf_select != 0) { 620 dev_select[dn].bytes = total_bytes; 621 if ((dev_select[dn].selected == 0) 622 || (dev_select[dn].selected > maxshowdevs)) 623 continue; 624 } 625 626 if (Kflag) { 627 int block_size = cur.dinfo->devices[di].block_size; 628 total_blocks = total_blocks * (block_size ? 629 block_size : 512) / 1024; 630 } 631 632 if (oflag > 0) { 633 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 634 635 if (Iflag == 0) 636 printf("%4.0Lf%4.0Lf%5.*Lf ", 637 blocks_per_second, 638 transfers_per_second, 639 msdig, 640 ms_per_transaction); 641 else 642 printf("%4.1qu%4.1qu%5.*Lf ", 643 total_blocks, 644 total_transfers, 645 msdig, 646 ms_per_transaction); 647 } else { 648 if (Iflag == 0) 649 printf(" %5.2Lf %3.0Lf %5.2Lf ", 650 kb_per_transfer, 651 transfers_per_second, 652 mb_per_second); 653 else { 654 total_mb = total_bytes; 655 total_mb /= 1024 * 1024; 656 657 printf(" %5.2Lf %3.1qu %5.2Lf ", 658 kb_per_transfer, 659 total_transfers, 660 total_mb); 661 } 662 } 663 } 664 } 665 666 static void 667 cpustats(void) 668 { 669 register int state; 670 double time; 671 672 time = 0.0; 673 674 for (state = 0; state < CPUSTATES; ++state) 675 time += cur.cp_time[state]; 676 for (state = 0; state < CPUSTATES; ++state) 677 printf("%3.0f", 678 100. * cur.cp_time[state] / (time ? time : 1)); 679 } 680