iostat.c revision 39387
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: iostat.c,v 1.10 1998/09/16 18:03:44 dillon Exp $ 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 119struct 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 134struct statinfo cur, last; 135int num_devices; 136struct device_selection *dev_select; 137int maxshowdevs; 138int 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 */ 144static void usage(void); 145static void phdr(int signo); 146static void devstats(int perf_select); 147static void cpustats(void); 148 149static void 150usage(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 163int 164main(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: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 devcies %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 cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 290 last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 291 bzero(cur.dinfo, sizeof(struct devinfo)); 292 bzero(last.dinfo, sizeof(struct devinfo)); 293 294 /* 295 * Grab all the devices. We don't look to see if the list has 296 * changed here, since it almost certainly has. We only look for 297 * errors. 298 */ 299 if (getdevs(&cur) == -1) 300 errx(1, "%s", devstat_errbuf); 301 302 num_devices = cur.dinfo->numdevs; 303 generation = cur.dinfo->generation; 304 305 /* 306 * If the user specified any devices on the command line, see if 307 * they are in the list of devices we have now. 308 */ 309 specified_devices = (char **)malloc(sizeof(char *)); 310 for (num_devices_specified = 0; *argv; ++argv) { 311 if (isdigit(**argv)) 312 break; 313 num_devices_specified++; 314 specified_devices = (char **)realloc(specified_devices, 315 sizeof(char *) * 316 num_devices_specified); 317 specified_devices[num_devices_specified - 1] = *argv; 318 319 } 320 if (nflag == 0 && maxshowdevs < num_devices_specified) 321 maxshowdevs = num_devices_specified; 322 323 dev_select = NULL; 324 325 if ((num_devices_specified == 0) && (num_matches == 0)) 326 select_mode = DS_SELECT_ADD; 327 else 328 select_mode = DS_SELECT_ONLY; 329 330 /* 331 * At this point, selectdevs will almost surely indicate that the 332 * device list has changed, so we don't look for return values of 0 333 * or 1. If we get back -1, though, there is an error. 334 */ 335 if (selectdevs(&dev_select, &num_selected, 336 &num_selections, &select_generation, 337 generation, cur.dinfo->devices, num_devices, 338 matches, num_matches, 339 specified_devices, num_devices_specified, 340 select_mode, maxshowdevs, hflag) == -1) 341 errx(1, "%s", devstat_errbuf); 342 343 /* 344 * Look for the traditional wait time and count arguments. 345 */ 346 if (*argv) { 347 waittime = atoi(*argv); 348 349 /* Let the user know he goofed, but keep going anyway */ 350 if (wflag != 0) 351 warnx("discarding previous wait interval, using" 352 " %d instead", waittime); 353 wflag++; 354 355 if (*++argv) { 356 count = atoi(*argv); 357 if (cflag != 0) 358 warnx("discarding previous count, using %d" 359 " instead", count); 360 cflag++; 361 } else 362 count = -1; 363 } 364 365 /* 366 * If the user specified a count, but not an interval, we default 367 * to an interval of 1 second. 368 */ 369 if ((wflag == 0) && (cflag > 0)) 370 waittime = 1; 371 372 /* 373 * If the user specified a wait time, but not a count, we want to 374 * go on ad infinitum. This can be redundant if the user uses the 375 * traditional method of specifying the wait, since in that case we 376 * already set count = -1 above. Oh well. 377 */ 378 if ((wflag > 0) && (cflag == 0)) 379 count = -1; 380 381 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 382 383 if (kd == 0) 384 errx(1, "kvm_openfiles: %s", errbuf); 385 386 if (kvm_nlist(kd, namelist) == -1) 387 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 388 389 (void)nlread(X_HZ, hz); 390 (void)nlread(X_STATHZ, stathz); 391 if (stathz) 392 hz = stathz; 393 394 /* 395 * If the user stops the program (control-Z) and then resumes it, 396 * print out the header again. 397 */ 398 (void)signal(SIGCONT, phdr); 399 400 for (headercount = 1;;) { 401 struct devinfo *tmp_dinfo; 402 long tmp; 403 double etime; 404 405 if (!--headercount) { 406 phdr(0); 407 headercount = 20; 408 } 409 (void)kvm_read(kd, namelist[X_TK_NIN].n_value, 410 &cur.tk_nin, sizeof(cur.tk_nin)); 411 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, 412 &cur.tk_nout, sizeof(cur.tk_nout)); 413 (void)kvm_read(kd, namelist[X_CP_TIME].n_value, 414 cur.cp_time, sizeof(cur.cp_time)); 415 416 tmp_dinfo = last.dinfo; 417 last.dinfo = cur.dinfo; 418 cur.dinfo = tmp_dinfo; 419 420 last.busy_time = cur.busy_time; 421 422 /* 423 * Here what we want to do is refresh our device stats. 424 * getdevs() returns 1 when the device list has changed. 425 * If the device list has changed, we want to go through 426 * the selection process again, in case a device that we 427 * were previously displaying has gone away. 428 */ 429 switch (getdevs(&cur)) { 430 case -1: 431 errx(1, "%s", devstat_errbuf); 432 break; 433 case 1: { 434 int retval; 435 436 num_devices = cur.dinfo->numdevs; 437 generation = cur.dinfo->generation; 438 retval = selectdevs(&dev_select, &num_selected, 439 &num_selections, &select_generation, 440 generation, cur.dinfo->devices, 441 num_devices, matches, num_matches, 442 specified_devices, 443 num_devices_specified, 444 select_mode, maxshowdevs, hflag); 445 switch(retval) { 446 case -1: 447 errx(1, "%s", devstat_errbuf); 448 break; 449 case 1: 450 phdr(0); 451 headercount = 20; 452 break; 453 default: 454 break; 455 } 456 break; 457 } 458 default: 459 break; 460 } 461 462 /* 463 * We only want to re-select devices if we're in 'top' 464 * mode. This is the only mode where the devices selected 465 * could actually change. 466 */ 467 if (hflag > 0) { 468 int retval; 469 retval = selectdevs(&dev_select, &num_selected, 470 &num_selections, &select_generation, 471 generation, cur.dinfo->devices, 472 num_devices, matches, num_matches, 473 specified_devices, 474 num_devices_specified, 475 select_mode, maxshowdevs, hflag); 476 switch(retval) { 477 case -1: 478 errx(1,"%s", devstat_errbuf); 479 break; 480 case 1: 481 phdr(0); 482 headercount = 20; 483 break; 484 default: 485 break; 486 } 487 } 488 489 tmp = cur.tk_nin; 490 cur.tk_nin -= last.tk_nin; 491 last.tk_nin = tmp; 492 tmp = cur.tk_nout; 493 cur.tk_nout -= last.tk_nout; 494 last.tk_nout = tmp; 495 496 etime = 0.0; 497 498#define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 499 500 for (i = 0; i < CPUSTATES; i++) { 501 X(cp_time); 502 etime += cur.cp_time[i]; 503 } 504 if (etime == 0.0) 505 etime = 1.0; 506 etime /= (float)hz; 507 if ((dflag == 0) || (Tflag > 0)) 508 printf("%4.0f%5.0f", cur.tk_nin / etime, 509 cur.tk_nout/etime); 510 devstats(hflag); 511 if ((dflag == 0) || (Cflag > 0)) 512 cpustats(); 513 printf("\n"); 514 fflush(stdout); 515 516 if (count >= 0 && --count <= 0) 517 break; 518 519 sleep(waittime); 520 } 521 522 exit(0); 523} 524 525static void 526phdr(int signo) 527{ 528 register int i; 529 int printed; 530 531 if ((dflag == 0) || (Tflag > 0)) 532 (void)printf(" tty"); 533 for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){ 534 int di; 535 if ((dev_select[i].selected != 0) 536 && (dev_select[i].selected <= maxshowdevs)) { 537 di = dev_select[i].position; 538 if (oflag > 0) 539 (void)printf("%12.6s%d ", 540 cur.dinfo->devices[di].device_name, 541 cur.dinfo->devices[di].unit_number); 542 else 543 printf("%15.6s%d ", 544 cur.dinfo->devices[di].device_name, 545 cur.dinfo->devices[di].unit_number); 546 printed++; 547 } 548 } 549 if ((dflag == 0) || (Cflag > 0)) 550 (void)printf(" cpu\n"); 551 else 552 (void)printf("\n"); 553 554 if ((dflag == 0) || (Tflag > 0)) 555 (void)printf(" tin tout"); 556 557 for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){ 558 if ((dev_select[i].selected != 0) 559 && (dev_select[i].selected <= maxshowdevs)) { 560 if (oflag > 0) { 561 if (Iflag == 0) 562 (void)printf(" sps tps msps "); 563 else 564 (void)printf(" blk xfr msps "); 565 } else { 566 if (Iflag == 0) 567 printf(" KB/t tps MB/s "); 568 else 569 printf(" KB/t xfrs MB "); 570 } 571 printed++; 572 } 573 } 574 if ((dflag == 0) || (Cflag > 0)) 575 (void)printf(" us ni sy in id\n"); 576 else 577 printf("\n"); 578 579} 580 581static void 582devstats(int perf_select) 583{ 584 register int dn; 585 long double transfers_per_second; 586 long double kb_per_transfer, mb_per_second; 587 u_int64_t total_bytes, total_transfers, total_blocks; 588 long double busy_seconds; 589 long double total_mb; 590 long double blocks_per_second, ms_per_transaction; 591 592 /* 593 * Calculate elapsed time up front, since it's the same for all 594 * devices. 595 */ 596 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 597 598 for (dn = 0; dn < num_devices; dn++) { 599 int di; 600 601 if (((perf_select == 0) && (dev_select[dn].selected == 0)) 602 || (dev_select[dn].selected > maxshowdevs)) 603 continue; 604 605 di = dev_select[dn].position; 606 607 if (compute_stats(&cur.dinfo->devices[di], 608 &last.dinfo->devices[di], busy_seconds, 609 &total_bytes, &total_transfers, 610 &total_blocks, &kb_per_transfer, 611 &transfers_per_second, &mb_per_second, 612 &blocks_per_second, &ms_per_transaction)!= 0) 613 errx(1, "%s", devstat_errbuf); 614 615 if (perf_select != 0) { 616 dev_select[dn].bytes = total_bytes; 617 if ((dev_select[dn].selected == 0) 618 || (dev_select[dn].selected > maxshowdevs)) 619 continue; 620 } 621 622 if (Kflag) { 623 int block_size = cur.dinfo->devices[di].block_size; 624 total_blocks = total_blocks * (block_size ? 625 block_size : 512) / 1024; 626 } 627 628 if (oflag > 0) { 629 int msdig = (ms_per_transaction < 100.0) ? 1 : 0; 630 631 if (Iflag == 0) 632 printf("%4.0Lf%4.0Lf%5.*Lf ", 633 blocks_per_second, 634 transfers_per_second, 635 msdig, 636 ms_per_transaction); 637 else 638 printf("%4.1qu%4.1qu%5.*Lf ", 639 total_blocks, 640 total_transfers, 641 msdig, 642 ms_per_transaction); 643 } else { 644 if (Iflag == 0) 645 printf(" %5.2Lf %3.0Lf %5.2Lf ", 646 kb_per_transfer, 647 transfers_per_second, 648 mb_per_second); 649 else { 650 total_mb = total_bytes; 651 total_mb /= 1024 * 1024; 652 653 printf(" %5.2Lf %3.1qu %5.2Lf ", 654 kb_per_transfer, 655 total_transfers, 656 total_mb); 657 } 658 } 659 } 660} 661 662static void 663cpustats(void) 664{ 665 register int state; 666 double time; 667 668 time = 0.0; 669 670 for (state = 0; state < CPUSTATES; ++state) 671 time += cur.cp_time[state]; 672 for (state = 0; state < CPUSTATES; ++state) 673 printf("%3.0f", 674 100. * cur.cp_time[state] / (time ? time : 1)); 675} 676