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