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