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