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