shorten name of blurb file
[unix-history] / usr / src / usr.bin / gprof / gprof.c
index d131dca..a382081 100644 (file)
@@ -1,34 +1,78 @@
 #ifndef lint
 #ifndef lint
-    static     char *sccsid = "@(#)gprof.c     1.8 (Berkeley) %G%";
+    static     char *sccsid = "@(#)gprof.c     1.24 (Berkeley) %G%";
 #endif lint
 
 #include "gprof.h"
 
 #endif lint
 
 #include "gprof.h"
 
+char   *whoami = "gprof";
+
+    /*
+     * things which get -E excluded by default.
+     */
+char   *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
+
 main(argc, argv)
 main(argc, argv)
-       int argc;
-       char **argv;
+    int argc;
+    char **argv;
 {
 {
+    char       **sp;
+    nltype     **timesortnlp;
 
     --argc;
     argv++;
     debug = 0;
 
     --argc;
     argv++;
     debug = 0;
+    bflag = TRUE;
     while ( *argv != 0 && **argv == '-' ) {
        (*argv)++;
     while ( *argv != 0 && **argv == '-' ) {
        (*argv)++;
-       if ( **argv == 'd' ) {
+       switch ( **argv ) {
+       case 'a':
+           aflag = TRUE;
+           break;
+       case 'b':
+           bflag = FALSE;
+           break;
+       case 'c':
+           cflag = TRUE;
+           break;
+       case 'd':
+           dflag = TRUE;
            (*argv)++;
            debug |= atoi( *argv );
            debug |= ANYDEBUG;
 #          ifdef DEBUG
            (*argv)++;
            debug |= atoi( *argv );
            debug |= ANYDEBUG;
 #          ifdef DEBUG
-               printf( "[main] debug = %d\n" , debug );
+               printf("[main] debug = %d\n", debug);
+#          else not DEBUG
+               printf("%s: -d ignored\n", whoami);
 #          endif DEBUG
 #          endif DEBUG
-       } else if ( **argv == 'a' ) {
-           aflag++;
-       } else if ( **argv == 'b' ) {
-           bflag++;
-       } else if ( **argv == 'c' ) {
-           cflag++;
-       } else if ( **argv == 'z' ) {
-           zflag++;
+           break;
+       case 'E':
+           ++argv;
+           addlist( Elist , *argv );
+           Eflag = TRUE;
+           addlist( elist , *argv );
+           eflag = TRUE;
+           break;
+       case 'e':
+           addlist( elist , *++argv );
+           eflag = TRUE;
+           break;
+       case 'F':
+           ++argv;
+           addlist( Flist , *argv );
+           Fflag = TRUE;
+           addlist( flist , *argv );
+           fflag = TRUE;
+           break;
+       case 'f':
+           addlist( flist , *++argv );
+           fflag = TRUE;
+           break;
+       case 's':
+           sflag = TRUE;
+           break;
+       case 'z':
+           zflag = TRUE;
+           break;
        }
        argv++;
     }
        }
        argv++;
     }
@@ -43,6 +87,24 @@ main(argc, argv)
        argv++;
     } else {
        gmonname = GMONNAME;
        argv++;
     } else {
        gmonname = GMONNAME;
+    }
+       /*
+        *      turn off default functions
+        */
+    for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
+       Eflag = TRUE;
+       addlist( Elist , *sp );
+       eflag = TRUE;
+       addlist( elist , *sp );
+    }
+       /*
+        *      how many ticks per second?
+        *      if we can't tell, report time in ticks.
+        */
+    hz = hertz();
+    if (hz == 0) {
+       hz = 1;
+       fprintf(stderr, "time is in ticks, not seconds\n");
     }
        /*
         *      get information about a.out file.
     }
        /*
         *      get information about a.out file.
@@ -51,27 +113,46 @@ main(argc, argv)
        /*
         *      get information about mon.out file(s).
         */
        /*
         *      get information about mon.out file(s).
         */
-    getpfile( gmonname );
+    do {
+       getpfile( gmonname );
+       if ( *argv != 0 ) {
+           gmonname = *argv;
+       }
+    } while ( *argv++ != 0 );
+       /*
+        *      dump out a gmon.sum file if requested
+        */
+    if ( sflag ) {
+       dumpsum( GMONSUM );
+    }
        /*
         *      assign samples to procedures
         */
     asgnsamples();
        /*
        /*
         *      assign samples to procedures
         */
     asgnsamples();
        /*
-        *      print the usual profile
+        *      assemble the dynamic profile
+        */
+    timesortnlp = doarcs();
+       /*
+        *      print the dynamic profile
+        */
+    printgprof( timesortnlp ); 
+       /*
+        *      print the flat profile
         */
     printprof();       
        /*
         */
     printprof();       
        /*
-        *      assemble and print the dynamic profile
+        *      print the index
         */
         */
-    doarcs();
+    printindex();      
     done();
 }
 
     done();
 }
 
-/*
- * Set up string and symbol tables from a.out.
*     and optionally the text space.
- * On return symbol table is sorted by value.
- */
+    /*
    * Set up string and symbol tables from a.out.
    * and optionally the text space.
    * On return symbol table is sorted by value.
    */
 getnfile()
 {
     FILE       *nfile;
 getnfile()
 {
     FILE       *nfile;
@@ -83,7 +164,7 @@ getnfile()
     }
     fread(&xbuf, 1, sizeof(xbuf), nfile);
     if (N_BADMAG(xbuf)) {
     }
     fread(&xbuf, 1, sizeof(xbuf), nfile);
     if (N_BADMAG(xbuf)) {
-       fprintf(stderr, "%s: bad format\n", a_outname );
+       fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
        done();
     }
     getstrtab(nfile);
        done();
     }
     getstrtab(nfile);
@@ -108,17 +189,19 @@ getstrtab(nfile)
 
     fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
     if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
 
     fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
     if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
-       fprintf(stderr, "%s: no string table (old format?)\n", a_outname );
+       fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
+               whoami , a_outname );
        done();
     }
     strtab = (char *)calloc(ssiz, 1);
     if (strtab == NULL) {
        done();
     }
     strtab = (char *)calloc(ssiz, 1);
     if (strtab == NULL) {
-       fprintf(stderr, "%s: no room for %d bytes of string table",
-               a_outname , ssiz);
+       fprintf(stderr, "%s: %s: no room for %d bytes of string table",
+               whoami , a_outname , ssiz);
        done();
     }
     if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
        done();
     }
     if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
-       fprintf(stderr, "%s: error reading string table\n", a_outname );
+       fprintf(stderr, "%s: %s: error reading string table\n",
+               whoami , a_outname );
        done();
     }
 }
        done();
     }
 }
@@ -144,21 +227,14 @@ getsymtab(nfile)
        nname++;
     }
     if (nname == 0) {
        nname++;
     }
     if (nname == 0) {
-       fprintf(stderr, "%s: no symbols\n", a_outname );
+       fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
        done();
     }
        done();
     }
-       /*
-        *      ask also for CYCLEFRACTION extra namelist entries for 
-        *      cycle entries.  these hide out at the end of the namelist
-        *      and aren't accessed unless the whole namelist (nname+ncycles)
-        *      is sorted and searched.
-        */
-    ncycles = nname * CYCLEFRACTION;
-    askfor = nname + 1 + ncycles;
+    askfor = nname + 1;
     nl = (nltype *) calloc( askfor , sizeof(nltype) );
     if (nl == 0) {
     nl = (nltype *) calloc( askfor , sizeof(nltype) );
     if (nl == 0) {
-       fprintf(stderr, "prof: No room for %d bytes of symbol table\n",
-               askfor * sizeof(nltype) );
+       fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
+               whoami, askfor * sizeof(nltype) );
        done();
     }
 
        done();
     }
 
@@ -189,7 +265,6 @@ getsymtab(nfile)
        nname++;
     }
     npe->value = -1;
        nname++;
     }
     npe->value = -1;
-    npe++;
 }
 
     /*
 }
 
     /*
@@ -205,14 +280,15 @@ gettextspace( nfile )
     }
     textspace = malloc( xbuf.a_text );
     if ( textspace == 0 ) {
     }
     textspace = malloc( xbuf.a_text );
     if ( textspace == 0 ) {
-       fprintf( stderr , "gprof: ran out room for %d bytes of text space:  " );
-       fprintf( stderr , "can't do -c\n" , xbuf.a_text );
+       fprintf( stderr , "%s: ran out room for %d bytes of text space:  " ,
+                       whoami , xbuf.a_text );
+       fprintf( stderr , "can't do -c\n" );
        return;
     }
     (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
     if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
        return;
     }
     (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
     if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
-       fprintf( stderr , "couldn't read text space:  " );
-       fprintf( stderr , "can't do -c\n" , xbuf.a_text );
+       fprintf( stderr , "%s: couldn't read text space:  " , whoami );
+       fprintf( stderr , "can't do -c\n" );
        free( textspace );
        textspace = 0;
        return;
        free( textspace );
        textspace = 0;
        return;
@@ -255,19 +331,38 @@ FILE *
 openpfile(filename)
     char *filename;
 {
 openpfile(filename)
     char *filename;
 {
+    struct hdr tmp;
     FILE       *pfile;
 
     if((pfile = fopen(filename, "r")) == NULL) {
        perror(filename);
        done();
     }
     FILE       *pfile;
 
     if((pfile = fopen(filename, "r")) == NULL) {
        perror(filename);
        done();
     }
-    fread(&h, sizeof(struct hdr), 1, pfile);
+    fread(&tmp, sizeof(struct hdr), 1, pfile);
+    if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
+        tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
+       fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
+       done();
+    }
+    h = tmp;
     s_lowpc = (unsigned long) h.lowpc;
     s_highpc = (unsigned long) h.highpc;
     s_lowpc = (unsigned long) h.lowpc;
     s_highpc = (unsigned long) h.highpc;
-    lowpc = h.lowpc - (UNIT *)0;
-    highpc = h.highpc - (UNIT *)0;
+    lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
+    highpc = (unsigned long)h.highpc / sizeof(UNIT);
     sampbytes = h.ncnt - sizeof(struct hdr);
     nsamples = sampbytes / sizeof (unsigned UNIT);
     sampbytes = h.ncnt - sizeof(struct hdr);
     nsamples = sampbytes / sizeof (unsigned UNIT);
+#   ifdef DEBUG
+       if ( debug & SAMPLEDEBUG ) {
+           printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
+               h.lowpc , h.highpc , h.ncnt );
+           printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
+               s_lowpc , s_highpc );
+           printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
+               lowpc , highpc );
+           printf( "[openpfile] sampbytes %d nsamples %d\n" ,
+               sampbytes , nsamples );
+       }
+#   endif DEBUG
     return(pfile);
 }
 
     return(pfile);
 }
 
@@ -289,6 +384,58 @@ tally( rawp )
     addarc( parentp , childp , rawp -> raw_count );
 }
 
     addarc( parentp , childp , rawp -> raw_count );
 }
 
+/*
+ * dump out the gmon.sum file
+ */
+dumpsum( sumfile )
+    char *sumfile;
+{
+    register nltype *nlp;
+    register arctype *arcp;
+    struct rawarc arc;
+    FILE *sfile;
+
+    if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
+       perror( sumfile );
+       done();
+    }
+    /*
+     * dump the header; use the last header read in
+     */
+    if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
+       perror( sumfile );
+       done();
+    }
+    /*
+     * dump the samples
+     */
+    if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) {
+       perror( sumfile );
+       done();
+    }
+    /*
+     * dump the normalized raw arc information
+     */
+    for ( nlp = nl ; nlp < npe ; nlp++ ) {
+       for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+           arc.raw_frompc = arcp -> arc_parentp -> value;
+           arc.raw_selfpc = arcp -> arc_childp -> value;
+           arc.raw_count = arcp -> arc_count;
+           if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
+               perror( sumfile );
+               done();
+           }
+#          ifdef DEBUG
+               if ( debug & SAMPLEDEBUG ) {
+                   printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
+                           arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
+               }
+#          endif DEBUG
+       }
+    }
+    fclose( sfile );
+}
+
 valcmp(p1, p2)
     nltype *p1, *p2;
 {
 valcmp(p1, p2)
     nltype *p1, *p2;
 {
@@ -310,8 +457,8 @@ readsamples(pfile)
     if (samples == 0) {
        samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT));
        if (samples == 0) {
     if (samples == 0) {
        samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT));
        if (samples == 0) {
-           fprintf( stderr , "prof: No room for %d sample pc's\n", 
-               sampbytes / sizeof (unsigned UNIT));
+           fprintf( stderr , "%s: No room for %d sample pc's\n", 
+               whoami , sampbytes / sizeof (unsigned UNIT));
            done();
        }
     }
            done();
        }
     }
@@ -323,14 +470,43 @@ readsamples(pfile)
     }
     if (i != nsamples) {
        fprintf(stderr,
     }
     if (i != nsamples) {
        fprintf(stderr,
-           "prof: unexpected EOF after reading %d/%d samples\n",
-               --i, nsamples);
+           "%s: unexpected EOF after reading %d/%d samples\n",
+               whoami , --i , nsamples );
        done();
     }
 }
 
 /*
        done();
     }
 }
 
 /*
- * Assign samples to the procedures to which they belong.
+ *     Assign samples to the procedures to which they belong.
+ *
+ *     There are three cases as to where pcl and pch can be
+ *     with respect to the routine entry addresses svalue0 and svalue1
+ *     as shown in the following diagram.  overlap computes the
+ *     distance between the arrows, the fraction of the sample
+ *     that is to be credited to the routine which starts at svalue0.
+ *
+ *         svalue0                                         svalue1
+ *            |                                               |
+ *            v                                               v
+ *
+ *            +-----------------------------------------------+
+ *            |                                               |
+ *       |  ->|    |<-         ->|         |<-         ->|    |<-  |
+ *       |         |             |         |             |         |
+ *       +---------+             +---------+             +---------+
+ *
+ *       ^         ^             ^         ^             ^         ^
+ *       |         |             |         |             |         |
+ *      pcl       pch           pcl       pch           pcl       pch
+ *
+ *     For the vax we assert that samples will never fall in the first
+ *     two bytes of any routine, since that is the entry mask,
+ *     thus we give call alignentries() to adjust the entry points if
+ *     the entry mask falls in one bucket but the code for the routine
+ *     doesn't start until the next bucket.  In conjunction with the
+ *     alignment of routine addresses, this should allow us to have
+ *     only one sample for every four bytes of text space and never
+ *     have any overlap (the two end cases, above).
  */
 asgnsamples()
 {
  */
 asgnsamples()
 {
@@ -345,48 +521,55 @@ asgnsamples()
     /* read samples and assign to namelist symbols */
     scale = highpc - lowpc;
     scale /= nsamples;
     /* read samples and assign to namelist symbols */
     scale = highpc - lowpc;
     scale /= nsamples;
-    for (i=0; i < nsamples; i++) {
+    alignentries();
+    for (i = 0, j = 1; i < nsamples; i++) {
        ccnt = samples[i];
        if (ccnt == 0)
                continue;
        ccnt = samples[i];
        if (ccnt == 0)
                continue;
-       pcl = lowpc + scale*i;
-       pch = lowpc + scale*(i+1);
+       pcl = lowpc + scale * i;
+       pch = lowpc + scale * (i + 1);
        time = ccnt;
 #      ifdef DEBUG
            if ( debug & SAMPLEDEBUG ) {
        time = ccnt;
 #      ifdef DEBUG
            if ( debug & SAMPLEDEBUG ) {
-               printf( "[asgnsamples] ccnt %d time %f totime %f\n" ,
-                       ccnt , time , totime );
+               printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
+                       pcl , pch , ccnt );
            }
 #      endif DEBUG
        totime += time;
            }
 #      endif DEBUG
        totime += time;
-       for (j=0; j<nname; j++) {
-           svalue0 = nl[j].value / sizeof(UNIT);
-           svalue1 = nl[j+1].value / sizeof(UNIT);
+       for (j = j - 1; j < nname; j++) {
+           svalue0 = nl[j].svalue;
+           svalue1 = nl[j+1].svalue;
+               /*
+                *      if high end of tick is below entry address, 
+                *      go for next tick.
+                */
            if (pch < svalue0)
                    break;
            if (pch < svalue0)
                    break;
+               /*
+                *      if low end of tick into next routine,
+                *      go for next routine.
+                */
            if (pcl >= svalue1)
                    continue;
            if (pcl >= svalue1)
                    continue;
-           overlap=min(pch,svalue1) - max(pcl,svalue0);
-           if (overlap>0) {
+           overlap = min(pch, svalue1) - max(pcl, svalue0);
+           if (overlap > 0) {
 #              ifdef DEBUG
 #              ifdef DEBUG
-                   if ( debug & SAMPLEDEBUG ) {
-                       printf( "[asgnsamples] %s gets %f ticks\n" ,
-                               nl[j].name , overlap*time/scale );
+                   if (debug & SAMPLEDEBUG) {
+                       printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
+                               nl[j].value/sizeof(UNIT), svalue0, svalue1,
+                               nl[j].name, 
+                               overlap * time / scale, overlap);
                    }
 #              endif DEBUG
                    }
 #              endif DEBUG
-               nl[j].time += overlap*time/scale;
+               nl[j].time += overlap * time / scale;
            }
        }
     }
 #   ifdef DEBUG
            }
        }
     }
 #   ifdef DEBUG
-       if ( debug & SAMPLEDEBUG ) {
-           printf( "[asgnsamples] totime %f\n" , totime );
+       if (debug & SAMPLEDEBUG) {
+           printf("[asgnsamples] totime %f\n", totime);
        }
 #   endif DEBUG
        }
 #   endif DEBUG
-    if (totime==0.0) {
-       fprintf( stderr , "No time accumulated\n" );
-       totime=1.0;
-    }
 }
 
 
 }
 
 
@@ -408,6 +591,34 @@ max(a, b)
     return(b);
 }
 
     return(b);
 }
 
+    /*
+     * calculate scaled entry point addresses (to save time in asgnsamples),
+     * and possibly push the scaled entry points over the entry mask,
+     * if it turns out that the entry point is in one bucket and the code
+     * for a routine is in the next bucket.
+     */
+alignentries()
+{
+    register struct nl *nlp;
+    unsigned long      bucket_of_entry;
+    unsigned long      bucket_of_code;
+
+    for (nlp = nl; nlp < npe; nlp++) {
+       nlp -> svalue = nlp -> value / sizeof(UNIT);
+       bucket_of_entry = (nlp->svalue - lowpc) / scale;
+       bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
+       if (bucket_of_entry < bucket_of_code) {
+#          ifdef DEBUG
+               if (debug & SAMPLEDEBUG) {
+                   printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
+                           nlp->svalue, nlp->svalue + UNITS_TO_CODE);
+               }
+#          endif DEBUG
+           nlp->svalue += UNITS_TO_CODE;
+       }
+    }
+}
+
 bool
 funcsymbol( nlistp )
     struct nlist       *nlistp;
 bool
 funcsymbol( nlistp )
     struct nlist       *nlistp;
@@ -425,7 +636,7 @@ funcsymbol( nlistp )
        return FALSE;
     }
        /*
        return FALSE;
     }
        /*
-        *      can't have any `funny characters in name,
+        *      can't have any `funny' characters in name,
         *      where `funny' includes  `.', .o file names
         *                      and     `$', pascal labels.
         */
         *      where `funny' includes  `.', .o file names
         *                      and     `$', pascal labels.
         */