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