1 /* $OpenBSD: iostat.c,v 1.46 2022/12/28 20:56:37 cheloha 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 <limits.h> 72 #include <signal.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #include <time.h> 77 #include <unistd.h> 78 #include <kvm.h> 79 80 #include "dkstats.h" 81 82 /* Defined in dkstats.c */ 83 extern struct _disk cur, last; 84 extern int dk_ndrive; 85 86 /* Namelist and memory files. */ 87 kvm_t *kd; 88 char *nlistf, *memf; 89 90 int hz, reps; 91 time_t interval; 92 static int todo = 0; 93 94 volatile sig_atomic_t wantheader; 95 96 #define ISSET(x, a) ((x) & (a)) 97 #define SHOW_CPU 0x0001 98 #define SHOW_TTY 0x0002 99 #define SHOW_STATS_1 0x0004 100 #define SHOW_STATS_2 0x0008 101 #define SHOW_TOTALS 0x0080 102 103 static void cpustats(void); 104 static void disk_stats(double); 105 static void disk_stats2(double); 106 static void sigalarm(int); 107 static void sigheader(int); 108 static void header(void); 109 static void usage(void); 110 static void display(void); 111 static void selectdrives(char **); 112 113 void dkswap(void); 114 void dkreadstats(void); 115 int dkinit(int); 116 117 int 118 main(int argc, char *argv[]) 119 { 120 struct itimerval itv; 121 const char *errstr; 122 sigset_t empty; 123 int ch, hdrcnt; 124 125 while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:")) != -1) 126 switch(ch) { 127 case 'c': 128 reps = strtonum(optarg, 1, INT_MAX, &errstr); 129 if (errstr) 130 errx(1, "repetition count is %s", errstr); 131 break; 132 case 'C': 133 todo |= SHOW_CPU; 134 break; 135 case 'd': 136 todo |= SHOW_STATS_1; 137 break; 138 case 'D': 139 todo |= SHOW_STATS_2; 140 break; 141 case 'I': 142 todo |= SHOW_TOTALS; 143 break; 144 case 'M': 145 memf = optarg; 146 break; 147 case 'N': 148 nlistf = optarg; 149 break; 150 case 'T': 151 todo |= SHOW_TTY; 152 break; 153 case 'w': 154 interval = strtonum(optarg, 1, UINT_MAX, &errstr); 155 if (errstr) 156 errx(1, "wait is %s", errstr); 157 break; 158 default: 159 usage(); 160 } 161 argc -= optind; 162 argv += optind; 163 164 if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_1 | SHOW_STATS_2)) 165 todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1; 166 167 dkinit(0); 168 169 if (unveil("/", "") == -1) 170 err(1, "unveil /"); 171 if (unveil(NULL, NULL) == -1) 172 err(1, "unveil"); 173 174 dkreadstats(); 175 selectdrives(argv); 176 177 /* print a new header on sigcont */ 178 signal(SIGCONT, sigheader); 179 180 if (interval != 0) { 181 if (signal(SIGALRM, sigalarm) == SIG_ERR) 182 err(1, "signal"); 183 sigemptyset(&empty); 184 itv.it_value.tv_sec = interval; 185 itv.it_value.tv_usec = 0; 186 itv.it_interval = itv.it_value; 187 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) 188 err(1, "setitimer"); 189 } 190 191 for (hdrcnt = 1;;) { 192 if (!--hdrcnt || wantheader) { 193 header(); 194 hdrcnt = 20; 195 wantheader = 0; 196 } 197 198 if (!ISSET(todo, SHOW_TOTALS)) 199 dkswap(); 200 display(); 201 202 if (reps >= 0 && --reps <= 0) 203 break; 204 sigsuspend(&empty); 205 dkreadstats(); 206 if (last.dk_ndrive != cur.dk_ndrive) 207 wantheader = 1; 208 } 209 exit(0); 210 } 211 212 static void 213 sigalarm(int signo) 214 { 215 } 216 217 /*ARGSUSED*/ 218 static void 219 sigheader(int signo) 220 { 221 wantheader = 1; 222 } 223 224 static void 225 header(void) 226 { 227 int i; 228 static int printedheader = 0; 229 230 if (printedheader && !isatty(STDOUT_FILENO)) 231 return; 232 233 /* Main Headers. */ 234 if (ISSET(todo, SHOW_TTY)) { 235 if (ISSET(todo, SHOW_TOTALS)) 236 printf(" tty"); 237 else 238 printf(" tty"); 239 } 240 241 if (ISSET(todo, SHOW_STATS_1)) 242 for (i = 0; i < dk_ndrive; i++) 243 if (cur.dk_select[i]) { 244 printf(" %18.18s ", cur.dk_name[i]); 245 } 246 if (ISSET(todo, SHOW_STATS_2)) 247 for (i = 0; i < dk_ndrive; i++) 248 if (cur.dk_select[i]) 249 printf(" %17.17s ", cur.dk_name[i]); 250 251 if (ISSET(todo, SHOW_CPU)) 252 printf(" cpu"); 253 printf("\n"); 254 255 /* Sub-Headers. */ 256 if (ISSET(todo, SHOW_TTY)) { 257 if (ISSET(todo, SHOW_TOTALS)) 258 printf(" tin tout"); 259 else 260 printf(" tin tout"); 261 } 262 263 if (ISSET(todo, SHOW_STATS_1)) 264 for (i = 0; i < dk_ndrive; i++) 265 if (cur.dk_select[i]) { 266 if (ISSET(todo, SHOW_TOTALS)) 267 printf(" KB/t xfr MB "); 268 else 269 printf(" KB/t t/s MB/s "); 270 } 271 if (ISSET(todo, SHOW_STATS_2)) 272 for (i = 0; i < dk_ndrive; i++) 273 if (cur.dk_select[i]) 274 printf(" KB xfr time "); 275 276 if (ISSET(todo, SHOW_CPU)) 277 printf(" us ni sy sp in id"); 278 printf("\n"); 279 } 280 281 static void 282 disk_stats(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_rxfer[dn] + cur.dk_wxfer[dn]) 293 mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 294 (1024.0)) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]); 295 else 296 mbps = 0.0; 297 298 printf(" %5.2f", mbps); 299 300 /* average transfers per second. */ 301 if (ISSET(todo, SHOW_TOTALS)) 302 printf(" %5.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 303 else 304 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 305 306 /* time busy in disk activity */ 307 atime = (double)cur.dk_time[dn].tv_sec + 308 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 309 310 /* Megabytes per second. */ 311 if (atime != 0.0) 312 mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 313 (double)(1024 * 1024); 314 else 315 mbps = 0; 316 if (ISSET(todo, SHOW_TOTALS)) 317 printf(" %6.2f ", mbps / etime); 318 else 319 printf(" %7.2f ", mbps / etime); 320 } 321 } 322 323 static void 324 disk_stats2(double etime) 325 { 326 int dn; 327 double atime; 328 329 for (dn = 0; dn < dk_ndrive; ++dn) { 330 if (!cur.dk_select[dn]) 331 continue; 332 333 /* average kbytes per second. */ 334 printf(" %7.0f", 335 (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / (1024.0) / etime); 336 337 /* average transfers per second. */ 338 printf(" %4.0f", (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime); 339 340 /* average time busy in disk activity. */ 341 atime = (double)cur.dk_time[dn].tv_sec + 342 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 343 printf(" %4.2f ", atime / etime); 344 } 345 } 346 347 static void 348 cpustats(void) 349 { 350 int state; 351 double t = 0; 352 353 for (state = 0; state < CPUSTATES; ++state) 354 t += cur.cp_time[state]; 355 if (!t) 356 t = 1.0; 357 /* States are generally never 100% and can use %3.0f. */ 358 for (state = 0; state < CPUSTATES; ++state) 359 printf("%3.0f", 100. * cur.cp_time[state] / t); 360 } 361 362 static void 363 usage(void) 364 { 365 fprintf(stderr, 366 "usage: iostat [-CDdIT] [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 367 exit(1); 368 } 369 370 static void 371 display(void) 372 { 373 int i; 374 double etime; 375 376 /* Sum up the elapsed ticks. */ 377 etime = 0.0; 378 for (i = 0; i < CPUSTATES; i++) 379 etime += cur.cp_time[i]; 380 if (etime == 0.0) 381 etime = 1.0; 382 /* Convert to seconds. */ 383 etime /= (float)hz; 384 385 /* If we're showing totals only, then don't divide by the 386 * system time. 387 */ 388 if (ISSET(todo, SHOW_TOTALS)) 389 etime = 1.0; 390 391 if (ISSET(todo, SHOW_TTY)) { 392 if (ISSET(todo, SHOW_TOTALS)) 393 printf("%6.0f %8.0f", cur.tk_nin / etime, 394 cur.tk_nout / etime); 395 else 396 printf("%4.0f %4.0f", cur.tk_nin / etime, 397 cur.tk_nout / etime); 398 } 399 400 if (ISSET(todo, SHOW_STATS_1)) 401 disk_stats(etime); 402 403 if (ISSET(todo, SHOW_STATS_2)) 404 disk_stats2(etime); 405 406 if (ISSET(todo, SHOW_CPU)) 407 cpustats(); 408 409 printf("\n"); 410 fflush(stdout); 411 } 412 413 static void 414 selectdrives(char *argv[]) 415 { 416 const char *errstr; 417 int i, ndrives; 418 419 /* 420 * Choose drives to be displayed. Priority goes to (in order) drives 421 * supplied as arguments and default drives. If everything isn't 422 * filled in and there are drives not taken care of, display the first 423 * few that fit. 424 * 425 * The backward compatibility syntax is: 426 * iostat [ drives ] [ interval [ count ] ] 427 */ 428 for (ndrives = 0; *argv; ++argv) { 429 if (isdigit((unsigned char)**argv)) 430 break; 431 for (i = 0; i < dk_ndrive; i++) { 432 if (strcmp(cur.dk_name[i], *argv)) 433 continue; 434 cur.dk_select[i] = 1; 435 ++ndrives; 436 break; 437 } 438 if (i == dk_ndrive) 439 errx(1, "invalid interval or drive name: %s", *argv); 440 } 441 if (*argv) { 442 interval = strtonum(*argv, 1, UINT_MAX, &errstr); 443 if (errstr) 444 errx(1, "interval is %s", errstr); 445 if (*++argv) { 446 reps = strtonum(*argv, 1, INT_MAX, &errstr); 447 if (errstr) 448 errx(1, "repetition count is %s", errstr); 449 ++argv; 450 } 451 } 452 if (*argv) 453 errx(1, "too many arguments"); 454 455 if (interval) { 456 if (!reps) 457 reps = -1; 458 } else 459 if (reps) 460 interval = 1; 461 462 /* Pick up to 4 drives if none specified. */ 463 if (ndrives == 0) 464 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 465 if (cur.dk_select[i]) 466 continue; 467 cur.dk_select[i] = 1; 468 ++ndrives; 469 } 470 } 471