xref: /freebsd/usr.bin/gprof/gprof.c (revision ce834215a70ff69e7e222827437116eee2f9ac6f)
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 const 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 #if 0
42 static char sccsid[] = "@(#)gprof.c	8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45 	"$Id: gprof.c,v 1.5 1997/07/10 06:45:00 charnier Exp $";
46 #endif /* not lint */
47 
48 #include <err.h>
49 #include "gprof.h"
50 
51     /*
52      *	things which get -E excluded by default.
53      */
54 char	*defaultEs[] = { "mcount" , "__mcleanup" , 0 };
55 
56 static struct gmonhdr	gmonhdr;
57 static	bool	uflag;
58 static int lflag;
59 static int Lflag;
60 
61 main(argc, argv)
62     int argc;
63     char **argv;
64 {
65     char	**sp;
66     nltype	**timesortnlp;
67 
68     --argc;
69     argv++;
70     debug = 0;
71     bflag = TRUE;
72     while ( *argv != 0 && **argv == '-' ) {
73 	(*argv)++;
74 	switch ( **argv ) {
75 	case 'a':
76 	    aflag = TRUE;
77 	    break;
78 	case 'b':
79 	    bflag = FALSE;
80 	    break;
81 	case 'C':
82 	    Cflag = TRUE;
83 	    cyclethreshold = atoi( *++argv );
84 	    break;
85 	case 'c':
86 #if defined(vax) || defined(tahoe)
87 	    cflag = TRUE;
88 #else
89 	    errx(1, "-c isn't supported on this architecture yet");
90 #endif
91 	    break;
92 	case 'd':
93 	    dflag = TRUE;
94 	    setlinebuf(stdout);
95 	    debug |= atoi( *++argv );
96 	    debug |= ANYDEBUG;
97 #	    ifdef DEBUG
98 		printf("[main] debug = %d\n", debug);
99 #	    else not DEBUG
100 		printf("gprof: -d ignored\n");
101 #	    endif DEBUG
102 	    break;
103 	case 'E':
104 	    ++argv;
105 	    addlist( Elist , *argv );
106 	    Eflag = TRUE;
107 	    addlist( elist , *argv );
108 	    eflag = TRUE;
109 	    break;
110 	case 'e':
111 	    addlist( elist , *++argv );
112 	    eflag = TRUE;
113 	    break;
114 	case 'F':
115 	    ++argv;
116 	    addlist( Flist , *argv );
117 	    Fflag = TRUE;
118 	    addlist( flist , *argv );
119 	    fflag = TRUE;
120 	    break;
121 	case 'f':
122 	    addlist( flist , *++argv );
123 	    fflag = TRUE;
124 	    break;
125 	case 'k':
126 	    addlist( kfromlist , *++argv );
127 	    addlist( ktolist , *++argv );
128 	    kflag = TRUE;
129 	    break;
130     case 'l':
131 	    lflag = 1;
132 	    Lflag = 0;
133 	    break;
134     case 'L':
135 	    Lflag = 1;
136 	    lflag = 0;
137 	    break;
138     case 's':
139 	    sflag = TRUE;
140 	    break;
141 	case 'u':
142 	    uflag = TRUE;
143 	    break;
144 	case 'z':
145 	    zflag = TRUE;
146 	    break;
147 	}
148 	argv++;
149     }
150     if ( *argv != 0 ) {
151 	a_outname  = *argv;
152 	argv++;
153     } else {
154 	a_outname  = A_OUTNAME;
155     }
156     if ( *argv != 0 ) {
157 	gmonname = *argv;
158 	argv++;
159     } else {
160 	gmonname = GMONNAME;
161     }
162 	/*
163 	 *	turn off default functions
164 	 */
165     for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
166 	Eflag = TRUE;
167 	addlist( Elist , *sp );
168 	eflag = TRUE;
169 	addlist( elist , *sp );
170     }
171 	/*
172 	 *	get information about a.out file.
173 	 */
174     getnfile();
175 	/*
176 	 *	get information about mon.out file(s).
177 	 */
178     do	{
179 	getpfile( gmonname );
180 	if ( *argv != 0 ) {
181 	    gmonname = *argv;
182 	}
183     } while ( *argv++ != 0 );
184 	/*
185 	 *	how many ticks per second?
186 	 *	if we can't tell, report time in ticks.
187 	 */
188     if (hz == 0) {
189 	hz = 1;
190 	fprintf(stderr, "time is in ticks, not seconds\n");
191     }
192 	/*
193 	 *	dump out a gmon.sum file if requested
194 	 */
195     if ( sflag ) {
196 	dumpsum( GMONSUM );
197     }
198 	/*
199 	 *	assign samples to procedures
200 	 */
201     asgnsamples();
202 	/*
203 	 *	assemble the dynamic profile
204 	 */
205     timesortnlp = doarcs();
206 	/*
207 	 *	print the dynamic profile
208 	 */
209     if(!lflag) {
210 	    printgprof( timesortnlp );
211     }
212 	/*
213 	 *	print the flat profile
214 	 */
215     if(!Lflag) {
216 	    printprof();
217     }
218 	/*
219 	 *	print the index
220 	 */
221     printindex();
222     done();
223 }
224 
225     /*
226      * Set up string and symbol tables from a.out.
227      *	and optionally the text space.
228      * On return symbol table is sorted by value.
229      */
230 getnfile()
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 
261 getstrtab(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      */
284 getsymtab(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      */
344 gettextspace( 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      */
370 getpfile(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 
398 FILE *
399 openpfile(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 
457 tally( 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  */
485 dumpsum( 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 
534 valcmp(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 
546 readsamples(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  */
603 asgnsamples()
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 
668 unsigned long
669 min(a, b)
670     unsigned long a,b;
671 {
672     if (a<b)
673 	return(a);
674     return(b);
675 }
676 
677 unsigned long
678 max(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      */
692 alignentries()
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 
714 bool
715 funcsymbol( 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' includes	`.', .o file names
732 	 *			and	`$', pascal labels.
733 	 *	need to make an exception for sparc .mul & co.
734 	 *	perhaps we should just drop this code entirely...
735 	 */
736     name = strtab + nlistp -> n_un.n_strx;
737     if ( uflag && *name != '_' )
738 	return FALSE;
739 #ifdef sparc
740     if ( *name == '.' ) {
741 	char *p = name + 1;
742 	if ( *p == 'u' )
743 	    p++;
744 	if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
745 	     strcmp ( p, "rem" ) == 0 )
746 		return TRUE;
747     }
748 #endif
749     while ( c = *name++ ) {
750 	if ( c == '.' || c == '$' ) {
751 	    return FALSE;
752 	}
753     }
754     return TRUE;
755 }
756 
757 done()
758 {
759 
760     exit(0);
761 }
762