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