GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/gprof/gprof.c Lines: 0 187 0.0 %
Date: 2016-12-06 Branches: 0 115 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: gprof.c,v 1.24 2016/03/09 16:28:50 deraadt Exp $	*/
2
/*	$NetBSD: gprof.c,v 1.8 1995/04/19 07:15:59 cgd Exp $	*/
3
4
/*
5
 * Copyright (c) 1983, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "gprof.h"
34
35
int valcmp(const void *, const void *);
36
37
static struct gmonhdr	gmonhdr;
38
extern char *__progname;
39
40
int
41
main(int argc, char *argv[])
42
{
43
    char	**sp;
44
    nltype	**timesortnlp;
45
    char	**defaultEs;
46
47
    if (pledge("stdio rpath wpath cpath", NULL) == -1)
48
        err(1, NULL);
49
50
    --argc;
51
    argv++;
52
    debug = 0;
53
    bflag = TRUE;
54
    while ( *argv != 0 && **argv == '-' ) {
55
	(*argv)++;
56
	switch ( **argv ) {
57
	case 'a':
58
	    aflag = TRUE;
59
	    break;
60
	case 'b':
61
	    bflag = FALSE;
62
	    break;
63
	case 'C':
64
	    Cflag = TRUE;
65
	    cyclethreshold = atoi( *++argv );
66
	    break;
67
	case 'c':
68
#if defined(__i386__) || defined(__sparc__) || defined(__sparc64__)
69
	    cflag = TRUE;
70
#else
71
	    fprintf(stderr, "%s: -c isn't supported on this architecture yet\n", __progname);
72
	    exit(1);
73
#endif
74
	    break;
75
	case 'd':
76
	    dflag = TRUE;
77
	    setvbuf(stdout, NULL, _IOLBF, 0);
78
	    debug |= atoi( *++argv );
79
	    debug |= ANYDEBUG;
80
#	    ifdef DEBUG
81
		printf("[main] debug = %d\n", debug);
82
#	    else /* not DEBUG */
83
		warnx("-d ignored");
84
#	    endif /* DEBUG */
85
	    break;
86
	case 'E':
87
	    ++argv;
88
	    addlist( Elist , *argv );
89
	    Eflag = TRUE;
90
	    addlist( elist , *argv );
91
	    eflag = TRUE;
92
	    break;
93
	case 'e':
94
	    addlist( elist , *++argv );
95
	    eflag = TRUE;
96
	    break;
97
	case 'F':
98
	    ++argv;
99
	    addlist( Flist , *argv );
100
	    Fflag = TRUE;
101
	    addlist( flist , *argv );
102
	    fflag = TRUE;
103
	    break;
104
	case 'f':
105
	    addlist( flist , *++argv );
106
	    fflag = TRUE;
107
	    break;
108
	case 'k':
109
	    addlist( kfromlist , *++argv );
110
	    addlist( ktolist , *++argv );
111
	    kflag = TRUE;
112
	    break;
113
	case 's':
114
	    sflag = TRUE;
115
	    break;
116
	case 'z':
117
	    zflag = TRUE;
118
	    break;
119
	}
120
	argv++;
121
    }
122
    if ( *argv != 0 ) {
123
	a_outname  = *argv;
124
	argv++;
125
    } else {
126
	a_outname  = A_OUTNAME;
127
    }
128
    if ( *argv != 0 ) {
129
	gmonname = *argv;
130
	argv++;
131
    } else {
132
	gmonname = GMONNAME;
133
    }
134
    if ( sflag == FALSE ) {
135
        if (pledge("stdio rpath wpath cpath", NULL) == -1)
136
            err(1, "pledge");
137
    }
138
	/*
139
	 *	get information about a.out file.
140
	 */
141
    if (getnfile(a_outname, &defaultEs) == -1)
142
	errx(1, "%s: bad format", a_outname);
143
	/*
144
	 *	sort symbol table.
145
	 */
146
    qsort(nl, nname, sizeof(nltype), valcmp);
147
	/*
148
	 *	turn off default functions
149
	 */
150
    for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
151
	Eflag = TRUE;
152
	addlist( Elist , *sp );
153
	eflag = TRUE;
154
	addlist( elist , *sp );
155
    }
156
	/*
157
	 *	get information about mon.out file(s).
158
	 */
159
    do	{
160
	getpfile( gmonname );
161
	if ( *argv != 0 ) {
162
	    gmonname = *argv;
163
	}
164
    } while ( *argv++ != 0 );
165
	/*
166
	 *	how many ticks per second?
167
	 *	if we can't tell, report time in ticks.
168
	 */
169
    if (hz == 0) {
170
	hz = 1;
171
	warnx("time is in ticks, not seconds");
172
    }
173
	/*
174
	 *	dump out a gmon.sum file if requested
175
	 */
176
    if ( sflag ) {
177
	dumpsum( GMONSUM );
178
    }
179
	/*
180
	 *	assign samples to procedures
181
	 */
182
    asgnsamples();
183
	/*
184
	 *	assemble the dynamic profile
185
	 */
186
    timesortnlp = doarcs();
187
	/*
188
	 *	print the dynamic profile
189
	 */
190
    printgprof( timesortnlp );
191
	/*
192
	 *	print the flat profile
193
	 */
194
    printprof();
195
	/*
196
	 *	print the index
197
	 */
198
    printindex();
199
200
    return (0);
201
}
202
203
    /*
204
     *	information from a gmon.out file is in two parts:
205
     *	an array of sampling hits within pc ranges,
206
     *	and the arcs.
207
     */
208
void
209
getpfile(const char *filename)
210
{
211
    FILE		*pfile;
212
    struct rawarc	arc;
213
214
    pfile = openpfile(filename);
215
    readsamples(pfile);
216
	/*
217
	 *	the rest of the file consists of
218
	 *	a bunch of <from,self,count> tuples.
219
	 */
220
    while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
221
#	ifdef DEBUG
222
	    if ( debug & SAMPLEDEBUG ) {
223
		printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
224
			arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
225
	    }
226
#	endif /* DEBUG */
227
	    /*
228
	     *	add this arc
229
	     */
230
	tally( &arc );
231
    }
232
    fclose(pfile);
233
}
234
235
FILE *
236
openpfile(const char *filename)
237
{
238
    struct gmonhdr	tmp;
239
    FILE		*pfile;
240
    int			size;
241
    int			rate;
242
243
    if((pfile = fopen(filename, "r")) == NULL)
244
	err(1, "fopen: %s", filename);
245
    if (fread(&tmp, sizeof(struct gmonhdr), 1, pfile) != 1)
246
	errx(1, "%s: bad gmon header", filename);
247
    if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
248
	 tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt))
249
	errx(1, "%s: incompatible with first gmon file", filename);
250
    gmonhdr = tmp;
251
    if ( gmonhdr.version == GMONVERSION ) {
252
	rate = gmonhdr.profrate;
253
	size = sizeof(struct gmonhdr);
254
    } else {
255
	fseek(pfile, sizeof(struct ophdr), SEEK_SET);
256
	size = sizeof(struct ophdr);
257
	gmonhdr.profrate = rate = hertz();
258
	gmonhdr.version = GMONVERSION;
259
    }
260
    if (hz == 0) {
261
	hz = rate;
262
    } else if (hz != rate)
263
	errx(1, "%s: profile clock rate (%d) incompatible with clock rate "
264
	    "(%ld) in first gmon file", filename, rate, hz);
265
    s_lowpc = (unsigned long) gmonhdr.lpc;
266
    s_highpc = (unsigned long) gmonhdr.hpc;
267
    lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT);
268
    highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT);
269
    sampbytes = gmonhdr.ncnt - size;
270
    nsamples = sampbytes / sizeof (UNIT);
271
#   ifdef DEBUG
272
	if ( debug & SAMPLEDEBUG ) {
273
	    printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n",
274
		gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
275
	    printf( "[openpfile]   s_lowpc 0x%lx   s_highpc 0x%lx\n" ,
276
		s_lowpc , s_highpc );
277
	    printf( "[openpfile]     lowpc 0x%lx     highpc 0x%lx\n" ,
278
		lowpc , highpc );
279
	    printf( "[openpfile] sampbytes %d nsamples %d\n" ,
280
		sampbytes , nsamples );
281
	    printf( "[openpfile] sample rate %ld\n" , hz );
282
	}
283
#   endif /* DEBUG */
284
    return(pfile);
285
}
286
287
void
288
tally(struct rawarc *rawp)
289
{
290
    nltype		*parentp;
291
    nltype		*childp;
292
293
    parentp = nllookup( rawp -> raw_frompc );
294
    childp = nllookup( rawp -> raw_selfpc );
295
    if ( parentp == 0 || childp == 0 )
296
	return;
297
    if ( kflag
298
	 && onlist( kfromlist , parentp -> name )
299
	 && onlist( ktolist , childp -> name ) ) {
300
	return;
301
    }
302
    childp -> ncall += rawp -> raw_count;
303
#   ifdef DEBUG
304
	if ( debug & TALLYDEBUG ) {
305
	    printf( "[tally] arc from %s to %s traversed %ld times\n" ,
306
		    parentp -> name , childp -> name , rawp -> raw_count );
307
	}
308
#   endif /* DEBUG */
309
    addarc( parentp , childp , rawp -> raw_count );
310
}
311
312
/*
313
 * dump out the gmon.sum file
314
 */
315
void
316
dumpsum(const char *sumfile)
317
{
318
    nltype *nlp;
319
    arctype *arcp;
320
    struct rawarc arc;
321
    FILE *sfile;
322
323
    if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL )
324
	err(1, "fopen: %s", sumfile);
325
    /*
326
     * dump the header; use the last header read in
327
     */
328
    if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 )
329
	err(1, "fwrite: %s", sumfile);
330
    /*
331
     * dump the samples
332
     */
333
    if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples)
334
	err(1, "fwrite: %s", sumfile);
335
    /*
336
     * dump the normalized raw arc information
337
     */
338
    for ( nlp = nl ; nlp < npe ; nlp++ ) {
339
	for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
340
	    arc.raw_frompc = arcp -> arc_parentp -> value;
341
	    arc.raw_selfpc = arcp -> arc_childp -> value;
342
	    arc.raw_count = arcp -> arc_count;
343
	    if (fwrite ( &arc , sizeof arc , 1 , sfile ) != 1)
344
	        err(1, "fwrite: %s", sumfile);
345
#	    ifdef DEBUG
346
		if ( debug & SAMPLEDEBUG ) {
347
		    printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
348
			    arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
349
		}
350
#	    endif /* DEBUG */
351
	}
352
    }
353
    fclose( sfile );
354
}
355
356
int
357
valcmp(const void *vp1, const void *vp2)
358
{
359
    const nltype *p1 = vp1;
360
    const nltype *p2 = vp2;
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
void
372
readsamples(FILE *pfile)
373
{
374
    UNIT	sample;
375
    int i;
376
377
    if (samples == 0) {
378
	samples = calloc(sampbytes, sizeof (UNIT));
379
	if (samples == 0)
380
	    errx(1, "No room for %ld sample pc's", sampbytes / sizeof (UNIT));
381
    }
382
    for (i = 0; i < nsamples; i++) {
383
	fread(&sample, sizeof (UNIT), 1, pfile);
384
	if (feof(pfile))
385
		break;
386
	samples[i] += sample;
387
    }
388
    if (i != nsamples)
389
	errx(1, "unexpected EOF after reading %d/%d samples", i, nsamples );
390
}
391
392
/*
393
 *	Assign samples to the procedures to which they belong.
394
 *
395
 *	There are three cases as to where pcl and pch can be
396
 *	with respect to the routine entry addresses svalue0 and svalue1
397
 *	as shown in the following diagram.  overlap computes the
398
 *	distance between the arrows, the fraction of the sample
399
 *	that is to be credited to the routine which starts at svalue0.
400
 *
401
 *	    svalue0                                         svalue1
402
 *	       |                                               |
403
 *	       v                                               v
404
 *
405
 *	       +-----------------------------------------------+
406
 *	       |					       |
407
 *	  |  ->|    |<-		->|         |<-		->|    |<-  |
408
 *	  |         |		  |         |		  |         |
409
 *	  +---------+		  +---------+		  +---------+
410
 *
411
 *	  ^         ^		  ^         ^		  ^         ^
412
 *	  |         |		  |         |		  |         |
413
 *	 pcl       pch		 pcl       pch		 pcl       pch
414
 *
415
 *	For the vax we assert that samples will never fall in the first
416
 *	two bytes of any routine, since that is the entry mask,
417
 *	thus we give call alignentries() to adjust the entry points if
418
 *	the entry mask falls in one bucket but the code for the routine
419
 *	doesn't start until the next bucket.  In conjunction with the
420
 *	alignment of routine addresses, this should allow us to have
421
 *	only one sample for every four bytes of text space and never
422
 *	have any overlap (the two end cases, above).
423
 */
424
void
425
asgnsamples(void)
426
{
427
    int	j;
428
    UNIT		ccnt;
429
    double		time;
430
    unsigned long	pcl, pch;
431
    unsigned long	i;
432
    unsigned long	overlap;
433
    unsigned long	svalue0, svalue1;
434
435
    /* read samples and assign to namelist symbols */
436
    scale = highpc - lowpc;
437
    scale /= nsamples;
438
    alignentries();
439
    for (i = 0, j = 1; i < nsamples; i++) {
440
	ccnt = samples[i];
441
	if (ccnt == 0)
442
		continue;
443
	pcl = lowpc + (unsigned long)(scale * i);
444
	pch = lowpc + (unsigned long)(scale * (i + 1));
445
	time = ccnt;
446
#	ifdef DEBUG
447
	    if ( debug & SAMPLEDEBUG ) {
448
		printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %d\n" ,
449
			pcl , pch , ccnt );
450
	    }
451
#	endif /* DEBUG */
452
	totime += time;
453
	for (j = j - 1; j < nname; j++) {
454
	    svalue0 = nl[j].svalue;
455
	    svalue1 = nl[j+1].svalue;
456
		/*
457
		 *	if high end of tick is below entry address,
458
		 *	go for next tick.
459
		 */
460
	    if (pch < svalue0)
461
		    break;
462
		/*
463
		 *	if low end of tick into next routine,
464
		 *	go for next routine.
465
		 */
466
	    if (pcl >= svalue1)
467
		    continue;
468
	    overlap = min(pch, svalue1) - max(pcl, svalue0);
469
	    if (overlap > 0) {
470
#		ifdef DEBUG
471
		    if (debug & SAMPLEDEBUG) {
472
			printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %ld overlap\n",
473
				nl[j].value/sizeof(UNIT), svalue0, svalue1,
474
				nl[j].name,
475
				overlap * time / scale, overlap);
476
		    }
477
#		endif /* DEBUG */
478
		nl[j].time += overlap * time / scale;
479
	    }
480
	}
481
    }
482
#   ifdef DEBUG
483
	if (debug & SAMPLEDEBUG) {
484
	    printf("[asgnsamples] totime %f\n", totime);
485
	}
486
#   endif /* DEBUG */
487
}
488
489
490
unsigned long
491
min(unsigned long a, unsigned long b)
492
{
493
    if (a<b)
494
	return(a);
495
    return(b);
496
}
497
498
unsigned long
499
max(unsigned long a, unsigned long b)
500
{
501
    if (a>b)
502
	return(a);
503
    return(b);
504
}
505
506
    /*
507
     *	calculate scaled entry point addresses (to save time in asgnsamples),
508
     *	and possibly push the scaled entry points over the entry mask,
509
     *	if it turns out that the entry point is in one bucket and the code
510
     *	for a routine is in the next bucket.
511
     */
512
void
513
alignentries(void)
514
{
515
    struct nl		*nlp;
516
    unsigned long	bucket_of_entry;
517
    unsigned long	bucket_of_code;
518
519
    for (nlp = nl; nlp < npe; nlp++) {
520
	nlp -> svalue = nlp -> value / sizeof(UNIT);
521
	bucket_of_entry = (nlp->svalue - lowpc) / scale;
522
	bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
523
	if (bucket_of_entry < bucket_of_code) {
524
#	    ifdef DEBUG
525
		if (debug & SAMPLEDEBUG) {
526
		    printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n",
527
			    nlp->svalue, nlp->svalue + UNITS_TO_CODE);
528
		}
529
#	    endif /* DEBUG */
530
	    nlp->svalue += UNITS_TO_CODE;
531
	}
532
    }
533
}