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