1 /*- 2 * Copyright (c) 1986, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char copyright[] = 36 "@(#) Copyright (c) 1986, 1991, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/buf.h> 46 #include <sys/dkstat.h> 47 48 #include <err.h> 49 #include <ctype.h> 50 #include <fcntl.h> 51 #include <kvm.h> 52 #include <limits.h> 53 #include <nlist.h> 54 #include <paths.h> 55 #include <signal.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 struct nlist namelist[] = { 62 #define X_DK_TIME 0 63 { "_dk_time" }, 64 #define X_DK_XFER 1 65 { "_dk_xfer" }, 66 #define X_DK_WDS 2 67 { "_dk_wds" }, 68 #define X_TK_NIN 3 69 { "_tk_nin" }, 70 #define X_TK_NOUT 4 71 { "_tk_nout" }, 72 #define X_DK_SEEK 5 73 { "_dk_seek" }, 74 #define X_CP_TIME 6 75 { "_cp_time" }, 76 #define X_DK_WPMS 7 77 { "_dk_wpms" }, 78 #define X_HZ 8 79 { "_hz" }, 80 #define X_STATHZ 9 81 { "_stathz" }, 82 #define X_DK_NDRIVE 10 83 { "_dk_ndrive" }, 84 #define X_END 10 85 #if defined(hp300) || defined(luna68k) 86 #define X_HPDINIT (X_END+1) 87 { "_hp_dinit" }, 88 #endif 89 #ifdef mips 90 #define X_SCSI_DINIT (X_END+1) 91 { "_scsi_dinit" }, 92 #endif 93 #ifdef tahoe 94 #define X_VBDINIT (X_END+1) 95 { "_vbdinit" }, 96 #endif 97 #ifdef vax 98 { "_mbdinit" }, 99 #define X_MBDINIT (X_END+1) 100 { "_ubdinit" }, 101 #define X_UBDINIT (X_END+2) 102 #endif 103 { NULL }, 104 }; 105 106 struct _disk { 107 long cp_time[CPUSTATES]; 108 long *dk_time; 109 long *dk_wds; 110 long *dk_seek; 111 long *dk_xfer; 112 long tk_nin; 113 long tk_nout; 114 } cur, last; 115 116 kvm_t *kd; 117 double etime; 118 long *dk_wpms; 119 int dk_ndrive, *dr_select, hz, kmemfd, ndrives; 120 char **dr_name; 121 122 #define nlread(x, v) \ 123 kvm_read(kd, namelist[x].n_value, &(v), sizeof(v)) 124 125 #include "names.c" /* XXX */ 126 127 void cpustats __P((void)); 128 void dkstats __P((void)); 129 void phdr __P((int)); 130 void usage __P((void)); 131 132 int 133 main(argc, argv) 134 int argc; 135 char *argv[]; 136 { 137 register int i; 138 long tmp; 139 int ch, hdrcnt, reps, interval, stathz, ndrives; 140 char **cp, *memf, *nlistf, buf[30]; 141 char errbuf[_POSIX2_LINE_MAX]; 142 143 interval = reps = 0; 144 nlistf = memf = NULL; 145 while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF) 146 switch(ch) { 147 case 'c': 148 if ((reps = atoi(optarg)) <= 0) 149 errx(1, "repetition count <= 0."); 150 break; 151 case 'M': 152 memf = optarg; 153 break; 154 case 'N': 155 nlistf = optarg; 156 break; 157 case 'w': 158 if ((interval = atoi(optarg)) <= 0) 159 errx(1, "interval <= 0."); 160 break; 161 case '?': 162 default: 163 usage(); 164 } 165 argc -= optind; 166 argv += optind; 167 168 /* 169 * Discard setgid privileges if not the running kernel so that bad 170 * guys can't print interesting stuff from kernel memory. 171 */ 172 if (nlistf != NULL || memf != NULL) 173 setgid(getgid()); 174 175 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 176 if (kd == 0) 177 errx(1, "kvm_openfiles: %s", errbuf); 178 if (kvm_nlist(kd, namelist) == -1) 179 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 180 if (namelist[X_DK_NDRIVE].n_type == 0) 181 errx(1, "dk_ndrive not found in namelist"); 182 (void)nlread(X_DK_NDRIVE, dk_ndrive); 183 if (dk_ndrive <= 0) 184 errx(1, "invalid dk_ndrive %d\n", dk_ndrive); 185 186 cur.dk_time = calloc(dk_ndrive, sizeof(long)); 187 cur.dk_wds = calloc(dk_ndrive, sizeof(long)); 188 cur.dk_seek = calloc(dk_ndrive, sizeof(long)); 189 cur.dk_xfer = calloc(dk_ndrive, sizeof(long)); 190 last.dk_time = calloc(dk_ndrive, sizeof(long)); 191 last.dk_wds = calloc(dk_ndrive, sizeof(long)); 192 last.dk_seek = calloc(dk_ndrive, sizeof(long)); 193 last.dk_xfer = calloc(dk_ndrive, sizeof(long)); 194 dr_select = calloc(dk_ndrive, sizeof(int)); 195 dr_name = calloc(dk_ndrive, sizeof(char *)); 196 dk_wpms = calloc(dk_ndrive, sizeof(long)); 197 198 for (i = 0; i < dk_ndrive; i++) { 199 (void)sprintf(buf, "dk%d", i); 200 dr_name[i] = strdup(buf); 201 } 202 if (!read_names()) 203 exit(1); 204 (void)nlread(X_HZ, hz); 205 (void)nlread(X_STATHZ, stathz); 206 if (stathz) 207 hz = stathz; 208 (void)kvm_read(kd, namelist[X_DK_WPMS].n_value, dk_wpms, 209 dk_ndrive * sizeof(dk_wpms)); 210 211 /* 212 * Choose drives to be displayed. Priority goes to (in order) drives 213 * supplied as arguments and default drives. If everything isn't 214 * filled in and there are drives not taken care of, display the first 215 * few that fit. 216 * 217 * The backward compatibility #ifdefs permit the syntax: 218 * iostat [ drives ] [ interval [ count ] ] 219 */ 220 #define BACKWARD_COMPATIBILITY 221 for (ndrives = 0; *argv; ++argv) { 222 #ifdef BACKWARD_COMPATIBILITY 223 if (isdigit(**argv)) 224 break; 225 #endif 226 for (i = 0; i < dk_ndrive; i++) { 227 if (strcmp(dr_name[i], *argv)) 228 continue; 229 dr_select[i] = 1; 230 ++ndrives; 231 } 232 } 233 #ifdef BACKWARD_COMPATIBILITY 234 if (*argv) { 235 interval = atoi(*argv); 236 if (*++argv) 237 reps = atoi(*argv); 238 } 239 #endif 240 241 if (interval) { 242 if (!reps) 243 reps = -1; 244 } else 245 if (reps) 246 interval = 1; 247 248 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 249 if (dr_select[i] || dk_wpms[i] == 0) 250 continue; 251 for (cp = defdrives; *cp; cp++) 252 if (strcmp(dr_name[i], *cp) == 0) { 253 dr_select[i] = 1; 254 ++ndrives; 255 break; 256 } 257 } 258 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 259 if (dr_select[i]) 260 continue; 261 dr_select[i] = 1; 262 ++ndrives; 263 } 264 265 (void)signal(SIGCONT, phdr); 266 267 for (hdrcnt = 1;;) { 268 if (!--hdrcnt) { 269 phdr(0); 270 hdrcnt = 20; 271 } 272 (void)kvm_read(kd, namelist[X_DK_TIME].n_value, 273 cur.dk_time, dk_ndrive * sizeof(long)); 274 (void)kvm_read(kd, namelist[X_DK_XFER].n_value, 275 cur.dk_xfer, dk_ndrive * sizeof(long)); 276 (void)kvm_read(kd, namelist[X_DK_WDS].n_value, 277 cur.dk_wds, dk_ndrive * sizeof(long)); 278 (void)kvm_read(kd, namelist[X_DK_SEEK].n_value, 279 cur.dk_seek, dk_ndrive * sizeof(long)); 280 (void)kvm_read(kd, namelist[X_TK_NIN].n_value, 281 &cur.tk_nin, sizeof(cur.tk_nin)); 282 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, 283 &cur.tk_nout, sizeof(cur.tk_nout)); 284 (void)kvm_read(kd, namelist[X_CP_TIME].n_value, 285 cur.cp_time, sizeof(cur.cp_time)); 286 for (i = 0; i < dk_ndrive; i++) { 287 if (!dr_select[i]) 288 continue; 289 #define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 290 X(dk_xfer); 291 X(dk_seek); 292 X(dk_wds); 293 X(dk_time); 294 } 295 tmp = cur.tk_nin; 296 cur.tk_nin -= last.tk_nin; 297 last.tk_nin = tmp; 298 tmp = cur.tk_nout; 299 cur.tk_nout -= last.tk_nout; 300 last.tk_nout = tmp; 301 etime = 0; 302 for (i = 0; i < CPUSTATES; i++) { 303 X(cp_time); 304 etime += cur.cp_time[i]; 305 } 306 if (etime == 0.0) 307 etime = 1.0; 308 etime /= (float)hz; 309 (void)printf("%4.0f%5.0f", 310 cur.tk_nin / etime, cur.tk_nout / etime); 311 dkstats(); 312 cpustats(); 313 (void)printf("\n"); 314 (void)fflush(stdout); 315 316 if (reps >= 0 && --reps <= 0) 317 break; 318 (void)sleep(interval); 319 } 320 exit(0); 321 } 322 323 /* ARGUSED */ 324 void 325 phdr(signo) 326 int signo; 327 { 328 register int i; 329 330 (void)printf(" tty"); 331 for (i = 0; i < dk_ndrive; i++) 332 if (dr_select[i]) 333 (void)printf(" %3.3s ", dr_name[i]); 334 (void)printf(" cpu\n tin tout"); 335 for (i = 0; i < dk_ndrive; i++) 336 if (dr_select[i]) 337 (void)printf(" sps tps msps "); 338 (void)printf(" us ni sy id\n"); 339 } 340 341 void 342 dkstats() 343 { 344 register int dn; 345 double atime, itime, msps, words, xtime; 346 347 for (dn = 0; dn < dk_ndrive; ++dn) { 348 if (!dr_select[dn]) 349 continue; 350 words = cur.dk_wds[dn] * 32; /* words xfer'd */ 351 (void)printf("%4.0f", /* sectors */ 352 words / (DEV_BSIZE / 2) / etime); 353 354 (void)printf("%4.0f", cur.dk_xfer[dn] / etime); 355 356 if (dk_wpms[dn] && cur.dk_xfer[dn]) { 357 atime = cur.dk_time[dn]; /* ticks disk busy */ 358 atime /= (float)hz; /* ticks to seconds */ 359 xtime = words / dk_wpms[dn]; /* transfer time */ 360 itime = atime - xtime; /* time not xfer'ing */ 361 if (itime < 0) 362 msps = 0; 363 else 364 msps = itime * 1000 / cur.dk_xfer[dn]; 365 } else 366 msps = 0; 367 (void)printf("%5.1f ", msps); 368 } 369 } 370 371 void 372 cpustats() 373 { 374 register int state; 375 double time; 376 377 time = 0; 378 for (state = 0; state < CPUSTATES; ++state) 379 time += cur.cp_time[state]; 380 for (state = 0; state < CPUSTATES; ++state) 381 (void)printf("%3.0f", 382 100. * cur.cp_time[state] / (time ? time : 1)); 383 } 384 385 void 386 usage() 387 { 388 (void)fprintf(stderr, 389 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 390 exit(1); 391 } 392