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