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