/* sadp.c 1.6 of 6/28/82 */ /* sadp.c - For VAX and PDP11 machines, disk profiler profiles rp06, rm05 and general disk drives. It reads system buffer header pool, physical buffer header pool and swap buffer header pool once every second, to examine disk drive's I/O queue. For 3b20s system, it profiles the regular disk drives, it reads the circular output queue for each drive once every second. usage : sadp [-th][-d device[-drive]] s [n] */ #include <stdio.h> #include <sys/types.h> #include <sys/param.h> #ifndef u3b #include <sys/sysmacros.h> #endif #include <sys/buf.h> #include <sys/elog.h> #include <a.out.h> #include <sys/iobuf.h> #ifdef u3b #include <sys/dma.h> #include <sys/dir.h> #include <sys/vtoc.h> #include <sys/macro.h> #include <sys/dfc.h> #endif #include <time.h> #include <sys/utsname.h> #include <sys/var.h> #include <ctype.h> /* cylinder profiling */ #define BLANK ' ' #define BLOB '*' #define TRACE '.' #define BRK '=' #define FOOT '-' #define CYLNO 1 #define SEEKD 2 #define CHPERCYL 103 /*the number of 8 cylinder chunks on a disk*/ #ifndef u3b #define NDRIVE 32 #define cylin b_resid; #define V 0 #define HPS 1 #define HMS 2 #define GDS 3 #define HPU 4 #define HMU 5 #define GDU 6 #define GDCNT 7 #define SBF 8 #define PBF 9 #ifdef vax struct nlist setup[] = { {"_v"}, {"_hpstat"}, {"_hmstat"}, {"_gdstat"}, {"_hputab"}, {"_hmutab"}, {"_gdutab"}, {"_gd_cnt"}, {"_sbuf"}, {"_pbuf"}, {"_swbuf1"}, {"_swbuf2"}, {0}, }; struct buf bp[2]; /* for swap buffer */ #define SWP1 10 #define SWP2 11 #define M 13 #else struct nlist setup[] = { {"_v"}, {"_hpstat"}, {"_hmstat"}, {"_gdstat"}, {"_hputab"}, {"_hmutab"}, {"_gdutab"}, {"_gd_cnt"}, {"_buf"}, {"_pbuf"}, {"_swbuf"}, {0}, }; #define NSWP 3 struct buf bp[NSWP]; #define SWP 10 #define M 11 #endif struct iobuf dkp[NDRIVE]; struct iotime ib[NDRIVE]; struct var tbl; char *sbuf,*phybuf; char devnm[3][5]={ "rp06", "rm05", "disk" }; int nonblk; int index; int index1,gdcnt; unsigned temp1; #else #define NDRIVE 100 #define DSKINFO 0 #define DSKCNT 1 struct nlist setup[] = { {"dskinfo"}, {"dsk_cnt"}, {0}, }; char *dskloc; char devnm[1][5]={ "disk" }; uint blkno,cptr; int dskcnt; struct { uint iocnt; uint lstptr; uint bknum; } linfo[8]; #endif int fflg,dflg,tflg,hflg,errflg; int s,n,ct; static int ub = 8; int sdist; int m; int dev; int temp; int f; int i; int n1,dleng,dashb,k; int dashf; int dn; int drvlist[NDRIVE]; struct HISTDATA { long hdata[CHPERCYL]; }; struct utsname name; char *nopt; char empty[30]; char drive[30]; char *malloc(); long lseek(); long dkcyl[8][CHPERCYL]; long skcyl[8][CHPERCYL]; long iocnt[8]; static char Sccsid[]="@(#)sadp.c 1.6"; main(argc,argv) int argc; char **argv; { unsigned sleep(); unsigned actlast,actcurr; extern int optind; extern char *optarg; int c,j; char *ctime(),*stime; long time(),curt; long skdist[8]; long disk[8]; while ((c = getopt(argc,argv,"thd:")) != EOF) switch(c) { case 't': tflg++; break; case 'h': hflg++; break; case 'd': dleng = strlen(optarg); if (dleng == 5){ errflg++; break; } if (dleng > 4){ for (i=5,n1=5;i<dleng;i++){ if (optarg[i] == ','){ if (n1 == i){ errflg++; break; } if (getdrvn() != 0) { errflg++; break; } if (dashf != 0) { if (dashb >= dn){ errflg++; break; } for (j=dashb;j<dn+1;j++) drvlist[j] = 1; dashb = 0; dashf = 0; } else drvlist[dn] = 1; n1 = i+1; } else { if (optarg[i] == '-'){ if (dashf != 0) { errflg++; break; } if (getdrvn() != 0) { errflg++; break; } drvlist[dn] = 1; dashb = dn; dashf = 1; n1=i+1; } else { if (i == dleng-1){ i++; if (getdrvn() != 0) { errflg++; break; } if (dashf != 0) for (j=dashb;j<dn+1;j++) drvlist[j] = 1; else drvlist[dn] =1; } } } } } else { if (dleng != 4){ errflg++; break; } for (i=0;i<8;i++) drvlist[i] = 1; } if (errflg) break; #ifndef u3b temp = 3; #else temp = 1; #endif for (i=0;i<temp;i++) if (strncmp(optarg,devnm[i],4) == 0){ dev = i; break; } if (i == temp){ errflg++; break; } dflg++; break; case '?': errflg++; break; } if (errflg) exit1(); /* if no frequency arguments present, exit */ if (optind == argc) exit1(); /* if a non-dash field is presented as an argument, check if it is a numerical arg. */ nopt = argv[optind]; if (tstdigit(nopt) != 0) exit1(); /* for frequency arguments, if only s is presented , set n to 1 */ if ((optind +1) == argc) { s = atoi(argv[optind]); n = 1; } /* if both s and n are specified, check if arg n is numeric. */ if ((optind +1) < argc) { nopt = argv[optind + 1]; if (tstdigit(nopt) != 0) exit1(); s = atoi(argv[optind]); n = atoi(argv[optind+1]); } if ( s <= 0 ) extmsg("bad value of s"); if ( n <= 0 ) extmsg("bad value of n"); ct = s; /* get entries defined in setup from /unix */ nlist("/unix",setup); #ifdef vax for(i=0;i<M;i++) setup[i].n_value &= ~(0x80000000); #endif /* open /dev/kmem */ if((f= open("/dev/kmem",0)) == -1) extmsg("cannot open /dev/kmem"); #ifndef u3b if (dflg == 0){ for (i=0;i<3;i++){ if (setup[i+HPU].n_value != 0 ){ dev = i; break; } } if (i == 4) extmsg("None of rp06, rm05 and disk is defined"); for (i=0;i<8;i++) drvlist[i] =1; } else if (setup[dev+HPU].n_value == 0){ fprintf(stderr,"Device %s is not defined\n",devnm[dev]); exit(2); } /* get gdcnt for gd drives */ if (dev == GDU-HPU){ lseek(f,(long)setup[GDCNT].n_value,0); if (read(f,&gdcnt,sizeof gdcnt) == -1) extmsg("cannot read general disk counter"); ub = gdcnt * 8; } /* get storage from memory for sysbuf pool , physical buy pool , and for queue cnt array */ lseek(f,(long)setup[0].n_value,0); read(f,&tbl,sizeof tbl); sbuf = malloc(sizeof (struct buf)* (tbl.v_buf + tbl.v_sabuf)); if (sbuf == NULL) exit2(); phybuf = malloc(sizeof (struct buf) * tbl.v_pbuf); if (phybuf == NULL) exit2(); #else if (dflg == 0){ for (i=0;i<8;i++) drvlist[i] = 1; dev = 0; } lseek(f,(long)setup[DSKCNT].n_value,0); if (read(f,&dskcnt,sizeof dskcnt) == -1) extmsg("cannot read disk counter"); dskloc = malloc(sizeof (struct dskinfo) * dskcnt); if (dskloc == NULL) exit2(); #endif #ifdef vax /* VAX only: system buffers found thru indirection */ if(setup[SBF].n_value == 0) extmsg("no 1st ptr to SBF"); lseek(f, (long)setup[SBF].n_value, 0); if(read(f, &setup[SBF].n_value, sizeof (int)) == -1) extmsg("no 2nd ptr to SBF"); setup[SBF].n_value &= ~(0x80000000); #endif /* get current I/O count for each drive */ for (;;){ s = ct; for (k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; for (j=0;j<CHPERCYL;j++){ dkcyl[i][j] = 0; skcyl[i][j] = 0; } iocnt[i] = 0; disk[i] = 0; skdist[i] = 0; i++; if (i == 8){ ub = k+1; /* only 8 drives are allowed */ break; } } /* if no drives are selected or illegal drive number is specified, exit */ if (i == 0) exit1(); /* get i/o count for each disk */ #ifndef u3b lseek(f,(long)setup[dev+HPS].n_value,0); if(read(f,ib,(sizeof (struct iotime)*ub)) == -1) extmsg("cannot read device status table"); for(k=0,i=0;k<ub;k++) { if (drvlist[k] == 0) continue; iocnt[i] = ib[k].io_cnt; i++; } #else /* initialize linfo table */ if (dsktbl(dskloc) == -1) exit3(); getiocnt(dskloc); /* save io count into linfo table */ for (i=0;i<8;i++) linfo[i].iocnt = iocnt[i]; ldlinfo(dskloc); #endif for (;;){ #ifndef u3b /* take a snapshot of buffer header pool , swap buffer pool and physical buffer header */ lseek(f,(long)setup[HPU+dev].n_value,0); if( read(f,dkp,(sizeof (struct iobuf)* ub)) == -1){ perror("sadp"); extmsg("cannot read disk drive iobuf"); } /* read system buffer header pool */ lseek(f,(long)setup[SBF].n_value,0); if (read(f,sbuf,tbl.v_buf*sizeof(struct buf)) == -1){ perror("sadp"); extmsg("cannot read system buffer pool"); } /* read physical buffer header pool */ lseek(f,(long)setup[PBF].n_value,0); if (read(f,phybuf,tbl.v_pbuf*sizeof(struct buf))== -1){ perror("sadp"); extmsg("cannot read physical buffer pool"); } #ifdef vax for (m=SWP1;m<SWP2+1;m++){ lseek(f,(long)setup[m].n_value,0); if (read(f,bp[m-SWP1],sizeof (struct buf)) == -1){ fprintf(stderr,"cannot read phy bufhdr - %d\n",m); perror("sadp"); exit(1); } } #else lseek(f,(long)setup[SWP].n_value,0); if(read(f,bp,sizeof bp) == -1){ fprintf(stderr,"cannot read swap bufhdr %o\n", setup[5].n_value); perror("sadp"); exit(1); } #endif for (k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; /* trace disk queue for I/O location, seek distance */ nonblk = 0; if(dkp[k].b_actf != NULL ) { #ifdef vax temp1 = (unsigned)dkp[k].b_actf - 0x80000000; actlast = (unsigned)dkp[k].b_actl - 0x80000000; #else temp1 = (unsigned)dkp[k].b_actf; actlast = (unsigned)dkp[k].b_actl; #endif index1 = 0; index = (int)(temp1 -setup[SBF].n_value)/ (sizeof (struct buf)); if ((testbuf() == -1) || (testdev() == -1) || ((unsigned)dkp[k].b_actf == (unsigned)dkp[k].b_actl)) { i++; continue; } sdist = temp; #ifdef vax while ((temp1 + 0x80000000) != NULL) { #else while (temp1 != NULL){ #endif nonblk = 0; actcurr =temp1; index1 =index; index =(int)(temp1 -setup[SBF].n_value)/ (sizeof (struct buf)); if ((testbuf() == -1) || (testdev() == -1)) break; sdist = temp - sdist; if (sdist < 0) sdist = -sdist; skcyl[i][(sdist+7) >> 3]++; sdist = temp; if (actcurr == actlast) break; } } i++; } #endif #ifdef u3b /* start to snapshot the dskinfo table */ if (dsktbl(dskloc) == -1) exit3(); getsample(dskloc); #endif if (--s) sleep(1); else{ /* at the end of sampling, get the present I/O count, and system name */ #ifndef u3b lseek(f,(long)setup[dev+HPS].n_value,0); read(f,ib,(sizeof (struct iotime)*ub)); #else if (dsktbl(dskloc) == -1) exit3(); getiocnt(dskloc); #endif uname(&name); /* print the report, there are two parts: cylinder profile, seeking distance profile */ curt = time((long *) 0); stime = ctime (&curt); printf("\n\n%s\n",stime); printf("%s %s %s %s %s\n", name.sysname, name.nodename, name.release, name.version, name.machine); for (k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; for (j=0;j<CHPERCYL;j++){ disk[i] = disk[i] +dkcyl[i][j]; skdist[i] = skdist[i] + skcyl[i][j]; } i++; } if ((tflg == 0) && (hflg == 0)) tflg = 1; if (tflg){ printf("\nCYLINDER ACCESS PROFILE\n"); for (k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; if (disk[i] != 0){ #ifndef u3b iocnt[i] = ib[k].io_cnt - iocnt[i]; #else iocnt[i] = iocnt[i] - (long)linfo[i].iocnt; #endif printf("\n%s-%d:\n", devnm[dev],k); printf("Cylinders\tTransfers\n"); for (j=0;j<CHPERCYL;j++){ if (dkcyl[i][j] > 0) printf("%3d - %3d\t%ld\n", j*8,j*8+7,dkcyl[i][j]); } printf("\nSampled I/O = %ld, Actual I/O = %ld\n", disk[i],iocnt[i]); if (iocnt[i] > 0) printf("Percentage of I/O sampled = %2.2f\n", ((float)disk[i] /(float)iocnt[i]) * 100.0); } i++; } printf("\n\n\nSEEK DISTANCE PROFILE\n"); for (k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; if (skdist[i] != 0){ printf("\n%s-%d:\n", devnm[dev],k); printf("Seek Distance\tSeeks\n"); for(j=0;j<CHPERCYL;j++) if (skcyl[i][j] > 0){ if (j == 0) printf(" 0\t%ld\n", skcyl[i][j]); else printf("%3d - %3d\t%ld\n", j*8-7,j*8,skcyl[i][j]); } printf("Total Seeks = %ld\n",skdist[i]); } i++; } } if (hflg){ for(k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; if (disk[i] != 0) { cylhdr(CYLNO,disk[i]); cylhist(disk[i],dkcyl[i]); cylftr(CYLNO); } i++; } for(k=0,i=0;k<ub;k++){ if (drvlist[k] == 0) continue; if (skdist[i] != 0){ cylhdr(SEEKD,skdist[i]); cylhist(skdist[i],skcyl[i]); cylftr(SEEKD); } i++; } } break; } } if (--n) continue; exit(0); } } #ifndef u3b /* determine if the I/O is from system buffer pool, or swap buffer pool or physical buffer */ int testbuf() { if ((temp1 <setup[SBF].n_value) || (index > (tbl.v_buf + tbl.v_sabuf))){ index = (int)(temp1 -setup[PBF].n_value)/ (sizeof (struct buf)); if (index < tbl.v_pbuf){ nonblk = 1; return(0); } #ifdef vax for (m=SWP1;m<SWP2;m++){ if (temp1 == setup[m].n_value){ m = m-SWP1; nonblk =2; return(0); } } return(-1); #else index = (int)(temp1 -setup[SWP].n_value)/ (sizeof (struct buf)); if (index < NSWP) { m = index; nonblk = 2; return(0); } else return(-1); #endif } return(0); } /* varify the I/O, get the cylinder number */ ckbits(x) register struct buf *x; { register p; for (p=0;p<index;p++,x++) continue; if((x->b_flags & B_BUSY) && ((x->b_flags & B_DONE) == 0)){ temp = x->cylin; #ifdef vax temp1 = (unsigned)x->av_forw - 0x80000000; #else temp1 = (unsigned)x->av_forw; #endif return(0); } else return(-1); } int testdev() { if((nonblk == 0) && (ckbits(sbuf) != -1)) goto endtest; else { if ((nonblk == 1) && (ckbits(phybuf) != -1)) goto endtest; else { if ((nonblk == 2) && ((bp[m].b_flags & B_BUSY) && ((bp[m].b_flags & B_DONE) == 0))){ temp = bp[m].cylin; #ifdef vax temp1 = (unsigned)bp[m].av_forw - 0x80000000; #else temp1 = bp[m].av_forw; #endif } else return(-1); } } endtest: dkcyl[i][temp >> 3]++; return(0); } #else dsktbl(x) register struct dskinfo *x; { lseek (f,(long)setup[DSKINFO].n_value,0); if (read(f,x,dskcnt*sizeof (struct dskinfo)) == -1) return(-1); return(0); } getiocnt(x) register struct dskinfo *x; { register p,i; for (p=0,i=0;p<ub;p++,x++) if (drvlist[p] == 0) continue; else{ #ifdef u3b iocnt[i] = x->devinfo.io_bcnt; #else iocnt[i] = x->blkcnt; #endif i++; } return; } ldlinfo(x) register struct dskinfo *x; { register i,k; for (i=0,k=0;k<ub;k++,x++) if (drvlist[k] == 0) continue; else { linfo[i].lstptr = (int)x->index == 0? 9: (x->index -1); linfo[i].bknum = x->outq[linfo[i].lstptr].blknum; i++; } } getsample(x) register struct dskinfo *x; { register i,k; int cntflg=0; register j; for (i=0,k=0;k<ub;k++,x++){ if (drvlist[k] == 0) continue; if (linfo[i].bknum == 0 && x->index == 0 && x->outq[x->index].blknum == 0){ i++; continue; } if (linfo[i].bknum == x->outq[linfo[i].lstptr].blknum) if (x->index == ((linfo[i].lstptr + 1) % 10)){ i++; continue; } else { cptr = linfo[i].lstptr; cntflg = 1; } else cptr = x->index; blkno = x->outq[cptr].blknum; getcyl(blkno,x); if (cntflg) cntflg = 0; else dkcyl[i][temp >> 3]++; sdist = temp; while (cptr != x->index){ blkno = x->outq[cptr].blknum; getcyl(blkno,x); dkcyl[i][temp >> 3]++; sdist = temp -sdist; if (sdist < 0) sdist = -sdist; skcyl[i][(sdist+7) >> 3]++; sdist = temp; } linfo[i].lstptr = (int)cptr == 0? 9: (cptr - 1); linfo[i].bknum = blkno; i++; } } getcyl(y,x) register struct dskinfo *x; register uint y; { int res; int bpercyl; /*blocks per cylinder*/ /* There are over 32600 tracks on a 675Mb disk and about 15485 tracks on a 300 Mb disk. In the following line of code the number 20000 is used to test which type of disk this is. There are 608 blocks per cylinder on a 300 Mb disk and 1280 blocks per cylinder on a 675 Mb disk. */ if (x->numtrks > 20000) bpercyl = 1280; else bpercyl = 608; res = (y % bpercyl) == 0? 0: 1; temp = (int)(blkno / bpercyl) + res; cptr = (cptr +1) % 10; return(0); } exit3() { fprintf(stderr,"cannot read dskinfo table\n"); exit(3); } #endif /* get drive number routine */ getdrvn() { extern char *optarg; char *strcpy(); char *strncat(); strcpy(drive,empty); strncat(drive,&optarg[n1],i-n1); if (tstdigit(drive) != 0) return (-1); dn =atoi(drive); if (dn >= NDRIVE) return(-1); return(0); } exit1() { fprintf(stderr,"usage: sadp [-th][-d device[-drive]] s [n]\n"); exit(1); } exit2() { fprintf(stderr,"sadp: can't get memory, TRY AGAIN!!\n"); exit(2); } extmsg(msg) char *msg; { fprintf(stderr, "sadp: %s\n", msg); exit(4); } int tstdigit(ss) char *ss; { int kk,cc; kk=0; while ((cc = ss[kk]) != '\0' ){ if (isdigit(cc) == 0) return(-1); kk++; } return(0); } /* the following routines are obtained from iostat */ /* cylinder profiling histogram */ /*.s'cylhist'Output Cylinder Histogram'*/ cylhist(at, dp) long at; register struct HISTDATA *dp; { register ii; int maxrow; long graph[CHPERCYL]; long max, max2; long data; long scale; max = 0; for(ii = 0; ii < CHPERCYL; ii++) { if(data = dp->hdata[ii]) { maxrow = ii; if(data > max) { max2 = max; max = data; } else if (data > max2) max2 = data; } } maxrow++; /* determine scaling */ scale = (max2 * 100)/at; for(ii = 24; ii > 2; ii /= 2) { if(scale > ii) break; } scale = 24 / ii; for(ii = 0; ii < maxrow; ii++) { if(dp->hdata[ii]) graph[ii] = (scale * 100 * dp->hdata[ii]) / at; else graph[ii] = -1; } prthist(graph, maxrow, scale, (long) (max*100*scale/at)); } /*.s'prthist'Print Histogram'*/ prthist(array, mrow, scale, gmax) long array[], scale, gmax; register mrow; { long line; line = 50; /* handle overflow in scaling */ if(gmax > 51) { line = 52; printf("\n%2ld%% -|", gmax/scale); pline(line--, array, mrow, BLOB); printf("\n %c", BRK); pline(line--, array, mrow, BRK); } else if ( gmax = 51 ) line = 51; while( line > 0) { if((line & 07) == 0) { printf("\n%2ld%% -|", line/scale); } else { printf("\n |"); } pline(line--, array, mrow, BLOB); } printf("\n 0%% -+"); line = -1; pline( line, array, mrow, FOOT); } /*.s'pline'Print Histogram Line'*/ pline(line, array, mrow, dot) long line, array[]; int mrow; char dot; { register ii; register char *lp; char lbuff[132]; lp = lbuff; for(ii = 0; ii < mrow; ii++) if(array[ii] < line) if(line == 1 && array[ii] == 0) *lp++ = TRACE; else *lp++ = BLANK; else *lp++ = dot; *lp++ = 0; printf("%s", lbuff); } /*.s'cylhdr'Print Cylinder Profiling Headers'*/ cylhdr( flag, total) long total; { if(fflg) printf("\014\n"); if( flag == CYLNO) printf("\nCYLINDER ACCESS HISTOGRAM\n"); if (flag == SEEKD) printf("\nSEEK DISTANCE HISTOGRAM\n"); printf("\n%s-%d:\n", devnm[dev],k); printf("Total %s = %ld\n", flag==CYLNO ? "transfers" : "seeks", total); } /*.s'cylftr'Print Histogram Footers'*/ cylftr( flag ) { if (flag == CYLNO) printf("\n \t\t\tCylinder number, granularity=8"); else printf("\n =<<\t<\t<\t<\t<\t<\t<\t<\t<"); printf("\n 081\t8\t1\t2\t2\t3\t4\t4\t5"); printf("\n 6\t0\t4\t0\t7\t3\t0\t6\t2"); printf("\n \t \t4\t8\t2\t6\t0\t4\t8"); printf("\n"); }