1 /* $NetBSD: iostat.c,v 1.59 2011/02/14 02:43:37 enami Exp $ */ 2 3 /* 4 * Copyright (c) 1996 John M. Vinopal 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed for the NetBSD Project 18 * by John M. Vinopal. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 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. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #ifndef lint 66 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\ 67 The Regents of the University of California. All rights reserved."); 68 #endif /* not lint */ 69 70 #ifndef lint 71 #if 0 72 static char sccsid[] = "@(#)iostat.c 8.3 (Berkeley) 4/28/95"; 73 #else 74 __RCSID("$NetBSD: iostat.c,v 1.59 2011/02/14 02:43:37 enami Exp $"); 75 #endif 76 #endif /* not lint */ 77 78 #include <sys/types.h> 79 #include <sys/ioctl.h> 80 #include <sys/sched.h> 81 #include <sys/time.h> 82 83 #include <err.h> 84 #include <ctype.h> 85 #include <signal.h> 86 #include <stdio.h> 87 #include <stdlib.h> 88 #include <string.h> 89 #include <unistd.h> 90 #include <math.h> 91 92 #include "drvstats.h" 93 94 /* Namelist and memory files. */ 95 char *nlistf, *memf; 96 97 int hz, reps, interval; 98 static int todo = 0; 99 static int defdrives; 100 static int winlines = 20; 101 static int wincols = 80; 102 103 #define MAX(a,b) (((a)>(b))?(a):(b)) 104 105 #define ISSET(x, a) ((x) & (a)) 106 #define SHOW_CPU (1<<0) 107 #define SHOW_TTY (1<<1) 108 #define SHOW_STATS_1 (1<<2) 109 #define SHOW_STATS_2 (1<<3) 110 #define SHOW_STATS_X (1<<4) 111 #define SHOW_TOTALS (1<<7) 112 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X) 113 114 static void cpustats(void); 115 static void drive_stats(double); 116 static void drive_stats2(double); 117 static void drive_statsx(double); 118 static void sig_header(int); 119 static volatile int do_header; 120 static void header(void); 121 static void usage(void); 122 static void display(void); 123 static int selectdrives(int, char *[]); 124 125 int main(int, char *[]); 126 127 int 128 main(int argc, char *argv[]) 129 { 130 int ch, hdrcnt, ndrives, lines; 131 struct timespec tv; 132 struct ttysize ts; 133 134 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1) 135 switch (ch) { 136 case 'c': 137 if ((reps = atoi(optarg)) <= 0) 138 errx(1, "repetition count <= 0."); 139 break; 140 case 'C': 141 todo |= SHOW_CPU; 142 break; 143 case 'd': 144 todo &= ~SHOW_STATS_ALL; 145 todo |= SHOW_STATS_1; 146 break; 147 case 'D': 148 todo &= ~SHOW_STATS_ALL; 149 todo |= SHOW_STATS_2; 150 break; 151 case 'I': 152 todo |= SHOW_TOTALS; 153 break; 154 case 'M': 155 memf = optarg; 156 break; 157 case 'N': 158 nlistf = optarg; 159 break; 160 case 'T': 161 todo |= SHOW_TTY; 162 break; 163 case 'w': 164 if ((interval = atoi(optarg)) <= 0) 165 errx(1, "interval <= 0."); 166 break; 167 case 'x': 168 todo &= ~SHOW_STATS_ALL; 169 todo |= SHOW_STATS_X; 170 break; 171 case '?': 172 default: 173 usage(); 174 } 175 argc -= optind; 176 argv += optind; 177 178 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL)) 179 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 180 if (ISSET(todo, SHOW_STATS_X)) { 181 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL); 182 todo |= SHOW_STATS_X; 183 } 184 185 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) { 186 if (ts.ts_lines) 187 winlines = ts.ts_lines; 188 if (ts.ts_cols) 189 wincols = ts.ts_cols; 190 } 191 192 defdrives = wincols; 193 if (ISSET(todo, SHOW_CPU)) 194 defdrives -= 16; /* XXX magic number */ 195 if (ISSET(todo, SHOW_TTY)) 196 defdrives -= 9; /* XXX magic number */ 197 defdrives /= 18; /* XXX magic number */ 198 199 drvinit(0); 200 cpureadstats(); 201 drvreadstats(); 202 ndrives = selectdrives(argc, argv); 203 if (ndrives == 0) { 204 /* No drives are selected. No need to show drive stats. */ 205 todo &= ~SHOW_STATS_ALL; 206 if (todo == 0) 207 errx(1, "no drives"); 208 } 209 if (ISSET(todo, SHOW_STATS_X)) 210 lines = ndrives; 211 else 212 lines = 1; 213 214 tv.tv_sec = interval; 215 tv.tv_nsec = 0; 216 217 /* print a new header on sigcont */ 218 (void)signal(SIGCONT, sig_header); 219 220 for (hdrcnt = 1;;) { 221 if (do_header || lines > 1 || (hdrcnt -= lines) <= 0) { 222 do_header = 0; 223 header(); 224 hdrcnt = winlines - 4; 225 } 226 227 if (!ISSET(todo, SHOW_TOTALS)) { 228 cpuswap(); 229 drvswap(); 230 tkswap(); 231 } 232 233 display(); 234 235 if (reps >= 0 && --reps <= 0) 236 break; 237 nanosleep(&tv, NULL); 238 cpureadstats(); 239 drvreadstats(); 240 } 241 exit(0); 242 } 243 244 static void 245 sig_header(int signo) 246 { 247 do_header = 1; 248 } 249 250 static void 251 header() 252 { 253 size_t i; 254 255 /* Main Headers. */ 256 if (ISSET(todo, SHOW_STATS_X)) { 257 if (ISSET(todo, SHOW_TOTALS)) { 258 (void)printf( 259 "device read KB/t xfr time MB "); 260 (void)printf(" write KB/t xfr time MB\n"); 261 } else { 262 (void)printf( 263 "device read KB/t r/s time MB/s"); 264 (void)printf(" write KB/t w/s time MB/s\n"); 265 } 266 return; 267 } 268 269 if (ISSET(todo, SHOW_TTY)) 270 (void)printf(" tty"); 271 272 if (ISSET(todo, SHOW_STATS_1)) { 273 for (i = 0; i < ndrive; i++) 274 if (cur.select[i]) 275 (void)printf(" %9.9s ", cur.name[i]); 276 } 277 278 if (ISSET(todo, SHOW_STATS_2)) { 279 for (i = 0; i < ndrive; i++) 280 if (cur.select[i]) 281 (void)printf(" %9.9s ", cur.name[i]); 282 } 283 284 if (ISSET(todo, SHOW_CPU)) 285 (void)printf(" CPU"); 286 287 printf("\n"); 288 289 /* Sub-Headers. */ 290 if (ISSET(todo, SHOW_TTY)) 291 printf(" tin tout"); 292 293 if (ISSET(todo, SHOW_STATS_1)) { 294 for (i = 0; i < ndrive; i++) 295 if (cur.select[i]) { 296 if (ISSET(todo, SHOW_TOTALS)) 297 (void)printf(" KB/t xfr MB "); 298 else 299 (void)printf(" KB/t t/s MB/s "); 300 } 301 } 302 303 if (ISSET(todo, SHOW_STATS_2)) { 304 for (i = 0; i < ndrive; i++) 305 if (cur.select[i]) 306 (void)printf(" KB xfr time "); 307 } 308 309 if (ISSET(todo, SHOW_CPU)) 310 (void)printf(" us ni sy in id"); 311 printf("\n"); 312 } 313 314 static void 315 drive_stats(double etime) 316 { 317 size_t dn; 318 double atime, mbps; 319 320 for (dn = 0; dn < ndrive; ++dn) { 321 if (!cur.select[dn]) 322 continue; 323 /* average Kbytes per transfer. */ 324 if (cur.rxfer[dn] + cur.wxfer[dn]) 325 mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) / 326 1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]); 327 else 328 mbps = 0.0; 329 (void)printf(" %5.*f", 330 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 331 332 /* average transfers per second. */ 333 (void)printf(" %4.0f", 334 (cur.rxfer[dn] + cur.wxfer[dn]) / etime); 335 336 /* time busy in drive activity */ 337 atime = (double)cur.time[dn].tv_sec + 338 ((double)cur.time[dn].tv_usec / (double)1000000); 339 340 /* Megabytes per second. */ 341 if (atime != 0.0) 342 mbps = (cur.rbytes[dn] + cur.wbytes[dn]) / 343 (double)(1024 * 1024); 344 else 345 mbps = 0; 346 mbps /= etime; 347 (void)printf(" %5.*f ", 348 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 349 } 350 } 351 352 static void 353 drive_stats2(double etime) 354 { 355 size_t dn; 356 double atime; 357 358 for (dn = 0; dn < ndrive; ++dn) { 359 if (!cur.select[dn]) 360 continue; 361 362 /* average kbytes per second. */ 363 (void)printf(" %5.0f", 364 (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / etime); 365 366 /* average transfers per second. */ 367 (void)printf(" %5.0f", 368 (cur.rxfer[dn] + cur.wxfer[dn]) / etime); 369 370 /* average time busy in drive activity */ 371 atime = (double)cur.time[dn].tv_sec + 372 ((double)cur.time[dn].tv_usec / (double)1000000); 373 (void)printf(" %4.2f ", atime / etime); 374 } 375 } 376 377 static void 378 drive_statsx(double etime) 379 { 380 size_t dn; 381 double atime, kbps; 382 383 for (dn = 0; dn < ndrive; ++dn) { 384 if (!cur.select[dn]) 385 continue; 386 387 (void)printf("%-8.8s", cur.name[dn]); 388 389 /* average read Kbytes per transfer */ 390 if (cur.rxfer[dn]) 391 kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn]; 392 else 393 kbps = 0.0; 394 (void)printf(" %8.2f", kbps); 395 396 /* average read transfers 397 (per second) */ 398 (void)printf(" %6.0f", cur.rxfer[dn] / etime); 399 400 /* time read busy in drive activity */ 401 atime = (double)cur.time[dn].tv_sec + 402 ((double)cur.time[dn].tv_usec / (double)1000000); 403 (void)printf(" %6.2f", atime / etime); 404 405 /* average read megabytes 406 (per second) */ 407 (void)printf(" %8.2f", 408 cur.rbytes[dn] / (1024.0 * 1024) / etime); 409 410 411 /* average write Kbytes per transfer */ 412 if (cur.wxfer[dn]) 413 kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn]; 414 else 415 kbps = 0.0; 416 (void)printf(" %8.2f", kbps); 417 418 /* average write transfers 419 (per second) */ 420 (void)printf(" %6.0f", cur.wxfer[dn] / etime); 421 422 /* time write busy in drive activity */ 423 atime = (double)cur.time[dn].tv_sec + 424 ((double)cur.time[dn].tv_usec / (double)1000000); 425 (void)printf(" %6.2f", atime / etime); 426 427 /* average write megabytes 428 (per second) */ 429 (void)printf(" %8.2f\n", 430 cur.wbytes[dn] / (1024.0 * 1024) / etime); 431 } 432 } 433 434 static void 435 cpustats(void) 436 { 437 int state; 438 double ttime; 439 440 ttime = 0; 441 for (state = 0; state < CPUSTATES; ++state) 442 ttime += cur.cp_time[state]; 443 if (!ttime) 444 ttime = 1.0; 445 /* States are generally never 100% and can use %3.0f. */ 446 for (state = 0; state < CPUSTATES; ++state) 447 printf(" %2.0f", 100. * cur.cp_time[state] / ttime); 448 } 449 450 static void 451 usage(void) 452 { 453 454 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] " 455 "[-N system] [-w wait] [drives]\n"); 456 exit(1); 457 } 458 459 static void 460 display(void) 461 { 462 double etime; 463 464 /* Sum up the elapsed ticks. */ 465 etime = cur.cp_etime; 466 467 /* 468 * If we're showing totals only, then don't divide by the 469 * system time. 470 */ 471 if (ISSET(todo, SHOW_TOTALS)) 472 etime = 1.0; 473 474 if (ISSET(todo, SHOW_STATS_X)) { 475 drive_statsx(etime); 476 goto out; 477 } 478 479 if (ISSET(todo, SHOW_TTY)) 480 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime); 481 482 if (ISSET(todo, SHOW_STATS_1)) { 483 drive_stats(etime); 484 } 485 486 487 if (ISSET(todo, SHOW_STATS_2)) { 488 drive_stats2(etime); 489 } 490 491 492 if (ISSET(todo, SHOW_CPU)) 493 cpustats(); 494 495 (void)printf("\n"); 496 497 out: 498 (void)fflush(stdout); 499 } 500 501 static int 502 selectdrives(int argc, char *argv[]) 503 { 504 int i, maxdrives, ndrives, tried; 505 506 /* 507 * Choose drives to be displayed. Priority goes to (in order) drives 508 * supplied as arguments and default drives. If everything isn't 509 * filled in and there are drives not taken care of, display the first 510 * few that fit. 511 * 512 * The backward compatibility #ifdefs permit the syntax: 513 * iostat [ drives ] [ interval [ count ] ] 514 */ 515 516 #define BACKWARD_COMPATIBILITY 517 for (tried = ndrives = 0; *argv; ++argv) { 518 #ifdef BACKWARD_COMPATIBILITY 519 if (isdigit((unsigned char)**argv)) 520 break; 521 #endif 522 tried++; 523 for (i = 0; i < (int)ndrive; i++) { 524 if (strcmp(cur.name[i], *argv)) 525 continue; 526 cur.select[i] = 1; 527 ++ndrives; 528 } 529 530 } 531 532 if (ndrives == 0 && tried == 0) { 533 /* 534 * Pick up to defdrives (or all if -x is given) drives 535 * if none specified. 536 */ 537 maxdrives = (ISSET(todo, SHOW_STATS_X) || 538 (int)ndrive < defdrives) 539 ? (int)(ndrive) : defdrives; 540 for (i = 0; i < maxdrives; i++) { 541 cur.select[i] = 1; 542 543 ++ndrives; 544 if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives) 545 break; 546 } 547 } 548 549 #ifdef BACKWARD_COMPATIBILITY 550 if (*argv) { 551 interval = atoi(*argv); 552 if (*++argv) 553 reps = atoi(*argv); 554 } 555 #endif 556 557 if (interval) { 558 if (!reps) 559 reps = -1; 560 } else 561 if (reps) 562 interval = 1; 563 564 return (ndrives); 565 } 566