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