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