+/*
+ * prof
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <a.out.h>
+#include <pagsiz.h>
+
+typedef short UNIT; /* unit of profiling */
+#define A_OUTNAME "a.out"
+#define MON_OUTNAME "mon.out"
+#define MON_SUMNAME "mon.sum"
+
+/*
+ * The symbol table;
+ * for each external in the specified file we gather
+ * its address, the number of calls and compute its share of cpu time.
+ */
+struct nl {
+ char *name;
+ unsigned value;
+ float time;
+ long ncall;
+} *nl;
+int nname;
+struct nl *np;
+struct nl *npe;
+
+/*
+ * The header on the mon.out file.
+ * Mon.out consists of one of these headers, an array of ncount
+ * cnt structures (as below) and then an array of samples
+ * representing the discretized program counter values.
+ */
+struct hdr {
+ UNIT *lowpc, *highpc;
+ int ncount;
+} h;
+
+/*
+ * Each counter has an address and a number of calls.
+ */
+struct cnt {
+ unsigned cvalue;
+ long cncall;
+} *cbuf;
+
+/*
+ * Each discretized pc sample has
+ * a count of the number of samples in its range
+ */
+unsigned UNIT *samples;
+
+FILE *pfile, *nfile;
+
+unsigned lowpc, highpc; /* range profiled */
+double ransca, ranoff; /* scaling for blowing up plots */
+unsigned sampbytes; /* number of bytes of samples */
+int nsamples; /* number of samples */
+double totime; /* total time for all routines */
+double maxtime; /* maximum time of any routine (for plot) */
+double scale; /* scale factor converting samples to pc
+ values: each sample covers scale bytes */
+char *strtab; /* string table in core */
+off_t ssiz; /* size of the string table */
+struct exec xbuf; /* exec header of a.out */
+
+int aflg;
+int nflg;
+int vflg;
+int lflg;
+int zflg;
+int sflag;
+
+char *namfil;
+
+int timcmp(), valcmp(), cntcmp();
+
+main(argc, argv)
+ char **argv;
+{
+ int lowpct, highpct;
+
+ /*
+ * Use highpct and lowpc as percentages, temporarily
+ * for graphing options involving blow-up
+ */
+ lowpct = -1;
+ highpct = -1;
+ argv++;
+ while ( *argv != 0 && **argv == '-' ) {
+ *argv += 1;
+ if (**argv == 'l')
+ lflg++;
+ else if (**argv == 'a')
+ aflg++;
+ else if (**argv == 'n')
+ nflg++;
+ else if (**argv == 'z')
+ zflg++;
+ else if (**argv == 'v')
+ vflg++;
+ else if ( **argv == 's' )
+ sflag++;
+ else if (**argv >= '0' && **argv <= '9') {
+ int i = atoi(*argv);
+ if (lowpct == -1)
+ lowpct = i;
+ else
+ highpct = i;
+ }
+ argv++;
+ }
+ if ( *argv != 0 ) {
+ namfil = *argv;
+ argv++;
+ } else {
+ namfil = A_OUTNAME;
+ }
+ if (lowpct >= 100)
+ lowpct = 0;
+ if (highpct <= lowpct || highpct > 100)
+ highpct = 100;
+ ransca = 100./(highpct-lowpct);
+ ranoff = 2040. + 40.8*lowpc*ransca;
+ /*
+ * get information about a.out file.
+ */
+ getnfile();
+ /*
+ * get information about mon.out file(s).
+ */
+ if ( *argv == 0 ) {
+ getpfile( MON_OUTNAME );
+ } else {
+ do {
+ getpfile( *argv );
+ argv++;
+ } while ( *argv != 0 );
+ }
+ asgnsamples(); /* assign samples to procedures */
+#ifdef plot
+ if (vflag)
+ plotprof(); /* a plotted or ... */
+ else
+#else
+ printprof(); /* a printed profile */
+#endif
+ if ( sflag != 0 ) {
+ putprof();
+ }
+ done();
+}
+
+printprof()
+{
+ double time, actime;
+
+ actime = 0;
+ printf(" %%time cumsecs #call ms/call name\n");
+ if (!lflg)
+ qsort(nl, nname, sizeof(struct nl), timcmp);
+ for (np = nl; np<npe-1; np++) {
+ if (zflg == 0 && np->time == 0 && np->ncall == 0)
+ continue;
+ time = np->time/totime;
+ actime += np->time;
+ printf("%6.1f%9.2f", 100*time, actime/60);
+ if (np->ncall != 0)
+ printf("%7ld %8.2f",
+ np->ncall, np->time/(np->ncall*.06));
+ else
+ printf("%7.7s %8.8s", "", "");
+ printf(" %s\n", np->name);
+ }
+}
+
+/*
+ * Set up string and symbol tables from a.out.
+ * On return symbol table is sorted by value.
+ */
+getnfile()
+{
+
+ nfile = fopen(namfil,"r");
+ if (nfile == NULL) {
+ perror(namfil);
+ done();
+ }
+ fread(&xbuf, 1, sizeof(xbuf), nfile);
+ if (N_BADMAG(xbuf)) {
+ fprintf(stderr, "%s: bad format\n", namfil);
+ done();
+ }
+ getstrtab();
+ getsymtab();
+ qsort(nl, nname, sizeof(struct nl), valcmp);
+}
+
+getstrtab()
+{
+
+ fseek(nfile, N_SYMOFF(xbuf) + xbuf.a_syms, 0);
+ if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
+ fprintf(stderr, "%s: no string table (old format?)\n", namfil);
+ done();
+ }
+ strtab = (char *)calloc(ssiz, 1);
+ if (strtab == NULL) {
+ fprintf(stderr, "%s: no room for %d bytes of string table",
+ namfil, ssiz);
+ done();
+ }
+ if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
+ fprintf(stderr, "%s: error reading string table\n", namfil);
+ done();
+ }
+}
+
+/*
+ * Read in symbol table
+ */
+getsymtab()
+{
+ register int i;
+
+ /* pass1 - count symbols */
+ fseek(nfile, N_SYMOFF(xbuf), 0);
+ nname = 0;
+ for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
+ struct nlist nbuf;
+ fread(&nbuf, sizeof(nbuf), 1, nfile);
+ if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
+ continue;
+ if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
+ continue;
+ nname++;
+ }
+ if (nname == 0) {
+ fprintf(stderr, "%s: no symbols\n", namfil);
+ done();
+ }
+ nl = (struct nl *)calloc((nname+1), sizeof (struct nl));
+ if (nl == 0) {
+ fprintf(stderr, "prof: No room for %d bytes of symbol table\n",
+ (nname+1) * sizeof (struct nlist));
+ done();
+ }
+
+ /* pass2 - read symbols */
+ fseek(nfile, N_SYMOFF(xbuf), 0);
+ npe = nl;
+ nname = 0;
+ for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
+ struct nlist nbuf;
+ fread(&nbuf, sizeof(nbuf), 1, nfile);
+ if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
+ continue;
+ if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
+ continue;
+ npe->value = nbuf.n_value/sizeof(UNIT);
+ npe->name = strtab+nbuf.n_un.n_strx;
+ npe++;
+ nname++;
+ }
+ npe->value = -1;
+ npe++;
+}
+
+ /*
+ * information from a mon.out file is in two parts:
+ * the counters of how many times each procedure was called,
+ * if it was called at all;
+ * and an array of sampling hits within pc ranges.
+ * the counters must be dealt with on a file-by-file basis,
+ * since which procedures are represented may vary.
+ * the samples ranges are fixed, but must be summed across
+ * files, and then distributed amoung procedures, because
+ * of the wierd way the plotting is done.
+ */
+getpfile( filename )
+ char *filename;
+{
+ openpfile( filename );
+ readcntrs();
+ asgncntrs(); /* assign counts to procedures */
+ readsamples();
+ closepfile();
+}
+
+openpfile( filename )
+ char *filename;
+{
+ struct stat stb;
+
+ if((pfile = fopen( filename ,"r")) == NULL) {
+ perror( filename );
+ done();
+ }
+ fstat(fileno(pfile), &stb);
+ fread(&h, sizeof(struct hdr), 1, pfile);
+ lowpc = h.lowpc - (UNIT *)0;
+ highpc = h.highpc - (UNIT *)0;
+ sampbytes =
+ stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt);
+ nsamples = sampbytes / sizeof (unsigned UNIT);
+}
+
+closepfile()
+{
+
+ fclose( pfile );
+ free( cbuf );
+}
+
+ /*
+ * read in procedure call counters
+ */
+readcntrs()
+{
+ struct cnt *kp;
+
+ cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt));
+ if (cbuf == 0) {
+ fprintf(stderr, "prof: No room for %d bytes of count buffer\n",
+ (h.ncount+1) * sizeof (struct cnt));
+ exit(1);
+ }
+ fread(cbuf, sizeof(struct cnt), h.ncount, pfile);
+ /* eliminate zero counters and scale counter pc values */
+ if (h.ncount) {
+ kp = &cbuf[h.ncount - 1];
+ for (;;) {
+ if (kp->cvalue==0) {
+ h.ncount=kp-cbuf;
+ ++kp;
+ break;
+ }
+ if (kp == cbuf) {
+ h.ncount = 0;
+ break;
+ }
+ --kp;
+ }
+ for (; --kp>=cbuf; )
+ kp->cvalue /= sizeof(UNIT);
+ }
+ /* sort counters */
+ qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp);
+}
+
+/*
+ * Assign counters to the procedures to which they belong
+ */
+asgncntrs()
+{
+ register int i;
+ struct cnt *kp;
+
+ kp = &cbuf[h.ncount-1];
+ np = npe;
+ while (--np>=nl) {
+ if (kp<cbuf || np->value > kp->cvalue)
+ continue;
+ while (kp >= cbuf && kp->cvalue - np->value > 11)
+ --kp;
+ if (kp->cvalue >= np->value) {
+ np->ncall += kp->cncall;
+ --kp;
+ }
+ }
+}
+
+ /*
+ * read pc samples
+ */
+readsamples()
+{
+ register i;
+ unsigned UNIT sample;
+
+ if ( samples == 0 ) {
+ samples = (unsigned UNIT *)
+ calloc( sampbytes , sizeof (unsigned UNIT) );
+ if ( samples == 0 ) {
+ printf( "prof: No room for %d sample pc's\n" ,
+ sampbytes / sizeof (unsigned UNIT) );
+ done();
+ }
+ }
+ for ( i = 0 ; ; i++ ) {
+ fread( &sample , sizeof (unsigned UNIT) , 1 , pfile );
+ if ( feof( pfile ) ) {
+ break;
+ }
+ samples[ i ] += sample;
+ }
+ if ( i != nsamples ) {
+ fprintf( stderr ,
+ "prof: unexpected EOF after reading %d/%d samples\n" ,
+ --i , nsamples );
+ done();
+ }
+}
+/*
+ * Assign samples to the procedures to which they belong.
+ */
+asgnsamples()
+{
+ register j;
+ unsigned UNIT ccnt;
+ double time;
+ unsigned pcl, pch;
+ register int i;
+ int overlap;
+
+ /* read samples and assign to namelist symbols */
+ scale = highpc - lowpc;
+ scale /= nsamples;
+ for(i=0 ; i < nsamples ; i++) {
+ ccnt = samples[i];
+ if (ccnt == 0)
+ continue;
+ pcl = lowpc + scale*i;
+ pch = lowpc + scale*(i+1);
+ time = ccnt;
+ totime += time;
+ if(time > maxtime)
+ maxtime = time;
+ for (j=0; j<nname; j++) {
+ if (pch < nl[j].value)
+ break;
+ if (pcl >= nl[j+1].value)
+ continue;
+ overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value));
+ if (overlap>0)
+ nl[j].time += overlap*time/scale;
+ }
+ }
+ if (totime==0.0) {
+ fprintf(stderr, "No time accumulated\n");
+/*
+ done();
+ */
+ totime=1.0;
+ }
+}
+
+ /*
+ * dump what you have out to a mon.out style file.
+ */
+putprof()
+{
+ FILE *sfile;
+ struct nl *np;
+ struct cnt kp;
+ int i;
+
+ sfile = fopen( MON_SUMNAME , "w" );
+ if ( sfile == NULL ) {
+ perror( MON_SUMNAME );
+ done();
+ }
+ /*
+ * build a new header.
+ * h.lowpc and h.highpc are already fine.
+ * fix h.ncount to count non-zero calls,
+ * and the one zero call which marks the end.
+ */
+ h.ncount = 0;
+ for ( np = nl ; np < npe -1 ; np++ ) {
+ if ( np -> ncall > 0 ) {
+ h.ncount++;
+ }
+ }
+ h.ncount++;
+ fwrite( &h , sizeof (struct hdr) , 1 , sfile );
+ /*
+ * write out the counters
+ */
+ for ( np = nl ; np < npe - 1 ; np++ ) {
+ if ( np -> ncall > 0 ) {
+ kp.cvalue = np -> value * sizeof (unsigned UNIT);
+ kp.cncall = np -> ncall;
+ fwrite( &kp , sizeof (struct cnt) , 1 , sfile );
+ }
+ }
+ kp.cvalue = 0;
+ kp.cncall = 0;
+ fwrite( &kp , sizeof (struct cnt) , 1 , sfile );
+ /*
+ * write out the samples
+ */
+ fwrite( samples , sizeof (unsigned UNIT) , nsamples , sfile );
+ fclose( sfile );
+}
+
+min(a, b)
+{
+ if (a<b)
+ return(a);
+ return(b);
+}
+
+max(a, b)
+{
+ if (a>b)
+ return(a);
+ return(b);
+}
+
+valcmp(p1, p2)
+ struct nl *p1, *p2;
+{
+
+ return(p1->value - p2->value);
+}
+
+timcmp(p1, p2)
+ struct nl *p1, *p2;
+{
+ float d;
+
+ if (nflg && p2->ncall != p1->ncall)
+ return (p2->ncall - p1->ncall);
+ d = p2->time - p1->time;
+ if (d > 0.0)
+ return(1);
+ if (d < 0.0)
+ return(-1);
+ return(strcmp(p1->name,p2->name));
+}
+
+cntcmp(p1, p2)
+ struct cnt *p1, *p2;
+{
+
+ return(p1->cvalue - p2->cvalue);
+}
+
+done()
+{
+
+#ifdef plot
+ if(vflg) {
+ point(0, -2040);
+ closepl();
+ }
+#endif
+ exit(0);
+}
+
+#ifdef plot
+plotprof()
+{
+ double time, lastx, lasty, lastsx;
+ register i;
+
+ openpl();
+ erase();
+ space(-2048, -2048, 2048, 2048);
+ line(-2040, -2040, -2040, 2040);
+ line(0, 2040, 0, -2040);
+ for(i=0; i<11; i++)
+ line(-2040, 2040-i*408, 0, 2040-i*408);
+ lastx = 0.;
+ lasty = ranoff;
+ scale = (4080.*ransca)/(sampbytes/sizeof(UNIT));
+ lastsx = 0.0;
+ for(i = 0 ; i < nsamples ; i++ ) {
+ unsigned UNIT ccnt;
+ double tx, ty;
+ ccnt = samples[i];
+ time = ccnt;
+ tx = lastsx;
+ ty = lasty;
+ lastsx =- 2000.*time/totime;
+ lasty =- scale;
+ if(lasty >= -2040. && ty <= 2040.) {
+ line((int)tx, (int)ty, (int)lastsx, (int)lasty);
+ if (ccnt!=0 || lastx!=0.0) {
+ tx = lastx;
+ lastx = -time*2000./maxtime;
+ ty =+ scale/2;
+ line(0, (int)ty, (int)tx, (int)ty);
+ }
+ }
+ }
+ scale = (4080.*ransca)/(highpc-lowpc);
+ lastx = 50.;
+ for(np = nl; np<npe; np++) {
+ if(np->value < lowpc)
+ continue;
+ if(np->value >= highpc)
+ continue;
+ time = np->time/totime;
+ lasty = ranoff - (np->value - lowpc)*scale;
+ if(lasty >= -2040. && lasty <= 2040.) {
+ char bufl[BUFSIZ], *namp;
+ register j;
+ line(0, (int)lasty, 50, (int)lasty);
+ line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty);
+ point((int)(lastx+30), (int)(lasty+10));
+ sprintf(bufl, "%s\n", np->name + np->name[0] == '_');
+ label(bufl);
+ }
+ lastx =+ 500.;
+ if(lastx > 2000.)
+ lastx = 50.;
+ }
+}
+#endif