xref: /freebsd/usr.bin/gprof/gprof.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)gprof.c	8.1 (Berkeley) 6/6/93";
42 #endif /* not lint */
43 
44 #include "gprof.h"
45 
46 char	*whoami = "gprof";
47 
48     /*
49      *	things which get -E excluded by default.
50      */
51 char	*defaultEs[] = { "mcount" , "__mcleanup" , 0 };
52 
53 static struct gmonhdr	gmonhdr;
54 static	bool	uflag;
55 static int lflag;
56 static int Lflag;
57 
58 main(argc, argv)
59     int argc;
60     char **argv;
61 {
62     char	**sp;
63     nltype	**timesortnlp;
64 
65     --argc;
66     argv++;
67     debug = 0;
68     bflag = TRUE;
69     while ( *argv != 0 && **argv == '-' ) {
70 	(*argv)++;
71 	switch ( **argv ) {
72 	case 'a':
73 	    aflag = TRUE;
74 	    break;
75 	case 'b':
76 	    bflag = FALSE;
77 	    break;
78 	case 'C':
79 	    Cflag = TRUE;
80 	    cyclethreshold = atoi( *++argv );
81 	    break;
82 	case 'c':
83 #if defined(vax) || defined(tahoe)
84 	    cflag = TRUE;
85 #else
86 	    fprintf(stderr, "gprof: -c isn't supported on this architecture yet\n");
87 	    exit(1);
88 #endif
89 	    break;
90 	case 'd':
91 	    dflag = TRUE;
92 	    setlinebuf(stdout);
93 	    debug |= atoi( *++argv );
94 	    debug |= ANYDEBUG;
95 #	    ifdef DEBUG
96 		printf("[main] debug = %d\n", debug);
97 #	    else not DEBUG
98 		printf("%s: -d ignored\n", whoami);
99 #	    endif DEBUG
100 	    break;
101 	case 'E':
102 	    ++argv;
103 	    addlist( Elist , *argv );
104 	    Eflag = TRUE;
105 	    addlist( elist , *argv );
106 	    eflag = TRUE;
107 	    break;
108 	case 'e':
109 	    addlist( elist , *++argv );
110 	    eflag = TRUE;
111 	    break;
112 	case 'F':
113 	    ++argv;
114 	    addlist( Flist , *argv );
115 	    Fflag = TRUE;
116 	    addlist( flist , *argv );
117 	    fflag = TRUE;
118 	    break;
119 	case 'f':
120 	    addlist( flist , *++argv );
121 	    fflag = TRUE;
122 	    break;
123 	case 'k':
124 	    addlist( kfromlist , *++argv );
125 	    addlist( ktolist , *++argv );
126 	    kflag = TRUE;
127 	    break;
128     case 'l':
129 	    lflag = 1;
130 	    Lflag = 0;
131 	    break;
132     case 'L':
133 	    Lflag = 1;
134 	    lflag = 0;
135 	    break;
136     case 's':
137 	    sflag = TRUE;
138 	    break;
139 	case 'u':
140 	    uflag = TRUE;
141 	    break;
142 	case 'z':
143 	    zflag = TRUE;
144 	    break;
145 	}
146 	argv++;
147     }
148     if ( *argv != 0 ) {
149 	a_outname  = *argv;
150 	argv++;
151     } else {
152 	a_outname  = A_OUTNAME;
153     }
154     if ( *argv != 0 ) {
155 	gmonname = *argv;
156 	argv++;
157     } else {
158 	gmonname = GMONNAME;
159     }
160 	/*
161 	 *	turn off default functions
162 	 */
163     for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
164 	Eflag = TRUE;
165 	addlist( Elist , *sp );
166 	eflag = TRUE;
167 	addlist( elist , *sp );
168     }
169 	/*
170 	 *	get information about a.out file.
171 	 */
172     getnfile();
173 	/*
174 	 *	get information about mon.out file(s).
175 	 */
176     do	{
177 	getpfile( gmonname );
178 	if ( *argv != 0 ) {
179 	    gmonname = *argv;
180 	}
181     } while ( *argv++ != 0 );
182 	/*
183 	 *	how many ticks per second?
184 	 *	if we can't tell, report time in ticks.
185 	 */
186     if (hz == 0) {
187 	hz = 1;
188 	fprintf(stderr, "time is in ticks, not seconds\n");
189     }
190 	/*
191 	 *	dump out a gmon.sum file if requested
192 	 */
193     if ( sflag ) {
194 	dumpsum( GMONSUM );
195     }
196 	/*
197 	 *	assign samples to procedures
198 	 */
199     asgnsamples();
200 	/*
201 	 *	assemble the dynamic profile
202 	 */
203     timesortnlp = doarcs();
204 	/*
205 	 *	print the dynamic profile
206 	 */
207     if(!lflag) {
208 	    printgprof( timesortnlp );
209     }
210 	/*
211 	 *	print the flat profile
212 	 */
213     if(!Lflag) {
214 	    printprof();
215     }
216 	/*
217 	 *	print the index
218 	 */
219     printindex();
220     done();
221 }
222 
223     /*
224      * Set up string and symbol tables from a.out.
225      *	and optionally the text space.
226      * On return symbol table is sorted by value.
227      */
228 getnfile()
229 {
230     FILE	*nfile;
231     int		valcmp();
232 
233     nfile = fopen( a_outname ,"r");
234     if (nfile == NULL) {
235 	perror( a_outname );
236 	done();
237     }
238     fread(&xbuf, 1, sizeof(xbuf), nfile);
239     if (N_BADMAG(xbuf)) {
240 	fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
241 	done();
242     }
243     getstrtab(nfile);
244     getsymtab(nfile);
245     gettextspace( nfile );
246     qsort(nl, nname, sizeof(nltype), valcmp);
247     fclose(nfile);
248 #   ifdef DEBUG
249 	if ( debug & AOUTDEBUG ) {
250 	    register int j;
251 
252 	    for (j = 0; j < nname; j++){
253 		printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
254 	    }
255 	}
256 #   endif DEBUG
257 }
258 
259 getstrtab(nfile)
260     FILE	*nfile;
261 {
262 
263     fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
264     if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
265 	fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
266 		whoami , a_outname );
267 	done();
268     }
269     strtab = calloc(ssiz, 1);
270     if (strtab == NULL) {
271 	fprintf(stderr, "%s: %s: no room for %d bytes of string table\n",
272 		whoami , a_outname , ssiz);
273 	done();
274     }
275     if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
276 	fprintf(stderr, "%s: %s: error reading string table\n",
277 		whoami , a_outname );
278 	done();
279     }
280 }
281 
282     /*
283      * Read in symbol table
284      */
285 getsymtab(nfile)
286     FILE	*nfile;
287 {
288     register long	i;
289     int			askfor;
290     struct nlist	nbuf;
291 
292     /* pass1 - count symbols */
293     fseek(nfile, (long)N_SYMOFF(xbuf), 0);
294     nname = 0;
295     for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
296 	fread(&nbuf, sizeof(nbuf), 1, nfile);
297 	if ( ! funcsymbol( &nbuf ) ) {
298 	    continue;
299 	}
300 	nname++;
301     }
302     if (nname == 0) {
303 	fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
304 	done();
305     }
306     askfor = nname + 1;
307     nl = (nltype *) calloc( askfor , sizeof(nltype) );
308     if (nl == 0) {
309 	fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
310 		whoami, askfor * sizeof(nltype) );
311 	done();
312     }
313 
314     /* pass2 - read symbols */
315     fseek(nfile, (long)N_SYMOFF(xbuf), 0);
316     npe = nl;
317     nname = 0;
318     for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
319 	fread(&nbuf, sizeof(nbuf), 1, nfile);
320 	if ( ! funcsymbol( &nbuf ) ) {
321 #	    ifdef DEBUG
322 		if ( debug & AOUTDEBUG ) {
323 		    printf( "[getsymtab] rejecting: 0x%x %s\n" ,
324 			    nbuf.n_type , strtab + nbuf.n_un.n_strx );
325 		}
326 #	    endif DEBUG
327 	    continue;
328 	}
329 	npe->value = nbuf.n_value;
330 	npe->name = strtab+nbuf.n_un.n_strx;
331 #	ifdef DEBUG
332 	    if ( debug & AOUTDEBUG ) {
333 		printf( "[getsymtab] %d %s 0x%08x\n" ,
334 			nname , npe -> name , npe -> value );
335 	    }
336 #	endif DEBUG
337 	npe++;
338 	nname++;
339     }
340     npe->value = -1;
341 }
342 
343     /*
344      *	read in the text space of an a.out file
345      */
346 gettextspace( nfile )
347     FILE	*nfile;
348 {
349 
350     if ( cflag == 0 ) {
351 	return;
352     }
353     textspace = (u_char *) malloc( xbuf.a_text );
354     if ( textspace == 0 ) {
355 	fprintf( stderr , "%s: ran out room for %d bytes of text space:  " ,
356 			whoami , xbuf.a_text );
357 	fprintf( stderr , "can't do -c\n" );
358 	return;
359     }
360     (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
361     if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
362 	fprintf( stderr , "%s: couldn't read text space:  " , whoami );
363 	fprintf( stderr , "can't do -c\n" );
364 	free( textspace );
365 	textspace = 0;
366 	return;
367     }
368 }
369     /*
370      *	information from a gmon.out file is in two parts:
371      *	an array of sampling hits within pc ranges,
372      *	and the arcs.
373      */
374 getpfile(filename)
375     char *filename;
376 {
377     FILE		*pfile;
378     FILE		*openpfile();
379     struct rawarc	arc;
380 
381     pfile = openpfile(filename);
382     readsamples(pfile);
383 	/*
384 	 *	the rest of the file consists of
385 	 *	a bunch of <from,self,count> tuples.
386 	 */
387     while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
388 #	ifdef DEBUG
389 	    if ( debug & SAMPLEDEBUG ) {
390 		printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
391 			arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
392 	    }
393 #	endif DEBUG
394 	    /*
395 	     *	add this arc
396 	     */
397 	tally( &arc );
398     }
399     fclose(pfile);
400 }
401 
402 FILE *
403 openpfile(filename)
404     char *filename;
405 {
406     struct gmonhdr	tmp;
407     FILE		*pfile;
408     int			size;
409     int			rate;
410 
411     if((pfile = fopen(filename, "r")) == NULL) {
412 	perror(filename);
413 	done();
414     }
415     fread(&tmp, sizeof(struct gmonhdr), 1, pfile);
416     if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
417 	 tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) ) {
418 	fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
419 	done();
420     }
421     gmonhdr = tmp;
422     if ( gmonhdr.version == GMONVERSION ) {
423 	rate = gmonhdr.profrate;
424 	size = sizeof(struct gmonhdr);
425     } else {
426 	fseek(pfile, sizeof(struct ophdr), SEEK_SET);
427 	size = sizeof(struct ophdr);
428 	gmonhdr.profrate = rate = hertz();
429 	gmonhdr.version = GMONVERSION;
430     }
431     if (hz == 0) {
432 	hz = rate;
433     } else if (hz != rate) {
434 	fprintf(stderr,
435 	    "%s: profile clock rate (%d) %s (%d) in first gmon file\n",
436 	    filename, rate, "incompatible with clock rate", hz);
437 	done();
438     }
439     s_lowpc = (unsigned long) gmonhdr.lpc;
440     s_highpc = (unsigned long) gmonhdr.hpc;
441     lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT);
442     highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT);
443     sampbytes = gmonhdr.ncnt - size;
444     nsamples = sampbytes / sizeof (UNIT);
445 #   ifdef DEBUG
446 	if ( debug & SAMPLEDEBUG ) {
447 	    printf( "[openpfile] hdr.lpc 0x%x hdr.hpc 0x%x hdr.ncnt %d\n",
448 		gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
449 	    printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
450 		s_lowpc , s_highpc );
451 	    printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
452 		lowpc , highpc );
453 	    printf( "[openpfile] sampbytes %d nsamples %d\n" ,
454 		sampbytes , nsamples );
455 	    printf( "[openpfile] sample rate %d\n" , hz );
456 	}
457 #   endif DEBUG
458     return(pfile);
459 }
460 
461 tally( rawp )
462     struct rawarc	*rawp;
463 {
464     nltype		*parentp;
465     nltype		*childp;
466 
467     parentp = nllookup( rawp -> raw_frompc );
468     childp = nllookup( rawp -> raw_selfpc );
469     if ( parentp == 0 || childp == 0 )
470 	return;
471     if ( kflag
472 	 && onlist( kfromlist , parentp -> name )
473 	 && onlist( ktolist , childp -> name ) ) {
474 	return;
475     }
476     childp -> ncall += rawp -> raw_count;
477 #   ifdef DEBUG
478 	if ( debug & TALLYDEBUG ) {
479 	    printf( "[tally] arc from %s to %s traversed %d times\n" ,
480 		    parentp -> name , childp -> name , rawp -> raw_count );
481 	}
482 #   endif DEBUG
483     addarc( parentp , childp , rawp -> raw_count );
484 }
485 
486 /*
487  * dump out the gmon.sum file
488  */
489 dumpsum( sumfile )
490     char *sumfile;
491 {
492     register nltype *nlp;
493     register arctype *arcp;
494     struct rawarc arc;
495     FILE *sfile;
496 
497     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
498 	perror( sumfile );
499 	done();
500     }
501     /*
502      * dump the header; use the last header read in
503      */
504     if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) {
505 	perror( sumfile );
506 	done();
507     }
508     /*
509      * dump the samples
510      */
511     if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
512 	perror( sumfile );
513 	done();
514     }
515     /*
516      * dump the normalized raw arc information
517      */
518     for ( nlp = nl ; nlp < npe ; nlp++ ) {
519 	for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
520 	    arc.raw_frompc = arcp -> arc_parentp -> value;
521 	    arc.raw_selfpc = arcp -> arc_childp -> value;
522 	    arc.raw_count = arcp -> arc_count;
523 	    if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
524 		perror( sumfile );
525 		done();
526 	    }
527 #	    ifdef DEBUG
528 		if ( debug & SAMPLEDEBUG ) {
529 		    printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
530 			    arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
531 		}
532 #	    endif DEBUG
533 	}
534     }
535     fclose( sfile );
536 }
537 
538 valcmp(p1, p2)
539     nltype *p1, *p2;
540 {
541     if ( p1 -> value < p2 -> value ) {
542 	return LESSTHAN;
543     }
544     if ( p1 -> value > p2 -> value ) {
545 	return GREATERTHAN;
546     }
547     return EQUALTO;
548 }
549 
550 readsamples(pfile)
551     FILE	*pfile;
552 {
553     register i;
554     UNIT	sample;
555 
556     if (samples == 0) {
557 	samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
558 	if (samples == 0) {
559 	    fprintf( stderr , "%s: No room for %d sample pc's\n",
560 		whoami , sampbytes / sizeof (UNIT));
561 	    done();
562 	}
563     }
564     for (i = 0; i < nsamples; i++) {
565 	fread(&sample, sizeof (UNIT), 1, pfile);
566 	if (feof(pfile))
567 		break;
568 	samples[i] += sample;
569     }
570     if (i != nsamples) {
571 	fprintf(stderr,
572 	    "%s: unexpected EOF after reading %d/%d samples\n",
573 		whoami , --i , nsamples );
574 	done();
575     }
576 }
577 
578 /*
579  *	Assign samples to the procedures to which they belong.
580  *
581  *	There are three cases as to where pcl and pch can be
582  *	with respect to the routine entry addresses svalue0 and svalue1
583  *	as shown in the following diagram.  overlap computes the
584  *	distance between the arrows, the fraction of the sample
585  *	that is to be credited to the routine which starts at svalue0.
586  *
587  *	    svalue0                                         svalue1
588  *	       |                                               |
589  *	       v                                               v
590  *
591  *	       +-----------------------------------------------+
592  *	       |					       |
593  *	  |  ->|    |<-		->|         |<-		->|    |<-  |
594  *	  |         |		  |         |		  |         |
595  *	  +---------+		  +---------+		  +---------+
596  *
597  *	  ^         ^		  ^         ^		  ^         ^
598  *	  |         |		  |         |		  |         |
599  *	 pcl       pch		 pcl       pch		 pcl       pch
600  *
601  *	For the vax we assert that samples will never fall in the first
602  *	two bytes of any routine, since that is the entry mask,
603  *	thus we give call alignentries() to adjust the entry points if
604  *	the entry mask falls in one bucket but the code for the routine
605  *	doesn't start until the next bucket.  In conjunction with the
606  *	alignment of routine addresses, this should allow us to have
607  *	only one sample for every four bytes of text space and never
608  *	have any overlap (the two end cases, above).
609  */
610 asgnsamples()
611 {
612     register int	j;
613     UNIT		ccnt;
614     double		time;
615     unsigned long	pcl, pch;
616     register int	i;
617     unsigned long	overlap;
618     unsigned long	svalue0, svalue1;
619 
620     /* read samples and assign to namelist symbols */
621     scale = highpc - lowpc;
622     scale /= nsamples;
623     alignentries();
624     for (i = 0, j = 1; i < nsamples; i++) {
625 	ccnt = samples[i];
626 	if (ccnt == 0)
627 		continue;
628 	pcl = lowpc + scale * i;
629 	pch = lowpc + scale * (i + 1);
630 	time = ccnt;
631 #	ifdef DEBUG
632 	    if ( debug & SAMPLEDEBUG ) {
633 		printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
634 			pcl , pch , ccnt );
635 	    }
636 #	endif DEBUG
637 	totime += time;
638 	for (j = j - 1; j < nname; j++) {
639 	    svalue0 = nl[j].svalue;
640 	    svalue1 = nl[j+1].svalue;
641 		/*
642 		 *	if high end of tick is below entry address,
643 		 *	go for next tick.
644 		 */
645 	    if (pch < svalue0)
646 		    break;
647 		/*
648 		 *	if low end of tick into next routine,
649 		 *	go for next routine.
650 		 */
651 	    if (pcl >= svalue1)
652 		    continue;
653 	    overlap = min(pch, svalue1) - max(pcl, svalue0);
654 	    if (overlap > 0) {
655 #		ifdef DEBUG
656 		    if (debug & SAMPLEDEBUG) {
657 			printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
658 				nl[j].value/sizeof(UNIT), svalue0, svalue1,
659 				nl[j].name,
660 				overlap * time / scale, overlap);
661 		    }
662 #		endif DEBUG
663 		nl[j].time += overlap * time / scale;
664 	    }
665 	}
666     }
667 #   ifdef DEBUG
668 	if (debug & SAMPLEDEBUG) {
669 	    printf("[asgnsamples] totime %f\n", totime);
670 	}
671 #   endif DEBUG
672 }
673 
674 
675 unsigned long
676 min(a, b)
677     unsigned long a,b;
678 {
679     if (a<b)
680 	return(a);
681     return(b);
682 }
683 
684 unsigned long
685 max(a, b)
686     unsigned long a,b;
687 {
688     if (a>b)
689 	return(a);
690     return(b);
691 }
692 
693     /*
694      *	calculate scaled entry point addresses (to save time in asgnsamples),
695      *	and possibly push the scaled entry points over the entry mask,
696      *	if it turns out that the entry point is in one bucket and the code
697      *	for a routine is in the next bucket.
698      */
699 alignentries()
700 {
701     register struct nl	*nlp;
702     unsigned long	bucket_of_entry;
703     unsigned long	bucket_of_code;
704 
705     for (nlp = nl; nlp < npe; nlp++) {
706 	nlp -> svalue = nlp -> value / sizeof(UNIT);
707 	bucket_of_entry = (nlp->svalue - lowpc) / scale;
708 	bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
709 	if (bucket_of_entry < bucket_of_code) {
710 #	    ifdef DEBUG
711 		if (debug & SAMPLEDEBUG) {
712 		    printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
713 			    nlp->svalue, nlp->svalue + UNITS_TO_CODE);
714 		}
715 #	    endif DEBUG
716 	    nlp->svalue += UNITS_TO_CODE;
717 	}
718     }
719 }
720 
721 bool
722 funcsymbol( nlistp )
723     struct nlist	*nlistp;
724 {
725     char	*name, c;
726 
727 	/*
728 	 *	must be a text symbol,
729 	 *	and static text symbols don't qualify if aflag set.
730 	 */
731     if ( ! (  ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
732 	   || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
733 	return FALSE;
734     }
735 	/*
736 	 *	name must start with an underscore if uflag is set.
737 	 *	can't have any `funny' characters in name,
738 	 *	where `funny' includes	`.', .o file names
739 	 *			and	`$', pascal labels.
740 	 *	need to make an exception for sparc .mul & co.
741 	 *	perhaps we should just drop this code entirely...
742 	 */
743     name = strtab + nlistp -> n_un.n_strx;
744     if ( uflag && *name != '_' )
745 	return FALSE;
746 #ifdef sparc
747     if ( *name == '.' ) {
748 	char *p = name + 1;
749 	if ( *p == 'u' )
750 	    p++;
751 	if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
752 	     strcmp ( p, "rem" ) == 0 )
753 		return TRUE;
754     }
755 #endif
756     while ( c = *name++ ) {
757 	if ( c == '.' || c == '$' ) {
758 	    return FALSE;
759 	}
760     }
761     return TRUE;
762 }
763 
764 done()
765 {
766 
767     exit(0);
768 }
769