|
|
1.1 ! root 1: /* ! 2: * /usr/src/cmd/prof.c ! 3: * 6/23/93 ! 4: * prof interprets the mon.out files produced by the runtime profiling option, ! 5: * i.e. by programs compiled with the cc option -p (a.k.a. -VPROF). ! 6: * This version understands both COH286 l.out and COH386 COFF executables, ! 7: * which have different mon.out sizes in addition to different symbol formats. ! 8: * Usage: ! 9: * prof [ -abcs ] [ l.out [ mon.out ] ] ! 10: * Options: ! 11: * -a use all symbols (as opposed to only external symbols) ! 12: * -b print all bin information (to detect hot spots) ! 13: * -c print all call information ! 14: * -s print stack depth information ! 15: */ ! 16: ! 17: #include <stdio.h> ! 18: #include <l.out.h> ! 19: #include <coff.h> ! 20: #include <sys/const.h> ! 21: #include <canon.h> ! 22: #include <mon.h> ! 23: #if _I386 ! 24: #include <oldmon.h> ! 25: #endif ! 26: ! 27: #if DEBUG ! 28: #define dbprintf(args) printf args ! 29: #else ! 30: #define dbprintf(args) ! 31: #endif ! 32: ! 33: /* ! 34: * Note that in setting PSCALE, one must guard against overflow. ! 35: * Examine putdata() carefully before changing this constant. ! 36: * Also note that certain divisibility properties are assumed ! 37: * there reguarding PSCALE and HZ. ! 38: */ ! 39: #define PSCALE ((long)100) /* pc count scale factor */ ! 40: #define CSYMLEN 255 /* maximum COFF symbol length */ ! 41: #define SYMWMAX 32 /* maximum symbol printf width */ ! 42: #define TRUE (0 == 0) ! 43: #define FALSE (0 != 0) ! 44: ! 45: typedef struct symbol { ! 46: off_t addr; /* address */ ! 47: long pcount; /* pc count, scaled by PSCALE */ ! 48: long ccount; /* number of times routine called */ ! 49: char name[]; /* name */ ! 50: } SYMBOL; ! 51: ! 52: /* Forward. */ ! 53: char *alloc(); ! 54: void centi(); ! 55: int cmpdata(); ! 56: int cmpsym(); ! 57: SYMBOL **credit(); ! 58: void fatal(); ! 59: void getcdata(); ! 60: void getdata(); ! 61: void getpdata(); ! 62: void getstring(); ! 63: void getsyms(); ! 64: void putdata(); ! 65: void readcoffsyms(); ! 66: void readsyms(); ! 67: void usage(); ! 68: void warning(); ! 69: ! 70: /* Globals. */ ! 71: int aflag = FALSE; /* use all symbols */ ! 72: int bflag = FALSE; /* dump bin info */ ! 73: int cflag = FALSE; /* dump call info */ ! 74: SYMBOL **dict; /* NULL-terminated SYMBOL list */ ! 75: int dsize; /* number of symbols */ ! 76: int iscoff = FALSE; /* COFF executable (not l.out) */ ! 77: #if _I386 ! 78: char *lout = "a.out"; /* executable file name */ ! 79: #else ! 80: char *lout = "l.out"; /* executable file name */ ! 81: #endif ! 82: caddr_t lowpc; /* lowest pc profiled */ ! 83: char *monout = "mon.out"; /* monitor file name */ ! 84: char name[CSYMLEN+1]; /* symbol name buffer */ ! 85: unsigned int scaler; /* scale factor */ ! 86: int sflag = FALSE; /* dump low stack mark */ ! 87: off_t stksz; /* stack size */ ! 88: long strtable; /* COFF string table offset */ ! 89: int symwidth = NCPLN; /* printf symbol field width */ ! 90: long tcalls; /* total number of calls */ ! 91: long tticks; /* total number of clock ticks */ ! 92: ! 93: main(argc, argv) int argc; register char *argv[]; ! 94: { ! 95: register char *cp, ch; ! 96: ! 97: for (cp=*++argv; cp != NULL && *cp++ == '-'; cp=*++argv) ! 98: while ((ch=*cp++) != '\0') ! 99: switch (ch) { ! 100: case 'a': aflag = TRUE; break; ! 101: case 'b': bflag = TRUE; break; ! 102: case 'c': cflag = TRUE; break; ! 103: case 's': sflag = TRUE; break; ! 104: default: usage(); break; ! 105: } ! 106: if (*argv != NULL) ! 107: lout = *argv++; ! 108: if (*argv != NULL) ! 109: monout = *argv++; ! 110: if (*argv != NULL) ! 111: usage(); ! 112: getsyms(); ! 113: getdata(); ! 114: if (sflag) ! 115: printf("%u bytes stack used\n", stksz); ! 116: if (!bflag && !cflag) ! 117: putdata(); ! 118: exit(0); ! 119: } ! 120: ! 121: /* ! 122: * alloc() is an interface to malloc() which exits if there is no room. ! 123: */ ! 124: char * ! 125: alloc(size) unsigned size; ! 126: { ! 127: char *result; ! 128: ! 129: result = (char *)malloc(size); ! 130: if (result == NULL) ! 131: fatal("out of space"); ! 132: return result; ! 133: } ! 134: ! 135: /* ! 136: * Print on standard output 'num' / 'den' to two places, with ! 137: * at least 'width' places to the left of the decimal point. ! 138: */ ! 139: void ! 140: centi(num, den, width) long num, den; int width; ! 141: { ! 142: long cv; ! 143: ! 144: cv = (num*100 + den/2) / den; ! 145: printf("%*ld.%02d", width, cv/100, (int)(cv%100)); ! 146: } ! 147: ! 148: /* ! 149: * Compare the data in the dictionary entries 'sp1' and 'sp2'. ! 150: * Return an int which reflects which entry should be listed first. ! 151: * If the value is positive, 'sp2' should occur first. ! 152: * If it is zero, it makes no difference. ! 153: * If it is negative, 'sp1' should occur first. ! 154: */ ! 155: int ! 156: cmpdata(sp1, sp2) SYMBOL **sp1, **sp2; ! 157: { ! 158: register SYMBOL *adr1, *adr2; ! 159: long rel; ! 160: ! 161: adr1 = *sp1; ! 162: adr2 = *sp2; ! 163: rel = adr2->pcount - adr1->pcount; ! 164: if (rel == 0) ! 165: rel = adr2->ccount - adr1->ccount; ! 166: if (rel > 0) ! 167: return 1; ! 168: else if (rel < 0) ! 169: return -1; ! 170: else ! 171: return strcmp(adr1->name, adr2->name); ! 172: } ! 173: ! 174: /* ! 175: * Compare the two SYMBOLs 'sp1' and 'sp2' and return an ! 176: * int corresponding to the relative order of the address fields. ! 177: */ ! 178: int ! 179: cmpsym(sp1, sp2) SYMBOL **sp1, **sp2; ! 180: { ! 181: register off_t adr1, adr2; ! 182: ! 183: adr1 = (*sp1)->addr; ! 184: adr2 = (*sp2)->addr; ! 185: if (adr1 > adr2) ! 186: return 1; ! 187: else if (adr1 == adr2) ! 188: return 0; ! 189: else ! 190: return -1; ! 191: } ! 192: ! 193: /* ! 194: * Account for tick information. ! 195: */ ! 196: SYMBOL ** ! 197: credit(tick, low, high, dpp) int tick; off_t low, high; SYMBOL **dpp; ! 198: { ! 199: register unsigned overlap; ! 200: register SYMBOL *cur, *nxt; ! 201: unsigned binlen; ! 202: ! 203: dbprintf(("credit(%d, %x, %x, %s)\n", tick, low, high, (*dpp)->name)); ! 204: binlen = high - low; ! 205: if (binlen == 0) ! 206: binlen = 1; /* avoid 0-divide below */ ! 207: ! 208: nxt = *dpp; ! 209: if (nxt == NULL || nxt->addr >= high) { ! 210: if (bflag) ! 211: printf("%3d %06o %06o\n", tick, low, high-1); ! 212: return dpp; ! 213: } ! 214: do { ! 215: cur = nxt; ! 216: nxt = *++dpp; ! 217: } while (nxt != NULL && nxt->addr <= low); ! 218: if (bflag) ! 219: printf("%3d %*s+%-4u ", tick, symwidth, cur->name, ! 220: low - cur->addr); ! 221: do { ! 222: if (nxt != NULL && nxt->addr < high) ! 223: overlap = nxt->addr; ! 224: else ! 225: overlap = high; ! 226: if (cur->addr > low) ! 227: overlap -= cur->addr; ! 228: else ! 229: overlap -= low; ! 230: cur->pcount += (PSCALE*overlap*tick + binlen/2) / binlen; ! 231: cur = nxt; ! 232: nxt = *++dpp; ! 233: } while (cur != NULL && cur->addr < high); ! 234: dpp -= 2; ! 235: if (bflag) ! 236: printf("%*s+%u\n", symwidth, dpp[0]->name, ! 237: high - 1 - dpp[0]->addr); ! 238: return dpp; ! 239: } ! 240: ! 241: /* ! 242: * Print fatal error message and die. ! 243: */ ! 244: void ! 245: fatal(str) char *str; ! 246: { ! 247: fprintf(stderr, "prof: %r\n", &str); ! 248: exit(1); ! 249: } ! 250: ! 251: /* ! 252: * Read function call information from the mon.out file. ! 253: */ ! 254: void ! 255: getcdata(fp, nfnc) FILE *fp; register unsigned nfnc; ! 256: { ! 257: register SYMBOL **dpp, *dp; ! 258: struct m_func buf; ! 259: #if _I386 ! 260: struct old_m_func obuf; ! 261: #endif ! 262: ! 263: dbprintf(("getcdata(): nfnc=%d\n", nfnc)); ! 264: while (nfnc-- != 0) { ! 265: #if _I386 ! 266: if (!iscoff) { ! 267: if (fread(&obuf, sizeof obuf, 1, fp) != 1) ! 268: fatal("unexpected end of file on \"%s\"", monout); ! 269: buf.m_addr = (caddr_t)obuf.m_addr; ! 270: buf.m_ncalls = obuf.m_ncalls; ! 271: } else ! 272: #endif ! 273: if (fread(&buf, sizeof buf, 1, fp) != 1) ! 274: fatal("unexpected end of file on \"%s\"", monout); ! 275: for (dpp=dict; (dp=*++dpp) != NULL && dp->addr <= (off_t)buf.m_addr;) ! 276: ; ! 277: dp = dpp[-1]; ! 278: if (cflag) ! 279: printf("%4ld %*s+%u\n", buf.m_ncalls, symwidth, ! 280: dp->name, buf.m_addr - dp->addr); ! 281: tcalls += buf.m_ncalls; ! 282: dp->ccount += buf.m_ncalls; ! 283: } ! 284: dbprintf((" tcalls=%ld\n", tcalls)); ! 285: } ! 286: ! 287: /* ! 288: * Read the mon.out file and put the information into the dictionary. ! 289: */ ! 290: void ! 291: getdata() ! 292: { ! 293: FILE *fp; ! 294: struct m_hdr hdr; ! 295: #if _I386 ! 296: struct old_m_hdr ohdr; ! 297: #endif ! 298: ! 299: dbprintf(("getdata():\n")); ! 300: fp = fopen(monout, "r"); ! 301: if (fp == NULL) ! 302: fatal("cannot open \"%s\"", monout); ! 303: #if _I386 ! 304: if (!iscoff) { ! 305: /* Read COH286 mon.out and massage accordingly. */ ! 306: if (fread(&ohdr, sizeof ohdr, 1, fp) != 1) ! 307: fatal("\"%s\" is not a 286 mon.out file", monout); ! 308: hdr.m_nbins = ohdr.m_nbins; ! 309: hdr.m_scale = ohdr.m_scale; ! 310: hdr.m_nfuncs = ohdr.m_nfuncs; ! 311: hdr.m_lowpc = (caddr_t)ohdr.m_lowpc; ! 312: hdr.m_lowsp = (caddr_t)ohdr.m_lowsp; ! 313: hdr.m_hisp = (caddr_t)ohdr.m_hisp; ! 314: } else ! 315: #endif ! 316: if (fread(&hdr, sizeof hdr, 1, fp) != 1) ! 317: fatal("\"%s\" is not a mon.out file", monout); ! 318: dbprintf((" nbins=%d scale=%d nfuncs=%d\n", hdr.m_nbins, hdr.m_scale, hdr.m_nfuncs)); ! 319: scaler = hdr.m_scale & 0xffff; ! 320: if ((scaler & 0xfff) == 0xfff) ! 321: scaler++; ! 322: lowpc = hdr.m_lowpc; ! 323: stksz = hdr.m_hisp - hdr.m_lowsp; ! 324: dbprintf((" lowpc=%x hisp=%x lowsp=%x\n", lowpc, hdr.m_hisp, hdr.m_lowsp)); ! 325: dbprintf((" scaler=%d stksz=%x\n", scaler, stksz)); ! 326: ! 327: /* Read call data or skip over it. */ ! 328: if (cflag || !bflag) ! 329: getcdata(fp, hdr.m_nfuncs); ! 330: else if (iscoff) ! 331: fseek(fp, hdr.m_nfuncs * (long)sizeof (struct m_func), SEEK_CUR); ! 332: else ! 333: fseek(fp, hdr.m_nfuncs * (long)sizeof (struct old_m_func), SEEK_CUR); ! 334: ! 335: /* Read clock tick profil data. */ ! 336: if (bflag || !cflag) ! 337: getpdata(fp, hdr.m_nbins); ! 338: fclose(fp); ! 339: } ! 340: ! 341: /* ! 342: * Reads in the profiling data and increment the corresponding ! 343: * symbols' pcount fields. ! 344: * N.B. the global scale must contain the mon.out scale divided by 2. ! 345: * ! 346: * scale .text bytes per bin ! 347: * 0x10000 2 ! 348: * 0xFFFF 2 (for historical reasons) ! 349: * 0x8000 4 ! 350: * 0x7FFF 4 (for historical reasons) ! 351: * 0x4000 8 ! 352: * ... ... ! 353: * 0x0002 65536 ! 354: * ! 355: */ ! 356: void ! 357: getpdata(fp, nbins) FILE *fp; unsigned nbins; ! 358: { ! 359: register SYMBOL **dpp; ! 360: off_t high, low; ! 361: int highr, inc, incr; ! 362: short tick; ! 363: ! 364: dbprintf(("getpdata(): nbins=%d\n", nbins)); ! 365: high = (off_t)lowpc; ! 366: highr = 0; ! 367: #if 1 ! 368: inc = ((long)1<<17) / scaler; ! 369: incr = ((long)1<<17) % scaler; ! 370: if (incr) { ! 371: ++inc; ! 372: incr -= scaler; ! 373: } ! 374: #else ! 375: inc = 131072L/scaler; ! 376: #endif ! 377: dbprintf((" inc=%d incr=%d scale=%d\n", inc, incr, scaler)); ! 378: for (dpp=dict; nbins > 0; --nbins) { ! 379: low = high; ! 380: high += inc; ! 381: highr += incr; ! 382: if (-highr >= scaler) { ! 383: --high; ! 384: highr += scaler; ! 385: } ! 386: if (fread(&tick, sizeof tick, 1, fp) != 1) ! 387: fatal("unexpected end of file on \"%s\"", monout); ! 388: if (tick == 0) ! 389: continue; ! 390: tticks += tick; ! 391: dpp = credit(tick, low, high, dpp); ! 392: } ! 393: if (fgetc(fp) != EOF) ! 394: warning("excess data in \"%s\"", monout); ! 395: } ! 396: ! 397: /* ! 398: * Read a NUL-terminated string from offset 'loc' (in COFF string table) ! 399: * in fp into name[]. ! 400: * Symbols longer than CSYMLEN are truncated. ! 401: */ ! 402: void ! 403: getstring(fp, loc) FILE *fp; long loc; ! 404: { ! 405: register long sav; ! 406: register char *cp; ! 407: register int c; ! 408: ! 409: sav = ftell(fp); ! 410: if (fseek(fp, strtable+loc, SEEK_SET) == -1L) ! 411: fatal("seek failed"); ! 412: for (cp = name; cp < &name[CSYMLEN]; *cp++ = c) ! 413: if ((c = fgetc(fp)) == '\0' || c == EOF) ! 414: break; ! 415: *cp = '\0'; ! 416: if (c != '\0' && fgetc(fp) != '\0') ! 417: warning("symbol truncated to %s", name); ! 418: if (fseek(fp, sav, SEEK_SET) == -1L) ! 419: fatal("seek failed"); ! 420: } ! 421: ! 422: /* ! 423: * Read the symbols from an l.out file. ! 424: * Set dict to an array of them, in sorted order. ! 425: */ ! 426: void ! 427: getsyms() ! 428: { ! 429: FILE *fp; ! 430: long skip; ! 431: struct ldheader hdr; ! 432: FILEHDR chdr; ! 433: ! 434: dbprintf(("getsyms():\n")); ! 435: fp = fopen(lout, "r"); ! 436: if (fp == NULL) ! 437: fatal("cannot open \"%s\"", lout); ! 438: if (fread(&hdr, sizeof hdr, 1, fp) != 1 || hdr.l_magic != L_MAGIC) { ! 439: /* File is not l.out, see if it is COFF. */ ! 440: rewind(fp); ! 441: if (fread(&chdr, sizeof chdr, 1, fp) != 1 ! 442: || !ISCOFF(chdr.f_magic)) ! 443: fatal("\"%s\" is neither l.out nor COFF executable", lout); ! 444: iscoff = TRUE; ! 445: dbprintf(("386 COFF executable\n")); ! 446: if (fseek(fp, chdr.f_symptr, SEEK_SET) == -1L) ! 447: fatal("seek to symbol table failed"); ! 448: strtable = chdr.f_symptr + chdr.f_nsyms * sizeof(SYMENT); ! 449: readcoffsyms(chdr.f_nsyms, fp); ! 450: } else { ! 451: /* File is l.out. */ ! 452: dbprintf(("286 l.out executable\n")); ! 453: cansize(hdr.l_ssize[L_SHRI]); ! 454: cansize(hdr.l_ssize[L_PRVI]); ! 455: cansize(hdr.l_ssize[L_SHRD]); ! 456: cansize(hdr.l_ssize[L_PRVD]); ! 457: cansize(hdr.l_ssize[L_DEBUG]); ! 458: cansize(hdr.l_ssize[L_SYM]); ! 459: skip = hdr.l_ssize[L_SHRI] + hdr.l_ssize[L_PRVI] ! 460: + hdr.l_ssize[L_SHRD] + hdr.l_ssize[L_PRVD] ! 461: + hdr.l_ssize[L_DEBUG]; ! 462: fseek(fp, skip, SEEK_CUR); ! 463: readsyms((int)(hdr.l_ssize[L_SYM]/sizeof (struct ldsym)), fp); ! 464: } ! 465: if (dsize == 0) ! 466: fatal("no symbols found in \"%s\"", lout); ! 467: dict = (SYMBOL *)realloc(dict, (dsize + 1) * sizeof *dict); ! 468: qsort(dict, dsize, sizeof *dict, cmpsym); ! 469: fclose(fp); ! 470: } ! 471: ! 472: /* ! 473: * Print out the results which have been tabulated in the dictionary. ! 474: */ ! 475: void ! 476: putdata() ! 477: { ! 478: register SYMBOL **dpp, *dp; ! 479: ! 480: dbprintf(("putdata():\n")); ! 481: qsort(dict, dsize, sizeof *dict, cmpdata); ! 482: for (dpp=dict; (dp=*dpp++) != NULL;) { ! 483: if (dp->pcount == 0 && dp->ccount == 0) ! 484: continue; ! 485: printf("%-*s", symwidth, dp->name); ! 486: if (tticks != 0) { ! 487: centi(dp->pcount, (PSCALE * tticks)/100, 2); ! 488: putchar('%'); ! 489: } ! 490: if (dp->ccount != 0) { ! 491: printf(" %7ld ", dp->ccount); ! 492: centi((1000*dp->pcount) / (PSCALE*10), ! 493: (HZ*dp->ccount) / 10, 3); ! 494: } ! 495: putchar('\n'); ! 496: } ! 497: } ! 498: ! 499: /* ! 500: * Read in nsyms COFF symbols from FILE fp. ! 501: * Set dict to an array of the resulting symbols. ! 502: */ ! 503: #define SCNUM_TEXT 1 /* COFF .text section number */ ! 504: void ! 505: readcoffsyms(nsyms, fp) register long nsyms; FILE *fp; ! 506: { ! 507: register SYMBOL **dpp, *dp; ! 508: SYMENT sym; ! 509: int len, maxlen; ! 510: ! 511: dbprintf(("readcoffsyms(): nsyms=%ld\n", nsyms)); ! 512: dict = dpp = (SYMBOL **)alloc((nsyms + 1) * sizeof *dpp); ! 513: maxlen = 0; ! 514: while (nsyms-- > 0) { ! 515: if (fread(&sym, sizeof sym, 1, fp) != 1) ! 516: fatal("symbol read failed"); ! 517: if (sym.n_scnum != SCNUM_TEXT || sym.n_sclass != C_EXT) ! 518: continue; /* ignore all but .text */ ! 519: if (sym.n_zeroes == 0L) ! 520: getstring(fp, sym.n_offset); ! 521: else { ! 522: strncpy(name, sym.n_name, 8); ! 523: name[8] = '\0'; ! 524: } ! 525: len = strlen(name); ! 526: dp = (SYMBOL *)alloc(sizeof *dp + len + 1); ! 527: strcpy(dp->name, name); ! 528: dp->addr = sym.n_value; ! 529: dp->pcount = dp->ccount = 0; ! 530: *dpp++ = dp; ! 531: if (len > maxlen) ! 532: maxlen = len; ! 533: } ! 534: *dpp = NULL; ! 535: dsize = dpp - dict; ! 536: if (maxlen > NCPLN) { ! 537: symwidth = maxlen; ! 538: if (symwidth > SYMWMAX) ! 539: symwidth = SYMWMAX; /* for readability */ ! 540: } ! 541: } ! 542: ! 543: /* ! 544: * Read in nsyms l.out ldsyms from the FILE fp. ! 545: * Set dict to an array of the resulting symbols. ! 546: */ ! 547: void ! 548: readsyms(nsyms, fp) register int nsyms; FILE *fp; ! 549: { ! 550: register SYMBOL **dpp, *dp; ! 551: struct ldsym lsym; ! 552: ! 553: dbprintf(("readsyms(): nsyms=%d\n", nsyms)); ! 554: dict = dpp = (SYMBOL **)alloc((nsyms + 1) * sizeof *dpp); ! 555: while (--nsyms >= 0) { ! 556: if (fread(&lsym, sizeof lsym, 1, fp) != 1) ! 557: fatal("unexpected end of file on \"%s\"", lout); ! 558: if ((lsym.ls_type & ~L_GLOBAL) > L_BSSI) ! 559: continue; ! 560: if ((lsym.ls_type & L_GLOBAL) == 0 && ! aflag) ! 561: continue; ! 562: strncpy(name, lsym.ls_id, NCPLN); ! 563: name[NCPLN] = '\0'; ! 564: dp = (SYMBOL *)alloc(sizeof *dp + strlen(name) + 1); ! 565: strcpy(dp->name, lsym.ls_id); ! 566: dp->addr = lsym.ls_addr; ! 567: dp->pcount = dp->ccount = 0; ! 568: *dpp++ = dp; ! 569: } ! 570: *dpp = NULL; ! 571: dsize = dpp - dict; ! 572: } ! 573: ! 574: /* ! 575: * Print usage message and die. ! 576: */ ! 577: void ! 578: usage() ! 579: { ! 580: fprintf(stderr, "Usage: prof [ -abcs ] [ l.out [ mon.out ] ]\n"); ! 581: exit(1); ! 582: } ! 583: ! 584: /* ! 585: * Print nonfatal warning message. ! 586: */ ! 587: void ! 588: warning(str) char *str; ! 589: { ! 590: fprintf(stderr, "prof: Warning: %r\n", &str); ! 591: } ! 592: ! 593: /* end of /usr/src/cmd/prof.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.