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