1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)gprof.c 5.1 (Berkeley) 6/4/85"; 15: #endif not lint 16: 17: #include "gprof.h" 18: 19: char *whoami = "gprof"; 20: 21: /* 22: * things which get -E excluded by default. 23: */ 24: char *defaultEs[] = { "mcount" , "__mcleanup" , 0 }; 25: 26: main(argc, argv) 27: int argc; 28: char **argv; 29: { 30: char **sp; 31: nltype **timesortnlp; 32: 33: --argc; 34: argv++; 35: debug = 0; 36: bflag = TRUE; 37: while ( *argv != 0 && **argv == '-' ) { 38: (*argv)++; 39: switch ( **argv ) { 40: case 'a': 41: aflag = TRUE; 42: break; 43: case 'b': 44: bflag = FALSE; 45: break; 46: case 'c': 47: cflag = TRUE; 48: break; 49: case 'd': 50: dflag = TRUE; 51: (*argv)++; 52: debug |= atoi( *argv ); 53: debug |= ANYDEBUG; 54: # ifdef DEBUG 55: printf("[main] debug = %d\n", debug); 56: # else not DEBUG 57: printf("%s: -d ignored\n", whoami); 58: # endif DEBUG 59: break; 60: case 'E': 61: ++argv; 62: addlist( Elist , *argv ); 63: Eflag = TRUE; 64: addlist( elist , *argv ); 65: eflag = TRUE; 66: break; 67: case 'e': 68: addlist( elist , *++argv ); 69: eflag = TRUE; 70: break; 71: case 'F': 72: ++argv; 73: addlist( Flist , *argv ); 74: Fflag = TRUE; 75: addlist( flist , *argv ); 76: fflag = TRUE; 77: break; 78: case 'f': 79: addlist( flist , *++argv ); 80: fflag = TRUE; 81: break; 82: case 's': 83: sflag = TRUE; 84: break; 85: case 'z': 86: zflag = TRUE; 87: break; 88: } 89: argv++; 90: } 91: if ( *argv != 0 ) { 92: a_outname = *argv; 93: argv++; 94: } else { 95: a_outname = A_OUTNAME; 96: } 97: if ( *argv != 0 ) { 98: gmonname = *argv; 99: argv++; 100: } else { 101: gmonname = GMONNAME; 102: } 103: /* 104: * turn off default functions 105: */ 106: for ( sp = &defaultEs[0] ; *sp ; sp++ ) { 107: Eflag = TRUE; 108: addlist( Elist , *sp ); 109: eflag = TRUE; 110: addlist( elist , *sp ); 111: } 112: /* 113: * how many ticks per second? 114: * if we can't tell, report time in ticks. 115: */ 116: hz = hertz(); 117: if (hz == 0) { 118: hz = 1; 119: fprintf(stderr, "time is in ticks, not seconds\n"); 120: } 121: /* 122: * get information about a.out file. 123: */ 124: getnfile(); 125: /* 126: * get information about mon.out file(s). 127: */ 128: do { 129: getpfile( gmonname ); 130: if ( *argv != 0 ) { 131: gmonname = *argv; 132: } 133: } while ( *argv++ != 0 ); 134: /* 135: * dump out a gmon.sum file if requested 136: */ 137: if ( sflag ) { 138: dumpsum( GMONSUM ); 139: } 140: /* 141: * assign samples to procedures 142: */ 143: asgnsamples(); 144: /* 145: * assemble the dynamic profile 146: */ 147: timesortnlp = doarcs(); 148: /* 149: * print the dynamic profile 150: */ 151: printgprof( timesortnlp ); 152: /* 153: * print the flat profile 154: */ 155: printprof(); 156: /* 157: * print the index 158: */ 159: printindex(); 160: done(); 161: } 162: 163: /* 164: * Set up string and symbol tables from a.out. 165: * and optionally the text space. 166: * On return symbol table is sorted by value. 167: */ 168: getnfile() 169: { 170: FILE *nfile; 171: 172: nfile = fopen( a_outname ,"r"); 173: if (nfile == NULL) { 174: perror( a_outname ); 175: done(); 176: } 177: fread(&xbuf, 1, sizeof(xbuf), nfile); 178: if (N_BADMAG(xbuf)) { 179: fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname ); 180: done(); 181: } 182: getstrtab(nfile); 183: getsymtab(nfile); 184: gettextspace( nfile ); 185: qsort(nl, nname, sizeof(nltype), valcmp); 186: fclose(nfile); 187: # ifdef DEBUG 188: if ( debug & AOUTDEBUG ) { 189: register int j; 190: 191: for (j = 0; j < nname; j++){ 192: printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name); 193: } 194: } 195: # endif DEBUG 196: } 197: 198: getstrtab(nfile) 199: FILE *nfile; 200: { 201: 202: fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0); 203: if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) { 204: fprintf(stderr, "%s: %s: no string table (old format?)\n" , 205: whoami , a_outname ); 206: done(); 207: } 208: strtab = (char *)calloc(ssiz, 1); 209: if (strtab == NULL) { 210: fprintf(stderr, "%s: %s: no room for %d bytes of string table", 211: whoami , a_outname , ssiz); 212: done(); 213: } 214: if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) { 215: fprintf(stderr, "%s: %s: error reading string table\n", 216: whoami , a_outname ); 217: done(); 218: } 219: } 220: 221: /* 222: * Read in symbol table 223: */ 224: getsymtab(nfile) 225: FILE *nfile; 226: { 227: register long i; 228: int askfor; 229: struct nlist nbuf; 230: 231: /* pass1 - count symbols */ 232: fseek(nfile, (long)N_SYMOFF(xbuf), 0); 233: nname = 0; 234: for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 235: fread(&nbuf, sizeof(nbuf), 1, nfile); 236: if ( ! funcsymbol( &nbuf ) ) { 237: continue; 238: } 239: nname++; 240: } 241: if (nname == 0) { 242: fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname ); 243: done(); 244: } 245: askfor = nname + 1; 246: nl = (nltype *) calloc( askfor , sizeof(nltype) ); 247: if (nl == 0) { 248: fprintf(stderr, "%s: No room for %d bytes of symbol table\n", 249: whoami, askfor * sizeof(nltype) ); 250: done(); 251: } 252: 253: /* pass2 - read symbols */ 254: fseek(nfile, (long)N_SYMOFF(xbuf), 0); 255: npe = nl; 256: nname = 0; 257: for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 258: fread(&nbuf, sizeof(nbuf), 1, nfile); 259: if ( ! funcsymbol( &nbuf ) ) { 260: # ifdef DEBUG 261: if ( debug & AOUTDEBUG ) { 262: printf( "[getsymtab] rejecting: 0x%x %s\n" , 263: nbuf.n_type , strtab + nbuf.n_un.n_strx ); 264: } 265: # endif DEBUG 266: continue; 267: } 268: npe->value = nbuf.n_value; 269: npe->name = strtab+nbuf.n_un.n_strx; 270: # ifdef DEBUG 271: if ( debug & AOUTDEBUG ) { 272: printf( "[getsymtab] %d %s 0x%08x\n" , 273: nname , npe -> name , npe -> value ); 274: } 275: # endif DEBUG 276: npe++; 277: nname++; 278: } 279: npe->value = -1; 280: } 281: 282: /* 283: * read in the text space of an a.out file 284: */ 285: gettextspace( nfile ) 286: FILE *nfile; 287: { 288: unsigned char *malloc(); 289: 290: if ( cflag == 0 ) { 291: return; 292: } 293: textspace = malloc( xbuf.a_text ); 294: if ( textspace == 0 ) { 295: fprintf( stderr , "%s: ran out room for %d bytes of text space: " , 296: whoami , xbuf.a_text ); 297: fprintf( stderr , "can't do -c\n" ); 298: return; 299: } 300: (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 ); 301: if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) { 302: fprintf( stderr , "%s: couldn't read text space: " , whoami ); 303: fprintf( stderr , "can't do -c\n" ); 304: free( textspace ); 305: textspace = 0; 306: return; 307: } 308: } 309: /* 310: * information from a gmon.out file is in two parts: 311: * an array of sampling hits within pc ranges, 312: * and the arcs. 313: */ 314: getpfile(filename) 315: char *filename; 316: { 317: FILE *pfile; 318: FILE *openpfile(); 319: struct rawarc arc; 320: 321: pfile = openpfile(filename); 322: readsamples(pfile); 323: /* 324: * the rest of the file consists of 325: * a bunch of <from,self,count> tuples. 326: */ 327: while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { 328: # ifdef DEBUG 329: if ( debug & SAMPLEDEBUG ) { 330: printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" , 331: arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 332: } 333: # endif DEBUG 334: /* 335: * add this arc 336: */ 337: tally( &arc ); 338: } 339: fclose(pfile); 340: } 341: 342: FILE * 343: openpfile(filename) 344: char *filename; 345: { 346: struct hdr tmp; 347: FILE *pfile; 348: 349: if((pfile = fopen(filename, "r")) == NULL) { 350: perror(filename); 351: done(); 352: } 353: fread(&tmp, sizeof(struct hdr), 1, pfile); 354: if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc || 355: tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) { 356: fprintf(stderr, "%s: incompatible with first gmon file\n", filename); 357: done(); 358: } 359: h = tmp; 360: s_lowpc = (unsigned long) h.lowpc; 361: s_highpc = (unsigned long) h.highpc; 362: lowpc = (unsigned long)h.lowpc / sizeof(UNIT); 363: highpc = (unsigned long)h.highpc / sizeof(UNIT); 364: sampbytes = h.ncnt - sizeof(struct hdr); 365: nsamples = sampbytes / sizeof (unsigned UNIT); 366: # ifdef DEBUG 367: if ( debug & SAMPLEDEBUG ) { 368: printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n", 369: h.lowpc , h.highpc , h.ncnt ); 370: printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" , 371: s_lowpc , s_highpc ); 372: printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" , 373: lowpc , highpc ); 374: printf( "[openpfile] sampbytes %d nsamples %d\n" , 375: sampbytes , nsamples ); 376: } 377: # endif DEBUG 378: return(pfile); 379: } 380: 381: tally( rawp ) 382: struct rawarc *rawp; 383: { 384: nltype *parentp; 385: nltype *childp; 386: 387: parentp = nllookup( rawp -> raw_frompc ); 388: childp = nllookup( rawp -> raw_selfpc ); 389: childp -> ncall += rawp -> raw_count; 390: # ifdef DEBUG 391: if ( debug & TALLYDEBUG ) { 392: printf( "[tally] arc from %s to %s traversed %d times\n" , 393: parentp -> name , childp -> name , rawp -> raw_count ); 394: } 395: # endif DEBUG 396: addarc( parentp , childp , rawp -> raw_count ); 397: } 398: 399: /* 400: * dump out the gmon.sum file 401: */ 402: dumpsum( sumfile ) 403: char *sumfile; 404: { 405: register nltype *nlp; 406: register arctype *arcp; 407: struct rawarc arc; 408: FILE *sfile; 409: 410: if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) { 411: perror( sumfile ); 412: done(); 413: } 414: /* 415: * dump the header; use the last header read in 416: */ 417: if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) { 418: perror( sumfile ); 419: done(); 420: } 421: /* 422: * dump the samples 423: */ 424: if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) { 425: perror( sumfile ); 426: done(); 427: } 428: /* 429: * dump the normalized raw arc information 430: */ 431: for ( nlp = nl ; nlp < npe ; nlp++ ) { 432: for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { 433: arc.raw_frompc = arcp -> arc_parentp -> value; 434: arc.raw_selfpc = arcp -> arc_childp -> value; 435: arc.raw_count = arcp -> arc_count; 436: if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) { 437: perror( sumfile ); 438: done(); 439: } 440: # ifdef DEBUG 441: if ( debug & SAMPLEDEBUG ) { 442: printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" , 443: arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 444: } 445: # endif DEBUG 446: } 447: } 448: fclose( sfile ); 449: } 450: 451: valcmp(p1, p2) 452: nltype *p1, *p2; 453: { 454: if ( p1 -> value < p2 -> value ) { 455: return LESSTHAN; 456: } 457: if ( p1 -> value > p2 -> value ) { 458: return GREATERTHAN; 459: } 460: return EQUALTO; 461: } 462: 463: readsamples(pfile) 464: FILE *pfile; 465: { 466: register i; 467: unsigned UNIT sample; 468: 469: if (samples == 0) { 470: samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT)); 471: if (samples == 0) { 472: fprintf( stderr , "%s: No room for %d sample pc's\n", 473: whoami , sampbytes / sizeof (unsigned UNIT)); 474: done(); 475: } 476: } 477: for (i = 0; i < nsamples; i++) { 478: fread(&sample, sizeof (unsigned UNIT), 1, pfile); 479: if (feof(pfile)) 480: break; 481: samples[i] += sample; 482: } 483: if (i != nsamples) { 484: fprintf(stderr, 485: "%s: unexpected EOF after reading %d/%d samples\n", 486: whoami , --i , nsamples ); 487: done(); 488: } 489: } 490: 491: /* 492: * Assign samples to the procedures to which they belong. 493: * 494: * There are three cases as to where pcl and pch can be 495: * with respect to the routine entry addresses svalue0 and svalue1 496: * as shown in the following diagram. overlap computes the 497: * distance between the arrows, the fraction of the sample 498: * that is to be credited to the routine which starts at svalue0. 499: * 500: * svalue0 svalue1 501: * | | 502: * v v 503: * 504: * +-----------------------------------------------+ 505: * | | 506: * | ->| |<- ->| |<- ->| |<- | 507: * | | | | | | 508: * +---------+ +---------+ +---------+ 509: * 510: * ^ ^ ^ ^ ^ ^ 511: * | | | | | | 512: * pcl pch pcl pch pcl pch 513: * 514: * For the vax we assert that samples will never fall in the first 515: * two bytes of any routine, since that is the entry mask, 516: * thus we give call alignentries() to adjust the entry points if 517: * the entry mask falls in one bucket but the code for the routine 518: * doesn't start until the next bucket. In conjunction with the 519: * alignment of routine addresses, this should allow us to have 520: * only one sample for every four bytes of text space and never 521: * have any overlap (the two end cases, above). 522: */ 523: asgnsamples() 524: { 525: register int j; 526: unsigned UNIT ccnt; 527: double time; 528: unsigned long pcl, pch; 529: register int i; 530: unsigned long overlap; 531: unsigned long svalue0, svalue1; 532: 533: /* read samples and assign to namelist symbols */ 534: scale = highpc - lowpc; 535: scale /= nsamples; 536: alignentries(); 537: for (i = 0, j = 1; i < nsamples; i++) { 538: ccnt = samples[i]; 539: if (ccnt == 0) 540: continue; 541: pcl = lowpc + scale * i; 542: pch = lowpc + scale * (i + 1); 543: time = ccnt; 544: # ifdef DEBUG 545: if ( debug & SAMPLEDEBUG ) { 546: printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" , 547: pcl , pch , ccnt ); 548: } 549: # endif DEBUG 550: totime += time; 551: for (j = j - 1; j < nname; j++) { 552: svalue0 = nl[j].svalue; 553: svalue1 = nl[j+1].svalue; 554: /* 555: * if high end of tick is below entry address, 556: * go for next tick. 557: */ 558: if (pch < svalue0) 559: break; 560: /* 561: * if low end of tick into next routine, 562: * go for next routine. 563: */ 564: if (pcl >= svalue1) 565: continue; 566: overlap = min(pch, svalue1) - max(pcl, svalue0); 567: if (overlap > 0) { 568: # ifdef DEBUG 569: if (debug & SAMPLEDEBUG) { 570: printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n", 571: nl[j].value/sizeof(UNIT), svalue0, svalue1, 572: nl[j].name, 573: overlap * time / scale, overlap); 574: } 575: # endif DEBUG 576: nl[j].time += overlap * time / scale; 577: } 578: } 579: } 580: # ifdef DEBUG 581: if (debug & SAMPLEDEBUG) { 582: printf("[asgnsamples] totime %f\n", totime); 583: } 584: # endif DEBUG 585: } 586: 587: 588: unsigned long 589: min(a, b) 590: unsigned long a,b; 591: { 592: if (a<b) 593: return(a); 594: return(b); 595: } 596: 597: unsigned long 598: max(a, b) 599: unsigned long a,b; 600: { 601: if (a>b) 602: return(a); 603: return(b); 604: } 605: 606: /* 607: * calculate scaled entry point addresses (to save time in asgnsamples), 608: * and possibly push the scaled entry points over the entry mask, 609: * if it turns out that the entry point is in one bucket and the code 610: * for a routine is in the next bucket. 611: */ 612: alignentries() 613: { 614: register struct nl *nlp; 615: unsigned long bucket_of_entry; 616: unsigned long bucket_of_code; 617: 618: for (nlp = nl; nlp < npe; nlp++) { 619: nlp -> svalue = nlp -> value / sizeof(UNIT); 620: bucket_of_entry = (nlp->svalue - lowpc) / scale; 621: bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale; 622: if (bucket_of_entry < bucket_of_code) { 623: # ifdef DEBUG 624: if (debug & SAMPLEDEBUG) { 625: printf("[alignentries] pushing svalue 0x%x to 0x%x\n", 626: nlp->svalue, nlp->svalue + UNITS_TO_CODE); 627: } 628: # endif DEBUG 629: nlp->svalue += UNITS_TO_CODE; 630: } 631: } 632: } 633: 634: bool 635: funcsymbol( nlistp ) 636: struct nlist *nlistp; 637: { 638: extern char *strtab; /* string table from a.out */ 639: extern int aflag; /* if static functions aren't desired */ 640: char *name; 641: 642: /* 643: * must be a text symbol, 644: * and static text symbols don't qualify if aflag set. 645: */ 646: if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) ) 647: || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) { 648: return FALSE; 649: } 650: /* 651: * can't have any `funny' characters in name, 652: * where `funny' includes `.', .o file names 653: * and `$', pascal labels. 654: */ 655: for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) { 656: if ( *name == '.' || *name == '$' ) { 657: return FALSE; 658: } 659: } 660: return TRUE; 661: } 662: 663: done() 664: { 665: 666: exit(0); 667: }