1 /* $NetBSD: iostat.c,v 1.60 2011/08/30 19:06:06 joerg 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.60 2011/08/30 19:06:06 joerg 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; 98 static int reps, interval; 99 static int todo = 0; 100 static int defdrives; 101 static int winlines = 20; 102 static int wincols = 80; 103 104 #define MAX(a,b) (((a)>(b))?(a):(b)) 105 106 #define ISSET(x, a) ((x) & (a)) 107 #define SHOW_CPU (1<<0) 108 #define SHOW_TTY (1<<1) 109 #define SHOW_STATS_1 (1<<2) 110 #define SHOW_STATS_2 (1<<3) 111 #define SHOW_STATS_X (1<<4) 112 #define SHOW_TOTALS (1<<7) 113 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X) 114 115 static void cpustats(void); 116 static void drive_stats(double); 117 static void drive_stats2(double); 118 static void drive_statsx(double); 119 static void sig_header(int); 120 static volatile int do_header; 121 static void header(void); 122 __dead static void usage(void); 123 static void display(void); 124 static int selectdrives(int, char *[]); 125 126 int 127 main(int argc, char *argv[]) 128 { 129 int ch, hdrcnt, ndrives, lines; 130 struct timespec tv; 131 struct ttysize ts; 132 133 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1) 134 switch (ch) { 135 case 'c': 136 if ((reps = atoi(optarg)) <= 0) 137 errx(1, "repetition count <= 0."); 138 break; 139 case 'C': 140 todo |= SHOW_CPU; 141 break; 142 case 'd': 143 todo &= ~SHOW_STATS_ALL; 144 todo |= SHOW_STATS_1; 145 break; 146 case 'D': 147 todo &= ~SHOW_STATS_ALL; 148 todo |= SHOW_STATS_2; 149 break; 150 case 'I': 151 todo |= SHOW_TOTALS; 152 break; 153 case 'M': 154 memf = optarg; 155 break; 156 case 'N': 157 nlistf = optarg; 158 break; 159 case 'T': 160 todo |= SHOW_TTY; 161 break; 162 case 'w': 163 if ((interval = atoi(optarg)) <= 0) 164 errx(1, "interval <= 0."); 165 break; 166 case 'x': 167 todo &= ~SHOW_STATS_ALL; 168 todo |= SHOW_STATS_X; 169 break; 170 case '?': 171 default: 172 usage(); 173 } 174 argc -= optind; 175 argv += optind; 176 177 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL)) 178 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 179 if (ISSET(todo, SHOW_STATS_X)) { 180 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL); 181 todo |= SHOW_STATS_X; 182 } 183 184 if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) { 185 if (ts.ts_lines) 186 winlines = ts.ts_lines; 187 if (ts.ts_cols) 188 wincols = ts.ts_cols; 189 } 190 191 defdrives = wincols; 192 if (ISSET(todo, SHOW_CPU)) 193 defdrives -= 16; /* XXX magic number */ 194 if (ISSET(todo, SHOW_TTY)) 195 defdrives -= 9; /* XXX magic number */ 196 defdrives /= 18; /* XXX magic number */ 197 198 drvinit(0); 199 cpureadstats(); 200 drvreadstats(); 201 ndrives = selectdrives(argc, argv); 202 if (ndrives == 0) { 203 /* No drives are selected. No need to show drive stats. */ 204 todo &= ~SHOW_STATS_ALL; 205 if (todo == 0) 206 errx(1, "no drives"); 207 } 208 if (ISSET(todo, SHOW_STATS_X)) 209 lines = ndrives; 210 else 211 lines = 1; 212 213 tv.tv_sec = interval; 214 tv.tv_nsec = 0; 215 216 /* print a new header on sigcont */ 217 (void)signal(SIGCONT, sig_header); 218 219 for (hdrcnt = 1;;) { 220 if (do_header || lines > 1 || (hdrcnt -= lines) <= 0) { 221 do_header = 0; 222 header(); 223 hdrcnt = winlines - 4; 224 } 225 226 if (!ISSET(todo, SHOW_TOTALS)) { 227 cpuswap(); 228 drvswap(); 229 tkswap(); 230 } 231 232 display(); 233 234 if (reps >= 0 && --reps <= 0) 235 break; 236 nanosleep(&tv, NULL); 237 cpureadstats(); 238 drvreadstats(); 239 } 240 exit(0); 241 } 242 243 static void 244 sig_header(int signo) 245 { 246 do_header = 1; 247 } 248 249 static void 250 header(void) 251 { 252 size_t i; 253 254 /* Main Headers. */ 255 if (ISSET(todo, SHOW_STATS_X)) { 256 if (ISSET(todo, SHOW_TOTALS)) { 257 (void)printf( 258 "device read KB/t xfr time MB "); 259 (void)printf(" write KB/t xfr time MB\n"); 260 } else { 261 (void)printf( 262 "device read KB/t r/s time MB/s"); 263 (void)printf(" write KB/t w/s time MB/s\n"); 264 } 265 return; 266 } 267 268 if (ISSET(todo, SHOW_TTY)) 269 (void)printf(" tty"); 270 271 if (ISSET(todo, SHOW_STATS_1)) { 272 for (i = 0; i < ndrive; i++) 273 if (cur.select[i]) 274 (void)printf(" %9.9s ", cur.name[i]); 275 } 276 277 if (ISSET(todo, SHOW_STATS_2)) { 278 for (i = 0; i < ndrive; i++) 279 if (cur.select[i]) 280 (void)printf(" %9.9s ", cur.name[i]); 281 } 282 283 if (ISSET(todo, SHOW_CPU)) 284 (void)printf(" CPU"); 285 286 printf("\n"); 287 288 /* Sub-Headers. */ 289 if (ISSET(todo, SHOW_TTY)) 290 printf(" tin tout"); 291 292 if (ISSET(todo, SHOW_STATS_1)) { 293 for (i = 0; i < ndrive; i++) 294 if (cur.select[i]) { 295 if (ISSET(todo, SHOW_TOTALS)) 296 (void)printf(" KB/t xfr MB "); 297 else 298 (void)printf(" KB/t t/s MB/s "); 299 } 300 } 301 302 if (ISSET(todo, SHOW_STATS_2)) { 303 for (i = 0; i < ndrive; i++) 304 if (cur.select[i]) 305 (void)printf(" KB xfr time "); 306 } 307 308 if (ISSET(todo, SHOW_CPU)) 309 (void)printf(" us ni sy in id"); 310 printf("\n"); 311 } 312 313 static void 314 drive_stats(double etime) 315 { 316 size_t dn; 317 double atime, mbps; 318 319 for (dn = 0; dn < ndrive; ++dn) { 320 if (!cur.select[dn]) 321 continue; 322 /* average Kbytes per transfer. */ 323 if (cur.rxfer[dn] + cur.wxfer[dn]) 324 mbps = ((cur.rbytes[dn] + cur.wbytes[dn]) / 325 1024.0) / (cur.rxfer[dn] + cur.wxfer[dn]); 326 else 327 mbps = 0.0; 328 (void)printf(" %5.*f", 329 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 330 331 /* average transfers per second. */ 332 (void)printf(" %4.0f", 333 (cur.rxfer[dn] + cur.wxfer[dn]) / etime); 334 335 /* time busy in drive activity */ 336 atime = (double)cur.time[dn].tv_sec + 337 ((double)cur.time[dn].tv_usec / (double)1000000); 338 339 /* Megabytes per second. */ 340 if (atime != 0.0) 341 mbps = (cur.rbytes[dn] + cur.wbytes[dn]) / 342 (double)(1024 * 1024); 343 else 344 mbps = 0; 345 mbps /= etime; 346 (void)printf(" %5.*f ", 347 MAX(0, 3 - (int)floor(log10(fmax(1.0, mbps)))), mbps); 348 } 349 } 350 351 static void 352 drive_stats2(double etime) 353 { 354 size_t dn; 355 double atime; 356 357 for (dn = 0; dn < ndrive; ++dn) { 358 if (!cur.select[dn]) 359 continue; 360 361 /* average kbytes per second. */ 362 (void)printf(" %5.0f", 363 (cur.rbytes[dn] + cur.wbytes[dn]) / 1024.0 / etime); 364 365 /* average transfers per second. */ 366 (void)printf(" %5.0f", 367 (cur.rxfer[dn] + cur.wxfer[dn]) / etime); 368 369 /* average time busy in drive activity */ 370 atime = (double)cur.time[dn].tv_sec + 371 ((double)cur.time[dn].tv_usec / (double)1000000); 372 (void)printf(" %4.2f ", atime / etime); 373 } 374 } 375 376 static void 377 drive_statsx(double etime) 378 { 379 size_t dn; 380 double atime, kbps; 381 382 for (dn = 0; dn < ndrive; ++dn) { 383 if (!cur.select[dn]) 384 continue; 385 386 (void)printf("%-8.8s", cur.name[dn]); 387 388 /* average read Kbytes per transfer */ 389 if (cur.rxfer[dn]) 390 kbps = (cur.rbytes[dn] / 1024.0) / cur.rxfer[dn]; 391 else 392 kbps = 0.0; 393 (void)printf(" %8.2f", kbps); 394 395 /* average read transfers 396 (per second) */ 397 (void)printf(" %6.0f", cur.rxfer[dn] / etime); 398 399 /* time read busy in drive activity */ 400 atime = (double)cur.time[dn].tv_sec + 401 ((double)cur.time[dn].tv_usec / (double)1000000); 402 (void)printf(" %6.2f", atime / etime); 403 404 /* average read megabytes 405 (per second) */ 406 (void)printf(" %8.2f", 407 cur.rbytes[dn] / (1024.0 * 1024) / etime); 408 409 410 /* average write Kbytes per transfer */ 411 if (cur.wxfer[dn]) 412 kbps = (cur.wbytes[dn] / 1024.0) / cur.wxfer[dn]; 413 else 414 kbps = 0.0; 415 (void)printf(" %8.2f", kbps); 416 417 /* average write transfers 418 (per second) */ 419 (void)printf(" %6.0f", cur.wxfer[dn] / etime); 420 421 /* time write busy in drive activity */ 422 atime = (double)cur.time[dn].tv_sec + 423 ((double)cur.time[dn].tv_usec / (double)1000000); 424 (void)printf(" %6.2f", atime / etime); 425 426 /* average write megabytes 427 (per second) */ 428 (void)printf(" %8.2f\n", 429 cur.wbytes[dn] / (1024.0 * 1024) / etime); 430 } 431 } 432 433 static void 434 cpustats(void) 435 { 436 int state; 437 double ttime; 438 439 ttime = 0; 440 for (state = 0; state < CPUSTATES; ++state) 441 ttime += cur.cp_time[state]; 442 if (!ttime) 443 ttime = 1.0; 444 /* States are generally never 100% and can use %3.0f. */ 445 for (state = 0; state < CPUSTATES; ++state) 446 printf(" %2.0f", 100. * cur.cp_time[state] / ttime); 447 } 448 449 static void 450 usage(void) 451 { 452 453 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] " 454 "[-N system] [-w wait] [drives]\n"); 455 exit(1); 456 } 457 458 static void 459 display(void) 460 { 461 double etime; 462 463 /* Sum up the elapsed ticks. */ 464 etime = cur.cp_etime; 465 466 /* 467 * If we're showing totals only, then don't divide by the 468 * system time. 469 */ 470 if (ISSET(todo, SHOW_TOTALS)) 471 etime = 1.0; 472 473 if (ISSET(todo, SHOW_STATS_X)) { 474 drive_statsx(etime); 475 goto out; 476 } 477 478 if (ISSET(todo, SHOW_TTY)) 479 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime); 480 481 if (ISSET(todo, SHOW_STATS_1)) { 482 drive_stats(etime); 483 } 484 485 486 if (ISSET(todo, SHOW_STATS_2)) { 487 drive_stats2(etime); 488 } 489 490 491 if (ISSET(todo, SHOW_CPU)) 492 cpustats(); 493 494 (void)printf("\n"); 495 496 out: 497 (void)fflush(stdout); 498 } 499 500 static int 501 selectdrives(int argc, char *argv[]) 502 { 503 int i, maxdrives, ndrives, tried; 504 505 /* 506 * Choose drives to be displayed. Priority goes to (in order) drives 507 * supplied as arguments and default drives. If everything isn't 508 * filled in and there are drives not taken care of, display the first 509 * few that fit. 510 * 511 * The backward compatibility #ifdefs permit the syntax: 512 * iostat [ drives ] [ interval [ count ] ] 513 */ 514 515 #define BACKWARD_COMPATIBILITY 516 for (tried = ndrives = 0; *argv; ++argv) { 517 #ifdef BACKWARD_COMPATIBILITY 518 if (isdigit((unsigned char)**argv)) 519 break; 520 #endif 521 tried++; 522 for (i = 0; i < (int)ndrive; i++) { 523 if (strcmp(cur.name[i], *argv)) 524 continue; 525 cur.select[i] = 1; 526 ++ndrives; 527 } 528 529 } 530 531 if (ndrives == 0 && tried == 0) { 532 /* 533 * Pick up to defdrives (or all if -x is given) drives 534 * if none specified. 535 */ 536 maxdrives = (ISSET(todo, SHOW_STATS_X) || 537 (int)ndrive < defdrives) 538 ? (int)(ndrive) : defdrives; 539 for (i = 0; i < maxdrives; i++) { 540 cur.select[i] = 1; 541 542 ++ndrives; 543 if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives) 544 break; 545 } 546 } 547 548 #ifdef BACKWARD_COMPATIBILITY 549 if (*argv) { 550 interval = atoi(*argv); 551 if (*++argv) 552 reps = atoi(*argv); 553 } 554 #endif 555 556 if (interval) { 557 if (!reps) 558 reps = -1; 559 } else 560 if (reps) 561 interval = 1; 562 563 return (ndrives); 564 } 565