1 /* $NetBSD: iostat.c,v 1.22 2000/06/03 21:03:25 thorpej 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.22 2000/06/03 21:03:25 thorpej Exp $"); 79 #endif 80 #endif /* not lint */ 81 82 #include <sys/types.h> 83 #include <sys/sched.h> 84 #include <sys/dkstat.h> 85 #include <sys/time.h> 86 87 #include <err.h> 88 #include <ctype.h> 89 #include <signal.h> 90 #include <stdio.h> 91 #include <stdlib.h> 92 #include <string.h> 93 #include <unistd.h> 94 95 #include "dkstats.h" 96 97 /* Defined in dkstats.c */ 98 extern struct _disk cur; 99 extern int dk_ndrive; 100 101 /* Namelist and memory files. */ 102 char *nlistf, *memf; 103 104 int hz, reps, interval; 105 static int todo = 0; 106 107 #define ISSET(x, a) ((x) & (a)) 108 #define SHOW_CPU 1<<0 109 #define SHOW_TTY 1<<1 110 #define SHOW_STATS_1 1<<2 111 #define SHOW_STATS_2 1<<3 112 #define SHOW_STATS_X 1<<4 113 #define SHOW_TOTALS 1<<7 114 #define SHOW_STATS_ALL (SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X) 115 116 static void cpustats __P((void)); 117 static void disk_stats __P((double)); 118 static void disk_stats2 __P((double)); 119 static void disk_statsx __P((double)); 120 static void header __P((int)); 121 static void usage __P((void)); 122 static void display __P((void)); 123 static void selectdrives __P((int, char **)); 124 125 void dkswap __P((void)); 126 void dkreadstats __P((void)); 127 int dkinit __P((int, gid_t)); 128 int main __P((int, char **)); 129 130 int 131 main(argc, argv) 132 int argc; 133 char *argv[]; 134 { 135 int ch, hdrcnt; 136 struct timeval tv; 137 gid_t egid = getegid(); 138 setegid(getgid()); 139 140 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1) 141 switch(ch) { 142 case 'c': 143 if ((reps = atoi(optarg)) <= 0) 144 errx(1, "repetition count <= 0."); 145 break; 146 case 'C': 147 todo |= SHOW_CPU; 148 break; 149 case 'd': 150 todo &= ~SHOW_STATS_ALL; 151 todo |= SHOW_STATS_1; 152 break; 153 case 'D': 154 todo &= ~SHOW_STATS_ALL; 155 todo |= SHOW_STATS_2; 156 break; 157 case 'I': 158 todo |= SHOW_TOTALS; 159 break; 160 case 'M': 161 memf = optarg; 162 break; 163 case 'N': 164 nlistf = optarg; 165 break; 166 case 'T': 167 todo |= SHOW_TTY; 168 break; 169 case 'w': 170 if ((interval = atoi(optarg)) <= 0) 171 errx(1, "interval <= 0."); 172 break; 173 case 'x': 174 todo &= ~SHOW_STATS_ALL; 175 todo |= SHOW_STATS_X; 176 break; 177 case '?': 178 default: 179 usage(); 180 } 181 argc -= optind; 182 argv += optind; 183 184 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL)) 185 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 186 if (ISSET(todo, SHOW_STATS_X)) { 187 todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL); 188 todo |= SHOW_STATS_X; 189 } 190 191 /* 192 * Discard setgid privileges if not the running kernel so that bad 193 * guys can't print interesting stuff from kernel memory. 194 */ 195 if (nlistf != NULL || memf != NULL) 196 setgid(getgid()); 197 198 dkinit(0, egid); 199 dkreadstats(); 200 selectdrives(argc, argv); 201 202 tv.tv_sec = interval; 203 tv.tv_usec = 0; 204 205 /* print a new header on sigcont */ 206 (void)signal(SIGCONT, header); 207 208 for (hdrcnt = 1;;) { 209 if (!--hdrcnt) { 210 header(0); 211 hdrcnt = 20; 212 } 213 214 if (!ISSET(todo, SHOW_TOTALS)) 215 dkswap(); 216 display(); 217 218 if (reps >= 0 && --reps <= 0) 219 break; 220 select(0, NULL, NULL, NULL, &tv); 221 dkreadstats(); 222 } 223 exit(0); 224 } 225 226 static void 227 header(signo) 228 int signo; 229 { 230 int i; 231 232 if (ISSET(todo, SHOW_STATS_X)) 233 return; 234 235 /* Main Headers. */ 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( 243 " %7.7s ", cur.dk_name[i]); 244 245 if (ISSET(todo, SHOW_STATS_2)) 246 for (i = 0; i < dk_ndrive; i++) 247 if (cur.dk_select[i]) 248 (void)printf( 249 " %7.7s ", cur.dk_name[i]); 250 251 if (ISSET(todo, SHOW_CPU)) 252 (void)printf(" cpu"); 253 254 printf("\n"); 255 256 /* Sub-Headers. */ 257 if (ISSET(todo, SHOW_TTY)) 258 printf(" tin tout"); 259 260 if (ISSET(todo, SHOW_STATS_1)) { 261 for (i = 0; i < dk_ndrive; i++) 262 if (cur.dk_select[i]) { 263 if (ISSET(todo, SHOW_TOTALS)) 264 (void)printf(" KB/t xfr MB "); 265 else 266 (void)printf(" KB/t t/s MB/s "); 267 } 268 } 269 270 if (ISSET(todo, SHOW_STATS_2)) 271 for (i = 0; i < dk_ndrive; i++) 272 if (cur.dk_select[i]) 273 (void)printf(" KB xfr time "); 274 275 if (ISSET(todo, SHOW_CPU)) 276 (void)printf(" us ni sy in id"); 277 printf("\n"); 278 } 279 280 static void 281 disk_stats(etime) 282 double etime; 283 { 284 int dn; 285 double atime, mbps; 286 287 for (dn = 0; dn < dk_ndrive; ++dn) { 288 if (!cur.dk_select[dn]) 289 continue; 290 291 /* average Kbytes per transfer. */ 292 if (cur.dk_xfer[dn]) 293 mbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn]; 294 else 295 mbps = 0.0; 296 (void)printf(" %5.2f", mbps); 297 298 /* average transfers per second. */ 299 (void)printf(" %3.0f", cur.dk_xfer[dn] / etime); 300 301 /* time busy in disk activity */ 302 atime = (double)cur.dk_time[dn].tv_sec + 303 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 304 305 /* Megabytes per second. */ 306 if (atime != 0.0) 307 mbps = cur.dk_bytes[dn] / (double)(1024 * 1024); 308 else 309 mbps = 0; 310 (void)printf(" %4.2f ", mbps / etime); 311 } 312 } 313 314 static void 315 disk_stats2(etime) 316 double etime; 317 { 318 int dn; 319 double atime; 320 321 for (dn = 0; dn < dk_ndrive; ++dn) { 322 if (!cur.dk_select[dn]) 323 continue; 324 325 /* average kbytes per second. */ 326 (void)printf(" %4.0f", cur.dk_bytes[dn] / (1024.0) / etime); 327 328 /* average transfers per second. */ 329 (void)printf(" %3.0f", cur.dk_xfer[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(etime) 340 double etime; 341 { 342 int dn; 343 double atime, kbps; 344 345 if (ISSET(todo, SHOW_TOTALS)) 346 (void)printf("device KB/t xfr time MB"); 347 else 348 (void)printf("device KB/t t/s time MB/s"); 349 350 for (dn = 0; dn < dk_ndrive; ++dn) { 351 (void)printf("\n"); 352 (void)printf("%-8.8s", cur.dk_name[dn]); 353 354 /* average Kbytes per transfer */ 355 if (cur.dk_xfer[dn]) 356 kbps = (cur.dk_bytes[dn] / (1024.0)) / cur.dk_xfer[dn]; 357 else 358 kbps = 0.0; 359 (void)printf(" %8.2f", kbps); 360 361 /* average transfers (per second) */ 362 (void)printf(" %8.0f", cur.dk_xfer[dn] / etime); 363 364 /* time busy in disk activity */ 365 atime = (double)cur.dk_time[dn].tv_sec + 366 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 367 (void)printf(" %8.2f", atime / etime); 368 369 /* average megabytes (per second) */ 370 (void)printf(" %8.2f", 371 cur.dk_bytes[dn] / (1024.0 * 1024) / etime); 372 373 } 374 } 375 376 static void 377 cpustats() 378 { 379 int state; 380 double time; 381 382 time = 0; 383 for (state = 0; state < CPUSTATES; ++state) 384 time += cur.cp_time[state]; 385 if (!time) 386 time = 1.0; 387 /* States are generally never 100% and can use %3.0f. */ 388 for (state = 0; state < CPUSTATES; ++state) 389 printf(" %2.0f", 100. * cur.cp_time[state] / time); 390 } 391 392 static void 393 usage() 394 { 395 396 (void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] \ 397 [-N system] [-w wait] [drives]\n"); 398 exit(1); 399 } 400 401 static void 402 display() 403 { 404 int i; 405 double etime; 406 407 /* Sum up the elapsed ticks. */ 408 etime = 0.0; 409 for (i = 0; i < CPUSTATES; i++) { 410 etime += cur.cp_time[i]; 411 } 412 if (etime == 0.0) 413 etime = 1.0; 414 /* Convert to seconds. */ 415 etime /= (float)hz; 416 417 /* If we're showing totals only, then don't divide by the 418 * system time. 419 */ 420 if (ISSET(todo, SHOW_TOTALS)) 421 etime = 1.0; 422 423 if (ISSET(todo, SHOW_TTY)) 424 printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime); 425 426 if (ISSET(todo, SHOW_STATS_1)) 427 disk_stats(etime); 428 429 if (ISSET(todo, SHOW_STATS_2)) 430 disk_stats2(etime); 431 432 if (ISSET(todo, SHOW_STATS_X)) 433 disk_statsx(etime); 434 435 if (ISSET(todo, SHOW_CPU)) 436 cpustats(); 437 438 (void)printf("\n"); 439 (void)fflush(stdout); 440 } 441 442 static void 443 selectdrives(argc, argv) 444 int argc; 445 char *argv[]; 446 { 447 int i, ndrives; 448 449 /* 450 * Choose drives to be displayed. Priority goes to (in order) drives 451 * supplied as arguments and default drives. If everything isn't 452 * filled in and there are drives not taken care of, display the first 453 * few that fit. 454 * 455 * The backward compatibility #ifdefs permit the syntax: 456 * iostat [ drives ] [ interval [ count ] ] 457 */ 458 if (ISSET(todo, SHOW_STATS_X)) { 459 for (i = 0; i < dk_ndrive; i++) 460 cur.dk_select[i] = 1; 461 } 462 463 #define BACKWARD_COMPATIBILITY 464 for (ndrives = 0; *argv; ++argv) { 465 #ifdef BACKWARD_COMPATIBILITY 466 if (isdigit(**argv)) 467 break; 468 #endif 469 if (!ISSET(todo, SHOW_STATS_X)) 470 for (i = 0; i < dk_ndrive; i++) { 471 if (strcmp(cur.dk_name[i], *argv)) 472 continue; 473 cur.dk_select[i] = 1; 474 ++ndrives; 475 } 476 } 477 #ifdef BACKWARD_COMPATIBILITY 478 if (*argv) { 479 interval = atoi(*argv); 480 if (*++argv) 481 reps = atoi(*argv); 482 } 483 #endif 484 485 if (interval) { 486 if (!reps) 487 reps = -1; 488 } else 489 if (reps) 490 interval = 1; 491 492 /* Pick up to 4 drives if none specified. */ 493 if (ndrives == 0) 494 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 495 if (cur.dk_select[i]) 496 continue; 497 cur.dk_select[i] = 1; 498 ++ndrives; 499 } 500 } 501 502