gprof.c revision 8874
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
35static 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
41static char sccsid[] = "@(#)gprof.c	8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44#include "gprof.h"
45
46char	*whoami = "gprof";
47
48    /*
49     *	things which get -E excluded by default.
50     */
51char	*defaultEs[] = { "mcount" , "__mcleanup" , 0 };
52
53static struct gmonhdr	gmonhdr;
54static	bool	uflag;
55static int lflag;
56static int Lflag;
57
58main(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     */
228getnfile()
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
259getstrtab(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     */
285getsymtab(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     */
346gettextspace( 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     */
374getpfile(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
402FILE *
403openpfile(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
461tally( 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 */
489dumpsum( 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
538valcmp(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
550readsamples(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 */
610asgnsamples()
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
675unsigned long
676min(a, b)
677    unsigned long a,b;
678{
679    if (a<b)
680	return(a);
681    return(b);
682}
683
684unsigned long
685max(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     */
699alignentries()
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
721bool
722funcsymbol( 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
764done()
765{
766
767    exit(0);
768}
769