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