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