1 #ifndef lint 2 static char *sccsid = "@(#)gprof.c 1.10 (Berkeley) 11/25/81"; 3 #endif lint 4 5 #include "gprof.h" 6 7 main(argc, argv) 8 int argc; 9 char **argv; 10 { 11 12 --argc; 13 argv++; 14 debug = 0; 15 while ( *argv != 0 && **argv == '-' ) { 16 (*argv)++; 17 switch ( **argv ) { 18 case 'd': 19 (*argv)++; 20 debug |= atoi( *argv ); 21 debug |= ANYDEBUG; 22 # ifdef DEBUG 23 printf( "[main] debug = %d\n" , debug ); 24 # endif DEBUG 25 break; 26 case 'a': 27 aflag++; 28 break; 29 case 'b': 30 bflag++; 31 break; 32 case 'c': 33 cflag++; 34 break; 35 case 's': 36 sflag++; 37 break; 38 case 'z': 39 zflag++; 40 break; 41 } 42 argv++; 43 } 44 if ( *argv != 0 ) { 45 a_outname = *argv; 46 argv++; 47 } else { 48 a_outname = A_OUTNAME; 49 } 50 if ( *argv != 0 ) { 51 gmonname = *argv; 52 argv++; 53 } else { 54 gmonname = GMONNAME; 55 } 56 /* 57 * get information about a.out file. 58 */ 59 getnfile(); 60 /* 61 * get information about mon.out file(s). 62 */ 63 do { 64 getpfile( gmonname ); 65 if ( *argv != 0 ) { 66 gmonname = *argv; 67 } 68 } while ( sflag && *argv++ != 0 ); 69 /* 70 * dump out a gmon.sum file if requested 71 */ 72 if ( sflag ) { 73 dumpsum( GMONSUM ); 74 } 75 /* 76 * assign samples to procedures 77 */ 78 asgnsamples(); 79 /* 80 * print the usual profile 81 */ 82 printprof(); 83 /* 84 * assemble and print the dynamic profile 85 */ 86 doarcs(); 87 done(); 88 } 89 90 /* 91 * Set up string and symbol tables from a.out. 92 * and optionally the text space. 93 * On return symbol table is sorted by value. 94 */ 95 getnfile() 96 { 97 FILE *nfile; 98 99 nfile = fopen( a_outname ,"r"); 100 if (nfile == NULL) { 101 perror( a_outname ); 102 done(); 103 } 104 fread(&xbuf, 1, sizeof(xbuf), nfile); 105 if (N_BADMAG(xbuf)) { 106 fprintf(stderr, "%s: bad format\n", a_outname ); 107 done(); 108 } 109 getstrtab(nfile); 110 getsymtab(nfile); 111 gettextspace( nfile ); 112 qsort(nl, nname, sizeof(nltype), valcmp); 113 fclose(nfile); 114 # ifdef DEBUG 115 if ( debug & AOUTDEBUG ) { 116 register int j; 117 118 for (j = 0; j < nname; j++){ 119 printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name); 120 } 121 } 122 # endif DEBUG 123 } 124 125 getstrtab(nfile) 126 FILE *nfile; 127 { 128 129 fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0); 130 if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) { 131 fprintf(stderr, "%s: no string table (old format?)\n", a_outname ); 132 done(); 133 } 134 strtab = (char *)calloc(ssiz, 1); 135 if (strtab == NULL) { 136 fprintf(stderr, "%s: no room for %d bytes of string table", 137 a_outname , ssiz); 138 done(); 139 } 140 if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) { 141 fprintf(stderr, "%s: error reading string table\n", a_outname ); 142 done(); 143 } 144 } 145 146 /* 147 * Read in symbol table 148 */ 149 getsymtab(nfile) 150 FILE *nfile; 151 { 152 register long i; 153 int askfor; 154 struct nlist nbuf; 155 156 /* pass1 - count symbols */ 157 fseek(nfile, (long)N_SYMOFF(xbuf), 0); 158 nname = 0; 159 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 160 fread(&nbuf, sizeof(nbuf), 1, nfile); 161 if ( ! funcsymbol( &nbuf ) ) { 162 continue; 163 } 164 nname++; 165 } 166 if (nname == 0) { 167 fprintf(stderr, "%s: no symbols\n", a_outname ); 168 done(); 169 } 170 /* 171 * ask also for CYCLEFRACTION extra namelist entries for 172 * cycle entries. these hide out at the end of the namelist 173 * and aren't accessed unless the whole namelist (nname+ncycles) 174 * is sorted and searched. 175 */ 176 ncycles = nname * CYCLEFRACTION; 177 askfor = nname + 1 + ncycles; 178 nl = (nltype *) calloc( askfor , sizeof(nltype) ); 179 if (nl == 0) { 180 fprintf(stderr, "prof: No room for %d bytes of symbol table\n", 181 askfor * sizeof(nltype) ); 182 done(); 183 } 184 185 /* pass2 - read symbols */ 186 fseek(nfile, (long)N_SYMOFF(xbuf), 0); 187 npe = nl; 188 nname = 0; 189 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 190 fread(&nbuf, sizeof(nbuf), 1, nfile); 191 if ( ! funcsymbol( &nbuf ) ) { 192 # ifdef DEBUG 193 if ( debug & AOUTDEBUG ) { 194 printf( "[getsymtab] rejecting: 0x%x %s\n" , 195 nbuf.n_type , strtab + nbuf.n_un.n_strx ); 196 } 197 # endif DEBUG 198 continue; 199 } 200 npe->value = nbuf.n_value; 201 npe->name = strtab+nbuf.n_un.n_strx; 202 # ifdef DEBUG 203 if ( debug & AOUTDEBUG ) { 204 printf( "[getsymtab] %d %s 0x%08x\n" , 205 nname , npe -> name , npe -> value ); 206 } 207 # endif DEBUG 208 npe++; 209 nname++; 210 } 211 npe->value = -1; 212 npe++; 213 } 214 215 /* 216 * read in the text space of an a.out file 217 */ 218 gettextspace( nfile ) 219 FILE *nfile; 220 { 221 unsigned char *malloc(); 222 223 if ( cflag == 0 ) { 224 return; 225 } 226 textspace = malloc( xbuf.a_text ); 227 if ( textspace == 0 ) { 228 fprintf( stderr , "gprof: ran out room for %d bytes of text space: " ); 229 fprintf( stderr , "can't do -c\n" , xbuf.a_text ); 230 return; 231 } 232 (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 ); 233 if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) { 234 fprintf( stderr , "couldn't read text space: " ); 235 fprintf( stderr , "can't do -c\n" , xbuf.a_text ); 236 free( textspace ); 237 textspace = 0; 238 return; 239 } 240 } 241 /* 242 * information from a gmon.out file is in two parts: 243 * an array of sampling hits within pc ranges, 244 * and the arcs. 245 */ 246 getpfile(filename) 247 char *filename; 248 { 249 FILE *pfile; 250 FILE *openpfile(); 251 struct rawarc arc; 252 253 pfile = openpfile(filename); 254 readsamples(pfile); 255 /* 256 * the rest of the file consists of 257 * a bunch of <from,self,count> tuples. 258 */ 259 while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { 260 # ifdef DEBUG 261 if ( debug & SAMPLEDEBUG ) { 262 printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" , 263 arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 264 } 265 # endif DEBUG 266 /* 267 * add this arc 268 */ 269 tally( &arc ); 270 } 271 fclose(pfile); 272 } 273 274 FILE * 275 openpfile(filename) 276 char *filename; 277 { 278 struct hdr tmp; 279 FILE *pfile; 280 281 if((pfile = fopen(filename, "r")) == NULL) { 282 perror(filename); 283 done(); 284 } 285 fread(&tmp, sizeof(struct hdr), 1, pfile); 286 if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc || 287 tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) { 288 fprintf(stderr, "%s: incompatible with first gmon file\n", filename); 289 done(); 290 } 291 h = tmp; 292 s_lowpc = (unsigned long) h.lowpc; 293 s_highpc = (unsigned long) h.highpc; 294 lowpc = h.lowpc - (UNIT *)0; 295 highpc = h.highpc - (UNIT *)0; 296 sampbytes = h.ncnt - sizeof(struct hdr); 297 nsamples = sampbytes / sizeof (unsigned UNIT); 298 return(pfile); 299 } 300 301 tally( rawp ) 302 struct rawarc *rawp; 303 { 304 nltype *parentp; 305 nltype *childp; 306 307 parentp = nllookup( rawp -> raw_frompc ); 308 childp = nllookup( rawp -> raw_selfpc ); 309 childp -> ncall += rawp -> raw_count; 310 # ifdef DEBUG 311 if ( debug & TALLYDEBUG ) { 312 printf( "[tally] arc from %s to %s traversed %d times\n" , 313 parentp -> name , childp -> name , rawp -> raw_count ); 314 } 315 # endif DEBUG 316 addarc( parentp , childp , rawp -> raw_count ); 317 } 318 319 /* 320 * dump out the gmon.sum file 321 */ 322 dumpsum( sumfile ) 323 char *sumfile; 324 { 325 register nltype *nlp; 326 register arctype *arcp; 327 struct rawarc arc; 328 FILE *sfile; 329 330 if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) { 331 perror( sumfile ); 332 done(); 333 } 334 /* 335 * dump the header; use the last header read in 336 */ 337 if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) { 338 perror( sumfile ); 339 done(); 340 } 341 /* 342 * dump the samples 343 */ 344 if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) { 345 perror( sumfile ); 346 done(); 347 } 348 /* 349 * dump the normalized raw arc information 350 */ 351 for ( nlp = nl ; nlp < npe - 1 ; nlp++ ) { 352 for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { 353 arc.raw_frompc = arcp -> arc_parentp -> value; 354 arc.raw_selfpc = arcp -> arc_childp -> value; 355 arc.raw_count = arcp -> arc_count; 356 if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) { 357 perror( sumfile ); 358 done(); 359 } 360 # ifdef DEBUG 361 if ( debug & SAMPLEDEBUG ) { 362 printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" , 363 arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 364 } 365 # endif DEBUG 366 } 367 } 368 fclose( sfile ); 369 } 370 371 valcmp(p1, p2) 372 nltype *p1, *p2; 373 { 374 if ( p1 -> value < p2 -> value ) { 375 return LESSTHAN; 376 } 377 if ( p1 -> value > p2 -> value ) { 378 return GREATERTHAN; 379 } 380 return EQUALTO; 381 } 382 383 readsamples(pfile) 384 FILE *pfile; 385 { 386 register i; 387 unsigned UNIT sample; 388 389 if (samples == 0) { 390 samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT)); 391 if (samples == 0) { 392 fprintf( stderr , "prof: No room for %d sample pc's\n", 393 sampbytes / sizeof (unsigned UNIT)); 394 done(); 395 } 396 } 397 for (i = 0; i < nsamples; i++) { 398 fread(&sample, sizeof (unsigned UNIT), 1, pfile); 399 if (feof(pfile)) 400 break; 401 samples[i] += sample; 402 } 403 if (i != nsamples) { 404 fprintf(stderr, 405 "prof: unexpected EOF after reading %d/%d samples\n", 406 --i, nsamples); 407 done(); 408 } 409 } 410 411 /* 412 * Assign samples to the procedures to which they belong. 413 */ 414 asgnsamples() 415 { 416 register int j; 417 unsigned UNIT ccnt; 418 double time; 419 unsigned long pcl, pch; 420 register int i; 421 unsigned long overlap; 422 unsigned long svalue0, svalue1; 423 424 /* read samples and assign to namelist symbols */ 425 scale = highpc - lowpc; 426 scale /= nsamples; 427 for (i=0; i < nsamples; i++) { 428 ccnt = samples[i]; 429 if (ccnt == 0) 430 continue; 431 pcl = lowpc + scale*i; 432 pch = lowpc + scale*(i+1); 433 time = ccnt; 434 # ifdef DEBUG 435 if ( debug & SAMPLEDEBUG ) { 436 printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" , 437 pcl , pch , ccnt ); 438 } 439 # endif DEBUG 440 totime += time; 441 for (j=0; j<nname; j++) { 442 svalue0 = nl[j].value / sizeof(UNIT); 443 svalue1 = nl[j+1].value / sizeof(UNIT); 444 if (pch < svalue0) 445 break; 446 if (pcl >= svalue1) 447 continue; 448 overlap=min(pch,svalue1) - max(pcl,svalue0); 449 if (overlap>0) { 450 # ifdef DEBUG 451 if ( debug & SAMPLEDEBUG ) { 452 printf( "[asgnsamples] (0x%x-0x%x) %s gets %f ticks\n" , 453 svalue0 , svalue1 , nl[j].name , 454 overlap*time/scale ); 455 } 456 # endif DEBUG 457 nl[j].time += overlap*time/scale; 458 } 459 } 460 } 461 # ifdef DEBUG 462 if ( debug & SAMPLEDEBUG ) { 463 printf( "[asgnsamples] totime %f\n" , totime ); 464 } 465 # endif DEBUG 466 if (totime==0.0) { 467 fprintf( stderr , "No time accumulated\n" ); 468 totime=1.0; 469 } 470 } 471 472 473 unsigned long 474 min(a, b) 475 unsigned long a,b; 476 { 477 if (a<b) 478 return(a); 479 return(b); 480 } 481 482 unsigned long 483 max(a, b) 484 unsigned long a,b; 485 { 486 if (a>b) 487 return(a); 488 return(b); 489 } 490 491 bool 492 funcsymbol( nlistp ) 493 struct nlist *nlistp; 494 { 495 extern char *strtab; /* string table from a.out */ 496 extern int aflag; /* if static functions aren't desired */ 497 char *name; 498 499 /* 500 * must be a text symbol, 501 * and static text symbols don't qualify if aflag set. 502 */ 503 if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) ) 504 || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) { 505 return FALSE; 506 } 507 /* 508 * can't have any `funny characters in name, 509 * where `funny' includes `.', .o file names 510 * and `$', pascal labels. 511 */ 512 for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) { 513 if ( *name == '.' || *name == '$' ) { 514 return FALSE; 515 } 516 } 517 return TRUE; 518 } 519 520 done() 521 { 522 523 exit(0); 524 } 525