local version
[unix-history] / usr / src / usr.bin / gprof / gprof.c
CommitLineData
3966d652 1#ifndef lint
eb6d6a16 2 static char *sccsid = "@(#)gprof.c 1.10 (Berkeley) %G%";
3966d652
PK
3#endif lint
4
31f0a970 5#include "gprof.h"
3966d652
PK
6
7main(argc, argv)
8 int argc;
9 char **argv;
10{
11
12 --argc;
13 argv++;
14 debug = 0;
15 while ( *argv != 0 && **argv == '-' ) {
16 (*argv)++;
0c0ac747
KM
17 switch ( **argv ) {
18 case 'd':
3966d652
PK
19 (*argv)++;
20 debug |= atoi( *argv );
21 debug |= ANYDEBUG;
22# ifdef DEBUG
23 printf( "[main] debug = %d\n" , debug );
24# endif DEBUG
0c0ac747
KM
25 break;
26 case 'a':
3f722622 27 aflag++;
0c0ac747
KM
28 break;
29 case 'b':
8b570c5c 30 bflag++;
0c0ac747
KM
31 break;
32 case 'c':
29da1d26 33 cflag++;
0c0ac747
KM
34 break;
35 case 's':
36 sflag++;
37 break;
38 case 'z':
8b570c5c 39 zflag++;
0c0ac747 40 break;
3966d652
PK
41 }
42 argv++;
43 }
44 if ( *argv != 0 ) {
45 a_outname = *argv;
46 argv++;
47 } else {
48 a_outname = A_OUTNAME;
49 }
50 if ( *argv != 0 ) {
31f0a970 51 gmonname = *argv;
3966d652
PK
52 argv++;
53 } else {
31f0a970 54 gmonname = GMONNAME;
3966d652
PK
55 }
56 /*
57 * get information about a.out file.
58 */
59 getnfile();
60 /*
61 * get information about mon.out file(s).
62 */
0c0ac747
KM
63 do {
64 getpfile( gmonname );
65 if ( *argv != 0 ) {
66 gmonname = *argv;
67 }
68 } while ( sflag && *argv++ != 0 );
69 /*
70 * dump out a gmon.sum file if requested
71 */
72 if ( sflag ) {
73 dumpsum( GMONSUM );
74 }
3966d652
PK
75 /*
76 * assign samples to procedures
77 */
78 asgnsamples();
79 /*
80 * print the usual profile
81 */
82 printprof();
83 /*
84 * assemble and print the dynamic profile
85 */
86 doarcs();
87 done();
88}
89
3966d652
PK
90/*
91 * Set up string and symbol tables from a.out.
29da1d26 92 * and optionally the text space.
3966d652
PK
93 * On return symbol table is sorted by value.
94 */
95getnfile()
96{
97 FILE *nfile;
98
99 nfile = fopen( a_outname ,"r");
100 if (nfile == NULL) {
101 perror( a_outname );
102 done();
103 }
104 fread(&xbuf, 1, sizeof(xbuf), nfile);
105 if (N_BADMAG(xbuf)) {
106 fprintf(stderr, "%s: bad format\n", a_outname );
107 done();
108 }
109 getstrtab(nfile);
110 getsymtab(nfile);
29da1d26 111 gettextspace( nfile );
3966d652
PK
112 qsort(nl, nname, sizeof(nltype), valcmp);
113 fclose(nfile);
114# ifdef DEBUG
115 if ( debug & AOUTDEBUG ) {
116 register int j;
117
118 for (j = 0; j < nname; j++){
119 printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
120 }
121 }
122# endif DEBUG
123}
124
125getstrtab(nfile)
126 FILE *nfile;
127{
128
129 fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
130 if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
131 fprintf(stderr, "%s: no string table (old format?)\n", a_outname );
132 done();
133 }
134 strtab = (char *)calloc(ssiz, 1);
135 if (strtab == NULL) {
136 fprintf(stderr, "%s: no room for %d bytes of string table",
137 a_outname , ssiz);
138 done();
139 }
140 if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
141 fprintf(stderr, "%s: error reading string table\n", a_outname );
142 done();
143 }
144}
145
146 /*
147 * Read in symbol table
148 */
149getsymtab(nfile)
150 FILE *nfile;
151{
152 register long i;
153 int askfor;
154 struct nlist nbuf;
155
156 /* pass1 - count symbols */
157 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
158 nname = 0;
159 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
160 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622 161 if ( ! funcsymbol( &nbuf ) ) {
3966d652
PK
162 continue;
163 }
164 nname++;
165 }
166 if (nname == 0) {
167 fprintf(stderr, "%s: no symbols\n", a_outname );
168 done();
169 }
170 /*
171 * ask also for CYCLEFRACTION extra namelist entries for
172 * cycle entries. these hide out at the end of the namelist
173 * and aren't accessed unless the whole namelist (nname+ncycles)
174 * is sorted and searched.
175 */
176 ncycles = nname * CYCLEFRACTION;
177 askfor = nname + 1 + ncycles;
178 nl = (nltype *) calloc( askfor , sizeof(nltype) );
179 if (nl == 0) {
180 fprintf(stderr, "prof: No room for %d bytes of symbol table\n",
181 askfor * sizeof(nltype) );
182 done();
183 }
184
185 /* pass2 - read symbols */
186 fseek(nfile, (long)N_SYMOFF(xbuf), 0);
187 npe = nl;
188 nname = 0;
189 for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
190 fread(&nbuf, sizeof(nbuf), 1, nfile);
3f722622
PK
191 if ( ! funcsymbol( &nbuf ) ) {
192# ifdef DEBUG
193 if ( debug & AOUTDEBUG ) {
194 printf( "[getsymtab] rejecting: 0x%x %s\n" ,
195 nbuf.n_type , strtab + nbuf.n_un.n_strx );
196 }
197# endif DEBUG
3966d652
PK
198 continue;
199 }
200 npe->value = nbuf.n_value;
201 npe->name = strtab+nbuf.n_un.n_strx;
202# ifdef DEBUG
203 if ( debug & AOUTDEBUG ) {
204 printf( "[getsymtab] %d %s 0x%08x\n" ,
205 nname , npe -> name , npe -> value );
206 }
207# endif DEBUG
208 npe++;
209 nname++;
210 }
211 npe->value = -1;
212 npe++;
213}
214
29da1d26
PK
215 /*
216 * read in the text space of an a.out file
217 */
218gettextspace( nfile )
219 FILE *nfile;
220{
221 unsigned char *malloc();
222
223 if ( cflag == 0 ) {
224 return;
225 }
226 textspace = malloc( xbuf.a_text );
227 if ( textspace == 0 ) {
228 fprintf( stderr , "gprof: ran out room for %d bytes of text space: " );
229 fprintf( stderr , "can't do -c\n" , xbuf.a_text );
230 return;
231 }
232 (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
233 if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
234 fprintf( stderr , "couldn't read text space: " );
235 fprintf( stderr , "can't do -c\n" , xbuf.a_text );
236 free( textspace );
237 textspace = 0;
238 return;
239 }
240}
3966d652 241 /*
31f0a970 242 * information from a gmon.out file is in two parts:
3966d652
PK
243 * an array of sampling hits within pc ranges,
244 * and the arcs.
245 */
246getpfile(filename)
247 char *filename;
248{
249 FILE *pfile;
250 FILE *openpfile();
251 struct rawarc arc;
252
253 pfile = openpfile(filename);
254 readsamples(pfile);
255 /*
256 * the rest of the file consists of
257 * a bunch of <from,self,count> tuples.
258 */
259 while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
260# ifdef DEBUG
261 if ( debug & SAMPLEDEBUG ) {
06ac0fb1 262 printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
3966d652
PK
263 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
264 }
265# endif DEBUG
266 /*
267 * add this arc
268 */
269 tally( &arc );
270 }
271 fclose(pfile);
272}
273
35e3e365
PK
274FILE *
275openpfile(filename)
3966d652
PK
276 char *filename;
277{
0c0ac747 278 struct hdr tmp;
3966d652
PK
279 FILE *pfile;
280
281 if((pfile = fopen(filename, "r")) == NULL) {
282 perror(filename);
283 done();
284 }
0c0ac747
KM
285 fread(&tmp, sizeof(struct hdr), 1, pfile);
286 if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
287 tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
288 fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
289 done();
290 }
291 h = tmp;
06ac0fb1
PK
292 s_lowpc = (unsigned long) h.lowpc;
293 s_highpc = (unsigned long) h.highpc;
3966d652
PK
294 lowpc = h.lowpc - (UNIT *)0;
295 highpc = h.highpc - (UNIT *)0;
296 sampbytes = h.ncnt - sizeof(struct hdr);
297 nsamples = sampbytes / sizeof (unsigned UNIT);
298 return(pfile);
299}
300
301tally( rawp )
302 struct rawarc *rawp;
303{
304 nltype *parentp;
305 nltype *childp;
3966d652
PK
306
307 parentp = nllookup( rawp -> raw_frompc );
308 childp = nllookup( rawp -> raw_selfpc );
309 childp -> ncall += rawp -> raw_count;
310# ifdef DEBUG
311 if ( debug & TALLYDEBUG ) {
312 printf( "[tally] arc from %s to %s traversed %d times\n" ,
313 parentp -> name , childp -> name , rawp -> raw_count );
314 }
315# endif DEBUG
29da1d26 316 addarc( parentp , childp , rawp -> raw_count );
3966d652
PK
317}
318
0c0ac747
KM
319/*
320 * dump out the gmon.sum file
321 */
322dumpsum( sumfile )
323 char *sumfile;
324{
325 register nltype *nlp;
326 register arctype *arcp;
327 struct rawarc arc;
328 FILE *sfile;
329
330 if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
331 perror( sumfile );
332 done();
333 }
334 /*
335 * dump the header; use the last header read in
336 */
337 if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
338 perror( sumfile );
339 done();
340 }
341 /*
342 * dump the samples
343 */
344 if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) {
345 perror( sumfile );
346 done();
347 }
348 /*
349 * dump the normalized raw arc information
350 */
351 for ( nlp = nl ; nlp < npe - 1 ; nlp++ ) {
352 for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
353 arc.raw_frompc = arcp -> arc_parentp -> value;
354 arc.raw_selfpc = arcp -> arc_childp -> value;
355 arc.raw_count = arcp -> arc_count;
356 if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
357 perror( sumfile );
358 done();
359 }
360# ifdef DEBUG
361 if ( debug & SAMPLEDEBUG ) {
362 printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
363 arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
364 }
365# endif DEBUG
366 }
367 }
368 fclose( sfile );
369}
370
3966d652
PK
371valcmp(p1, p2)
372 nltype *p1, *p2;
373{
374 if ( p1 -> value < p2 -> value ) {
375 return LESSTHAN;
376 }
377 if ( p1 -> value > p2 -> value ) {
378 return GREATERTHAN;
379 }
380 return EQUALTO;
381}
382
383readsamples(pfile)
384 FILE *pfile;
385{
386 register i;
387 unsigned UNIT sample;
388
389 if (samples == 0) {
390 samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT));
391 if (samples == 0) {
392 fprintf( stderr , "prof: No room for %d sample pc's\n",
393 sampbytes / sizeof (unsigned UNIT));
394 done();
395 }
396 }
397 for (i = 0; i < nsamples; i++) {
398 fread(&sample, sizeof (unsigned UNIT), 1, pfile);
399 if (feof(pfile))
400 break;
401 samples[i] += sample;
402 }
403 if (i != nsamples) {
404 fprintf(stderr,
405 "prof: unexpected EOF after reading %d/%d samples\n",
406 --i, nsamples);
407 done();
408 }
409}
410
411/*
412 * Assign samples to the procedures to which they belong.
413 */
414asgnsamples()
415{
416 register int j;
417 unsigned UNIT ccnt;
418 double time;
419 unsigned long pcl, pch;
420 register int i;
35e3e365 421 unsigned long overlap;
3966d652
PK
422 unsigned long svalue0, svalue1;
423
424 /* read samples and assign to namelist symbols */
425 scale = highpc - lowpc;
426 scale /= nsamples;
427 for (i=0; i < nsamples; i++) {
428 ccnt = samples[i];
429 if (ccnt == 0)
430 continue;
431 pcl = lowpc + scale*i;
432 pch = lowpc + scale*(i+1);
433 time = ccnt;
434# ifdef DEBUG
435 if ( debug & SAMPLEDEBUG ) {
eb6d6a16
KM
436 printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
437 pcl , pch , ccnt );
3966d652
PK
438 }
439# endif DEBUG
440 totime += time;
441 for (j=0; j<nname; j++) {
442 svalue0 = nl[j].value / sizeof(UNIT);
443 svalue1 = nl[j+1].value / sizeof(UNIT);
444 if (pch < svalue0)
445 break;
446 if (pcl >= svalue1)
447 continue;
448 overlap=min(pch,svalue1) - max(pcl,svalue0);
449 if (overlap>0) {
450# ifdef DEBUG
451 if ( debug & SAMPLEDEBUG ) {
eb6d6a16
KM
452 printf( "[asgnsamples] (0x%x-0x%x) %s gets %f ticks\n" ,
453 svalue0 , svalue1 , nl[j].name ,
454 overlap*time/scale );
3966d652
PK
455 }
456# endif DEBUG
457 nl[j].time += overlap*time/scale;
458 }
459 }
460 }
461# ifdef DEBUG
462 if ( debug & SAMPLEDEBUG ) {
463 printf( "[asgnsamples] totime %f\n" , totime );
464 }
465# endif DEBUG
466 if (totime==0.0) {
467 fprintf( stderr , "No time accumulated\n" );
468 totime=1.0;
469 }
470}
471
472
35e3e365 473unsigned long
3966d652 474min(a, b)
35e3e365 475 unsigned long a,b;
3966d652
PK
476{
477 if (a<b)
478 return(a);
479 return(b);
480}
481
35e3e365 482unsigned long
3966d652 483max(a, b)
35e3e365 484 unsigned long a,b;
3966d652
PK
485{
486 if (a>b)
487 return(a);
488 return(b);
489}
490
3f722622
PK
491bool
492funcsymbol( nlistp )
493 struct nlist *nlistp;
494{
495 extern char *strtab; /* string table from a.out */
d51dad81 496 extern int aflag; /* if static functions aren't desired */
3f722622
PK
497 char *name;
498
499 /*
500 * must be a text symbol,
d51dad81 501 * and static text symbols don't qualify if aflag set.
3f722622
PK
502 */
503 if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
d51dad81 504 || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
3f722622
PK
505 return FALSE;
506 }
507 /*
508 * can't have any `funny characters in name,
509 * where `funny' includes `.', .o file names
510 * and `$', pascal labels.
511 */
512 for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
513 if ( *name == '.' || *name == '$' ) {
514 return FALSE;
515 }
516 }
517 return TRUE;
518}
519
3966d652
PK
520done()
521{
522
523 exit(0);
524}