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