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