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