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