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