gprof.c revision 38194
144603Sdcs/*
244603Sdcs * Copyright (c) 1983, 1993
344603Sdcs *	The Regents of the University of California.  All rights reserved.
444603Sdcs *
544603Sdcs * Redistribution and use in source and binary forms, with or without
644603Sdcs * modification, are permitted provided that the following conditions
744603Sdcs * are met:
844603Sdcs * 1. Redistributions of source code must retain the above copyright
944603Sdcs *    notice, this list of conditions and the following disclaimer.
1044603Sdcs * 2. Redistributions in binary form must reproduce the above copyright
1144603Sdcs *    notice, this list of conditions and the following disclaimer in the
1244603Sdcs *    documentation and/or other materials provided with the distribution.
1344603Sdcs * 3. All advertising materials mentioning features or use of this software
1444603Sdcs *    must display the following acknowledgement:
1544603Sdcs *	This product includes software developed by the University of
1644603Sdcs *	California, Berkeley and its contributors.
1744603Sdcs * 4. Neither the name of the University nor the names of its contributors
1844603Sdcs *    may be used to endorse or promote products derived from this software
1944603Sdcs *    without specific prior written permission.
2044603Sdcs *
2144603Sdcs * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2244603Sdcs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2344603Sdcs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2444603Sdcs * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2550477Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2644603Sdcs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2761694Sdcs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2861694Sdcs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2987636Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3087636Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3161694Sdcs * SUCH DAMAGE.
3261694Sdcs */
3361694Sdcs
3461694Sdcs#ifndef lint
3561694Sdcsstatic const char copyright[] =
3661694Sdcs"@(#) Copyright (c) 1983, 1993\n\
3761694Sdcs	The Regents of the University of California.  All rights reserved.\n";
3861379Sdcs#endif /* not lint */
3977444Sdcs
4077444Sdcs#ifndef lint
4177444Sdcs#if 0
4244603Sdcsstatic char sccsid[] = "@(#)gprof.c	8.1 (Berkeley) 6/6/93";
43241361Sdteske#endif
4444603Sdcsstatic const char rcsid[] =
4565883Sdcs	"$Id: gprof.c,v 1.6 1997/07/15 08:04:40 charnier Exp $";
4661376Sdcs#endif /* not lint */
47228985Spluknet
48229881Spluknet#include <err.h>
49229881Spluknet#include "gprof.h"
50229881Spluknet
51229881Spluknet    /*
52229881Spluknet     *	things which get -E excluded by default.
53229881Spluknet     */
54229881Spluknetchar	*defaultEs[] = { "mcount" , "__mcleanup" , 0 };
55229881Spluknet
56229881Spluknetstatic struct gmonhdr	gmonhdr;
57228985Spluknetstatic	bool	uflag;
58229881Spluknetstatic int lflag;
59229881Spluknetstatic int Lflag;
60229881Spluknet
61229881Spluknetmain(argc, argv)
62228985Spluknet    int argc;
63242667Sdteske    char **argv;
64242667Sdteske{
65242667Sdteske    char	**sp;
66242667Sdteske    nltype	**timesortnlp;
67242667Sdteske
68242667Sdteske    --argc;
69228985Spluknet    argv++;
70228985Spluknet    debug = 0;
7165883Sdcs    bflag = TRUE;
7265949Sdcs    while ( *argv != 0 && **argv == '-' ) {
7361376Sdcs	(*argv)++;
74241361Sdteske	switch ( **argv ) {
75241361Sdteske	case 'a':
76241361Sdteske	    aflag = TRUE;
77241361Sdteske	    break;
78241361Sdteske	case 'b':
79241361Sdteske	    bflag = FALSE;
8065630Sdcs	    break;
8165883Sdcs	case 'C':
8265883Sdcs	    Cflag = TRUE;
8365630Sdcs	    cyclethreshold = atoi( *++argv );
8465630Sdcs	    break;
8565630Sdcs	case 'c':
8666871Sdcs#if defined(vax) || defined(tahoe)
87228985Spluknet	    cflag = TRUE;
8866871Sdcs#else
8965938Sdcs	    errx(1, "-c isn't supported on this architecture yet");
9066871Sdcs#endif
9166871Sdcs	    break;
92228985Spluknet	case 'd':
9366346Sdcs	    dflag = TRUE;
9465630Sdcs	    setlinebuf(stdout);
9565630Sdcs	    debug |= atoi( *++argv );
9666871Sdcs	    debug |= ANYDEBUG;
97228985Spluknet#	    ifdef DEBUG
9866871Sdcs		printf("[main] debug = %d\n", debug);
9965938Sdcs#	    else not DEBUG
10066871Sdcs		printf("gprof: -d ignored\n");
10166871Sdcs#	    endif DEBUG
102228985Spluknet	    break;
10366346Sdcs	case 'E':
10465630Sdcs	    ++argv;
10565945Sdcs	    addlist( Elist , *argv );
10665621Sdcs	    Eflag = TRUE;
10765621Sdcs	    addlist( elist , *argv );
10865621Sdcs	    eflag = TRUE;
109228985Spluknet	    break;
110228985Spluknet	case 'e':
111228985Spluknet	    addlist( elist , *++argv );
112228985Spluknet	    eflag = TRUE;
11365621Sdcs	    break;
11465949Sdcs	case 'F':
11565630Sdcs	    ++argv;
11665945Sdcs	    addlist( Flist , *argv );
11765621Sdcs	    Fflag = TRUE;
11865621Sdcs	    addlist( flist , *argv );
11965621Sdcs	    fflag = TRUE;
12065621Sdcs	    break;
12165883Sdcs	case 'f':
12265621Sdcs	    addlist( flist , *++argv );
12361376Sdcs	    fflag = TRUE;
12465883Sdcs	    break;
12561376Sdcs	case 'k':
12661376Sdcs	    addlist( kfromlist , *++argv );
127222417Sjulian	    addlist( ktolist , *++argv );
12853672Sdcs	    kflag = TRUE;
12944603Sdcs	    break;
13044603Sdcs    case 'l':
13144603Sdcs	    lflag = 1;
13244603Sdcs	    Lflag = 0;
13344603Sdcs	    break;
13444603Sdcs    case 'L':
13544603Sdcs	    Lflag = 1;
13644603Sdcs	    lflag = 0;
13744603Sdcs	    break;
13897201Sgordon    case 's':
13944603Sdcs	    sflag = TRUE;
14044603Sdcs	    break;
14144603Sdcs	case 'u':
14244603Sdcs	    uflag = TRUE;
14344603Sdcs	    break;
14444603Sdcs	case 'z':
14544603Sdcs	    zflag = TRUE;
14644603Sdcs	    break;
14747198Sdcs	}
14847198Sdcs	argv++;
14947198Sdcs    }
15047198Sdcs    if ( *argv != 0 ) {
15147198Sdcs	a_outname  = *argv;
15247198Sdcs	argv++;
15347198Sdcs    } else {
15447198Sdcs	a_outname  = A_OUTNAME;
15547198Sdcs    }
15697201Sgordon    if ( *argv != 0 ) {
15747198Sdcs	gmonname = *argv;
15847198Sdcs	argv++;
15947198Sdcs    } else {
16044603Sdcs	gmonname = GMONNAME;
16144603Sdcs    }
16244603Sdcs	/*
16344603Sdcs	 *	turn off default functions
16444603Sdcs	 */
16544603Sdcs    for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
166186789Sluigi	Eflag = TRUE;
16744603Sdcs	addlist( Elist , *sp );
16844603Sdcs	eflag = TRUE;
16944603Sdcs	addlist( elist , *sp );
17044603Sdcs    }
17144603Sdcs	/*
17244603Sdcs	 *	get information about a.out file.
17344603Sdcs	 */
17444603Sdcs    getnfile();
17544603Sdcs	/*
17644603Sdcs	 *	get information about mon.out file(s).
17744603Sdcs	 */
17844603Sdcs    do	{
17944603Sdcs	getpfile( gmonname );
180186789Sluigi	if ( *argv != 0 ) {
181186789Sluigi	    gmonname = *argv;
18246005Sdcs	}
183186789Sluigi    } while ( *argv++ != 0 );
184186789Sluigi	/*
185186789Sluigi	 *	how many ticks per second?
186186789Sluigi	 *	if we can't tell, report time in ticks.
18746005Sdcs	 */
18846005Sdcs    if (hz == 0) {
189186789Sluigi	hz = 1;
19046005Sdcs	fprintf(stderr, "time is in ticks, not seconds\n");
191186789Sluigi    }
19246005Sdcs	/*
193186789Sluigi	 *	dump out a gmon.sum file if requested
19446005Sdcs	 */
19544603Sdcs    if ( sflag ) {
19644603Sdcs	dumpsum( GMONSUM );
19744603Sdcs    }
19844603Sdcs	/*
199186789Sluigi	 *	assign samples to procedures
20044603Sdcs	 */
20144603Sdcs    asgnsamples();
20244603Sdcs	/*
20344603Sdcs	 *	assemble the dynamic profile
20444603Sdcs	 */
20544603Sdcs    timesortnlp = doarcs();
20644603Sdcs	/*
20744603Sdcs	 *	print the dynamic profile
20865949Sdcs	 */
20965949Sdcs    if(!lflag) {
21065949Sdcs	    printgprof( timesortnlp );
21165949Sdcs    }
21265949Sdcs	/*
21365949Sdcs	 *	print the flat profile
21465949Sdcs	 */
21565949Sdcs    if(!Lflag) {
21665949Sdcs	    printprof();
21765949Sdcs    }
21865949Sdcs	/*
21965949Sdcs	 *	print the index
22065949Sdcs	 */
22165949Sdcs    printindex();
22265949Sdcs    done();
22365949Sdcs}
22465949Sdcs
22565949Sdcs    /*
22644603Sdcs     * Set up string and symbol tables from a.out.
22747198Sdcs     *	and optionally the text space.
228     * On return symbol table is sorted by value.
229     */
230getnfile()
231{
232    FILE	*nfile;
233    int		valcmp();
234
235    nfile = fopen( a_outname ,"r");
236    if (nfile == NULL) {
237	perror( a_outname );
238	done();
239    }
240    fread(&xbuf, 1, sizeof(xbuf), nfile);
241    if (N_BADMAG(xbuf)) {
242	warnx("%s: bad format", a_outname );
243	done();
244    }
245    getstrtab(nfile);
246    getsymtab(nfile);
247    gettextspace( nfile );
248    qsort(nl, nname, sizeof(nltype), valcmp);
249    fclose(nfile);
250#   ifdef DEBUG
251	if ( debug & AOUTDEBUG ) {
252	    register int j;
253
254	    for (j = 0; j < nname; j++){
255		printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
256	    }
257	}
258#   endif DEBUG
259}
260
261getstrtab(nfile)
262    FILE	*nfile;
263{
264
265    fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
266    if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
267	warnx("%s: no string table (old format?)" , a_outname );
268	done();
269    }
270    strtab = calloc(ssiz, 1);
271    if (strtab == NULL) {
272	warnx("%s: no room for %d bytes of string table", a_outname , ssiz);
273	done();
274    }
275    if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
276	warnx("%s: error reading string table", a_outname );
277	done();
278    }
279}
280
281    /*
282     * Read in symbol table
283     */
284getsymtab(nfile)
285    FILE	*nfile;
286{
287    register long	i;
288    int			askfor;
289    struct nlist	nbuf;
290
291    /* pass1 - count symbols */
292    fseek(nfile, (long)N_SYMOFF(xbuf), 0);
293    nname = 0;
294    for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
295	fread(&nbuf, sizeof(nbuf), 1, nfile);
296	if ( ! funcsymbol( &nbuf ) ) {
297	    continue;
298	}
299	nname++;
300    }
301    if (nname == 0) {
302	warnx("%s: no symbols", a_outname );
303	done();
304    }
305    askfor = nname + 1;
306    nl = (nltype *) calloc( askfor , sizeof(nltype) );
307    if (nl == 0) {
308	warnx("no room for %d bytes of symbol table", askfor * sizeof(nltype) );
309	done();
310    }
311
312    /* pass2 - read symbols */
313    fseek(nfile, (long)N_SYMOFF(xbuf), 0);
314    npe = nl;
315    nname = 0;
316    for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
317	fread(&nbuf, sizeof(nbuf), 1, nfile);
318	if ( ! funcsymbol( &nbuf ) ) {
319#	    ifdef DEBUG
320		if ( debug & AOUTDEBUG ) {
321		    printf( "[getsymtab] rejecting: 0x%x %s\n" ,
322			    nbuf.n_type , strtab + nbuf.n_un.n_strx );
323		}
324#	    endif DEBUG
325	    continue;
326	}
327	npe->value = nbuf.n_value;
328	npe->name = strtab+nbuf.n_un.n_strx;
329#	ifdef DEBUG
330	    if ( debug & AOUTDEBUG ) {
331		printf( "[getsymtab] %d %s 0x%08x\n" ,
332			nname , npe -> name , npe -> value );
333	    }
334#	endif DEBUG
335	npe++;
336	nname++;
337    }
338    npe->value = -1;
339}
340
341    /*
342     *	read in the text space of an a.out file
343     */
344gettextspace( nfile )
345    FILE	*nfile;
346{
347
348    if ( cflag == 0 ) {
349	return;
350    }
351    textspace = (u_char *) malloc( xbuf.a_text );
352    if ( textspace == 0 ) {
353	warnx("ran out room for %d bytes of text space: can't do -c" ,
354		  xbuf.a_text );
355	return;
356    }
357    (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
358    if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
359	warnx("couldn't read text space: can't do -c");
360	free( textspace );
361	textspace = 0;
362	return;
363    }
364}
365    /*
366     *	information from a gmon.out file is in two parts:
367     *	an array of sampling hits within pc ranges,
368     *	and the arcs.
369     */
370getpfile(filename)
371    char *filename;
372{
373    FILE		*pfile;
374    FILE		*openpfile();
375    struct rawarc	arc;
376
377    pfile = openpfile(filename);
378    readsamples(pfile);
379	/*
380	 *	the rest of the file consists of
381	 *	a bunch of <from,self,count> tuples.
382	 */
383    while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
384#	ifdef DEBUG
385	    if ( debug & SAMPLEDEBUG ) {
386		printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
387			arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
388	    }
389#	endif DEBUG
390	    /*
391	     *	add this arc
392	     */
393	tally( &arc );
394    }
395    fclose(pfile);
396}
397
398FILE *
399openpfile(filename)
400    char *filename;
401{
402    struct gmonhdr	tmp;
403    FILE		*pfile;
404    int			size;
405    int			rate;
406
407    if((pfile = fopen(filename, "r")) == NULL) {
408	perror(filename);
409	done();
410    }
411    fread(&tmp, sizeof(struct gmonhdr), 1, pfile);
412    if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
413	 tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) ) {
414	warnx("%s: incompatible with first gmon file", filename);
415	done();
416    }
417    gmonhdr = tmp;
418    if ( gmonhdr.version == GMONVERSION ) {
419	rate = gmonhdr.profrate;
420	size = sizeof(struct gmonhdr);
421    } else {
422	fseek(pfile, sizeof(struct ophdr), SEEK_SET);
423	size = sizeof(struct ophdr);
424	gmonhdr.profrate = rate = hertz();
425	gmonhdr.version = GMONVERSION;
426    }
427    if (hz == 0) {
428	hz = rate;
429    } else if (hz != rate) {
430	fprintf(stderr,
431	    "%s: profile clock rate (%d) %s (%d) in first gmon file\n",
432	    filename, rate, "incompatible with clock rate", hz);
433	done();
434    }
435    s_lowpc = (unsigned long) gmonhdr.lpc;
436    s_highpc = (unsigned long) gmonhdr.hpc;
437    lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT);
438    highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT);
439    sampbytes = gmonhdr.ncnt - size;
440    nsamples = sampbytes / sizeof (UNIT);
441#   ifdef DEBUG
442	if ( debug & SAMPLEDEBUG ) {
443	    printf( "[openpfile] hdr.lpc 0x%x hdr.hpc 0x%x hdr.ncnt %d\n",
444		gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
445	    printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
446		s_lowpc , s_highpc );
447	    printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
448		lowpc , highpc );
449	    printf( "[openpfile] sampbytes %d nsamples %d\n" ,
450		sampbytes , nsamples );
451	    printf( "[openpfile] sample rate %d\n" , hz );
452	}
453#   endif DEBUG
454    return(pfile);
455}
456
457tally( rawp )
458    struct rawarc	*rawp;
459{
460    nltype		*parentp;
461    nltype		*childp;
462
463    parentp = nllookup( rawp -> raw_frompc );
464    childp = nllookup( rawp -> raw_selfpc );
465    if ( parentp == 0 || childp == 0 )
466	return;
467    if ( kflag
468	 && onlist( kfromlist , parentp -> name )
469	 && onlist( ktolist , childp -> name ) ) {
470	return;
471    }
472    childp -> ncall += rawp -> raw_count;
473#   ifdef DEBUG
474	if ( debug & TALLYDEBUG ) {
475	    printf( "[tally] arc from %s to %s traversed %d times\n" ,
476		    parentp -> name , childp -> name , rawp -> raw_count );
477	}
478#   endif DEBUG
479    addarc( parentp , childp , rawp -> raw_count );
480}
481
482/*
483 * dump out the gmon.sum file
484 */
485dumpsum( sumfile )
486    char *sumfile;
487{
488    register nltype *nlp;
489    register arctype *arcp;
490    struct rawarc arc;
491    FILE *sfile;
492
493    if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
494	perror( sumfile );
495	done();
496    }
497    /*
498     * dump the header; use the last header read in
499     */
500    if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) {
501	perror( sumfile );
502	done();
503    }
504    /*
505     * dump the samples
506     */
507    if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
508	perror( sumfile );
509	done();
510    }
511    /*
512     * dump the normalized raw arc information
513     */
514    for ( nlp = nl ; nlp < npe ; nlp++ ) {
515	for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
516	    arc.raw_frompc = arcp -> arc_parentp -> value;
517	    arc.raw_selfpc = arcp -> arc_childp -> value;
518	    arc.raw_count = arcp -> arc_count;
519	    if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
520		perror( sumfile );
521		done();
522	    }
523#	    ifdef DEBUG
524		if ( debug & SAMPLEDEBUG ) {
525		    printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
526			    arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
527		}
528#	    endif DEBUG
529	}
530    }
531    fclose( sfile );
532}
533
534valcmp(p1, p2)
535    nltype *p1, *p2;
536{
537    if ( p1 -> value < p2 -> value ) {
538	return LESSTHAN;
539    }
540    if ( p1 -> value > p2 -> value ) {
541	return GREATERTHAN;
542    }
543    return EQUALTO;
544}
545
546readsamples(pfile)
547    FILE	*pfile;
548{
549    register i;
550    UNIT	sample;
551
552    if (samples == 0) {
553	samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
554	if (samples == 0) {
555	    warnx("no room for %d sample pc's", sampbytes / sizeof (UNIT));
556	    done();
557	}
558    }
559    for (i = 0; i < nsamples; i++) {
560	fread(&sample, sizeof (UNIT), 1, pfile);
561	if (feof(pfile))
562		break;
563	samples[i] += sample;
564    }
565    if (i != nsamples) {
566	warnx("unexpected EOF after reading %d/%d samples", --i , nsamples );
567	done();
568    }
569}
570
571/*
572 *	Assign samples to the procedures to which they belong.
573 *
574 *	There are three cases as to where pcl and pch can be
575 *	with respect to the routine entry addresses svalue0 and svalue1
576 *	as shown in the following diagram.  overlap computes the
577 *	distance between the arrows, the fraction of the sample
578 *	that is to be credited to the routine which starts at svalue0.
579 *
580 *	    svalue0                                         svalue1
581 *	       |                                               |
582 *	       v                                               v
583 *
584 *	       +-----------------------------------------------+
585 *	       |					       |
586 *	  |  ->|    |<-		->|         |<-		->|    |<-  |
587 *	  |         |		  |         |		  |         |
588 *	  +---------+		  +---------+		  +---------+
589 *
590 *	  ^         ^		  ^         ^		  ^         ^
591 *	  |         |		  |         |		  |         |
592 *	 pcl       pch		 pcl       pch		 pcl       pch
593 *
594 *	For the vax we assert that samples will never fall in the first
595 *	two bytes of any routine, since that is the entry mask,
596 *	thus we give call alignentries() to adjust the entry points if
597 *	the entry mask falls in one bucket but the code for the routine
598 *	doesn't start until the next bucket.  In conjunction with the
599 *	alignment of routine addresses, this should allow us to have
600 *	only one sample for every four bytes of text space and never
601 *	have any overlap (the two end cases, above).
602 */
603asgnsamples()
604{
605    register int	j;
606    UNIT		ccnt;
607    double		time;
608    unsigned long	pcl, pch;
609    register int	i;
610    unsigned long	overlap;
611    unsigned long	svalue0, svalue1;
612
613    /* read samples and assign to namelist symbols */
614    scale = highpc - lowpc;
615    scale /= nsamples;
616    alignentries();
617    for (i = 0, j = 1; i < nsamples; i++) {
618	ccnt = samples[i];
619	if (ccnt == 0)
620		continue;
621	pcl = lowpc + scale * i;
622	pch = lowpc + scale * (i + 1);
623	time = ccnt;
624#	ifdef DEBUG
625	    if ( debug & SAMPLEDEBUG ) {
626		printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
627			pcl , pch , ccnt );
628	    }
629#	endif DEBUG
630	totime += time;
631	for (j = j - 1; j < nname; j++) {
632	    svalue0 = nl[j].svalue;
633	    svalue1 = nl[j+1].svalue;
634		/*
635		 *	if high end of tick is below entry address,
636		 *	go for next tick.
637		 */
638	    if (pch < svalue0)
639		    break;
640		/*
641		 *	if low end of tick into next routine,
642		 *	go for next routine.
643		 */
644	    if (pcl >= svalue1)
645		    continue;
646	    overlap = min(pch, svalue1) - max(pcl, svalue0);
647	    if (overlap > 0) {
648#		ifdef DEBUG
649		    if (debug & SAMPLEDEBUG) {
650			printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
651				nl[j].value/sizeof(UNIT), svalue0, svalue1,
652				nl[j].name,
653				overlap * time / scale, overlap);
654		    }
655#		endif DEBUG
656		nl[j].time += overlap * time / scale;
657	    }
658	}
659    }
660#   ifdef DEBUG
661	if (debug & SAMPLEDEBUG) {
662	    printf("[asgnsamples] totime %f\n", totime);
663	}
664#   endif DEBUG
665}
666
667
668unsigned long
669min(a, b)
670    unsigned long a,b;
671{
672    if (a<b)
673	return(a);
674    return(b);
675}
676
677unsigned long
678max(a, b)
679    unsigned long a,b;
680{
681    if (a>b)
682	return(a);
683    return(b);
684}
685
686    /*
687     *	calculate scaled entry point addresses (to save time in asgnsamples),
688     *	and possibly push the scaled entry points over the entry mask,
689     *	if it turns out that the entry point is in one bucket and the code
690     *	for a routine is in the next bucket.
691     */
692alignentries()
693{
694    register struct nl	*nlp;
695    unsigned long	bucket_of_entry;
696    unsigned long	bucket_of_code;
697
698    for (nlp = nl; nlp < npe; nlp++) {
699	nlp -> svalue = nlp -> value / sizeof(UNIT);
700	bucket_of_entry = (nlp->svalue - lowpc) / scale;
701	bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
702	if (bucket_of_entry < bucket_of_code) {
703#	    ifdef DEBUG
704		if (debug & SAMPLEDEBUG) {
705		    printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
706			    nlp->svalue, nlp->svalue + UNITS_TO_CODE);
707		}
708#	    endif DEBUG
709	    nlp->svalue += UNITS_TO_CODE;
710	}
711    }
712}
713
714bool
715funcsymbol( nlistp )
716    struct nlist	*nlistp;
717{
718    char	*name, c;
719
720	/*
721	 *	must be a text symbol,
722	 *	and static text symbols don't qualify if aflag set.
723	 */
724    if ( ! (  ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
725	   || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
726	return FALSE;
727    }
728	/*
729	 *	name must start with an underscore if uflag is set.
730	 *	can't have any `funny' characters in name,
731	 *	where `funny' means `.' (.o file names)
732	 *	need to make an exception for sparc .mul & co.
733	 *	perhaps we should just drop this code entirely...
734	 */
735    name = strtab + nlistp -> n_un.n_strx;
736    if ( uflag && *name != '_' )
737	return FALSE;
738#ifdef sparc
739    if ( *name == '.' ) {
740	char *p = name + 1;
741	if ( *p == 'u' )
742	    p++;
743	if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
744	     strcmp ( p, "rem" ) == 0 )
745		return TRUE;
746    }
747#endif
748    while ( c = *name++ ) {
749	if ( c == '.' ) {
750	    return FALSE;
751	}
752    }
753    return TRUE;
754}
755
756done()
757{
758
759    exit(0);
760}
761