/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * ULTRIX-11 iostat command (iostat.c) * * Prints statistics about disk I/O rates * and CPU times. * * Fred Canter * */ #include <a.out.h> #include <sys/types.h> #include <sys/ra_info.h> static char Sccsid[] = "@(#)iostat.c 3.1 3/26/87"; int bflg; int dflg; int tflg; int iflg; int aflg; int sflg; int dsflag; #define NSYMCK 6 /* # of symbols to check (must be in /unix) */ /* check goes from nl[0] -> nl[NSYMCK-1] */ struct nlist nl[] = { { "_cp_time" }, { "_io_info" }, { "_dk_iop" }, { "_dk_nd" }, { "_tk_nin" }, { "_tk_nout" }, { "_nuda" }, { "_nra" }, { "_ra_inde" }, { "_ra_drv" }, { "" }, }; /* * WARNING !, * DK_NC is defined here and in /usr/include/sys/systm.h, * if one is changed the other must also be changed. * There are currently 8 possible "normal" controllers * plus 4 possible MSCP controllers. */ #define DK_NC 12 /* number of possible disk controllers */ long cp_t[5]; long cp_t1[5]; /* * Iostat structures, * two for each possible drive. * Assumes 8 units per controller. */ struct ios { char dk_tr; /* drive xfer rate */ char dk_busy; /* drive activity flag */ long dk_numb; /* drive xfer count */ long dk_wds; /* drive words xfer'd (32 word clicks) */ long dk_time; /* drive active time tally */ } dk_ios[DK_NC*8], dk_ios1[DK_NC*8]; struct ios *dk_iop[DK_NC]; /* iostat structure pointers */ char dk_nd[DK_NC]; /* number of drives per controller */ /* * Disk controller information. * * CAUTION, order must not change, * add new disks at the end. */ #define MSCP 1 #define RA_OFF 8 /* Position of first MSCP controller */ struct dk_info { char *di_name; /* 2 char disk name */ int di_flag; /* flags (like MSCP or not) */ int di_dkn; /* DK_N controller number */ /* (set dynamically for MSCP controllers */ int di_units; /* selected units (bit0 = unit 0, etc. */ } dk_info[] = { "hp", 0, 0, 0377, "hm", 0, 1, 0377, "hj", 0, 2, 0377, "hk", 0, 3, 0377, "rp", 0, 4, 0377, "rl", 0, 5, 0377, "rk", 0, 6, 0377, "hs", 0, 7, 0377, "ra", MSCP, -1, 0, "rc", MSCP, -1, 0, "rx", MSCP, -1, 0, "rd", MSCP, -1, 0, 0 }; char dname[10]; /* drive select name */ char ds[7]; /* drive select numbers */ /* * Number of tty char's in/out. */ long tin, tin1; long tout, tout1; /* usec per word for the various disks */ double xf[] = { 0.0, /* dummy - no drive available */ 2.48, /* RM02 and RP04/5/6 */ 1.65, /* RM03, RM05 */ 4.3, /* RK06/7 */ 1.0, /* ML11 - 2.0 mb */ 2.0, /* ML11 - 1.0 mb */ 4.0, /* ML11 - 0.5 mb */ 8.0, /* ML11 - 0.25 mb */ 7.5, /* RP03 */ 3.9, /* RL01/2 */ 11.1, /* RK03/5 */ 4.0, /* RS03 non sector interleaved */ 8.0, /* RS03 sector interleaved */ 2.0, /* RS04 non sector interleaved */ 4.0, /* RS04 sector interleaved */ 2.3, /* NOT USED - was RA81 */ 2.3, /* RA60/RA80/RA81 (see note below) */ 68.5, /* RX50 */ 3.2, /* RD31/RD32/RD51/RD52/RD53/RD54 */ 3.1, /* RC25 */ }; /* * Transfer rate values for MSCP disks are only very * rough estimates (by scoping NPG). Actual transfer * rate varies between CPU types. * All the drives have the same peek transfer rate (1MB/SEC) * 4 Micro-sec per long word. Tha average rate slows down from * there, depending on the speed of the disk and system load. */ struct iostat { int nbuf; long nread; long nreada; long ncache; long nwrite; long bufcount[100]; } io_info, io_delta; double etime; int mf; int nuda; char nra[MAXUDA]; char ra_index[MAXUDA]; struct ra_drv ra_drv[MAXUDA*8]; main(argc, argv) char *argv[]; { struct ios *iop; extern char *ctime(); register i, j, k; int interval, count, lc; int cn, dn, ind; char *p; double f1, f2; long t; nlist("/unix", nl); for(i=0; i<NSYMCK; i++) if(nl[i].n_type == 0) { printf("\niostat: can't find %s in /unix namelist\n", nl[i].n_name); exit(1); } mf = open("/dev/mem", 0); if(mf < 0) { printf("\niostat: can't open /dev/mem\n"); exit(1); } if((nl[6].n_value==0)||(nl[7].n_value==0)||(nl[8].n_value==0)) nuda = 0; /* no MSCP controllers to worry about */ else { lseek(mf, (long)nl[6].n_value, 0); /* nuda */ read(mf, (char *)&nuda, sizeof(nuda)); if(nuda == 0) goto no_uda; /* just in case */ lseek(mf, (long)nl[7].n_value, 0); /* nra */ read(mf, (char *)&nra, nuda); lseek(mf, (long)nl[8].n_value, 0); /* ra_index */ read(mf, (char *)&ra_index, MAXUDA); for(i=0, j=0; i<nuda; i++) j += nra[i]; lseek(mf, (long)nl[9].n_value, 0); /* ra_drv */ read(mf, (char *)&ra_drv, sizeof(struct ra_drv) * j); for(cn=0; cn<nuda; cn++) for(dn=0; dn<nra[cn]; dn++) { ind = ra_index[cn] + dn; switch(ra_drv[ind].ra_dt) { case RX33: case RX50: p = "rx"; break; case RD31: case RD32: case RD51: case RD52: case RD53: case RD54: p = "rd"; break; case RA60: case RA80: case RA81: p = "ra"; break; case RC25: p = "rc"; break; default: p = "xx"; break; } for(i=0; i<DK_NC; i++) if(equal(p, dk_info[i].di_name)) { dk_info[i].di_dkn = cn + RA_OFF; dk_info[i].di_units |= (1<<dn); break; } } } no_uda: interval = 0; count = 0; for(i=1; i<argc; i++) { if(argv[i][0] == '-') { /* option */ if (argv[i][1]=='d') dflg++; else if (argv[i][1]=='s') sflg++; else if (argv[i][1]=='a') aflg++; else if (argv[i][1]=='t') tflg++; else if (argv[i][1]=='i') iflg++; else if (argv[i][1]=='b') bflg++; else printf("\niostat: (%s) bad option - ignored\n", argv[i]); continue; } if((argv[i][0] >= '0') && (argv[i][0] <= '9')) { /* number */ if(interval == 0) { interval = atoi(argv[i]); if(interval == 0) intervall++; } else count = atoi(argv[i]); continue; } /* not option or number, must be drive select */ if(strlen(argv[i]) != 3) { bad_ds: printf("\niostat: (%s) invalid drive - ignored\n", argv[i]); continue; } dname[0] = argv[i][0]; dname[1] = argv[i][1]; dname[2] = '\0'; for(j=0; j<DK_NC; j++) if(equal(&dname, dk_info[j].di_name)) break; if(dk_info[j].di_name == 0) goto bad_ds; if((argv[i][2] < '0') || (argv[i][2] > '7')) goto bad_ds; if(dsflag == 0) { dsflag++; for(k=0; k<DK_NC; k++) dk_info[k].di_units = 0; } dk_info[j].di_units |= (1 << (argv[i][2] - '0')); continue; } /* * Locate and read in the pointers to the * iostat structures for each controller and * the number of drives on each controller. */ lseek(mf, (long)nl[2].n_value, 0); read(mf, (char *)&dk_iop, sizeof(dk_iop)); lseek(mf, (long)nl[3].n_value, 0); read(mf, (char *)&dk_nd, sizeof(dk_nd)); loop: lseek(mf, (long)nl[0].n_value, 0); read(mf, (char *)&cp_t, sizeof(cp_t)); for(i=0; i<5; i++) { t = cp_t[i]; cp_t[i] -= cp_t1[i]; cp_t1[i] = t; } lseek(mf, (long)nl[4].n_value, 0); read(mf, (char *)&tin, sizeof(tin)); lseek(mf, (long)nl[5].n_value, 0); read(mf, (char *)&tout, sizeof(tout)); t = tin; tin -= tin1; tin1 = t; t = tout; tout -= tout1; tout1 = t; for(i=0; i<DK_NC; i++) { if(dk_iop[i] == 0) continue; iop = &dk_ios[0]; iop += (i*8); lseek(mf, (long)dk_iop[i], 0); read(mf, (char *)iop, (sizeof(struct ios)*dk_nd[i])); } for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; for(j=0; j<8; j++) if(dk_info[i].di_units&(1 << j)) { k = (dk_info[i].di_dkn * 8) + j; dk_ios[k].dk_busy++; } } for(i=0; i<(DK_NC * 8); i++) { t = dk_ios[i].dk_numb; dk_ios[i].dk_numb -= dk_ios1[i].dk_numb; dk_ios1[i].dk_numb = t; t = dk_ios[i].dk_wds; dk_ios[i].dk_wds -= dk_ios1[i].dk_wds; dk_ios1[i].dk_wds = t; t = dk_ios[i].dk_time; dk_ios[i].dk_time -= dk_ios1[i].dk_time; dk_ios1[i].dk_time = t; } if(lc == 0) { if (!(sflg|iflg|bflg)) { if (dflg) { long tm; time(&tm); printf("\n%s", ctime(&tm)); } printf("\n"); if(tflg) printf(" TTY"); if (bflg==0) { for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; for(j=0; j<8; j++) { k = (dk_info[i].di_dkn * 8) + j; if(dk_ios[k].dk_tr && dk_ios[k].dk_busy && (dk_info[i].di_units&(1<<j))) printf(" %s%d ", dk_info[i].di_name, j); } } printf(" PERCENT\n"); } if(tflg) printf(" tin tout"); if(bflg==0) { for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; for(j=0; j<8; j++) { k = (dk_info[i].di_dkn * 8) + j; if(dk_ios[k].dk_tr && dk_ios[k].dk_busy && (dk_info[i].di_units&(1<<j))) printf(" tpm msps mspt"); } } printf(" user nice systm idle\n"); } } } if(++lc == 10) lc = 0; t = 0; for(i=0; i<4; i++) t += cp_t[i]; etime = t; if(etime == 0.) etime = 1.; if (bflg) { biostats(); goto contin; } if (sflg) { stats2(etime); goto contin; } if (iflg) { stats3(etime); goto contin; } etime /= 60.; if(tflg) { f1 = tin; f2 = tout; printf("%6.1f", f1/etime); printf("%6.1f", f2/etime); } for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; for(j=0; j<8; j++) { k = (dk_info[i].di_dkn * 8) + j; if(dk_ios[k].dk_tr && dk_ios[k].dk_busy && (dk_info[i].di_units&(1<<j))) stats(k, i); } } for(i=0; i<4; i++) stat1(i); printf("\n"); if (aflg) printf("%.2f minutes total\n", etime/60); contin: --count; if(count) if(interval) { sleep(interval); goto loop; } } stats(dn, dt) { double f1, f2, f3; double f4, f5, f6; long t; t = dk_ios[dn].dk_time; f1 = t; f1 = f1/60.; f2 = dk_ios[dn].dk_numb; if(f2 == 0.) { printf("%6.0f%6.1f%6.1f", 0.0, 0.0, 0.0); return; } f3 = dk_ios[dn].dk_wds; f3 = f3*32.; f4 = xf[dk_ios[dn].dk_tr]; f4 = f4*1.0e-6; f5 = f1 - f4*f3; f6 = f1 - f5; printf("%6.0f", f2*60./etime); f1 = (f5*1000.)/f2; if(f1 < 0.) f1 = 0.0; if(dk_info[dt].di_flag&MSCP) printf("%6.1f", f1*1.2); /* fudge msps on MSCP disks */ else printf("%6.1f", f1); printf("%6.1f", f6*1000./f2); } stat1(o) { register i; long t; double f1, f2; t = 0; for(i=0; i<4; i++) t += cp_t[i]; f1 = t; if(f1 == 0.) f1 = 1.; f2 = cp_t[o]; printf("%6.2f", f2*100./f1); } stats2(t) double t; { register i, j, k; if (dflg) { long tm; time(&tm); printf("\n\n%s", ctime(&tm)); } printf("\ndisk\tdrive\tus/word\t%%time\txfer's\t words\n"); for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; for (j=0; j<8; j++) { k = (dk_info[i].di_dkn * 8) + j; if(dk_ios[k].dk_tr == 0) continue; printf("\n%s\t%d\t", dk_info[i].di_name, j); printf("%4.2f\t", xf[dk_ios[k].dk_tr]); printf("%5.2f\t", dk_ios[i*8+j].dk_time/(t/100)); printf("%D\t ", dk_ios[k].dk_numb); printf("%D", (dk_ios[k].dk_wds)*32); } } printf("\n"); if (aflg) printf("\n%.2f minutes total\n", etime/3600); } stats3(t) double t; { register i, j, k; double sum; if (dflg) { long tm; time(&tm); printf("\n\n%s", ctime(&tm)); } printf("\n %%time state\n"); t /= 100; sum = cp_t[3]; printf("%6.2f idle\n", sum/t); sum = cp_t[0]; printf("%6.2f user\n", sum/t); sum = cp_t[1]; printf("%6.2f nice\n", sum/t); sum = cp_t[2]; printf("%6.2f system\n", sum/t); sum = cp_t[4]; printf("%6.2f IO wait\n", sum/t); sum = 0; for (i=0; i<(DK_NC * 8); i++) if(dk_ios[i].dk_tr) sum += dk_ios[i].dk_time; printf("%6.2f IO active\n", sum/t); for(i=0; dk_info[i].di_name; i++) { if(dk_info[i].di_dkn < 0) continue; sum = 0; for(j=0; j<8; j++) k = (dk_info[i].di_dkn * 8) + j; sum += dk_ios[k].dk_time; if(sum) printf("%6.2f %s active\n", sum/t, dk_info[i].di_name); } if (aflg) printf("\n%.2f minutes total\n", etime/3600); } biostats() { register i; if (dflg) { long tm; time(&tm); printf("\n%s", ctime(&tm)); } lseek(mf,(long)nl[1].n_value, 0); read(mf, (char *)&io_info, sizeof(io_info)); printf("\nnbuf\tnread\tnreada\tncache\tnwrite\n"); printf("%d\t%D\t%D\t%D\t%D\n", io_info.nbuf, io_info.nread-io_delta.nread, io_info.nreada-io_delta.nreada, io_info.ncache-io_delta.ncache, io_info.nwrite-io_delta.nwrite); printf("\nI/O operations per buffer (0->NBUF)\n"); for(i=0; i<io_info.nbuf; ) { printf("%D\t",(long)io_info.bufcount[i]-io_delta.bufcount[i]); i++; if (i % 10 == 0) printf("\n"); } io_delta = io_info; printf("\n"); if (aflg) printf("\n%.2f minutes total\n", etime/3600); } equal(a, b) char *a, *b; { return(!strcmp(a, b)); }