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: src/usr.sbin/iostat/iostat.c,v 1.17.2.2 2001/07/19 04:15:42 kris Exp $ 29 * $DragonFly: src/usr.sbin/iostat/iostat.c,v 1.3 2003/07/29 23:15:36 drhodus Exp $ 30 */ 31 /* 32 * Parts of this program are derived from the original FreeBSD iostat 33 * program: 34 */ 35 /*- 36 * Copyright (c) 1986, 1991, 1993 37 * The Regents of the University of California. All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. All advertising materials mentioning features or use of this software 48 * must display the following acknowledgement: 49 * This product includes software developed by the University of 50 * California, Berkeley and its contributors. 51 * 4. Neither the name of the University nor the names of its contributors 52 * may be used to endorse or promote products derived from this software 53 * without specific prior written permission. 54 * 55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65 * SUCH DAMAGE. 66 */ 67 /* 68 * Ideas for the new iostat statistics output modes taken from the NetBSD 69 * version of iostat: 70 */ 71 /* 72 * Copyright (c) 1996 John M. Vinopal 73 * All rights reserved. 74 * 75 * Redistribution and use in source and binary forms, with or without 76 * modification, are permitted provided that the following conditions 77 * are met: 78 * 1. Redistributions of source code must retain the above copyright 79 * notice, this list of conditions and the following disclaimer. 80 * 2. Redistributions in binary form must reproduce the above copyright 81 * notice, this list of conditions and the following disclaimer in the 82 * documentation and/or other materials provided with the distribution. 83 * 3. All advertising materials mentioning features or use of this software 84 * must display the following acknowledgement: 85 * This product includes software developed for the NetBSD Project 86 * by John M. Vinopal. 87 * 4. The name of the author may not be used to endorse or promote products 88 * derived from this software without specific prior written permission. 89 * 90 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 91 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 92 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 93 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 94 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 95 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 96 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 97 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 98 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 99 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 100 * SUCH DAMAGE. 101 */ 102 103 104 #include <sys/param.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 kvm_t *kd; 175 int hz, stathz; 176 int headercount; 177 long generation; 178 int num_devices_specified; 179 int num_selected, num_selections; 180 long 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:CdhIKM: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 'K': 208 Kflag++; 209 break; 210 case 'M': 211 memf = optarg; 212 break; 213 case 'n': 214 nflag++; 215 maxshowdevs = atoi(optarg); 216 if (maxshowdevs < 0) 217 errx(1, "number of devices %d is < 0", 218 maxshowdevs); 219 break; 220 case 'N': 221 nlistf = optarg; 222 break; 223 case 'o': 224 oflag++; 225 break; 226 case 't': 227 tflag++; 228 if (buildmatch(optarg, &matches, 229 &num_matches) != 0) 230 errx(1, "%s", devstat_errbuf); 231 break; 232 case 'T': 233 Tflag++; 234 break; 235 case 'w': 236 wflag++; 237 waittime = atoi(optarg); 238 if (waittime < 1) 239 errx(1, "wait time is < 1"); 240 break; 241 default: 242 usage(); 243 exit(1); 244 break; 245 } 246 } 247 248 argc -= optind; 249 argv += optind; 250 251 /* 252 * Discard setgid privileges if not the running kernel so that bad 253 * guys can't print interesting stuff from kernel memory. 254 */ 255 if (nlistf != NULL || memf != NULL) 256 setgid(getgid()); 257 258 /* 259 * Make sure that the userland devstat version matches the kernel 260 * devstat version. If not, exit and print a message informing 261 * the user of his mistake. 262 */ 263 if (checkversion() < 0) 264 errx(1, "%s", devstat_errbuf); 265 266 /* 267 * Figure out how many devices we should display. 268 */ 269 if (nflag == 0) { 270 if (oflag > 0) { 271 if ((dflag > 0) && (Cflag == 0) && (Tflag == 0)) 272 maxshowdevs = 5; 273 else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0)) 274 maxshowdevs = 5; 275 else 276 maxshowdevs = 4; 277 } else { 278 if ((dflag > 0) && (Cflag == 0)) 279 maxshowdevs = 4; 280 else 281 maxshowdevs = 3; 282 } 283 } 284 285 /* find out how many devices we have */ 286 if ((num_devices = getnumdevs()) < 0) 287 err(1, "can't get number of devices"); 288 289 if ((cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 290 NULL) 291 err(1, "devinfo malloc failed"); 292 if ((last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo))) == 293 NULL) 294 err(1, "devinfo malloc failed"); 295 bzero(cur.dinfo, sizeof(struct devinfo)); 296 bzero(last.dinfo, sizeof(struct devinfo)); 297 298 /* 299 * Grab all the devices. We don't look to see if the list has 300 * changed here, since it almost certainly has. We only look for 301 * errors. 302 */ 303 if (getdevs(&cur) == -1) 304 errx(1, "%s", devstat_errbuf); 305 306 num_devices = cur.dinfo->numdevs; 307 generation = cur.dinfo->generation; 308 309 /* 310 * If the user specified any devices on the command line, see if 311 * they are in the list of devices we have now. 312 */ 313 if ((specified_devices = (char **)malloc(sizeof(char *))) == NULL) 314 err(1, "specified_devices malloc failed"); 315 for (num_devices_specified = 0; *argv; ++argv) { 316 if (isdigit(**argv)) 317 break; 318 num_devices_specified++; 319 specified_devices = (char **)realloc(specified_devices, 320 sizeof(char *) * 321 num_devices_specified); 322 specified_devices[num_devices_specified - 1] = *argv; 323 324 } 325 if (nflag == 0 && maxshowdevs < num_devices_specified) 326 maxshowdevs = num_devices_specified; 327 328 dev_select = NULL; 329 330 if ((num_devices_specified == 0) && (num_matches == 0)) 331 select_mode = DS_SELECT_ADD; 332 else 333 select_mode = DS_SELECT_ONLY; 334 335 /* 336 * At this point, selectdevs will almost surely indicate that the 337 * device list has changed, so we don't look for return values of 0 338 * or 1. If we get back -1, though, there is an error. 339 */ 340 if (selectdevs(&dev_select, &num_selected, 341 &num_selections, &select_generation, 342 generation, cur.dinfo->devices, num_devices, 343 matches, num_matches, 344 specified_devices, num_devices_specified, 345 select_mode, maxshowdevs, hflag) == -1) 346 errx(1, "%s", devstat_errbuf); 347 348 /* 349 * Look for the traditional wait time and count arguments. 350 */ 351 if (*argv) { 352 waittime = atoi(*argv); 353 354 /* Let the user know he goofed, but keep going anyway */ 355 if (wflag != 0) 356 warnx("discarding previous wait interval, using" 357 " %d instead", waittime); 358 wflag++; 359 360 if (*++argv) { 361 count = atoi(*argv); 362 if (cflag != 0) 363 warnx("discarding previous count, using %d" 364 " instead", count); 365 cflag++; 366 } else 367 count = -1; 368 } 369 370 /* 371 * If the user specified a count, but not an interval, we default 372 * to an interval of 1 second. 373 */ 374 if ((wflag == 0) && (cflag > 0)) 375 waittime = 1; 376 377 /* 378 * If the user specified a wait time, but not a count, we want to 379 * go on ad infinitum. This can be redundant if the user uses the 380 * traditional method of specifying the wait, since in that case we 381 * already set count = -1 above. Oh well. 382 */ 383 if ((wflag > 0) && (cflag == 0)) 384 count = -1; 385 386 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 387 388 if (kd == 0) 389 errx(1, "kvm_openfiles: %s", errbuf); 390 391 if (kvm_nlist(kd, namelist) == -1) 392 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 393 394 (void)nlread(X_HZ, hz); 395 (void)nlread(X_STATHZ, stathz); 396 if (stathz) 397 hz = stathz; 398 399 /* 400 * If the user stops the program (control-Z) and then resumes it, 401 * print out the header again. 402 */ 403 (void)signal(SIGCONT, phdr); 404 405 for (headercount = 1;;) { 406 struct devinfo *tmp_dinfo; 407 long tmp; 408 double etime; 409 410 if (!--headercount) { 411 phdr(0); 412 headercount = 20; 413 } 414 (void)kvm_read(kd, namelist[X_TK_NIN].n_value, 415 &cur.tk_nin, sizeof(cur.tk_nin)); 416 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, 417 &cur.tk_nout, sizeof(cur.tk_nout)); 418 (void)kvm_read(kd, namelist[X_CP_TIME].n_value, 419 cur.cp_time, sizeof(cur.cp_time)); 420 421 tmp_dinfo = last.dinfo; 422 last.dinfo = cur.dinfo; 423 cur.dinfo = tmp_dinfo; 424 425 last.busy_time = cur.busy_time; 426 427 /* 428 * Here what we want to do is refresh our device stats. 429 * getdevs() returns 1 when the device list has changed. 430 * If the device list has changed, we want to go through 431 * the selection process again, in case a device that we 432 * were previously displaying has gone away. 433 */ 434 switch (getdevs(&cur)) { 435 case -1: 436 errx(1, "%s", devstat_errbuf); 437 break; 438 case 1: { 439 int retval; 440 441 num_devices = cur.dinfo->numdevs; 442 generation = cur.dinfo->generation; 443 retval = selectdevs(&dev_select, &num_selected, 444 &num_selections, &select_generation, 445 generation, cur.dinfo->devices, 446 num_devices, matches, num_matches, 447 specified_devices, 448 num_devices_specified, 449 select_mode, maxshowdevs, hflag); 450 switch(retval) { 451 case -1: 452 errx(1, "%s", devstat_errbuf); 453 break; 454 case 1: 455 phdr(0); 456 headercount = 20; 457 break; 458 default: 459 break; 460 } 461 break; 462 } 463 default: 464 break; 465 } 466 467 /* 468 * We only want to re-select devices if we're in 'top' 469 * mode. This is the only mode where the devices selected 470 * could actually change. 471 */ 472 if (hflag > 0) { 473 int retval; 474 retval = selectdevs(&dev_select, &num_selected, 475 &num_selections, &select_generation, 476 generation, cur.dinfo->devices, 477 num_devices, matches, num_matches, 478 specified_devices, 479 num_devices_specified, 480 select_mode, maxshowdevs, hflag); 481 switch(retval) { 482 case -1: 483 errx(1,"%s", devstat_errbuf); 484 break; 485 case 1: 486 phdr(0); 487 headercount = 20; 488 break; 489 default: 490 break; 491 } 492 } 493 494 tmp = cur.tk_nin; 495 cur.tk_nin -= last.tk_nin; 496 last.tk_nin = tmp; 497 tmp = cur.tk_nout; 498 cur.tk_nout -= last.tk_nout; 499 last.tk_nout = tmp; 500 501 etime = 0.0; 502 503 #define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 504 505 for (i = 0; i < CPUSTATES; i++) { 506 X(cp_time); 507 etime += cur.cp_time[i]; 508 } 509 if (etime == 0.0) 510 etime = 1.0; 511 etime /= (float)hz; 512 if ((dflag == 0) || (Tflag > 0)) 513 printf("%4.0f%5.0f", cur.tk_nin / etime, 514 cur.tk_nout/etime); 515 devstats(hflag); 516 if ((dflag == 0) || (Cflag > 0)) 517 cpustats(); 518 printf("\n"); 519 fflush(stdout); 520 521 if (count >= 0 && --count <= 0) 522 break; 523 524 sleep(waittime); 525 } 526 527 exit(0); 528 } 529 530 static void 531 phdr(int signo) 532 { 533 register int i; 534 int printed; 535 536 if ((dflag == 0) || (Tflag > 0)) 537 (void)printf(" tty"); 538 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ 539 int di; 540 if ((dev_select[i].selected != 0) 541 && (dev_select[i].selected <= maxshowdevs)) { 542 di = dev_select[i].position; 543 if (oflag > 0) 544 (void)printf("%12.6s%d ", 545 cur.dinfo->devices[di].device_name, 546 cur.dinfo->devices[di].unit_number); 547 else 548 printf("%15.6s%d ", 549 cur.dinfo->devices[di].device_name, 550 cur.dinfo->devices[di].unit_number); 551 printed++; 552 } 553 } 554 if ((dflag == 0) || (Cflag > 0)) 555 (void)printf(" cpu\n"); 556 else 557 (void)printf("\n"); 558 559 if ((dflag == 0) || (Tflag > 0)) 560 (void)printf(" tin tout"); 561 562 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ 563 if ((dev_select[i].selected != 0) 564 && (dev_select[i].selected <= maxshowdevs)) { 565 if (oflag > 0) { 566 if (Iflag == 0) 567 (void)printf(" sps tps msps "); 568 else 569 (void)printf(" blk xfr msps "); 570 } else { 571 if (Iflag == 0) 572 printf(" KB/t tps MB/s "); 573 else 574 printf(" KB/t xfrs MB "); 575 } 576 printed++; 577 } 578 } 579 if ((dflag == 0) || (Cflag > 0)) 580 (void)printf(" us ni sy in id\n"); 581 else 582 printf("\n"); 583 584 } 585 586 static void 587 devstats(int perf_select) 588 { 589 register int dn; 590 long double transfers_per_second; 591 long double kb_per_transfer, mb_per_second; 592 u_int64_t total_bytes, total_transfers, total_blocks; 593 long double busy_seconds; 594 long double total_mb; 595 long double blocks_per_second, ms_per_transaction; 596 597 /* 598 * Calculate elapsed time up front, since it's the same for all 599 * devices. 600 */ 601 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 602 603 for (dn = 0; dn < num_devices; dn++) { 604 int di; 605 606 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 607 || (dev_select[dn].selected > maxshowdevs)) 608 continue; 609 610 di = dev_select[dn].position; 611 612 if (compute_stats(&cur.dinfo->devices[di], 613 &last.dinfo->devices[di], busy_seconds, 614 &total_bytes, &total_transfers, 615 &total_blocks, &kb_per_transfer, 616 &transfers_per_second, &mb_per_second, 617 &blocks_per_second, &ms_per_transaction)!= 0) 618 errx(1, "%s", devstat_errbuf); 619 620 if (perf_select != 0) { 621 dev_select[dn].bytes = total_bytes; 622 if ((dev_select[dn].selected == 0) 623 || (dev_select[dn].selected > maxshowdevs)) 624 continue; 625 } 626 627 if (Kflag) { 628 int block_size = cur.dinfo->devices[di].block_size; 629 total_blocks = total_blocks * (block_size ? 630 block_size : 512) / 1024; 631 } 632 633 if (oflag > 0) { 634 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 635 636 if (Iflag == 0) 637 printf("%4.0Lf%4.0Lf%5.*Lf ", 638 blocks_per_second, 639 transfers_per_second, 640 msdig, 641 ms_per_transaction); 642 else 643 printf("%4.1qu%4.1qu%5.*Lf ", 644 total_blocks, 645 total_transfers, 646 msdig, 647 ms_per_transaction); 648 } else { 649 if (Iflag == 0) 650 printf(" %5.2Lf %3.0Lf %5.2Lf ", 651 kb_per_transfer, 652 transfers_per_second, 653 mb_per_second); 654 else { 655 total_mb = total_bytes; 656 total_mb /= 1024 * 1024; 657 658 printf(" %5.2Lf %3.1qu %5.2Lf ", 659 kb_per_transfer, 660 total_transfers, 661 total_mb); 662 } 663 } 664 } 665 } 666 667 static void 668 cpustats(void) 669 { 670 register int state; 671 double time; 672 673 time = 0.0; 674 675 for (state = 0; state < CPUSTATES; ++state) 676 time += cur.cp_time[state]; 677 for (state = 0; state < CPUSTATES; ++state) 678 printf(" %2.0f", 679 100. * cur.cp_time[state] / (time ? time : 1)); 680 } 681