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