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