|
|
1.1 root 1: /*
2: * prof
3: */
4: #include <stdio.h>
5: #include <sys/types.h>
6: #include <sys/stat.h>
7: #include <a.out.h>
8: #include <pagsiz.h>
9:
10: typedef short UNIT; /* unit of profiling */
11: #define A_OUTNAME "a.out"
12: #define MON_OUTNAME "mon.out"
13: #define MON_SUMNAME "mon.sum"
14:
15: /*
16: * The symbol table;
17: * for each external in the specified file we gather
18: * its address, the number of calls and compute its share of cpu time.
19: */
20: struct nl {
21: char *name;
22: unsigned value;
23: float time;
24: long ncall;
25: } *nl;
26: int nname;
27: struct nl *np;
28: struct nl *npe;
29:
30: /*
31: * The header on the mon.out file.
32: * Mon.out consists of one of these headers, an array of ncount
33: * cnt structures (as below) and then an array of samples
34: * representing the discretized program counter values.
35: */
36: struct hdr {
37: UNIT *lowpc, *highpc;
38: int ncount;
39: } h;
40:
41: /*
42: * Each counter has an address and a number of calls.
43: */
44: struct cnt {
45: unsigned cvalue;
46: long cncall;
47: } *cbuf;
48:
49: /*
50: * Each discretized pc sample has
51: * a count of the number of samples in its range
52: */
53: unsigned UNIT *samples;
54:
55: FILE *pfile, *nfile;
56:
57: unsigned lowpc, highpc; /* range profiled */
58: double ransca, ranoff; /* scaling for blowing up plots */
59: unsigned sampbytes; /* number of bytes of samples */
60: int nsamples; /* number of samples */
61: double totime; /* total time for all routines */
62: double maxtime; /* maximum time of any routine (for plot) */
63: double scale; /* scale factor converting samples to pc
64: values: each sample covers scale bytes */
65: char *strtab; /* string table in core */
66: off_t ssiz; /* size of the string table */
67: struct exec xbuf; /* exec header of a.out */
68:
69: int aflg;
70: int nflg;
71: int vflg;
72: int lflg;
73: int zflg;
74: int sflag;
75:
76: char *namfil;
77:
78: int timcmp(), valcmp(), cntcmp();
79:
80: main(argc, argv)
81: char **argv;
82: {
83: int lowpct, highpct;
84:
85: /*
86: * Use highpct and lowpc as percentages, temporarily
87: * for graphing options involving blow-up
88: */
89: lowpct = -1;
90: highpct = -1;
91: argv++;
92: while ( *argv != 0 && **argv == '-' ) {
93: *argv += 1;
94: if (**argv == 'l')
95: lflg++;
96: else if (**argv == 'a')
97: aflg++;
98: else if (**argv == 'n')
99: nflg++;
100: else if (**argv == 'z')
101: zflg++;
102: else if (**argv == 'v')
103: vflg++;
104: else if ( **argv == 's' )
105: sflag++;
106: else if (**argv >= '0' && **argv <= '9') {
107: int i = atoi(*argv);
108: if (lowpct == -1)
109: lowpct = i;
110: else
111: highpct = i;
112: }
113: argv++;
114: }
115: if ( *argv != 0 ) {
116: namfil = *argv;
117: argv++;
118: } else {
119: namfil = A_OUTNAME;
120: }
121: if (lowpct >= 100)
122: lowpct = 0;
123: if (highpct <= lowpct || highpct > 100)
124: highpct = 100;
125: ransca = 100./(highpct-lowpct);
126: ranoff = 2040. + 40.8*lowpc*ransca;
127: /*
128: * get information about a.out file.
129: */
130: getnfile();
131: /*
132: * get information about mon.out file(s).
133: */
134: if ( *argv == 0 ) {
135: getpfile( MON_OUTNAME );
136: } else {
137: do {
138: getpfile( *argv );
139: argv++;
140: } while ( *argv != 0 );
141: }
142: asgnsamples(); /* assign samples to procedures */
143: #ifdef plot
144: if (vflag)
145: plotprof(); /* a plotted or ... */
146: else
147: #else
148: printprof(); /* a printed profile */
149: #endif
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 amoung procedures, because
280: * of the wierd way the plotting is done.
281: */
282: getpfile( filename )
283: char *filename;
284: {
285: openpfile( filename );
286: readcntrs();
287: asgncntrs(); /* assign counts to procedures */
288: readsamples();
289: closepfile();
290: }
291:
292: openpfile( filename )
293: char *filename;
294: {
295: struct stat stb;
296:
297: if((pfile = fopen( filename ,"r")) == NULL) {
298: perror( filename );
299: done();
300: }
301: fstat(fileno(pfile), &stb);
302: fread(&h, sizeof(struct hdr), 1, pfile);
303: lowpc = h.lowpc - (UNIT *)0;
304: highpc = h.highpc - (UNIT *)0;
305: sampbytes =
306: stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt);
307: nsamples = sampbytes / sizeof (unsigned UNIT);
308: }
309:
310: closepfile()
311: {
312:
313: fclose( pfile );
314: free( cbuf );
315: }
316:
317: /*
318: * read in procedure call counters
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: while (kp >= cbuf && kp->cvalue - np->value > 11)
367: --kp;
368: if (kp->cvalue >= np->value) {
369: np->ncall += kp->cncall;
370: --kp;
371: }
372: }
373: }
374:
375: /*
376: * read pc samples
377: */
378: readsamples()
379: {
380: register i;
381: unsigned UNIT sample;
382:
383: if ( samples == 0 ) {
384: samples = (unsigned UNIT *)
385: calloc( sampbytes , sizeof (unsigned UNIT) );
386: if ( samples == 0 ) {
387: printf( "prof: No room for %d sample pc's\n" ,
388: sampbytes / sizeof (unsigned UNIT) );
389: done();
390: }
391: }
392: for ( i = 0 ; ; i++ ) {
393: fread( &sample , sizeof (unsigned UNIT) , 1 , pfile );
394: if ( feof( pfile ) ) {
395: break;
396: }
397: samples[ i ] += sample;
398: }
399: if ( i != nsamples ) {
400: fprintf( stderr ,
401: "prof: unexpected EOF after reading %d/%d samples\n" ,
402: --i , nsamples );
403: done();
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: }
476: }
477: h.ncount++;
478: fwrite( &h , sizeof (struct hdr) , 1 , sfile );
479: /*
480: * write out the counters
481: */
482: for ( np = nl ; np < npe - 1 ; np++ ) {
483: if ( np -> ncall > 0 ) {
484: kp.cvalue = np -> value * sizeof (unsigned UNIT);
485: kp.cncall = np -> ncall;
486: fwrite( &kp , sizeof (struct cnt) , 1 , sfile );
487: }
488: }
489: kp.cvalue = 0;
490: kp.cncall = 0;
491: fwrite( &kp , sizeof (struct cnt) , 1 , sfile );
492: /*
493: * write out the samples
494: */
495: fwrite( samples , sizeof (unsigned UNIT) , nsamples , sfile );
496: fclose( sfile );
497: }
498:
499: min(a, b)
500: {
501: if (a<b)
502: return(a);
503: return(b);
504: }
505:
506: max(a, b)
507: {
508: if (a>b)
509: return(a);
510: return(b);
511: }
512:
513: valcmp(p1, p2)
514: struct nl *p1, *p2;
515: {
516:
517: return(p1->value - p2->value);
518: }
519:
520: timcmp(p1, p2)
521: struct nl *p1, *p2;
522: {
523: float d;
524:
525: if (nflg && p2->ncall != p1->ncall)
526: return (p2->ncall - p1->ncall);
527: d = p2->time - p1->time;
528: if (d > 0.0)
529: return(1);
530: if (d < 0.0)
531: return(-1);
532: return(strcmp(p1->name,p2->name));
533: }
534:
535: cntcmp(p1, p2)
536: struct cnt *p1, *p2;
537: {
538:
539: return(p1->cvalue - p2->cvalue);
540: }
541:
542: done()
543: {
544:
545: #ifdef plot
546: if(vflg) {
547: point(0, -2040);
548: closepl();
549: }
550: #endif
551: exit(0);
552: }
553:
554: #ifdef plot
555: plotprof()
556: {
557: double time, lastx, lasty, lastsx;
558: register i;
559:
560: openpl();
561: erase();
562: space(-2048, -2048, 2048, 2048);
563: line(-2040, -2040, -2040, 2040);
564: line(0, 2040, 0, -2040);
565: for(i=0; i<11; i++)
566: line(-2040, 2040-i*408, 0, 2040-i*408);
567: lastx = 0.;
568: lasty = ranoff;
569: scale = (4080.*ransca)/(sampbytes/sizeof(UNIT));
570: lastsx = 0.0;
571: for(i = 0 ; i < nsamples ; i++ ) {
572: unsigned UNIT ccnt;
573: double tx, ty;
574: ccnt = samples[i];
575: time = ccnt;
576: tx = lastsx;
577: ty = lasty;
578: lastsx =- 2000.*time/totime;
579: lasty =- scale;
580: if(lasty >= -2040. && ty <= 2040.) {
581: line((int)tx, (int)ty, (int)lastsx, (int)lasty);
582: if (ccnt!=0 || lastx!=0.0) {
583: tx = lastx;
584: lastx = -time*2000./maxtime;
585: ty =+ scale/2;
586: line(0, (int)ty, (int)tx, (int)ty);
587: }
588: }
589: }
590: scale = (4080.*ransca)/(highpc-lowpc);
591: lastx = 50.;
592: for(np = nl; np<npe; np++) {
593: if(np->value < lowpc)
594: continue;
595: if(np->value >= highpc)
596: continue;
597: time = np->time/totime;
598: lasty = ranoff - (np->value - lowpc)*scale;
599: if(lasty >= -2040. && lasty <= 2040.) {
600: char bufl[BUFSIZ], *namp;
601: register j;
602: line(0, (int)lasty, 50, (int)lasty);
603: line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty);
604: point((int)(lastx+30), (int)(lasty+10));
605: sprintf(bufl, "%s\n", np->name + np->name[0] == '_');
606: label(bufl);
607: }
608: lastx =+ 500.;
609: if(lastx > 2000.)
610: lastx = 50.;
611: }
612: }
613: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.