.Xr botch fix
[unix-history] / usr / src / usr.bin / gprof / gprof.c
CommitLineData
c0bc4ef7
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
ddb85eed
KB
3 * All rights reserved.
4 *
6ecf3d85 5 * %sccs.include.redist.c%
c0bc4ef7
DF
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1983 Regents of the University of California.\n\
11 All rights reserved.\n";
ddb85eed 12#endif /* not lint */
c0bc4ef7 13
3966d652 14#ifndef lint
6ecf3d85 15static char sccsid[] = "@(#)gprof.c 5.6 (Berkeley) %G%";
ddb85eed 16#endif /* not lint */
3966d652 17
31f0a970 18#include "gprof.h"
3966d652 19
ad3b82ad
PK
20char *whoami = "gprof";
21
a441395b
PK
22 /*
23 * things which get -E excluded by default.
24 */
25char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
7ec9eedc 26
3966d652 27main(argc, argv)
7ec9eedc
PK
28 int argc;
29 char **argv;
3966d652 30{
7ec9eedc 31 char **sp;
b461ee6c 32 nltype **timesortnlp;
3966d652
PK
33
34 --argc;
35 argv++;
36 debug = 0;
355ab5a4 37 bflag = TRUE;
3966d652
PK
38 while ( *argv != 0 && **argv == '-' ) {
39 (*argv)++;
0c0ac747 40 switch ( **argv ) {
7ec9eedc
PK
41 case 'a':
42 aflag = TRUE;
43 break;
44 case 'b':
355ab5a4 45 bflag = FALSE;
7ec9eedc
PK
46 break;
47 case 'c':
48 cflag = TRUE;
49 break;
0c0ac747 50 case 'd':
7ec9eedc 51 dflag = TRUE;
3966d652
PK
52 (*argv)++;
53 debug |= atoi( *argv );
54 debug |= ANYDEBUG;
55# ifdef DEBUG
074671d4
KM
56 printf("[main] debug = %d\n", debug);
57# else not DEBUG
58 printf("%s: -d ignored\n", whoami);
3966d652 59# endif DEBUG
0c0ac747 60 break;
a441395b
PK
61 case 'E':
62 ++argv;
63 addlist( Elist , *argv );
64 Eflag = TRUE;
65 addlist( elist , *argv );
66 eflag = TRUE;
67 break;
7ec9eedc 68 case 'e':
a441395b 69 addlist( elist , *++argv );
7ec9eedc 70 eflag = TRUE;
a441395b
PK
71 break;
72 case 'F':
73 ++argv;
74 addlist( Flist , *argv );
75 Fflag = TRUE;
76 addlist( flist , *argv );
77 fflag = TRUE;
0c0ac747 78 break;
7ec9eedc 79 case 'f':
a441395b 80 addlist( flist , *++argv );
7ec9eedc 81 fflag = TRUE;
0c0ac747 82 break;
3cdecae8
KM
83 case 'k':
84 addlist( kfromlist , *++argv );
85 addlist( ktolist , *++argv );
86 kflag = TRUE;
87 break;
0c0ac747 88 case 's':
7ec9eedc 89 sflag = TRUE;
0c0ac747
KM
90 break;
91 case 'z':
7ec9eedc 92 zflag = TRUE;
0c0ac747 93 break;
3966d652
PK
94 }
95 argv++;
96 }
97 if ( *argv != 0 ) {
98 a_outname = *argv;
99 argv++;
100 } else {
101 a_outname = A_OUTNAME;
102 }
103 if ( *argv != 0 ) {
31f0a970 104 gmonname = *argv;
3966d652
PK
105 argv++;
106 } else {
31f0a970 107 gmonname = GMONNAME;
7ec9eedc
PK
108 }
109 /*
110 * turn off default functions
111 */
a441395b
PK
112 for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
113 Eflag = TRUE;
114 addlist( Elist , *sp );
7ec9eedc 115 eflag = TRUE;
a441395b 116 addlist( elist , *sp );
3966d652 117 }
89bcca98 118 /*
82fa08a4
PK
119 * how many ticks per second?
120 * if we can't tell, report time in ticks.
89bcca98
PK
121 */
122 hz = hertz();
82fa08a4
PK
123 if (hz == 0) {
124 hz = 1;
125 fprintf(stderr, "time is in ticks, not seconds\n");
126 }
3966d652
PK
127 /*
128 * get information about a.out file.
129 */
130 getnfile();
131 /*
132 * get information about mon.out file(s).
133 */
0c0ac747
KM
134 do {
135 getpfile( gmonname );
136 if ( *argv != 0 ) {
137 gmonname = *argv;
138 }
ad3b82ad 139 } while ( *argv++ != 0 );
0c0ac747
KM
140 /*
141 * dump out a gmon.sum file if requested
142 */
ad3b82ad
PK
143 if ( sflag ) {
144 dumpsum( GMONSUM );
145 }
3966d652
PK
146 /*
147 * assign samples to procedures
148 */
149 asgnsamples();
150 /*
b461ee6c
KM
151 * assemble the dynamic profile
152 */
153 timesortnlp = doarcs();
154 /*
155 * print the dynamic profile
156 */
157 printgprof( timesortnlp );
158 /*
159 * print the flat profile
3966d652
PK
160 */
161 printprof();
162 /*
b461ee6c 163 * print the index
3966d652 164 */
b461ee6c 165 printindex();
3966d652
PK
166 done();
167}
168
ad3b82ad
PK
169 /*
170 * Set up string and symbol tables from a.out.
171 * and optionally the text space.
172 * On return symbol table is sorted by value.
173 */
3966d652
PK
174getnfile()
175{
176 FILE *nfile;
104ad219 177 int valcmp();
3966d652
PK
178
179 nfile = fopen( a_outname ,"r");
180 if (nfile == NULL) {
181 perror( a_outname );
182 done();
183 }
184 fread(&xbuf, 1, sizeof(xbuf), nfile);
185 if (N_BADMAG(xbuf)) {
ad3b82ad 186 fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
3966d652
PK
187 done();
188 }
189 getstrtab(nfile);
190 getsymtab(nfile);
29da1d26 191 gettextspace( nfile );
3966d652
PK
192 qsort(nl, nname, sizeof(nltype), valcmp);
193 fclose(nfile);
194# ifdef DEBUG
195 if ( debug & AOUTDEBUG ) {
196 register int j;
197
198 for (j = 0; j < nname; j++){
199 printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
200 }
201 }
202# endif DEBUG
203}
204
205getstrtab(nfile)
206 FILE *nfile;
207{
208
209 fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
210 if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
ad3b82ad
PK
211 fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
212 whoami , a_outname );
3966d652
PK
213 done();
214 }
215 strtab = (char *)calloc(ssiz, 1);
216 if (strtab == NULL) {
ad3b82ad
PK
217 fprintf(stderr, "%s: %s: no room for %d bytes of string table",
218 whoami , a_outname , ssiz);
3966d652
PK
219 done();
220 }
221 if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
ad3b82ad
PK
222 fprintf(stderr, "%s: %s: error reading string table\n",
223 whoami , a_outname );
3966d652
PK
224 done();
225 }
226}
227
228 /*
229 * Read in symbol table
230 */
231getsymtab(nfile)
232 FILE *nfile;
233{
234 register long i;
235 int askfor;
236 struct nlist nbuf;
237
238 /* pass1 - count symbols */
239 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
240 nname = 0;
241 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
242 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622 243 if ( ! funcsymbol( &nbuf ) ) {
3966d652
PK
244 continue;
245 }
246 nname++;
247 }
248 if (nname == 0) {
ad3b82ad 249 fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
3966d652
PK
250 done();
251 }
ad3b82ad 252 askfor = nname + 1;
3966d652
PK
253 nl = (nltype *) calloc( askfor , sizeof(nltype) );
254 if (nl == 0) {
ad3b82ad
PK
255 fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
256 whoami, askfor * sizeof(nltype) );
3966d652
PK
257 done();
258 }
259
260 /* pass2 - read symbols */
261 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
262 npe = nl;
263 nname = 0;
264 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
265 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622
PK
266 if ( ! funcsymbol( &nbuf ) ) {
267# ifdef DEBUG
268 if ( debug & AOUTDEBUG ) {
269 printf( "[getsymtab] rejecting: 0x%x %s\n" ,
270 nbuf.n_type , strtab + nbuf.n_un.n_strx );
271 }
272# endif DEBUG
3966d652
PK
273 continue;
274 }
275 npe->value = nbuf.n_value;
276 npe->name = strtab+nbuf.n_un.n_strx;
277# ifdef DEBUG
278 if ( debug & AOUTDEBUG ) {
279 printf( "[getsymtab] %d %s 0x%08x\n" ,
280 nname , npe -> name , npe -> value );
281 }
282# endif DEBUG
283 npe++;
284 nname++;
285 }
286 npe->value = -1;
3966d652
PK
287}
288
29da1d26
PK
289 /*
290 * read in the text space of an a.out file
291 */
292gettextspace( nfile )
293 FILE *nfile;
294{
104ad219 295 char *malloc();
29da1d26
PK
296
297 if ( cflag == 0 ) {
298 return;
299 }
104ad219 300 textspace = (u_char *) malloc( xbuf.a_text );
29da1d26 301 if ( textspace == 0 ) {
ad3b82ad
PK
302 fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
303 whoami , xbuf.a_text );
304 fprintf( stderr , "can't do -c\n" );
29da1d26
PK
305 return;
306 }
307 (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
308 if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
ad3b82ad
PK
309 fprintf( stderr , "%s: couldn't read text space: " , whoami );
310 fprintf( stderr , "can't do -c\n" );
29da1d26
PK
311 free( textspace );
312 textspace = 0;
313 return;
314 }
315}
3966d652 316 /*
31f0a970 317 * information from a gmon.out file is in two parts:
3966d652
PK
318 * an array of sampling hits within pc ranges,
319 * and the arcs.
320 */
321getpfile(filename)
322 char *filename;
323{
324 FILE *pfile;
325 FILE *openpfile();
326 struct rawarc arc;
327
328 pfile = openpfile(filename);
329 readsamples(pfile);
330 /*
331 * the rest of the file consists of
332 * a bunch of <from,self,count> tuples.
333 */
334 while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
335# ifdef DEBUG
336 if ( debug & SAMPLEDEBUG ) {
06ac0fb1 337 printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
3966d652
PK
338 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
339 }
340# endif DEBUG
341 /*
342 * add this arc
343 */
344 tally( &arc );
345 }
346 fclose(pfile);
347}
348
35e3e365
PK
349FILE *
350openpfile(filename)
3966d652
PK
351 char *filename;
352{
0c0ac747 353 struct hdr tmp;
3966d652
PK
354 FILE *pfile;
355
356 if((pfile = fopen(filename, "r")) == NULL) {
357 perror(filename);
358 done();
359 }
0c0ac747
KM
360 fread(&tmp, sizeof(struct hdr), 1, pfile);
361 if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
362 tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
363 fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
364 done();
365 }
366 h = tmp;
06ac0fb1
PK
367 s_lowpc = (unsigned long) h.lowpc;
368 s_highpc = (unsigned long) h.highpc;
6d94fded
KM
369 lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
370 highpc = (unsigned long)h.highpc / sizeof(UNIT);
3966d652 371 sampbytes = h.ncnt - sizeof(struct hdr);
104ad219 372 nsamples = sampbytes / sizeof (UNIT);
33e41a83
PK
373# ifdef DEBUG
374 if ( debug & SAMPLEDEBUG ) {
375 printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
376 h.lowpc , h.highpc , h.ncnt );
377 printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
378 s_lowpc , s_highpc );
379 printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
380 lowpc , highpc );
381 printf( "[openpfile] sampbytes %d nsamples %d\n" ,
382 sampbytes , nsamples );
383 }
384# endif DEBUG
3966d652
PK
385 return(pfile);
386}
387
388tally( rawp )
389 struct rawarc *rawp;
390{
391 nltype *parentp;
392 nltype *childp;
3966d652
PK
393
394 parentp = nllookup( rawp -> raw_frompc );
395 childp = nllookup( rawp -> raw_selfpc );
3cdecae8
KM
396 if ( kflag
397 && onlist( kfromlist , parentp -> name )
398 && onlist( ktolist , childp -> name ) ) {
399 return;
400 }
3966d652
PK
401 childp -> ncall += rawp -> raw_count;
402# ifdef DEBUG
403 if ( debug & TALLYDEBUG ) {
404 printf( "[tally] arc from %s to %s traversed %d times\n" ,
405 parentp -> name , childp -> name , rawp -> raw_count );
406 }
407# endif DEBUG
29da1d26 408 addarc( parentp , childp , rawp -> raw_count );
3966d652
PK
409}
410
0c0ac747
KM
411/*
412 * dump out the gmon.sum file
413 */
414dumpsum( sumfile )
415 char *sumfile;
416{
417 register nltype *nlp;
418 register arctype *arcp;
419 struct rawarc arc;
420 FILE *sfile;
421
422 if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
423 perror( sumfile );
424 done();
425 }
426 /*
427 * dump the header; use the last header read in
428 */
429 if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
430 perror( sumfile );
431 done();
432 }
433 /*
434 * dump the samples
435 */
104ad219 436 if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
0c0ac747
KM
437 perror( sumfile );
438 done();
439 }
440 /*
441 * dump the normalized raw arc information
442 */
074671d4 443 for ( nlp = nl ; nlp < npe ; nlp++ ) {
0c0ac747
KM
444 for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
445 arc.raw_frompc = arcp -> arc_parentp -> value;
446 arc.raw_selfpc = arcp -> arc_childp -> value;
447 arc.raw_count = arcp -> arc_count;
448 if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
449 perror( sumfile );
450 done();
451 }
452# ifdef DEBUG
453 if ( debug & SAMPLEDEBUG ) {
454 printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
455 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
456 }
457# endif DEBUG
458 }
459 }
460 fclose( sfile );
461}
462
3966d652
PK
463valcmp(p1, p2)
464 nltype *p1, *p2;
465{
466 if ( p1 -> value < p2 -> value ) {
467 return LESSTHAN;
468 }
469 if ( p1 -> value > p2 -> value ) {
470 return GREATERTHAN;
471 }
472 return EQUALTO;
473}
474
475readsamples(pfile)
476 FILE *pfile;
477{
478 register i;
104ad219 479 UNIT sample;
3966d652
PK
480
481 if (samples == 0) {
104ad219 482 samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
3966d652 483 if (samples == 0) {
ad3b82ad 484 fprintf( stderr , "%s: No room for %d sample pc's\n",
104ad219 485 whoami , sampbytes / sizeof (UNIT));
3966d652
PK
486 done();
487 }
488 }
489 for (i = 0; i < nsamples; i++) {
104ad219 490 fread(&sample, sizeof (UNIT), 1, pfile);
3966d652
PK
491 if (feof(pfile))
492 break;
493 samples[i] += sample;
494 }
495 if (i != nsamples) {
496 fprintf(stderr,
ad3b82ad
PK
497 "%s: unexpected EOF after reading %d/%d samples\n",
498 whoami , --i , nsamples );
3966d652
PK
499 done();
500 }
501}
502
503/*
4855b699
KM
504 * Assign samples to the procedures to which they belong.
505 *
506 * There are three cases as to where pcl and pch can be
507 * with respect to the routine entry addresses svalue0 and svalue1
508 * as shown in the following diagram. overlap computes the
509 * distance between the arrows, the fraction of the sample
510 * that is to be credited to the routine which starts at svalue0.
511 *
512 * svalue0 svalue1
513 * | |
514 * v v
515 *
516 * +-----------------------------------------------+
517 * | |
518 * | ->| |<- ->| |<- ->| |<- |
519 * | | | | | |
520 * +---------+ +---------+ +---------+
521 *
522 * ^ ^ ^ ^ ^ ^
523 * | | | | | |
524 * pcl pch pcl pch pcl pch
525 *
526 * For the vax we assert that samples will never fall in the first
074671d4
KM
527 * two bytes of any routine, since that is the entry mask,
528 * thus we give call alignentries() to adjust the entry points if
529 * the entry mask falls in one bucket but the code for the routine
530 * doesn't start until the next bucket. In conjunction with the
531 * alignment of routine addresses, this should allow us to have
532 * only one sample for every four bytes of text space and never
533 * have any overlap (the two end cases, above).
3966d652
PK
534 */
535asgnsamples()
536{
537 register int j;
104ad219 538 UNIT ccnt;
3966d652
PK
539 double time;
540 unsigned long pcl, pch;
541 register int i;
35e3e365 542 unsigned long overlap;
3966d652
PK
543 unsigned long svalue0, svalue1;
544
545 /* read samples and assign to namelist symbols */
546 scale = highpc - lowpc;
547 scale /= nsamples;
074671d4 548 alignentries();
0fcb1e15 549 for (i = 0, j = 1; i < nsamples; i++) {
3966d652
PK
550 ccnt = samples[i];
551 if (ccnt == 0)
552 continue;
4855b699
KM
553 pcl = lowpc + scale * i;
554 pch = lowpc + scale * (i + 1);
3966d652
PK
555 time = ccnt;
556# ifdef DEBUG
557 if ( debug & SAMPLEDEBUG ) {
eb6d6a16
KM
558 printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
559 pcl , pch , ccnt );
3966d652
PK
560 }
561# endif DEBUG
562 totime += time;
16051da5 563 for (j = j - 1; j < nname; j++) {
074671d4
KM
564 svalue0 = nl[j].svalue;
565 svalue1 = nl[j+1].svalue;
566 /*
567 * if high end of tick is below entry address,
568 * go for next tick.
569 */
3966d652
PK
570 if (pch < svalue0)
571 break;
074671d4
KM
572 /*
573 * if low end of tick into next routine,
574 * go for next routine.
575 */
3966d652
PK
576 if (pcl >= svalue1)
577 continue;
074671d4 578 overlap = min(pch, svalue1) - max(pcl, svalue0);
4855b699 579 if (overlap > 0) {
3966d652 580# ifdef DEBUG
4855b699 581 if (debug & SAMPLEDEBUG) {
074671d4
KM
582 printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
583 nl[j].value/sizeof(UNIT), svalue0, svalue1,
584 nl[j].name,
585 overlap * time / scale, overlap);
3966d652
PK
586 }
587# endif DEBUG
4855b699 588 nl[j].time += overlap * time / scale;
3966d652
PK
589 }
590 }
591 }
592# ifdef DEBUG
4855b699
KM
593 if (debug & SAMPLEDEBUG) {
594 printf("[asgnsamples] totime %f\n", totime);
3966d652
PK
595 }
596# endif DEBUG
3966d652
PK
597}
598
599
35e3e365 600unsigned long
3966d652 601min(a, b)
35e3e365 602 unsigned long a,b;
3966d652
PK
603{
604 if (a<b)
605 return(a);
606 return(b);
607}
608
35e3e365 609unsigned long
3966d652 610max(a, b)
35e3e365 611 unsigned long a,b;
3966d652
PK
612{
613 if (a>b)
614 return(a);
615 return(b);
616}
617
074671d4
KM
618 /*
619 * calculate scaled entry point addresses (to save time in asgnsamples),
620 * and possibly push the scaled entry points over the entry mask,
621 * if it turns out that the entry point is in one bucket and the code
622 * for a routine is in the next bucket.
623 */
624alignentries()
625{
626 register struct nl *nlp;
627 unsigned long bucket_of_entry;
628 unsigned long bucket_of_code;
629
630 for (nlp = nl; nlp < npe; nlp++) {
631 nlp -> svalue = nlp -> value / sizeof(UNIT);
632 bucket_of_entry = (nlp->svalue - lowpc) / scale;
633 bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
634 if (bucket_of_entry < bucket_of_code) {
635# ifdef DEBUG
636 if (debug & SAMPLEDEBUG) {
637 printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
638 nlp->svalue, nlp->svalue + UNITS_TO_CODE);
639 }
640# endif DEBUG
641 nlp->svalue += UNITS_TO_CODE;
642 }
643 }
644}
645
3f722622
PK
646bool
647funcsymbol( nlistp )
648 struct nlist *nlistp;
649{
650 extern char *strtab; /* string table from a.out */
d51dad81 651 extern int aflag; /* if static functions aren't desired */
3f722622
PK
652 char *name;
653
654 /*
655 * must be a text symbol,
d51dad81 656 * and static text symbols don't qualify if aflag set.
3f722622
PK
657 */
658 if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
d51dad81 659 || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
3f722622
PK
660 return FALSE;
661 }
662 /*
ad3b82ad 663 * can't have any `funny' characters in name,
3f722622
PK
664 * where `funny' includes `.', .o file names
665 * and `$', pascal labels.
666 */
667 for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
668 if ( *name == '.' || *name == '$' ) {
669 return FALSE;
670 }
671 }
672 return TRUE;
673}
674
3966d652
PK
675done()
676{
677
678 exit(0);
679}