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