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