1 #ifndef lint 2 static char *sccsid = "@(#)prof.c 4.3 (Berkeley) 07/02/83"; 3 #endif 4 /* 5 * prof 6 */ 7 #include <stdio.h> 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 #include <a.out.h> 11 12 typedef short UNIT; /* unit of profiling */ 13 #define PCFUDGE 11 14 #define A_OUTNAME "a.out" 15 #define MON_OUTNAME "mon.out" 16 #define MON_SUMNAME "mon.sum" 17 18 /* 19 * The symbol table; 20 * for each external in the specified file we gather 21 * its address, the number of calls and compute its share of cpu time. 22 */ 23 struct nl { 24 char *name; 25 unsigned value; 26 float time; 27 long ncall; 28 } *nl; 29 int nname; 30 struct nl *np; 31 struct nl *npe; 32 33 /* 34 * The header on the mon.out file. 35 * Mon.out consists of one of these headers, an array of ncount 36 * cnt structures (as below) and then an array of samples 37 * representing the discretized program counter values. 38 */ 39 struct hdr { 40 UNIT *lowpc, *highpc; 41 int ncount; 42 } h; 43 44 /* 45 * Each counter has an address and a number of calls. 46 */ 47 struct cnt { 48 unsigned cvalue; 49 long cncall; 50 } *cbuf; 51 52 /* 53 * Each discretized pc sample has 54 * a count of the number of samples in its range 55 */ 56 unsigned UNIT *samples; 57 58 FILE *pfile, *nfile; 59 60 unsigned lowpc, highpc; /* range profiled */ 61 double ransca, ranoff; /* scaling for blowing up plots */ 62 unsigned sampbytes; /* number of bytes of samples */ 63 int nsamples; /* number of samples */ 64 double totime; /* total time for all routines */ 65 double maxtime; /* maximum time of any routine (for plot) */ 66 double scale; /* scale factor converting samples to pc 67 values: each sample covers scale bytes */ 68 char *strtab; /* string table in core */ 69 off_t ssiz; /* size of the string table */ 70 struct exec xbuf; /* exec header of a.out */ 71 72 int aflg; 73 int nflg; 74 int vflg; 75 int lflg; 76 int zflg; 77 int sflag; 78 79 char *namfil; 80 81 int timcmp(), valcmp(), cntcmp(); 82 83 main(argc, argv) 84 char **argv; 85 { 86 int lowpct, highpct; 87 88 /* 89 * Use highpct and lowpc as percentages, temporarily 90 * for graphing options involving blow-up 91 */ 92 lowpct = -1; 93 highpct = -1; 94 argv++; 95 while ( *argv != 0 && **argv == '-' ) { 96 *argv += 1; 97 if (**argv == 'l') 98 lflg++; 99 else if (**argv == 'a') 100 aflg++; 101 else if (**argv == 'n') 102 nflg++; 103 else if (**argv == 'z') 104 zflg++; 105 else if (**argv == 'v') 106 vflg++; 107 else if ( **argv == 's' ) 108 sflag++; 109 else if (**argv >= '0' && **argv <= '9') { 110 int i = atoi(*argv); 111 if (lowpct == -1) 112 lowpct = i; 113 else 114 highpct = i; 115 } 116 argv++; 117 } 118 if ( *argv != 0 ) { 119 namfil = *argv; 120 argv++; 121 } else { 122 namfil = A_OUTNAME; 123 } 124 if (lowpct >= 100) 125 lowpct = 0; 126 if (highpct <= lowpct || highpct > 100) 127 highpct = 100; 128 ransca = 100./(highpct-lowpct); 129 ranoff = 2040. + 40.8*lowpc*ransca; 130 /* 131 * get information about a.out file. 132 */ 133 getnfile(); 134 /* 135 * get information about mon.out file(s). 136 */ 137 if ( *argv == 0 ) { 138 getpfile( MON_OUTNAME ); 139 } else { 140 do { 141 getpfile( *argv ); 142 argv++; 143 } while ( *argv != 0 ); 144 } 145 asgnsamples(); /* assign samples to procedures */ 146 #ifdef plot 147 if (vflg) 148 plotprof(); /* a plotted or ... */ 149 else 150 #endif 151 printprof(); /* a printed profile */ 152 if ( sflag != 0 ) { 153 putprof(); 154 } 155 done(); 156 } 157 158 printprof() 159 { 160 double time, actime; 161 162 actime = 0; 163 printf(" %%time cumsecs #call ms/call name\n"); 164 if (!lflg) 165 qsort(nl, nname, sizeof(struct nl), timcmp); 166 for (np = nl; np<npe-1; np++) { 167 if (zflg == 0 && np->time == 0 && np->ncall == 0) 168 continue; 169 time = np->time/totime; 170 actime += np->time; 171 printf("%6.1f%9.2f", 100*time, actime/60); 172 if (np->ncall != 0) 173 printf("%7ld %8.2f", 174 np->ncall, np->time/(np->ncall*.06)); 175 else 176 printf("%7.7s %8.8s", "", ""); 177 printf(" %s\n", np->name); 178 } 179 } 180 181 /* 182 * Set up string and symbol tables from a.out. 183 * On return symbol table is sorted by value. 184 */ 185 getnfile() 186 { 187 188 nfile = fopen(namfil,"r"); 189 if (nfile == NULL) { 190 perror(namfil); 191 done(); 192 } 193 fread(&xbuf, 1, sizeof(xbuf), nfile); 194 if (N_BADMAG(xbuf)) { 195 fprintf(stderr, "%s: bad format\n", namfil); 196 done(); 197 } 198 getstrtab(); 199 getsymtab(); 200 qsort(nl, nname, sizeof(struct nl), valcmp); 201 } 202 203 getstrtab() 204 { 205 206 fseek(nfile, N_SYMOFF(xbuf) + xbuf.a_syms, 0); 207 if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) { 208 fprintf(stderr, "%s: no string table (old format?)\n", namfil); 209 done(); 210 } 211 strtab = (char *)calloc(ssiz, 1); 212 if (strtab == NULL) { 213 fprintf(stderr, "%s: no room for %d bytes of string table", 214 namfil, ssiz); 215 done(); 216 } 217 if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) { 218 fprintf(stderr, "%s: error reading string table\n", namfil); 219 done(); 220 } 221 } 222 223 /* 224 * Read in symbol table 225 */ 226 getsymtab() 227 { 228 register int i; 229 230 /* pass1 - count symbols */ 231 fseek(nfile, N_SYMOFF(xbuf), 0); 232 nname = 0; 233 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 234 struct nlist nbuf; 235 fread(&nbuf, sizeof(nbuf), 1, nfile); 236 if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT) 237 continue; 238 if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT) 239 continue; 240 nname++; 241 } 242 if (nname == 0) { 243 fprintf(stderr, "%s: no symbols\n", namfil); 244 done(); 245 } 246 nl = (struct nl *)calloc((nname+1), sizeof (struct nl)); 247 if (nl == 0) { 248 fprintf(stderr, "prof: No room for %d bytes of symbol table\n", 249 (nname+1) * sizeof (struct nlist)); 250 done(); 251 } 252 253 /* pass2 - read symbols */ 254 fseek(nfile, N_SYMOFF(xbuf), 0); 255 npe = nl; 256 nname = 0; 257 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 258 struct nlist nbuf; 259 fread(&nbuf, sizeof(nbuf), 1, nfile); 260 if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT) 261 continue; 262 if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT) 263 continue; 264 npe->value = nbuf.n_value/sizeof(UNIT); 265 npe->name = strtab+nbuf.n_un.n_strx; 266 npe++; 267 nname++; 268 } 269 npe->value = -1; 270 npe++; 271 } 272 273 /* 274 * information from a mon.out file is in two parts: 275 * the counters of how many times each procedure was called, 276 * if it was called at all; 277 * and an array of sampling hits within pc ranges. 278 * the counters must be dealt with on a file-by-file basis, 279 * since which procedures are represented may vary. 280 * the samples ranges are fixed, but must be summed across 281 * files, and then distributed among procedures, because 282 * of the wierd way the plotting is done. 283 */ 284 getpfile(filename) 285 char *filename; 286 { 287 288 openpfile(filename); 289 readcntrs(); 290 asgncntrs(); /* assign counts to procedures */ 291 readsamples(); 292 closepfile(); 293 } 294 295 openpfile(filename) 296 char *filename; 297 { 298 struct stat stb; 299 300 if((pfile = fopen(filename, "r")) == NULL) { 301 perror(filename); 302 done(); 303 } 304 fstat(fileno(pfile), &stb); 305 fread(&h, sizeof(struct hdr), 1, pfile); 306 lowpc = h.lowpc - (UNIT *)0; 307 highpc = h.highpc - (UNIT *)0; 308 sampbytes = 309 stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt); 310 nsamples = sampbytes / sizeof (unsigned UNIT); 311 } 312 313 closepfile() 314 { 315 316 fclose(pfile); 317 free(cbuf); 318 } 319 320 readcntrs() 321 { 322 struct cnt *kp; 323 324 cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt)); 325 if (cbuf == 0) { 326 fprintf(stderr, "prof: No room for %d bytes of count buffer\n", 327 (h.ncount+1) * sizeof (struct cnt)); 328 exit(1); 329 } 330 fread(cbuf, sizeof(struct cnt), h.ncount, pfile); 331 /* eliminate zero counters and scale counter pc values */ 332 if (h.ncount) { 333 kp = &cbuf[h.ncount - 1]; 334 for (;;) { 335 if (kp->cvalue==0) { 336 h.ncount=kp-cbuf; 337 ++kp; 338 break; 339 } 340 if (kp == cbuf) { 341 h.ncount = 0; 342 break; 343 } 344 --kp; 345 } 346 for (; --kp>=cbuf; ) 347 kp->cvalue /= sizeof(UNIT); 348 } 349 /* sort counters */ 350 qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp); 351 } 352 353 /* 354 * Assign counters to the procedures to which they belong 355 */ 356 asgncntrs() 357 { 358 register int i; 359 struct cnt *kp; 360 361 kp = &cbuf[h.ncount-1]; 362 np = npe; 363 while (--np>=nl) { 364 if (kp<cbuf || np->value > kp->cvalue) 365 continue; 366 /* skip ``static'' functions */ 367 while (kp >= cbuf && kp->cvalue > np->value + PCFUDGE) 368 --kp; 369 if (kp->cvalue >= np->value) { 370 np->ncall += kp->cncall; 371 --kp; 372 } 373 } 374 } 375 376 readsamples() 377 { 378 register i; 379 unsigned UNIT sample; 380 int totalt; 381 382 if (samples == 0) { 383 samples = (unsigned UNIT *) 384 calloc(sampbytes, sizeof (unsigned UNIT)); 385 if (samples == 0) { 386 printf("prof: No room for %d sample pc's\n", 387 sampbytes / sizeof (unsigned UNIT)); 388 done(); 389 } 390 } 391 for (i = 0; ; i++) { 392 fread(&sample, sizeof (unsigned UNIT), 1, pfile); 393 if (feof(pfile)) 394 break; 395 samples[i] += sample; 396 totalt += sample; 397 } 398 if (i != nsamples) { 399 fprintf(stderr, 400 "prof: unexpected EOF after reading %d/%d samples\n", 401 --i, nsamples); 402 done(); 403 } 404 } 405 406 /* 407 * Assign samples to the procedures to which they belong. 408 */ 409 asgnsamples() 410 { 411 register j; 412 unsigned UNIT ccnt; 413 double time; 414 unsigned pcl, pch; 415 register int i; 416 int overlap; 417 418 /* read samples and assign to namelist symbols */ 419 scale = highpc - lowpc; 420 scale /= nsamples; 421 for (i=0; i < nsamples; i++) { 422 ccnt = samples[i]; 423 if (ccnt == 0) 424 continue; 425 pcl = lowpc + scale*i; 426 pch = lowpc + scale*(i+1); 427 time = ccnt; 428 totime += time; 429 if(time > maxtime) 430 maxtime = time; 431 for (j=0; j<nname; j++) { 432 if (pch < nl[j].value) 433 break; 434 if (pcl >= nl[j+1].value) 435 continue; 436 overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value)); 437 if (overlap>0) 438 nl[j].time += overlap*time/scale; 439 } 440 } 441 if (totime==0.0) { 442 fprintf(stderr, "No time accumulated\n"); 443 /* 444 done(); 445 */ 446 totime=1.0; 447 } 448 } 449 450 /* 451 * dump what you have out to a mon.out style file. 452 */ 453 putprof() 454 { 455 FILE *sfile; 456 struct nl *np; 457 struct cnt kp; 458 int i; 459 460 sfile = fopen(MON_SUMNAME, "w"); 461 if (sfile == NULL) { 462 perror(MON_SUMNAME); 463 done(); 464 } 465 /* 466 * build a new header. 467 * h.lowpc and h.highpc are already fine. 468 * fix h.ncount to count non-zero calls, 469 * and the one zero call which marks the end. 470 */ 471 h.ncount = 0; 472 for (np = nl; np < npe-1 ; np++) 473 if (np->ncall > 0) 474 h.ncount++; 475 h.ncount++; 476 fwrite(&h, sizeof (struct hdr), 1, sfile); 477 for (np = nl; np < npe-1; np++) { 478 if (np->ncall > 0) { 479 kp.cvalue = np->value * sizeof (unsigned UNIT); 480 kp.cncall = np->ncall; 481 fwrite(&kp, sizeof (struct cnt), 1, sfile); 482 } 483 } 484 kp.cvalue = 0; 485 kp.cncall = 0; 486 fwrite(&kp, sizeof (struct cnt), 1, sfile); 487 fwrite(samples, sizeof (unsigned UNIT), nsamples, sfile); 488 fclose(sfile); 489 } 490 491 min(a, b) 492 { 493 if (a<b) 494 return(a); 495 return(b); 496 } 497 498 max(a, b) 499 { 500 if (a>b) 501 return(a); 502 return(b); 503 } 504 505 valcmp(p1, p2) 506 struct nl *p1, *p2; 507 { 508 509 return(p1->value - p2->value); 510 } 511 512 timcmp(p1, p2) 513 struct nl *p1, *p2; 514 { 515 float d; 516 517 if (nflg && p2->ncall != p1->ncall) 518 return (p2->ncall - p1->ncall); 519 d = p2->time - p1->time; 520 if (d > 0.0) 521 return(1); 522 if (d < 0.0) 523 return(-1); 524 return(strcmp(p1->name,p2->name)); 525 } 526 527 cntcmp(p1, p2) 528 struct cnt *p1, *p2; 529 { 530 531 return(p1->cvalue - p2->cvalue); 532 } 533 534 done() 535 { 536 537 #ifdef plot 538 if(vflg) { 539 point(0, -2040); 540 closepl(); 541 } 542 #endif 543 exit(0); 544 } 545 546 #ifdef plot 547 plotprof() 548 { 549 double time, lastx, lasty, lastsx; 550 register i; 551 552 openpl(); 553 erase(); 554 space(-2048, -2048, 2048, 2048); 555 line(-2040, -2040, -2040, 2040); 556 line(0, 2040, 0, -2040); 557 for(i=0; i<11; i++) 558 line(-2040, 2040-i*408, 0, 2040-i*408); 559 lastx = 0.; 560 lasty = ranoff; 561 scale = (4080.*ransca)/(sampbytes/sizeof(UNIT)); 562 lastsx = 0.0; 563 for(i = 0; i < nsamples; i++) { 564 unsigned UNIT ccnt; 565 double tx, ty; 566 ccnt = samples[i]; 567 time = ccnt; 568 tx = lastsx; 569 ty = lasty; 570 lastsx -= 2000.*time/totime; 571 lasty -= scale; 572 if(lasty >= -2040. && ty <= 2040.) { 573 line((int)tx, (int)ty, (int)lastsx, (int)lasty); 574 if (ccnt!=0 || lastx!=0.0) { 575 tx = lastx; 576 lastx = -time*2000./maxtime; 577 ty += scale/2; 578 line(0, (int)ty, (int)tx, (int)ty); 579 } 580 } 581 } 582 scale = (4080.*ransca)/(highpc-lowpc); 583 lastx = 50.; 584 for(np = nl; np<npe; np++) { 585 if(np->value < lowpc) 586 continue; 587 if(np->value >= highpc) 588 continue; 589 if(zflg == 0 && np->time == 0 && np->ncall == 0) 590 continue; 591 time = np->time/totime; 592 lasty = ranoff - (np->value - lowpc)*scale; 593 if(lasty >= -2040. && lasty <= 2040.) { 594 char bufl[BUFSIZ], *namp; 595 register j; 596 line(0, (int)lasty, 50, (int)lasty); 597 line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty); 598 move((int)(lastx+30), (int)(lasty+10)); 599 sprintf(bufl, "%s", np->name + (np->name[0] == '_')); 600 label(bufl); 601 } 602 lastx += 500.; 603 if(lastx > 2000.) 604 lastx = 50.; 605 } 606 } 607 #endif 608