BSD 4 development
[unix-history] / usr / src / cmd / prof / prof.c
CommitLineData
86b66dcd
BJ
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
10typedef 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 */
20struct nl {
21 char *name;
22 unsigned value;
23 float time;
24 long ncall;
25} *nl;
26int nname;
27struct nl *np;
28struct 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 */
36struct hdr {
37 UNIT *lowpc, *highpc;
38 int ncount;
39} h;
40
41/*
42 * Each counter has an address and a number of calls.
43 */
44struct 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 */
53unsigned UNIT *samples;
54
55FILE *pfile, *nfile;
56
57unsigned lowpc, highpc; /* range profiled */
58double ransca, ranoff; /* scaling for blowing up plots */
59unsigned sampbytes; /* number of bytes of samples */
60int nsamples; /* number of samples */
61double totime; /* total time for all routines */
62double maxtime; /* maximum time of any routine (for plot) */
63double scale; /* scale factor converting samples to pc
64 values: each sample covers scale bytes */
65char *strtab; /* string table in core */
66off_t ssiz; /* size of the string table */
67struct exec xbuf; /* exec header of a.out */
68
69int aflg;
70int nflg;
71int vflg;
72int lflg;
73int zflg;
74int sflag;
75
76char *namfil;
77
78int timcmp(), valcmp(), cntcmp();
79
80main(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
156printprof()
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 */
183getnfile()
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
201getstrtab()
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 */
224getsymtab()
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 */
282getpfile( filename )
283 char *filename;
284{
285 openpfile( filename );
286 readcntrs();
287 asgncntrs(); /* assign counts to procedures */
288 readsamples();
289 closepfile();
290}
291
292openpfile( 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
310closepfile()
311{
312
313 fclose( pfile );
314 free( cbuf );
315}
316
317 /*
318 * read in procedure call counters
319 */
320readcntrs()
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 */
356asgncntrs()
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 */
378readsamples()
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 */
409asgnsamples()
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 */
453putprof()
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
499min(a, b)
500{
501 if (a<b)
502 return(a);
503 return(b);
504}
505
506max(a, b)
507{
508 if (a>b)
509 return(a);
510 return(b);
511}
512
513valcmp(p1, p2)
514 struct nl *p1, *p2;
515{
516
517 return(p1->value - p2->value);
518}
519
520timcmp(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
535cntcmp(p1, p2)
536 struct cnt *p1, *p2;
537{
538
539 return(p1->cvalue - p2->cvalue);
540}
541
542done()
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
555plotprof()
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