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