1 #ifndef lint
2 static char *sccsid = "@(#)prof.c 4.5 (Berkeley) 07/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
main(argc,argv)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
printprof()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 */
getnfile()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
getstrtab()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 */
getsymtab()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 */
getpfile(filename)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
openpfile(filename)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
closepfile()315 closepfile()
316 {
317
318 fclose(pfile);
319 free(cbuf);
320 }
321
readcntrs()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 */
asgncntrs()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
readsamples()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 */
asgnsamples()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 */
putprof()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 */
hertz()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
min(a,b)512 min(a, b)
513 {
514 if (a<b)
515 return(a);
516 return(b);
517 }
518
max(a,b)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
done()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
plotprof()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
629