1 /*- 2 * Copyright (c) 1986, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1986, 1991 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)iostat.c 5.8 (Berkeley) 04/24/91"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/buf.h> 20 #include <sys/dkstat.h> 21 #include <signal.h> 22 #include <fcntl.h> 23 #include <nlist.h> 24 #include <unistd.h> 25 #include <stdio.h> 26 #include <ctype.h> 27 #include <stdlib.h> 28 #include <stdarg.h> 29 #include <string.h> 30 #include <paths.h> 31 #include <kvm.h> 32 33 struct nlist nl[] = { 34 #define X_DK_BUSY 0 35 { "_dk_busy" }, 36 #define X_DK_TIME 1 37 { "_dk_time" }, 38 #define X_DK_XFER 2 39 { "_dk_xfer" }, 40 #define X_DK_WDS 3 41 { "_dk_wds" }, 42 #define X_TK_NIN 4 43 { "_tk_nin" }, 44 #define X_TK_NOUT 5 45 { "_tk_nout" }, 46 #define X_DK_SEEK 6 47 { "_dk_seek" }, 48 #define X_CP_TIME 7 49 { "_cp_time" }, 50 #define X_DK_WPMS 8 51 { "_dk_wpms" }, 52 #define X_HZ 9 53 { "_hz" }, 54 #define X_PHZ 10 55 { "_phz" }, 56 #define X_DK_NDRIVE 11 57 { "_dk_ndrive" }, 58 #define X_END 11 59 #ifdef hp300 60 #define X_HPDINIT (X_END+1) 61 { "_hp_dinit" }, 62 #endif 63 #ifdef tahoe 64 #define X_VBDINIT (X_END+1) 65 { "_vbdinit" }, 66 #endif 67 #ifdef vax 68 { "_mbdinit" }, 69 #define X_MBDINIT (X_END+1) 70 { "_ubdinit" }, 71 #define X_UBDINIT (X_END+2) 72 #endif 73 { 0 }, 74 }; 75 76 struct _disk { 77 int dk_busy; 78 long cp_time[CPUSTATES]; 79 long *dk_time; 80 long *dk_wds; 81 long *dk_seek; 82 long *dk_xfer; 83 long tk_nin; 84 long tk_nout; 85 } cur, last; 86 87 double etime; 88 long *dk_wpms; 89 int dk_ndrive, *dr_select, hz, kmemfd, ndrives; 90 char **dr_name; 91 92 #include "names.c" /* XXX */ 93 94 #define nlread(x, v) \ 95 kvm_read((void *)nl[x].n_value, (void *)&(v), sizeof(v)) 96 97 main(argc, argv) 98 int argc; 99 char **argv; 100 { 101 extern int optind; 102 register int i; 103 long tmp; 104 int ch, hdrcnt, reps, interval, phz, ndrives; 105 char **cp, *memfile, *namelist, buf[30]; 106 void cpustats(), dkstats(), phdr(), read_names(), usage(), error(); 107 108 interval = reps = 0; 109 namelist = memfile = NULL; 110 while ((ch = getopt(argc, argv, "c:M:N:w:")) != EOF) 111 switch(ch) { 112 case 'c': 113 reps = atoi(optarg); 114 break; 115 case 'M': 116 memfile = optarg; 117 break; 118 case 'N': 119 namelist = optarg; 120 break; 121 case 'w': 122 interval = atoi(optarg); 123 break; 124 case '?': 125 default: 126 usage(); 127 } 128 argc -= optind; 129 argv += optind; 130 131 if (kvm_openfiles(namelist, memfile, NULL) == -1) { 132 error("kvm_openfiles: %s", kvm_geterr()); 133 exit(1); 134 } 135 if (kvm_nlist(nl) == -1) { 136 error("kvm_nlist: %s", kvm_geterr()); 137 exit(1); 138 } 139 if (nl[X_DK_BUSY].n_type == 0) { 140 error("dk_busy not found namelist"); 141 exit(1); 142 } 143 if (nl[X_DK_NDRIVE].n_type == 0) { 144 error("dk_ndrive not found in namelist"); 145 exit(1); 146 } 147 (void)nlread(X_DK_NDRIVE, dk_ndrive); 148 if (dk_ndrive <= 0) { 149 error("invalid dk_ndrive %d\n", dk_ndrive); 150 exit(1); 151 } 152 153 cur.dk_time = calloc(dk_ndrive, sizeof(long)); 154 cur.dk_wds = calloc(dk_ndrive, sizeof(long)); 155 cur.dk_seek = calloc(dk_ndrive, sizeof(long)); 156 cur.dk_xfer = calloc(dk_ndrive, sizeof(long)); 157 last.dk_time = calloc(dk_ndrive, sizeof(long)); 158 last.dk_wds = calloc(dk_ndrive, sizeof(long)); 159 last.dk_seek = calloc(dk_ndrive, sizeof(long)); 160 last.dk_xfer = calloc(dk_ndrive, sizeof(long)); 161 dr_select = calloc(dk_ndrive, sizeof(int)); 162 dr_name = calloc(dk_ndrive, sizeof(char *)); 163 dk_wpms = calloc(dk_ndrive, sizeof(long)); 164 165 for (i = 0; i < dk_ndrive; i++) { 166 (void)sprintf(buf, "dk%d", i); 167 dr_name[i] = strdup(buf); 168 } 169 read_names(); 170 (void)nlread(X_HZ, hz); 171 (void)nlread(X_PHZ, phz); 172 if (phz) 173 hz = phz; 174 (void)kvm_read((void *)nl[X_DK_WPMS].n_value, dk_wpms, 175 dk_ndrive * sizeof(dk_wpms)); 176 177 /* 178 * Choose drives to be displayed. Priority goes to (in order) drives 179 * supplied as arguments and default drives. If everything isn't 180 * filled in and there are drives not taken care of, display the first 181 * few that fit. 182 * 183 * The backward compatibility #ifdefs permit the syntax: 184 * iostat [ drives ] [ interval [ count ] ] 185 */ 186 #define BACKWARD_COMPATIBILITY 187 for (ndrives = 0; *argv; ++argv) { 188 #ifdef BACKWARD_COMPATIBILITY 189 if (isdigit(**argv)) 190 break; 191 #endif 192 for (i = 0; i < dk_ndrive; i++) { 193 if (strcmp(dr_name[i], *argv)) 194 continue; 195 dr_select[i] = 1; 196 ++ndrives; 197 } 198 } 199 #ifdef BACKWARD_COMPATIBILITY 200 if (*argv) { 201 interval = atoi(*argv); 202 if (*++argv) 203 reps = atoi(*argv); 204 } 205 #endif 206 207 if (interval) { 208 if (!reps) 209 reps = -1; 210 } else 211 if (reps) 212 interval = 1; 213 214 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 215 if (dr_select[i] || dk_wpms[i] == 0) 216 continue; 217 for (cp = defdrives; *cp; cp++) 218 if (strcmp(dr_name[i], *cp) == 0) { 219 dr_select[i] = 1; 220 ++ndrives; 221 break; 222 } 223 } 224 for (i = 0; i < dk_ndrive && ndrives < 4; i++) { 225 if (dr_select[i]) 226 continue; 227 dr_select[i] = 1; 228 ++ndrives; 229 } 230 231 (void)signal(SIGCONT, phdr); 232 233 for (hdrcnt = 1;;) { 234 if (!--hdrcnt) { 235 phdr(); 236 hdrcnt = 20; 237 } 238 (void)nlread(X_DK_BUSY, cur.dk_busy); 239 (void)kvm_read((void *)nl[X_DK_TIME].n_value, 240 cur.dk_time, dk_ndrive * sizeof(long)); 241 (void)kvm_read((void *)nl[X_DK_XFER].n_value, 242 cur.dk_xfer, dk_ndrive * sizeof(long)); 243 (void)kvm_read((void *)nl[X_DK_WDS].n_value, 244 cur.dk_wds, dk_ndrive * sizeof(long)); 245 (void)kvm_read((void *)nl[X_DK_SEEK].n_value, 246 cur.dk_seek, dk_ndrive * sizeof(long)); 247 (void)kvm_read((void *)nl[X_TK_NIN].n_value, 248 &cur.tk_nin, sizeof(cur.tk_nin)); 249 (void)kvm_read((void *)nl[X_TK_NOUT].n_value, 250 &cur.tk_nout, sizeof(cur.tk_nout)); 251 (void)kvm_read((void *)nl[X_CP_TIME].n_value, 252 cur.cp_time, sizeof(cur.cp_time)); 253 for (i = 0; i < dk_ndrive; i++) { 254 if (!dr_select[i]) 255 continue; 256 #define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp 257 X(dk_xfer); 258 X(dk_seek); 259 X(dk_wds); 260 X(dk_time); 261 } 262 tmp = cur.tk_nin; 263 cur.tk_nin -= last.tk_nin; 264 last.tk_nin = tmp; 265 tmp = cur.tk_nout; 266 cur.tk_nout -= last.tk_nout; 267 last.tk_nout = tmp; 268 etime = 0; 269 for (i = 0; i < CPUSTATES; i++) { 270 X(cp_time); 271 etime += cur.cp_time[i]; 272 } 273 if (etime == 0.0) 274 etime = 1.0; 275 etime /= (float)hz; 276 (void)printf("%4.0f%5.0f", 277 cur.tk_nin / etime, cur.tk_nout / etime); 278 dkstats(i); 279 cpustats(i); 280 (void)printf("\n"); 281 (void)fflush(stdout); 282 283 if (reps >= 0 && --reps <= 0) 284 break; 285 (void)sleep(interval); 286 } 287 exit(0); 288 } 289 290 void 291 phdr() 292 { 293 register int i; 294 295 (void)printf(" tty"); 296 for (i = 0; i < dk_ndrive; i++) 297 if (dr_select[i]) 298 (void)printf(" %3.3s ", dr_name[i]); 299 (void)printf(" cpu\n tin tout"); 300 for (i = 0; i < dk_ndrive; i++) 301 if (dr_select[i]) 302 (void)printf(" sps tps msps "); 303 (void)printf(" us ni sy id\n"); 304 } 305 306 void 307 dkstats() 308 { 309 register int dn; 310 double atime, itime, msps, words, xtime; 311 312 for (dn = 0; dn < dk_ndrive; ++dn) { 313 if (!dr_select[dn]) 314 continue; 315 words = cur.dk_wds[dn] * 32; /* words xfer'd */ 316 (void)printf("%4.0f", /* sectors */ 317 words / (DEV_BSIZE / 2) / etime); 318 319 (void)printf("%4.0f", cur.dk_xfer[dn] / etime); 320 321 if (dk_wpms[dn] && cur.dk_xfer[dn]) { 322 atime = cur.dk_time[dn]; /* ticks disk busy */ 323 atime /= (float)hz; /* ticks to seconds */ 324 xtime = words / dk_wpms[dn]; /* transfer time */ 325 itime = atime - xtime; /* time not xfer'ing */ 326 if (itime < 0) 327 msps = 0; 328 else 329 msps = itime * 1000 / cur.dk_xfer[dn]; 330 } else 331 msps = 0; 332 (void)printf("%5.1f ", msps); 333 } 334 } 335 336 void 337 cpustats() 338 { 339 register int state; 340 double time; 341 342 time = 0; 343 for (state = 0; state < CPUSTATES; ++state) 344 time += cur.cp_time[state]; 345 for (state = 0; state < CPUSTATES; ++state) 346 (void)printf("%3.0f", 347 100. * cur.cp_time[state] / (time ? time : 1)); 348 } 349 350 void 351 usage() 352 { 353 (void)fprintf(stderr, 354 "usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n"); 355 exit(1); 356 } 357 358 void 359 error(fmt) 360 char *fmt; 361 { 362 va_list ap; 363 364 va_start(ap, fmt); 365 (void)fprintf(stderr, "iostat: "); 366 (void)vfprintf(stderr, fmt, ap); 367 (void)fprintf(stderr, "\n"); 368 va_end(ap); 369 } 370