|
|
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.