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