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