|
|
1.1 root 1: #ifndef lint
2: static char *sccsid = "@(#)prof.c 4.3 (Berkeley) 7/2/83";
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:
12: typedef short UNIT; /* unit of profiling */
13: #define PCFUDGE 11
14: #define A_OUTNAME "a.out"
15: #define MON_OUTNAME "mon.out"
16: #define MON_SUMNAME "mon.sum"
17:
18: /*
19: * The symbol table;
20: * for each external in the specified file we gather
21: * its address, the number of calls and compute its share of cpu time.
22: */
23: struct nl {
24: char *name;
25: unsigned value;
26: float time;
27: long ncall;
28: } *nl;
29: int nname;
30: struct nl *np;
31: struct nl *npe;
32:
33: /*
34: * The header on the mon.out file.
35: * Mon.out consists of one of these headers, an array of ncount
36: * cnt structures (as below) and then an array of samples
37: * representing the discretized program counter values.
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*lowpc*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;
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/sizeof(UNIT);
265: npe->name = strtab+nbuf.n_un.n_strx;
266: npe++;
267: nname++;
268: }
269: npe->value = -1;
270: npe++;
271: }
272:
273: /*
274: * information from a mon.out file is in two parts:
275: * the counters of how many times each procedure was called,
276: * if it was called at all;
277: * and an array of sampling hits within pc ranges.
278: * the counters must be dealt with on a file-by-file basis,
279: * since which procedures are represented may vary.
280: * the samples ranges are fixed, but must be summed across
281: * files, and then distributed among procedures, because
282: * of the wierd way the plotting is done.
283: */
284: getpfile(filename)
285: char *filename;
286: {
287:
288: openpfile(filename);
289: readcntrs();
290: asgncntrs(); /* assign counts to procedures */
291: readsamples();
292: closepfile();
293: }
294:
295: openpfile(filename)
296: char *filename;
297: {
298: struct stat stb;
299:
300: if((pfile = fopen(filename, "r")) == NULL) {
301: perror(filename);
302: done();
303: }
304: fstat(fileno(pfile), &stb);
305: fread(&h, sizeof(struct hdr), 1, pfile);
306: lowpc = h.lowpc - (UNIT *)0;
307: highpc = h.highpc - (UNIT *)0;
308: sampbytes =
309: stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt);
310: nsamples = sampbytes / sizeof (unsigned UNIT);
311: }
312:
313: closepfile()
314: {
315:
316: fclose(pfile);
317: free(cbuf);
318: }
319:
320: readcntrs()
321: {
322: struct cnt *kp;
323:
324: cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt));
325: if (cbuf == 0) {
326: fprintf(stderr, "prof: No room for %d bytes of count buffer\n",
327: (h.ncount+1) * sizeof (struct cnt));
328: exit(1);
329: }
330: fread(cbuf, sizeof(struct cnt), h.ncount, pfile);
331: /* eliminate zero counters and scale counter pc values */
332: if (h.ncount) {
333: kp = &cbuf[h.ncount - 1];
334: for (;;) {
335: if (kp->cvalue==0) {
336: h.ncount=kp-cbuf;
337: ++kp;
338: break;
339: }
340: if (kp == cbuf) {
341: h.ncount = 0;
342: break;
343: }
344: --kp;
345: }
346: for (; --kp>=cbuf; )
347: kp->cvalue /= sizeof(UNIT);
348: }
349: /* sort counters */
350: qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp);
351: }
352:
353: /*
354: * Assign counters to the procedures to which they belong
355: */
356: asgncntrs()
357: {
358: register int i;
359: struct cnt *kp;
360:
361: kp = &cbuf[h.ncount-1];
362: np = npe;
363: while (--np>=nl) {
364: if (kp<cbuf || np->value > kp->cvalue)
365: continue;
366: /* skip ``static'' functions */
367: while (kp >= cbuf && kp->cvalue > np->value + PCFUDGE)
368: --kp;
369: if (kp->cvalue >= np->value) {
370: np->ncall += kp->cncall;
371: --kp;
372: }
373: }
374: }
375:
376: readsamples()
377: {
378: register i;
379: unsigned UNIT sample;
380: int totalt;
381:
382: if (samples == 0) {
383: samples = (unsigned UNIT *)
384: calloc(sampbytes, sizeof (unsigned UNIT));
385: if (samples == 0) {
386: printf("prof: No room for %d sample pc's\n",
387: sampbytes / sizeof (unsigned UNIT));
388: done();
389: }
390: }
391: for (i = 0; ; i++) {
392: fread(&sample, sizeof (unsigned UNIT), 1, pfile);
393: if (feof(pfile))
394: break;
395: samples[i] += sample;
396: totalt += sample;
397: }
398: if (i != nsamples) {
399: fprintf(stderr,
400: "prof: unexpected EOF after reading %d/%d samples\n",
401: --i, nsamples);
402: done();
403: }
404: }
405:
406: /*
407: * Assign samples to the procedures to which they belong.
408: */
409: asgnsamples()
410: {
411: register j;
412: unsigned UNIT ccnt;
413: double time;
414: unsigned pcl, pch;
415: register int i;
416: int overlap;
417:
418: /* read samples and assign to namelist symbols */
419: scale = highpc - lowpc;
420: scale /= nsamples;
421: for (i=0; i < nsamples; i++) {
422: ccnt = samples[i];
423: if (ccnt == 0)
424: continue;
425: pcl = lowpc + scale*i;
426: pch = lowpc + scale*(i+1);
427: time = ccnt;
428: totime += time;
429: if(time > maxtime)
430: maxtime = time;
431: for (j=0; j<nname; j++) {
432: if (pch < nl[j].value)
433: break;
434: if (pcl >= nl[j+1].value)
435: continue;
436: overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value));
437: if (overlap>0)
438: nl[j].time += overlap*time/scale;
439: }
440: }
441: if (totime==0.0) {
442: fprintf(stderr, "No time accumulated\n");
443: /*
444: done();
445: */
446: totime=1.0;
447: }
448: }
449:
450: /*
451: * dump what you have out to a mon.out style file.
452: */
453: putprof()
454: {
455: FILE *sfile;
456: struct nl *np;
457: struct cnt kp;
458: int i;
459:
460: sfile = fopen(MON_SUMNAME, "w");
461: if (sfile == NULL) {
462: perror(MON_SUMNAME);
463: done();
464: }
465: /*
466: * build a new header.
467: * h.lowpc and h.highpc are already fine.
468: * fix h.ncount to count non-zero calls,
469: * and the one zero call which marks the end.
470: */
471: h.ncount = 0;
472: for (np = nl; np < npe-1 ; np++)
473: if (np->ncall > 0)
474: h.ncount++;
475: h.ncount++;
476: fwrite(&h, sizeof (struct hdr), 1, sfile);
477: for (np = nl; np < npe-1; np++) {
478: if (np->ncall > 0) {
479: kp.cvalue = np->value * sizeof (unsigned UNIT);
480: kp.cncall = np->ncall;
481: fwrite(&kp, sizeof (struct cnt), 1, sfile);
482: }
483: }
484: kp.cvalue = 0;
485: kp.cncall = 0;
486: fwrite(&kp, sizeof (struct cnt), 1, sfile);
487: fwrite(samples, sizeof (unsigned UNIT), nsamples, sfile);
488: fclose(sfile);
489: }
490:
491: min(a, b)
492: {
493: if (a<b)
494: return(a);
495: return(b);
496: }
497:
498: max(a, b)
499: {
500: if (a>b)
501: return(a);
502: return(b);
503: }
504:
505: valcmp(p1, p2)
506: struct nl *p1, *p2;
507: {
508:
509: return(p1->value - p2->value);
510: }
511:
512: timcmp(p1, p2)
513: struct nl *p1, *p2;
514: {
515: float d;
516:
517: if (nflg && p2->ncall != p1->ncall)
518: return (p2->ncall - p1->ncall);
519: d = p2->time - p1->time;
520: if (d > 0.0)
521: return(1);
522: if (d < 0.0)
523: return(-1);
524: return(strcmp(p1->name,p2->name));
525: }
526:
527: cntcmp(p1, p2)
528: struct cnt *p1, *p2;
529: {
530:
531: return(p1->cvalue - p2->cvalue);
532: }
533:
534: done()
535: {
536:
537: #ifdef plot
538: if(vflg) {
539: point(0, -2040);
540: closepl();
541: }
542: #endif
543: exit(0);
544: }
545:
546: #ifdef plot
547: plotprof()
548: {
549: double time, lastx, lasty, lastsx;
550: register i;
551:
552: openpl();
553: erase();
554: space(-2048, -2048, 2048, 2048);
555: line(-2040, -2040, -2040, 2040);
556: line(0, 2040, 0, -2040);
557: for(i=0; i<11; i++)
558: line(-2040, 2040-i*408, 0, 2040-i*408);
559: lastx = 0.;
560: lasty = ranoff;
561: scale = (4080.*ransca)/(sampbytes/sizeof(UNIT));
562: lastsx = 0.0;
563: for(i = 0; i < nsamples; i++) {
564: unsigned UNIT ccnt;
565: double tx, ty;
566: ccnt = samples[i];
567: time = ccnt;
568: tx = lastsx;
569: ty = lasty;
570: lastsx -= 2000.*time/totime;
571: lasty -= scale;
572: if(lasty >= -2040. && ty <= 2040.) {
573: line((int)tx, (int)ty, (int)lastsx, (int)lasty);
574: if (ccnt!=0 || lastx!=0.0) {
575: tx = lastx;
576: lastx = -time*2000./maxtime;
577: ty += scale/2;
578: line(0, (int)ty, (int)tx, (int)ty);
579: }
580: }
581: }
582: scale = (4080.*ransca)/(highpc-lowpc);
583: lastx = 50.;
584: for(np = nl; np<npe; np++) {
585: if(np->value < lowpc)
586: continue;
587: if(np->value >= highpc)
588: continue;
589: if(zflg == 0 && np->time == 0 && np->ncall == 0)
590: continue;
591: time = np->time/totime;
592: lasty = ranoff - (np->value - lowpc)*scale;
593: if(lasty >= -2040. && lasty <= 2040.) {
594: char bufl[BUFSIZ], *namp;
595: register j;
596: line(0, (int)lasty, 50, (int)lasty);
597: line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty);
598: move((int)(lastx+30), (int)(lasty+10));
599: sprintf(bufl, "%s", np->name + (np->name[0] == '_'));
600: label(bufl);
601: }
602: lastx += 500.;
603: if(lastx > 2000.)
604: lastx = 50.;
605: }
606: }
607: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.