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