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 #if defined(i386) 90 #define X_DK_NAMES (X_END+1) 91 { "_dk_names" }, 92 #endif 93 #ifdef mips 94 #define X_SCSI_DINIT (X_END+1) 95 { "_scsi_dinit" }, 96 #endif 97 #ifdef tahoe 98 #define X_VBDINIT (X_END+1) 99 { "_vbdinit" }, 100 #endif 101 #ifdef vax 102 { "_mbdinit" }, 103 #define X_MBDINIT (X_END+1) 104 { "_ubdinit" }, 105 #define X_UBDINIT (X_END+2) 106 #endif 107 { NULL }, 108 }; 109 110 struct _disk { 111 long cp_time[CPUSTATES]; 112 long *dk_time; 113 long *dk_wds; 114 long *dk_seek; 115 long *dk_xfer; 116 long tk_nin; 117 long tk_nout; 118 } cur, last; 119 120 kvm_t *kd; 121 double etime; 122 long *dk_wpms; 123 int dk_ndrive, *dr_select, hz, kmemfd, ndrives; 124 char **dr_name; 125 126 #define nlread(x, v) \ 127 kvm_read(kd, namelist[x].n_value, &(v), sizeof(v)) 128 129 #include "names.c" /* XXX */ 130 131 void cpustats __P((void)); 132 void dkstats __P((void)); 133 void phdr __P((int)); 134 void usage __P((void)); 135 136 int 137 main(argc, argv) 138 int argc; 139 char *argv[]; 140 { 141 register int i; 142 long tmp; 143 int ch, hdrcnt, reps, interval, stathz, ndrives; 144 char **cp, *memf, *nlistf, buf[30]; 145 char errbuf[_POSIX2_LINE_MAX]; 146 147 interval = reps = 0; 148 nlistf = memf = NULL; 149 while ((ch = getopt(argc, argv, "c:M:N:w:")) != -1) 150 switch(ch) { 151 case 'c': 152 if ((reps = atoi(optarg)) <= 0) 153 errx(1, "repetition count <= 0."); 154 break; 155 case 'M': 156 memf = optarg; 157 break; 158 case 'N': 159 nlistf = optarg; 160 break; 161 case 'w': 162 if ((interval = atoi(optarg)) <= 0) 163 errx(1, "interval <= 0."); 164 break; 165 case '?': 166 default: 167 usage(); 168 } 169 argc -= optind; 170 argv += optind; 171 172 /* 173 * Discard setgid privileges if not the running kernel so that bad 174 * guys can't print interesting stuff from kernel memory. 175 */ 176 if (nlistf != NULL || memf != NULL) 177 setgid(getgid()); 178 179 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); 180 if (kd == 0) 181 errx(1, "kvm_openfiles: %s", errbuf); 182 if (kvm_nlist(kd, namelist) == -1) 183 errx(1, "kvm_nlist: %s", kvm_geterr(kd)); 184 if (namelist[X_DK_NDRIVE].n_type == 0) 185 errx(1, "dk_ndrive not found in namelist"); 186 (void)nlread(X_DK_NDRIVE, dk_ndrive); 187 if (dk_ndrive < 0) 188 errx(1, "invalid dk_ndrive %d\n", dk_ndrive); 189 190 cur.dk_time = calloc(dk_ndrive, sizeof(long)); 191 cur.dk_wds = calloc(dk_ndrive, sizeof(long)); 192 cur.dk_seek = calloc(dk_ndrive, sizeof(long)); 193 cur.dk_xfer = calloc(dk_ndrive, sizeof(long)); 194 last.dk_time = calloc(dk_ndrive, sizeof(long)); 195 last.dk_wds = calloc(dk_ndrive, sizeof(long)); 196 last.dk_seek = calloc(dk_ndrive, sizeof(long)); 197 last.dk_xfer = calloc(dk_ndrive, sizeof(long)); 198 dr_select = calloc(dk_ndrive, sizeof(int)); 199 dr_name = calloc(dk_ndrive, sizeof(char *)); 200 dk_wpms = calloc(dk_ndrive, sizeof(long)); 201 202 for (i = 0; i < dk_ndrive; i++) { 203 (void)sprintf(buf, "dk%d", i); 204 dr_name[i] = strdup(buf); 205 } 206 if (!read_names()) 207 exit(1); 208 (void)nlread(X_HZ, hz); 209 (void)nlread(X_STATHZ, stathz); 210 if (stathz) 211 hz = stathz; 212 (void)kvm_read(kd, namelist[X_DK_WPMS].n_value, dk_wpms, 213 dk_ndrive * sizeof(dk_wpms)); 214 215 /* 216 * Choose drives to be displayed. Priority goes to (in order) drives 217 * supplied as arguments and default drives. If everything isn't 218 * filled in and there are drives not taken care of, display the first 219 * few that fit. 220 * 221 * The backward compatibility #ifdefs permit the syntax: 222 * iostat [ drives ] [ interval [ count ] ] 223 */ 224 #define BACKWARD_COMPATIBILITY 225 for (ndrives = 0; *argv; ++argv) { 226 #ifdef BACKWARD_COMPATIBILITY 227 if (isdigit(**argv)) 228 break; 229 #endif 230 for (i = 0; i < dk_ndrive; i++) { 231 if (strcmp(dr_name[i], *argv)) 232 continue; 233 dr_select[i] = 1; 234 ++ndrives; 235 } 236 } 237 #ifdef BACKWARD_COMPATIBILITY 238 if (*argv) { 239 interval = atoi(*argv); 240 if (*++argv) 241 reps = atoi(*argv); 242 } 243 #endif 244 245 if (interval) { 246 if (!reps) 247 reps = -1; 248 } else 249 if (reps) 250 interval = 1; 251 252 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 253 if (dr_select[i] || dk_wpms[i] == 0) 254 continue; 255 for (cp = defdrives; *cp; cp++) 256 if (strcmp(dr_name[i], *cp) == 0) { 257 dr_select[i] = 1; 258 ++ndrives; 259 break; 260 } 261 } 262 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 263 if (dr_select[i]) 264 continue; 265 dr_select[i] = 1; 266 ++ndrives; 267 } 268 269 (void)signal(SIGCONT, phdr); 270 271 for (hdrcnt = 1;;) { 272 if (!--hdrcnt) { 273 phdr(0); 274 hdrcnt = 20; 275 } 276 (void)kvm_read(kd, namelist[X_DK_TIME].n_value, 277 cur.dk_time, dk_ndrive * sizeof(long)); 278 (void)kvm_read(kd, namelist[X_DK_XFER].n_value, 279 cur.dk_xfer, dk_ndrive * sizeof(long)); 280 (void)kvm_read(kd, namelist[X_DK_WDS].n_value, 281 cur.dk_wds, dk_ndrive * sizeof(long)); 282 (void)kvm_read(kd, namelist[X_DK_SEEK].n_value, 283 cur.dk_seek, dk_ndrive * sizeof(long)); 284 (void)kvm_read(kd, namelist[X_TK_NIN].n_value, 285 &cur.tk_nin, sizeof(cur.tk_nin)); 286 (void)kvm_read(kd, namelist[X_TK_NOUT].n_value, 287 &cur.tk_nout, sizeof(cur.tk_nout)); 288 (void)kvm_read(kd, namelist[X_CP_TIME].n_value, 289 cur.cp_time, sizeof(cur.cp_time)); 290 for (i = 0; i < dk_ndrive; i++) { 291 if (!dr_select[i]) 292 continue; 293 #define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 294 X(dk_xfer); 295 X(dk_seek); 296 X(dk_wds); 297 X(dk_time); 298 } 299 tmp = cur.tk_nin; 300 cur.tk_nin -= last.tk_nin; 301 last.tk_nin = tmp; 302 tmp = cur.tk_nout; 303 cur.tk_nout -= last.tk_nout; 304 last.tk_nout = tmp; 305 etime = 0; 306 for (i = 0; i < CPUSTATES; i++) { 307 X(cp_time); 308 etime += cur.cp_time[i]; 309 } 310 if (etime == 0.0) 311 etime = 1.0; 312 etime /= (float)hz; 313 (void)printf("%4.0f%5.0f", 314 cur.tk_nin / etime, cur.tk_nout / etime); 315 dkstats(); 316 cpustats(); 317 (void)printf("\n"); 318 (void)fflush(stdout); 319 320 if (reps >= 0 && --reps <= 0) 321 break; 322 (void)sleep(interval); 323 } 324 exit(0); 325 } 326 327 /* ARGUSED */ 328 void 329 phdr(signo) 330 int signo; 331 { 332 register int i; 333 334 (void)printf(" tty"); 335 for (i = 0; i < dk_ndrive; i++) 336 if (dr_select[i]) 337 (void)printf(" %4.4s ", dr_name[i]); 338 (void)printf(" cpu\n tin tout"); 339 for (i = 0; i < dk_ndrive; i++) 340 if (dr_select[i]) 341 (void)printf(" sps tps msps "); 342 (void)printf(" us ni sy in id\n"); 343 } 344 345 void 346 dkstats() 347 { 348 register int dn; 349 double atime, itime, msps, words, xtime; 350 351 for (dn = 0; dn < dk_ndrive; ++dn) { 352 if (!dr_select[dn]) 353 continue; 354 words = (double)cur.dk_wds[dn] * 32; /* words xfer'd */ 355 (void)printf("%4.0f", /* sectors */ 356 words / (DEV_BSIZE / 2) / etime); 357 358 (void)printf("%4.0f", cur.dk_xfer[dn] / etime); 359 360 if (dk_wpms[dn] && cur.dk_xfer[dn]) { 361 atime = cur.dk_time[dn]; /* ticks disk busy */ 362 atime /= (float)hz; /* ticks to seconds */ 363 xtime = words / dk_wpms[dn]; /* transfer time */ 364 itime = atime - xtime; /* time not xfer'ing */ 365 if (itime < 0) 366 msps = 0; 367 else 368 msps = itime * 1000 / cur.dk_xfer[dn]; 369 } else 370 msps = 0; 371 (void)printf("%5.1f ", msps); 372 } 373 } 374 375 void 376 cpustats() 377 { 378 register int state; 379 double time; 380 381 time = 0; 382 for (state = 0; state < CPUSTATES; ++state) 383 time += cur.cp_time[state]; 384 for (state = 0; state < CPUSTATES; ++state) 385 (void)printf("%3.0f", 386 100. * cur.cp_time[state] / (time ? time : 1)); 387 } 388 389 void 390 usage() 391 { 392 (void)fprintf(stderr, 393 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 394 exit(1); 395 } 396