gmon-sol2.c revision 52284
150397Sobrien/*-
250397Sobrien * Copyright (c) 1991 The Regents of the University of California.
350397Sobrien * All rights reserved.
450397Sobrien *
550397Sobrien * Redistribution and use in source and binary forms, with or without
650397Sobrien * modification, are permitted provided that the following conditions
750397Sobrien * are met:
850397Sobrien * 1. Redistributions of source code must retain the above copyright
950397Sobrien *    notice, this list of conditions and the following disclaimer.
1050397Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1150397Sobrien *    notice, this list of conditions and the following disclaimer in the
1250397Sobrien *    documentation and/or other materials provided with the distribution.
1350397Sobrien * 3. All advertising materials mentioning features or use of this software
1450397Sobrien *    must display the following acknowledgement:
1550397Sobrien *	This product includes software developed by the University of
1650397Sobrien *	California, Berkeley and its contributors.
1750397Sobrien * 4. Neither the name of the University nor the names of its contributors
1850397Sobrien *    may be used to endorse or promote products derived from this software
1950397Sobrien *    without specific prior written permission.
2050397Sobrien *
2150397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2250397Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2350397Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2450397Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2550397Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2650397Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2750397Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2850397Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2950397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3050397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3150397Sobrien * SUCH DAMAGE.
3250397Sobrien */
3350397Sobrien
3450397Sobrien/* Mangled into a form that works on Sparc Solaris 2 by Mark Eichin
3550397Sobrien * for Cygnus Support, July 1992.
3650397Sobrien */
3750397Sobrien
3852284Sobrien#include "config.h"
3952284Sobrien#include "system.h"
4050397Sobrien
4150397Sobrien#if 0
4250397Sobrien#include "sparc/gmon.h"
4350397Sobrien#else
4450397Sobrienstruct phdr {
4550397Sobrien  char *lpc;
4650397Sobrien  char *hpc;
4750397Sobrien  int ncnt;
4850397Sobrien};
4950397Sobrien#define HISTFRACTION 2
5050397Sobrien#define HISTCOUNTER unsigned short
5150397Sobrien#define HASHFRACTION 1
5250397Sobrien#define ARCDENSITY 2
5350397Sobrien#define MINARCS 50
5450397Sobrienstruct tostruct {
5550397Sobrien  char *selfpc;
5650397Sobrien  long count;
5750397Sobrien  unsigned short link;
5850397Sobrien};
5950397Sobrienstruct rawarc {
6050397Sobrien    unsigned long       raw_frompc;
6150397Sobrien    unsigned long       raw_selfpc;
6250397Sobrien    long                raw_count;
6350397Sobrien};
6450397Sobrien#define ROUNDDOWN(x,y)  (((x)/(y))*(y))
6550397Sobrien#define ROUNDUP(x,y)    ((((x)+(y)-1)/(y))*(y))
6650397Sobrien
6750397Sobrien#endif
6850397Sobrien
6950397Sobrien/* extern mcount() asm ("mcount"); */
7050397Sobrien/*extern*/ char *minbrk /* asm ("minbrk") */;
7150397Sobrien
7250397Sobrien    /*
7350397Sobrien     *	froms is actually a bunch of unsigned shorts indexing tos
7450397Sobrien     */
7550397Sobrienstatic int		profiling = 3;
7650397Sobrienstatic unsigned short	*froms;
7750397Sobrienstatic struct tostruct	*tos = 0;
7850397Sobrienstatic long		tolimit = 0;
7950397Sobrienstatic char		*s_lowpc = 0;
8050397Sobrienstatic char		*s_highpc = 0;
8150397Sobrienstatic unsigned long	s_textsize = 0;
8250397Sobrien
8350397Sobrienstatic int	ssiz;
8450397Sobrienstatic char	*sbuf;
8550397Sobrienstatic int	s_scale;
8650397Sobrien    /* see profil(2) where this is describe (incorrectly) */
8750397Sobrien#define		SCALE_1_TO_1	0x10000L
8850397Sobrien
8950397Sobrien#define	MSG "No space for profiling buffer(s)\n"
9050397Sobrien
9152284Sobrienstatic void moncontrol	PROTO ((int));
9252284Sobrienextern void monstartup	PROTO ((char *, char *));
9352284Sobrienextern void _mcleanup	PROTO ((void));
9450397Sobrien
9550397Sobrienvoid monstartup(lowpc, highpc)
9650397Sobrien    char	*lowpc;
9750397Sobrien    char	*highpc;
9850397Sobrien{
9950397Sobrien    int			monsize;
10050397Sobrien    char		*buffer;
10150397Sobrien    register int	o;
10250397Sobrien
10350397Sobrien	/*
10450397Sobrien	 *	round lowpc and highpc to multiples of the density we're using
10550397Sobrien	 *	so the rest of the scaling (here and in gprof) stays in ints.
10650397Sobrien	 */
10750397Sobrien    lowpc = (char *)
10850397Sobrien	    ROUNDDOWN((unsigned)lowpc, HISTFRACTION*sizeof(HISTCOUNTER));
10950397Sobrien    s_lowpc = lowpc;
11050397Sobrien    highpc = (char *)
11150397Sobrien	    ROUNDUP((unsigned)highpc, HISTFRACTION*sizeof(HISTCOUNTER));
11250397Sobrien    s_highpc = highpc;
11350397Sobrien    s_textsize = highpc - lowpc;
11450397Sobrien    monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr);
11550397Sobrien    buffer = sbrk( monsize );
11650397Sobrien    if ( buffer == (char *) -1 ) {
11750397Sobrien	write( 2 , MSG , sizeof(MSG) );
11850397Sobrien	return;
11950397Sobrien    }
12050397Sobrien    froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION );
12150397Sobrien    if ( froms == (unsigned short *) -1 ) {
12250397Sobrien	write( 2 , MSG , sizeof(MSG) );
12350397Sobrien	froms = 0;
12450397Sobrien	return;
12550397Sobrien    }
12650397Sobrien    tolimit = s_textsize * ARCDENSITY / 100;
12750397Sobrien    if ( tolimit < MINARCS ) {
12850397Sobrien	tolimit = MINARCS;
12950397Sobrien    } else if ( tolimit > 65534 ) {
13050397Sobrien	tolimit = 65534;
13150397Sobrien    }
13250397Sobrien    tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) );
13350397Sobrien    if ( tos == (struct tostruct *) -1 ) {
13450397Sobrien	write( 2 , MSG , sizeof(MSG) );
13550397Sobrien	froms = 0;
13650397Sobrien	tos = 0;
13750397Sobrien	return;
13850397Sobrien    }
13950397Sobrien    minbrk = sbrk(0);
14050397Sobrien    tos[0].link = 0;
14150397Sobrien    sbuf = buffer;
14250397Sobrien    ssiz = monsize;
14350397Sobrien    ( (struct phdr *) buffer ) -> lpc = lowpc;
14450397Sobrien    ( (struct phdr *) buffer ) -> hpc = highpc;
14550397Sobrien    ( (struct phdr *) buffer ) -> ncnt = ssiz;
14650397Sobrien    monsize -= sizeof(struct phdr);
14750397Sobrien    if ( monsize <= 0 )
14850397Sobrien	return;
14950397Sobrien    o = highpc - lowpc;
15050397Sobrien    if( monsize < o )
15150397Sobrien#ifndef hp300
15250397Sobrien	s_scale = ( (float) monsize / o ) * SCALE_1_TO_1;
15350397Sobrien#else /* avoid floating point */
15450397Sobrien    {
15550397Sobrien	int quot = o / monsize;
15650397Sobrien
15750397Sobrien	if (quot >= 0x10000)
15850397Sobrien		s_scale = 1;
15950397Sobrien	else if (quot >= 0x100)
16050397Sobrien		s_scale = 0x10000 / quot;
16150397Sobrien	else if (o >= 0x800000)
16250397Sobrien		s_scale = 0x1000000 / (o / (monsize >> 8));
16350397Sobrien	else
16450397Sobrien		s_scale = 0x1000000 / ((o << 8) / monsize);
16550397Sobrien    }
16650397Sobrien#endif
16750397Sobrien    else
16850397Sobrien	s_scale = SCALE_1_TO_1;
16950397Sobrien    moncontrol(1);
17050397Sobrien}
17150397Sobrien
17250397Sobrienvoid
17350397Sobrien_mcleanup()
17450397Sobrien{
17550397Sobrien    int			fd;
17650397Sobrien    int			fromindex;
17750397Sobrien    int			endfrom;
17850397Sobrien    char		*frompc;
17950397Sobrien    int			toindex;
18050397Sobrien    struct rawarc	rawarc;
18150397Sobrien    char		*profdir;
18252284Sobrien    const char		*proffile;
18350397Sobrien    char		*progname;
18450397Sobrien    char		 buf[PATH_MAX];
18550397Sobrien    extern char	       **___Argv;
18650397Sobrien
18750397Sobrien    moncontrol(0);
18850397Sobrien
18950397Sobrien    if ((profdir = getenv("PROFDIR")) != NULL) {
19050397Sobrien	/* If PROFDIR contains a null value, no profiling output is produced */
19150397Sobrien	if (*profdir == '\0') {
19250397Sobrien	    return;
19350397Sobrien	}
19450397Sobrien
19550397Sobrien	progname=strrchr(___Argv[0], '/');
19650397Sobrien	if (progname == NULL)
19750397Sobrien	    progname=___Argv[0];
19850397Sobrien	else
19950397Sobrien	    progname++;
20050397Sobrien
20150397Sobrien	sprintf(buf, "%s/%ld.%s", profdir, getpid(), progname);
20250397Sobrien	proffile = buf;
20350397Sobrien    } else {
20450397Sobrien	proffile = "gmon.out";
20550397Sobrien    }
20650397Sobrien
20750397Sobrien    fd = creat( proffile, 0666 );
20850397Sobrien    if ( fd < 0 ) {
20950397Sobrien	perror( proffile );
21050397Sobrien	return;
21150397Sobrien    }
21250397Sobrien#   ifdef DEBUG
21350397Sobrien	fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz );
21450397Sobrien#   endif DEBUG
21550397Sobrien    write( fd , sbuf , ssiz );
21650397Sobrien    endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
21750397Sobrien    for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
21850397Sobrien	if ( froms[fromindex] == 0 ) {
21950397Sobrien	    continue;
22050397Sobrien	}
22150397Sobrien	frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
22250397Sobrien	for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
22350397Sobrien#	    ifdef DEBUG
22450397Sobrien		fprintf( stderr ,
22550397Sobrien			"[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" ,
22650397Sobrien			frompc , tos[toindex].selfpc , tos[toindex].count );
22750397Sobrien#	    endif DEBUG
22850397Sobrien	    rawarc.raw_frompc = (unsigned long) frompc;
22950397Sobrien	    rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc;
23050397Sobrien	    rawarc.raw_count = tos[toindex].count;
23150397Sobrien	    write( fd , &rawarc , sizeof rawarc );
23250397Sobrien	}
23350397Sobrien    }
23450397Sobrien    close( fd );
23550397Sobrien}
23650397Sobrien
23750397Sobrien/*
23850397Sobrien * The Sparc stack frame is only held together by the frame pointers
23950397Sobrien * in the register windows. According to the SVR4 SPARC ABI
24050397Sobrien * Supplement, Low Level System Information/Operating System
24150397Sobrien * Interface/Software Trap Types, a type 3 trap will flush all of the
24250397Sobrien * register windows to the stack, which will make it possible to walk
24350397Sobrien * the frames and find the return addresses.
24450397Sobrien * 	However, it seems awfully expensive to incur a trap (system
24550397Sobrien * call) for every function call. It turns out that "call" simply puts
24650397Sobrien * the return address in %o7 expecting the "save" in the procedure to
24750397Sobrien * shift it into %i7; this means that before the "save" occurs, %o7
24850397Sobrien * contains the address of the call to mcount, and %i7 still contains
24950397Sobrien * the caller above that. The asm mcount here simply saves those
25050397Sobrien * registers in argument registers and branches to internal_mcount,
25150397Sobrien * simulating a call with arguments.
25250397Sobrien * 	Kludges:
25350397Sobrien * 	1) the branch to internal_mcount is hard coded; it should be
25450397Sobrien * possible to tell asm to use the assembler-name of a symbol.
25550397Sobrien * 	2) in theory, the function calling mcount could have saved %i7
25650397Sobrien * somewhere and reused the register; in practice, I *think* this will
25750397Sobrien * break longjmp (and maybe the debugger) but I'm not certain. (I take
25850397Sobrien * some comfort in the knowledge that it will break the native mcount
25950397Sobrien * as well.)
26050397Sobrien * 	3) if builtin_return_address worked, this could be portable.
26150397Sobrien * However, it would really have to be optimized for arguments of 0
26250397Sobrien * and 1 and do something like what we have here in order to avoid the
26350397Sobrien * trap per function call performance hit.
26450397Sobrien * 	4) the atexit and monsetup calls prevent this from simply
26550397Sobrien * being a leaf routine that doesn't do a "save" (and would thus have
26650397Sobrien * access to %o7 and %i7 directly) but the call to write() at the end
26750397Sobrien * would have also prevented this.
26850397Sobrien *
26950397Sobrien * -- [eichin:19920702.1107EST]
27050397Sobrien */
27150397Sobrien
27252284Sobrienstatic void internal_mcount PROTO((char *, unsigned short *)) ATTRIBUTE_UNUSED;
27352284Sobrien
27450397Sobrien/* i7 == last ret, -> frompcindex */
27550397Sobrien/* o7 == current ret, -> selfpc */
27650397Sobrien/* Solaris 2 libraries use _mcount.  */
27750397Sobrienasm(".global _mcount; _mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
27850397Sobrien/* This is for compatibility with old versions of gcc which used mcount.  */
27950397Sobrienasm(".global mcount; mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
28050397Sobrien
28150397Sobrienstatic void internal_mcount(selfpc, frompcindex)
28250397Sobrien	register char			*selfpc;
28350397Sobrien	register unsigned short		*frompcindex;
28450397Sobrien{
28550397Sobrien	register struct tostruct	*top;
28650397Sobrien	register struct tostruct	*prevtop;
28750397Sobrien	register long			toindex;
28850397Sobrien	static char already_setup;
28950397Sobrien
29050397Sobrien	/*
29150397Sobrien	 *	find the return address for mcount,
29250397Sobrien	 *	and the return address for mcount's caller.
29350397Sobrien	 */
29450397Sobrien
29550397Sobrien	if(!already_setup) {
29652284Sobrien          extern char etext[];
29750397Sobrien	  already_setup = 1;
29852284Sobrien	  monstartup(0, (char *)etext);
29950397Sobrien#ifdef USE_ONEXIT
30050397Sobrien	  on_exit(_mcleanup, 0);
30150397Sobrien#else
30250397Sobrien	  atexit(_mcleanup);
30350397Sobrien#endif
30450397Sobrien	}
30550397Sobrien	/*
30650397Sobrien	 *	check that we are profiling
30750397Sobrien	 *	and that we aren't recursively invoked.
30850397Sobrien	 */
30950397Sobrien	if (profiling) {
31050397Sobrien		goto out;
31150397Sobrien	}
31250397Sobrien	profiling++;
31350397Sobrien	/*
31450397Sobrien	 *	check that frompcindex is a reasonable pc value.
31550397Sobrien	 *	for example:	signal catchers get called from the stack,
31650397Sobrien	 *			not from text space.  too bad.
31750397Sobrien	 */
31850397Sobrien	frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc);
31950397Sobrien	if ((unsigned long)frompcindex > s_textsize) {
32050397Sobrien		goto done;
32150397Sobrien	}
32250397Sobrien	frompcindex =
32350397Sobrien	    &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))];
32450397Sobrien	toindex = *frompcindex;
32550397Sobrien	if (toindex == 0) {
32650397Sobrien		/*
32750397Sobrien		 *	first time traversing this arc
32850397Sobrien		 */
32950397Sobrien		toindex = ++tos[0].link;
33050397Sobrien		if (toindex >= tolimit) {
33150397Sobrien			goto overflow;
33250397Sobrien		}
33350397Sobrien		*frompcindex = toindex;
33450397Sobrien		top = &tos[toindex];
33550397Sobrien		top->selfpc = selfpc;
33650397Sobrien		top->count = 1;
33750397Sobrien		top->link = 0;
33850397Sobrien		goto done;
33950397Sobrien	}
34050397Sobrien	top = &tos[toindex];
34150397Sobrien	if (top->selfpc == selfpc) {
34250397Sobrien		/*
34350397Sobrien		 *	arc at front of chain; usual case.
34450397Sobrien		 */
34550397Sobrien		top->count++;
34650397Sobrien		goto done;
34750397Sobrien	}
34850397Sobrien	/*
34950397Sobrien	 *	have to go looking down chain for it.
35050397Sobrien	 *	top points to what we are looking at,
35150397Sobrien	 *	prevtop points to previous top.
35250397Sobrien	 *	we know it is not at the head of the chain.
35350397Sobrien	 */
35450397Sobrien	for (; /* goto done */; ) {
35550397Sobrien		if (top->link == 0) {
35650397Sobrien			/*
35750397Sobrien			 *	top is end of the chain and none of the chain
35850397Sobrien			 *	had top->selfpc == selfpc.
35950397Sobrien			 *	so we allocate a new tostruct
36050397Sobrien			 *	and link it to the head of the chain.
36150397Sobrien			 */
36250397Sobrien			toindex = ++tos[0].link;
36350397Sobrien			if (toindex >= tolimit) {
36450397Sobrien				goto overflow;
36550397Sobrien			}
36650397Sobrien			top = &tos[toindex];
36750397Sobrien			top->selfpc = selfpc;
36850397Sobrien			top->count = 1;
36950397Sobrien			top->link = *frompcindex;
37050397Sobrien			*frompcindex = toindex;
37150397Sobrien			goto done;
37250397Sobrien		}
37350397Sobrien		/*
37450397Sobrien		 *	otherwise, check the next arc on the chain.
37550397Sobrien		 */
37650397Sobrien		prevtop = top;
37750397Sobrien		top = &tos[top->link];
37850397Sobrien		if (top->selfpc == selfpc) {
37950397Sobrien			/*
38050397Sobrien			 *	there it is.
38150397Sobrien			 *	increment its count
38250397Sobrien			 *	move it to the head of the chain.
38350397Sobrien			 */
38450397Sobrien			top->count++;
38550397Sobrien			toindex = prevtop->link;
38650397Sobrien			prevtop->link = top->link;
38750397Sobrien			top->link = *frompcindex;
38850397Sobrien			*frompcindex = toindex;
38950397Sobrien			goto done;
39050397Sobrien		}
39150397Sobrien
39250397Sobrien	}
39350397Sobriendone:
39450397Sobrien	profiling--;
39550397Sobrien	/* and fall through */
39650397Sobrienout:
39750397Sobrien	return;		/* normal return restores saved registers */
39850397Sobrien
39950397Sobrienoverflow:
40050397Sobrien	profiling++; /* halt further profiling */
40150397Sobrien#   define	TOLIMIT	"mcount: tos overflow\n"
40250397Sobrien	write(2, TOLIMIT, sizeof(TOLIMIT));
40350397Sobrien	goto out;
40450397Sobrien}
40550397Sobrien
40650397Sobrien/*
40750397Sobrien * Control profiling
40850397Sobrien *	profiling is what mcount checks to see if
40950397Sobrien *	all the data structures are ready.
41050397Sobrien */
41150397Sobrienstatic void moncontrol(mode)
41250397Sobrien    int mode;
41350397Sobrien{
41450397Sobrien    if (mode) {
41550397Sobrien	/* start */
41650397Sobrien	profil((unsigned short *)(sbuf + sizeof(struct phdr)),
41750397Sobrien	       ssiz - sizeof(struct phdr),
41850397Sobrien	       (int)s_lowpc, s_scale);
41950397Sobrien	profiling = 0;
42050397Sobrien    } else {
42150397Sobrien	/* stop */
42250397Sobrien	profil((unsigned short *)0, 0, 0, 0);
42350397Sobrien	profiling = 3;
42450397Sobrien    }
42550397Sobrien}
426