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