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