1 /* $OpenBSD: iostat.c,v 1.34 2014/02/13 21:01:23 tedu Exp $ */ 2 /* $NetBSD: iostat.c,v 1.10 1996/10/25 18:21:58 scottr Exp $ */ 3 4 /* 5 * Copyright (c) 1996 John M. Vinopal 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed for the NetBSD Project 19 * by John M. Vinopal. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /*- 37 * Copyright (c) 1986, 1991, 1993 38 * The Regents of the University of California. All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. Neither the name of the University nor the names of its contributors 49 * may be used to endorse or promote products derived from this software 50 * without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/dkstat.h> 66 #include <sys/limits.h> 67 #include <sys/time.h> 68 69 #include <err.h> 70 #include <ctype.h> 71 #include <signal.h> 72 #include <stdio.h> 73 #include <stdlib.h> 74 #include <string.h> 75 #include <unistd.h> 76 #include <kvm.h> 77 78 #include "dkstats.h" 79 80 /* Defined in dkstats.c */ 81 extern struct _disk cur, last; 82 extern int dk_ndrive; 83 84 /* Namelist and memory files. */ 85 kvm_t *kd; 86 char *nlistf, *memf; 87 88 int hz, reps, interval; 89 static int todo = 0; 90 91 volatile sig_atomic_t wantheader; 92 93 #define ISSET(x, a) ((x) & (a)) 94 #define SHOW_CPU 0x0001 95 #define SHOW_TTY 0x0002 96 #define SHOW_STATS_1 0x0004 97 #define SHOW_STATS_2 0x0008 98 #define SHOW_TOTALS 0x0080 99 100 static void cpustats(void); 101 static void disk_stats(double); 102 static void disk_stats2(double); 103 static void sigheader(int); 104 static void header(void); 105 static void usage(void); 106 static void display(void); 107 static void selectdrives(char **); 108 109 void dkswap(void); 110 void dkreadstats(void); 111 int dkinit(int); 112 113 int 114 main(int argc, char *argv[]) 115 { 116 const char *errstr; 117 int ch, hdrcnt; 118 struct timeval tv; 119 120 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:")) != -1) 121 switch(ch) { 122 case 'c': 123 reps = strtonum(optarg, 1, INT_MAX, &errstr); 124 if (errstr) 125 errx(1, "repetition count is %s", errstr); 126 break; 127 case 'C': 128 todo |= SHOW_CPU; 129 break; 130 case 'd': 131 todo |= SHOW_STATS_1; 132 break; 133 case 'D': 134 todo |= SHOW_STATS_2; 135 break; 136 case 'I': 137 todo |= SHOW_TOTALS; 138 break; 139 case 'M': 140 memf = optarg; 141 break; 142 case 'N': 143 nlistf = optarg; 144 break; 145 case 'T': 146 todo |= SHOW_TTY; 147 break; 148 case 'w': 149 interval = strtonum(optarg, 1, INT_MAX, &errstr); 150 if (errstr) 151 errx(1, "interval is %s", errstr); 152 break; 153 case '?': 154 default: 155 usage(); 156 } 157 argc -= optind; 158 argv += optind; 159 160 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_1 | SHOW_STATS_2)) 161 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 162 163 dkinit(0); 164 dkreadstats(); 165 selectdrives(argv); 166 167 tv.tv_sec = interval; 168 tv.tv_usec = 0; 169 170 /* print a new header on sigcont */ 171 signal(SIGCONT, sigheader); 172 173 for (hdrcnt = 1;;) { 174 if (!--hdrcnt || wantheader) { 175 header(); 176 hdrcnt = 20; 177 wantheader = 0; 178 } 179 180 if (!ISSET(todo, SHOW_TOTALS)) 181 dkswap(); 182 display(); 183 184 if (reps >= 0 && --reps <= 0) 185 break; 186 select(0, NULL, NULL, NULL, &tv); 187 dkreadstats(); 188 if (last.dk_ndrive != cur.dk_ndrive) 189 wantheader = 1; 190 } 191 exit(0); 192 } 193 194 /*ARGSUSED*/ 195 static void 196 sigheader(int signo) 197 { 198 wantheader = 1; 199 } 200 201 static void 202 header(void) 203 { 204 int i; 205 static int printedheader = 0; 206 207 if (printedheader && !isatty(STDOUT_FILENO)) 208 return; 209 210 /* Main Headers. */ 211 if (ISSET(todo, SHOW_TTY)) 212 if (ISSET(todo, SHOW_TOTALS)) 213 printf(" tty"); 214 else 215 printf(" tty"); 216 217 if (ISSET(todo, SHOW_STATS_1)) 218 for (i = 0; i < dk_ndrive; i++) 219 if (cur.dk_select[i]) 220 if (ISSET(todo, SHOW_TOTALS)) 221 printf(" %18.18s ", cur.dk_name[i]); 222 else 223 printf(" %16.16s ", cur.dk_name[i]); 224 if (ISSET(todo, SHOW_STATS_2)) 225 for (i = 0; i < dk_ndrive; i++) 226 if (cur.dk_select[i]) 227 printf(" %16.16s ", cur.dk_name[i]); 228 229 if (ISSET(todo, SHOW_CPU)) 230 printf(" cpu"); 231 printf("\n"); 232 233 /* Sub-Headers. */ 234 if (ISSET(todo, SHOW_TTY)) 235 if (ISSET(todo, SHOW_TOTALS)) 236 printf(" tin tout"); 237 else 238 printf(" tin tout"); 239 240 if (ISSET(todo, SHOW_STATS_1)) 241 for (i = 0; i < dk_ndrive; i++) 242 if (cur.dk_select[i]) { 243 if (ISSET(todo, SHOW_TOTALS)) 244 printf(" KB/t xfr MB "); 245 else 246 printf(" KB/t t/s MB/s "); 247 } 248 if (ISSET(todo, SHOW_STATS_2)) 249 for (i = 0; i < dk_ndrive; i++) 250 if (cur.dk_select[i]) 251 printf(" KB xfr time "); 252 253 if (ISSET(todo, SHOW_CPU)) 254 printf(" us ni sy in id"); 255 printf("\n"); 256 } 257 258 static void 259 disk_stats(double etime) 260 { 261 int dn; 262 double atime, mbps; 263 264 for (dn = 0; dn < dk_ndrive; ++dn) { 265 if (!cur.dk_select[dn]) 266 continue; 267 268 /* average Kbytes per transfer. */ 269 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) 270 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 271 (1024.0)) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]); 272 else 273 mbps = 0.0; 274 275 printf(" %5.2f", mbps); 276 277 /* average transfers per second. */ 278 if (ISSET(todo, SHOW_TOTALS)) 279 printf(" %5.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 280 else 281 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 282 283 /* time busy in disk activity */ 284 atime = (double)cur.dk_time[dn].tv_sec + 285 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 286 287 /* Megabytes per second. */ 288 if (atime != 0.0) 289 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 290 (double)(1024 * 1024); 291 else 292 mbps = 0; 293 if (ISSET(todo, SHOW_TOTALS)) 294 printf(" %6.2f ", mbps / etime); 295 else 296 printf(" %5.2f ", mbps / etime); 297 } 298 } 299 300 static void 301 disk_stats2(double etime) 302 { 303 int dn; 304 double atime; 305 306 for (dn = 0; dn < dk_ndrive; ++dn) { 307 if (!cur.dk_select[dn]) 308 continue; 309 310 /* average kbytes per second. */ 311 printf(" %6.0f", 312 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / (1024.0) / etime); 313 314 /* average transfers per second. */ 315 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 316 317 /* average time busy in disk activity. */ 318 atime = (double)cur.dk_time[dn].tv_sec + 319 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 320 printf(" %4.2f ", atime / etime); 321 } 322 } 323 324 static void 325 cpustats(void) 326 { 327 int state; 328 double t = 0; 329 330 for (state = 0; state < CPUSTATES; ++state) 331 t += cur.cp_time[state]; 332 if (!t) 333 t = 1.0; 334 /* States are generally never 100% and can use %3.0f. */ 335 for (state = 0; state < CPUSTATES; ++state) 336 printf("%3.0f", 100. * cur.cp_time[state] / t); 337 } 338 339 static void 340 usage(void) 341 { 342 fprintf(stderr, 343 "usage: iostat [-CDdIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 344 exit(1); 345 } 346 347 static void 348 display(void) 349 { 350 int i; 351 double etime; 352 353 /* Sum up the elapsed ticks. */ 354 etime = 0.0; 355 for (i = 0; i < CPUSTATES; i++) 356 etime += cur.cp_time[i]; 357 if (etime == 0.0) 358 etime = 1.0; 359 /* Convert to seconds. */ 360 etime /= (float)hz; 361 362 /* If we're showing totals only, then don't divide by the 363 * system time. 364 */ 365 if (ISSET(todo, SHOW_TOTALS)) 366 etime = 1.0; 367 368 if (ISSET(todo, SHOW_TTY)) 369 if (ISSET(todo, SHOW_TOTALS)) 370 printf("%6.0f %8.0f", cur.tk_nin / etime, 371 cur.tk_nout / etime); 372 else 373 printf("%4.0f %4.0f", cur.tk_nin / etime, 374 cur.tk_nout / etime); 375 376 if (ISSET(todo, SHOW_STATS_1)) 377 disk_stats(etime); 378 379 if (ISSET(todo, SHOW_STATS_2)) 380 disk_stats2(etime); 381 382 if (ISSET(todo, SHOW_CPU)) 383 cpustats(); 384 385 printf("\n"); 386 fflush(stdout); 387 } 388 389 static void 390 selectdrives(char *argv[]) 391 { 392 const char *errstr; 393 int i, ndrives; 394 395 /* 396 * Choose drives to be displayed. Priority goes to (in order) drives 397 * supplied as arguments and default drives. If everything isn't 398 * filled in and there are drives not taken care of, display the first 399 * few that fit. 400 * 401 * The backward compatibility #ifdefs permit the syntax: 402 * iostat [ drives ] [ interval [ count ] ] 403 */ 404 #define BACKWARD_COMPATIBILITY 405 for (ndrives = 0; *argv; ++argv) { 406 #ifdef BACKWARD_COMPATIBILITY 407 if (isdigit((unsigned char)**argv)) 408 break; 409 #endif 410 for (i = 0; i < dk_ndrive; i++) { 411 if (strcmp(cur.dk_name[i], *argv)) 412 continue; 413 cur.dk_select[i] = 1; 414 ++ndrives; 415 } 416 } 417 #ifdef BACKWARD_COMPATIBILITY 418 if (*argv) { 419 interval = strtonum(*argv, 1, INT_MAX, &errstr); 420 if (errstr) 421 errx(1, "interval is %s", errstr); 422 if (*++argv) { 423 reps = strtonum(*argv, 1, INT_MAX, &errstr); 424 if (errstr) 425 errx(1, "repetition count is %s", errstr); 426 } 427 } 428 #endif 429 430 if (interval) { 431 if (!reps) 432 reps = -1; 433 } else 434 if (reps) 435 interval = 1; 436 437 /* Pick up to 4 drives if none specified. */ 438 if (ndrives == 0) 439 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 440 if (cur.dk_select[i]) 441 continue; 442 cur.dk_select[i] = 1; 443 ++ndrives; 444 } 445 } 446