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