BSD 4 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Sat, 18 Oct 1980 18:55:23 +0000 (10:55 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Sat, 18 Oct 1980 18:55:23 +0000 (10:55 -0800)
Work on file usr/src/cmd/prof/Makefile
Work on file usr/src/cmd/prof/prof.c

Synthesized-from: CSRG//cd1/4.0

usr/src/cmd/prof/Makefile [new file with mode: 0644]
usr/src/cmd/prof/prof.c [new file with mode: 0644]

diff --git a/usr/src/cmd/prof/Makefile b/usr/src/cmd/prof/Makefile
new file mode 100644 (file)
index 0000000..ca99ddc
--- /dev/null
@@ -0,0 +1,11 @@
+# If you don't want to plot, take out the -Dplot and the ref. to plot.a
+PLOT = -lplot -Dplot
+CFLAGS=-O 
+prof:  prof.o
+       $(CC) -o prof prof.o $(PLOT)
+
+install:
+       install -s prof $(DESTDIR)/usr/bin
+
+clean :
+       rm -f *.o prof
diff --git a/usr/src/cmd/prof/prof.c b/usr/src/cmd/prof/prof.c
new file mode 100644 (file)
index 0000000..6b254cd
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ * 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