1 /* $OpenBSD: iostat.c,v 1.39 2015/10/23 08:21:27 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/limits.h> 66 #include <sys/time.h> 67 #include <sys/sched.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 timespec ts; 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 ts.tv_sec = interval; 168 ts.tv_nsec = 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 nanosleep(&ts, NULL); 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 218 if (ISSET(todo, SHOW_STATS_1)) 219 for (i = 0; i < dk_ndrive; i++) 220 if (cur.dk_select[i]) { 221 if (ISSET(todo, SHOW_TOTALS)) 222 printf(" %18.18s ", cur.dk_name[i]); 223 else 224 printf(" %16.16s ", cur.dk_name[i]); 225 } 226 if (ISSET(todo, SHOW_STATS_2)) 227 for (i = 0; i < dk_ndrive; i++) 228 if (cur.dk_select[i]) 229 printf(" %16.16s ", cur.dk_name[i]); 230 231 if (ISSET(todo, SHOW_CPU)) 232 printf(" cpu"); 233 printf("\n"); 234 235 /* Sub-Headers. */ 236 if (ISSET(todo, SHOW_TTY)) { 237 if (ISSET(todo, SHOW_TOTALS)) 238 printf(" tin tout"); 239 else 240 printf(" tin tout"); 241 } 242 243 if (ISSET(todo, SHOW_STATS_1)) 244 for (i = 0; i < dk_ndrive; i++) 245 if (cur.dk_select[i]) { 246 if (ISSET(todo, SHOW_TOTALS)) 247 printf(" KB/t xfr MB "); 248 else 249 printf(" KB/t t/s MB/s "); 250 } 251 if (ISSET(todo, SHOW_STATS_2)) 252 for (i = 0; i < dk_ndrive; i++) 253 if (cur.dk_select[i]) 254 printf(" KB xfr time "); 255 256 if (ISSET(todo, SHOW_CPU)) 257 printf(" us ni sy in id"); 258 printf("\n"); 259 } 260 261 static void 262 disk_stats(double etime) 263 { 264 int dn; 265 double atime, mbps; 266 267 for (dn = 0; dn < dk_ndrive; ++dn) { 268 if (!cur.dk_select[dn]) 269 continue; 270 271 /* average Kbytes per transfer. */ 272 if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) 273 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 274 (1024.0)) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]); 275 else 276 mbps = 0.0; 277 278 printf(" %5.2f", mbps); 279 280 /* average transfers per second. */ 281 if (ISSET(todo, SHOW_TOTALS)) 282 printf(" %5.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 283 else 284 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 285 286 /* time busy in disk activity */ 287 atime = (double)cur.dk_time[dn].tv_sec + 288 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 289 290 /* Megabytes per second. */ 291 if (atime != 0.0) 292 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 293 (double)(1024 * 1024); 294 else 295 mbps = 0; 296 if (ISSET(todo, SHOW_TOTALS)) 297 printf(" %6.2f ", mbps / etime); 298 else 299 printf(" %5.2f ", mbps / etime); 300 } 301 } 302 303 static void 304 disk_stats2(double etime) 305 { 306 int dn; 307 double atime; 308 309 for (dn = 0; dn < dk_ndrive; ++dn) { 310 if (!cur.dk_select[dn]) 311 continue; 312 313 /* average kbytes per second. */ 314 printf(" %6.0f", 315 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / (1024.0) / etime); 316 317 /* average transfers per second. */ 318 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 319 320 /* average time busy in disk activity. */ 321 atime = (double)cur.dk_time[dn].tv_sec + 322 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 323 printf(" %4.2f ", atime / etime); 324 } 325 } 326 327 static void 328 cpustats(void) 329 { 330 int state; 331 double t = 0; 332 333 for (state = 0; state < CPUSTATES; ++state) 334 t += cur.cp_time[state]; 335 if (!t) 336 t = 1.0; 337 /* States are generally never 100% and can use %3.0f. */ 338 for (state = 0; state < CPUSTATES; ++state) 339 printf("%3.0f", 100. * cur.cp_time[state] / t); 340 } 341 342 static void 343 usage(void) 344 { 345 fprintf(stderr, 346 "usage: iostat [-CDdIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 347 exit(1); 348 } 349 350 static void 351 display(void) 352 { 353 int i; 354 double etime; 355 356 /* Sum up the elapsed ticks. */ 357 etime = 0.0; 358 for (i = 0; i < CPUSTATES; i++) 359 etime += cur.cp_time[i]; 360 if (etime == 0.0) 361 etime = 1.0; 362 /* Convert to seconds. */ 363 etime /= (float)hz; 364 365 /* If we're showing totals only, then don't divide by the 366 * system time. 367 */ 368 if (ISSET(todo, SHOW_TOTALS)) 369 etime = 1.0; 370 371 if (ISSET(todo, SHOW_TTY)) { 372 if (ISSET(todo, SHOW_TOTALS)) 373 printf("%6.0f %8.0f", cur.tk_nin / etime, 374 cur.tk_nout / etime); 375 else 376 printf("%4.0f %4.0f", cur.tk_nin / etime, 377 cur.tk_nout / etime); 378 } 379 380 if (ISSET(todo, SHOW_STATS_1)) 381 disk_stats(etime); 382 383 if (ISSET(todo, SHOW_STATS_2)) 384 disk_stats2(etime); 385 386 if (ISSET(todo, SHOW_CPU)) 387 cpustats(); 388 389 printf("\n"); 390 fflush(stdout); 391 } 392 393 static void 394 selectdrives(char *argv[]) 395 { 396 const char *errstr; 397 int i, ndrives; 398 399 /* 400 * Choose drives to be displayed. Priority goes to (in order) drives 401 * supplied as arguments and default drives. If everything isn't 402 * filled in and there are drives not taken care of, display the first 403 * few that fit. 404 * 405 * The backward compatibility syntax is: 406 * iostat [ drives ] [ interval [ count ] ] 407 */ 408 for (ndrives = 0; *argv; ++argv) { 409 if (isdigit((unsigned char)**argv)) 410 break; 411 for (i = 0; i < dk_ndrive; i++) { 412 if (strcmp(cur.dk_name[i], *argv)) 413 continue; 414 cur.dk_select[i] = 1; 415 ++ndrives; 416 break; 417 } 418 if (i == dk_ndrive) 419 errx(1, "invalid interval or drive name: %s", *argv); 420 } 421 if (*argv) { 422 interval = strtonum(*argv, 1, INT_MAX, &errstr); 423 if (errstr) 424 errx(1, "interval is %s", errstr); 425 if (*++argv) { 426 reps = strtonum(*argv, 1, INT_MAX, &errstr); 427 if (errstr) 428 errx(1, "repetition count is %s", errstr); 429 ++argv; 430 } 431 } 432 if (*argv) 433 errx(1, "too many arguments"); 434 435 if (interval) { 436 if (!reps) 437 reps = -1; 438 } else 439 if (reps) 440 interval = 1; 441 442 /* Pick up to 4 drives if none specified. */ 443 if (ndrives == 0) 444 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 445 if (cur.dk_select[i]) 446 continue; 447 cur.dk_select[i] = 1; 448 ++ndrives; 449 } 450 } 451