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