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