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