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