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