Adb naively tries to print the reserved operand with %f and gets a
[unix-history] / usr / src / old / prof / prof.c
CommitLineData
53cf6719 1#ifndef lint
d4612f99 2static char *sccsid = "@(#)prof.c 4.4 (Berkeley) %G%";
53cf6719
SL
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>
d4612f99 11#include <sys/time.h>
53cf6719
SL
12
13typedef short UNIT; /* unit of profiling */
2d19cd70 14#define PCFUDGE 11
53cf6719
SL
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 */
24struct nl {
25 char *name;
26 unsigned value;
27 float time;
28 long ncall;
29} *nl;
30int nname;
31struct nl *np;
32struct 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 */
40struct hdr {
41 UNIT *lowpc, *highpc;
42 int ncount;
43} h;
44
45/*
46 * Each counter has an address and a number of calls.
47 */
48struct 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 */
57unsigned UNIT *samples;
58
59FILE *pfile, *nfile;
60
61unsigned lowpc, highpc; /* range profiled */
62double ransca, ranoff; /* scaling for blowing up plots */
63unsigned sampbytes; /* number of bytes of samples */
64int nsamples; /* number of samples */
65double totime; /* total time for all routines */
66double maxtime; /* maximum time of any routine (for plot) */
67double scale; /* scale factor converting samples to pc
68 values: each sample covers scale bytes */
69char *strtab; /* string table in core */
70off_t ssiz; /* size of the string table */
71struct exec xbuf; /* exec header of a.out */
72
73int aflg;
74int nflg;
75int vflg;
76int lflg;
77int zflg;
78int sflag;
79
80char *namfil;
81
82int timcmp(), valcmp(), cntcmp();
83
84main(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
159printprof()
160{
d4612f99 161 double time, actime, hz;
53cf6719
SL
162
163 actime = 0;
d4612f99 164 hz = hertz();
53cf6719
SL
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;
d4612f99 173 printf("%6.1f%9.2f", 100*time, actime/hz);
53cf6719
SL
174 if (np->ncall != 0)
175 printf("%7ld %8.2f",
d4612f99 176 np->ncall, (np->time*1000/hz)/np->ncall);
53cf6719
SL
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 */
187getnfile()
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
205getstrtab()
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 */
228getsymtab()
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 */
286getpfile(filename)
287 char *filename;
288{
289
290 openpfile(filename);
291 readcntrs();
292 asgncntrs(); /* assign counts to procedures */
293 readsamples();
294 closepfile();
295}
296
297openpfile(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 (unsigned UNIT);
313}
314
315closepfile()
316{
317
318 fclose(pfile);
319 free(cbuf);
320}
321
322readcntrs()
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 */
358asgncntrs()
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 */
2d19cd70 369 while (kp >= cbuf && kp->cvalue > np->value + PCFUDGE)
53cf6719
SL
370 --kp;
371 if (kp->cvalue >= np->value) {
372 np->ncall += kp->cncall;
373 --kp;
374 }
375 }
376}
377
378readsamples()
379{
380 register i;
381 unsigned UNIT sample;
382 int totalt;
383
384 if (samples == 0) {
385 samples = (unsigned UNIT *)
386 calloc(sampbytes, sizeof (unsigned UNIT));
387 if (samples == 0) {
388 printf("prof: No room for %d sample pc's\n",
389 sampbytes / sizeof (unsigned UNIT));
390 done();
391 }
392 }
393 for (i = 0; ; i++) {
394 fread(&sample, sizeof (unsigned 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 */
411asgnsamples()
412{
413 register j;
414 unsigned 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 */
455putprof()
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 (unsigned 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 (unsigned UNIT), nsamples, sfile);
490 fclose(sfile);
491}
492
d4612f99
KM
493/*
494 * discover the tick frequency of the machine
495 * if something goes wrong, we return 1.
496 */
497hertz()
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
53cf6719
SL
512min(a, b)
513{
514 if (a<b)
515 return(a);
516 return(b);
517}
518
519max(a, b)
520{
521 if (a>b)
522 return(a);
523 return(b);
524}
525
526valcmp(p1, p2)
527 struct nl *p1, *p2;
528{
529
530 return(p1->value - p2->value);
531}
532
533timcmp(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
548cntcmp(p1, p2)
549 struct cnt *p1, *p2;
550{
551
552 return(p1->cvalue - p2->cvalue);
553}
554
555done()
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
568plotprof()
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 unsigned 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;
d28c78f5
BJ
610 if(zflg == 0 && np->time == 0 && np->ncall == 0)
611 continue;
53cf6719
SL
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