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/*
3250397Sobrien * This is a modified gmon.c by J.W.Hawtin <oolon@ankh.org>,
3350397Sobrien * 14/8/96 based on the original gmon.c in GCC and the hacked version
3450397Sobrien * solaris 2 sparc version (config/sparc/gmon-sol.c) by Mark Eichin. To do
3550397Sobrien * process profiling on solaris 2.X X86
3650397Sobrien *
3750397Sobrien * It must be used in conjunction with sol2-gc1.asm, which is used to start
3850397Sobrien * and stop process monitoring.
3950397Sobrien *
4050397Sobrien * Differences.
4150397Sobrien *
4250397Sobrien * On Solaris 2 _mcount is called by library functions not mcount, so support
4350397Sobrien * has been added for both.
4450397Sobrien *
4550397Sobrien * Also the prototype for profil() is different
4650397Sobrien *
4750397Sobrien * Solaris 2 does not seem to have char *minbrk whcih allows the setting of
4850397Sobrien * the minimum SBRK region so this code has been removed and lets pray malloc
4950397Sobrien * does not mess it up.
5050397Sobrien *
5150397Sobrien * Notes
5250397Sobrien *
5350397Sobrien * This code could easily be integrated with the original gmon.c and perhaps
5450397Sobrien * should be.
5550397Sobrien */
56169689Skan#include "tconfig.h"
57169689Skan#include "tsystem.h"
58169689Skan#include <fcntl.h> /* for creat() */
5950397Sobrien
6050397Sobrien#ifdef DEBUG
6150397Sobrien#include <stdio.h>
6250397Sobrien#endif
6350397Sobrien
64169689Skanstatic void moncontrol (int);
65169689Skanextern void monstartup (char *, char *);
66169689Skanextern void _mcleanup (void);
67169689Skanextern void internal_mcount (void);
6850397Sobrien
69169689Skan
7050397Sobrienstruct phdr {
7150397Sobrien                char    *lpc;
7250397Sobrien                char    *hpc;
7350397Sobrien                int     ncnt;
7450397Sobrien};
7550397Sobrien
7650397Sobrien
7750397Sobrien#define HISTFRACTION 2
7850397Sobrien#define HISTCOUNTER unsigned short
7950397Sobrien#define HASHFRACTION 1
8050397Sobrien#define ARCDENSITY 2
8150397Sobrien#define MINARCS 50
8250397Sobrien#define BASEADDRESS 0x8000000 /* On Solaris 2 X86 all executables start here
8350397Sobrien				 and not at 0 */
8450397Sobrien
8550397Sobrienstruct tostruct {
8650397Sobrien  char *selfpc;
8750397Sobrien  long count;
8850397Sobrien  unsigned short link;
8950397Sobrien};
90169689Skan
9150397Sobrienstruct rawarc {
9250397Sobrien    unsigned long       raw_frompc;
9350397Sobrien    unsigned long       raw_selfpc;
9450397Sobrien    long                raw_count;
9550397Sobrien};
9650397Sobrien#define ROUNDDOWN(x,y)  (((x)/(y))*(y))
9750397Sobrien#define ROUNDUP(x,y)    ((((x)+(y)-1)/(y))*(y))
9850397Sobrien
9950397Sobrien/* char *minbrk; */
10050397Sobrien
10150397Sobrien    /*
10250397Sobrien     *	froms is actually a bunch of unsigned shorts indexing tos
10350397Sobrien     */
10450397Sobrienstatic int		profiling = 3;
10550397Sobrienstatic unsigned short	*froms;
10650397Sobrienstatic struct tostruct	*tos = 0;
10750397Sobrienstatic long		tolimit = 0;
10850397Sobrienstatic char		*s_lowpc = 0;
10950397Sobrienstatic char		*s_highpc = 0;
11050397Sobrienstatic unsigned long	s_textsize = 0;
11150397Sobrien
11250397Sobrienstatic int	ssiz;
11350397Sobrienstatic char	*sbuf;
11450397Sobrienstatic int	s_scale;
11550397Sobrien    /* see profil(2) where this is describe (incorrectly) */
11650397Sobrien#define		SCALE_1_TO_1	0x10000L
11750397Sobrien
11850397Sobrien#define	MSG "No space for profiling buffer(s)\n"
11950397Sobrien
12050397Sobrienextern int errno;
12150397Sobrien
122169689Skanvoid
123169689Skanmonstartup(char *lowpc, char *highpc)
12450397Sobrien{
12550397Sobrien    int			monsize;
12650397Sobrien    char		*buffer;
12750397Sobrien    register int	o;
12850397Sobrien
12950397Sobrien	/*
13050397Sobrien	 *	round lowpc and highpc to multiples of the density we're using
13150397Sobrien	 *	so the rest of the scaling (here and in gprof) stays in ints.
13250397Sobrien	 */
13350397Sobrien    lowpc = (char *)
134169689Skan	    ROUNDDOWN((unsigned long)lowpc, HISTFRACTION*sizeof(HISTCOUNTER));
13550397Sobrien    s_lowpc = lowpc;
13650397Sobrien    highpc = (char *)
137169689Skan	    ROUNDUP((unsigned long)highpc, HISTFRACTION*sizeof(HISTCOUNTER));
13850397Sobrien    s_highpc = highpc;
13950397Sobrien    s_textsize = highpc - lowpc;
14050397Sobrien    monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr);
14150397Sobrien    buffer = (char *) sbrk( monsize );
14250397Sobrien    if ( buffer == (char *) -1 ) {
14350397Sobrien	write( 2 , MSG , sizeof(MSG) );
14450397Sobrien	return;
14550397Sobrien    }
14650397Sobrien    froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION );
14750397Sobrien    if ( froms == (unsigned short *) -1 ) {
14850397Sobrien	write( 2 , MSG , sizeof(MSG) );
14950397Sobrien	froms = 0;
15050397Sobrien	return;
15150397Sobrien    }
15250397Sobrien    tolimit = s_textsize * ARCDENSITY / 100;
15350397Sobrien    if ( tolimit < MINARCS ) {
15450397Sobrien	tolimit = MINARCS;
15550397Sobrien    } else if ( tolimit > 65534 ) {
15650397Sobrien	tolimit = 65534;
15750397Sobrien    }
15850397Sobrien    tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) );
15950397Sobrien    if ( tos == (struct tostruct *) -1 ) {
16050397Sobrien	write( 2 , MSG , sizeof(MSG) );
16150397Sobrien	froms = 0;
16250397Sobrien	tos = 0;
16350397Sobrien	return;
16450397Sobrien    }
16550397Sobrien/*    minbrk = (char *) sbrk(0);*/
16650397Sobrien    tos[0].link = 0;
16750397Sobrien    sbuf = buffer;
16850397Sobrien    ssiz = monsize;
16950397Sobrien    ( (struct phdr *) buffer ) -> lpc = lowpc;
17050397Sobrien    ( (struct phdr *) buffer ) -> hpc = highpc;
17150397Sobrien    ( (struct phdr *) buffer ) -> ncnt = ssiz;
17250397Sobrien    monsize -= sizeof(struct phdr);
17350397Sobrien    if ( monsize <= 0 )
17450397Sobrien	return;
17550397Sobrien    o = highpc - lowpc;
17650397Sobrien    if( monsize < o )
17750397Sobrien#ifndef hp300
17850397Sobrien	s_scale = ( (float) monsize / o ) * SCALE_1_TO_1;
17950397Sobrien#else /* avoid floating point */
18050397Sobrien    {
18150397Sobrien	int quot = o / monsize;
18250397Sobrien
18350397Sobrien	if (quot >= 0x10000)
18450397Sobrien		s_scale = 1;
18550397Sobrien	else if (quot >= 0x100)
18650397Sobrien		s_scale = 0x10000 / quot;
18750397Sobrien	else if (o >= 0x800000)
18850397Sobrien		s_scale = 0x1000000 / (o / (monsize >> 8));
18950397Sobrien	else
19050397Sobrien		s_scale = 0x1000000 / ((o << 8) / monsize);
19150397Sobrien    }
19250397Sobrien#endif
19350397Sobrien    else
19450397Sobrien	s_scale = SCALE_1_TO_1;
19550397Sobrien    moncontrol(1);
19650397Sobrien}
19750397Sobrien
198169689Skanvoid
199169689Skan_mcleanup (void)
20050397Sobrien{
20150397Sobrien    int			fd;
20250397Sobrien    int			fromindex;
20350397Sobrien    int			endfrom;
20450397Sobrien    char		*frompc;
20550397Sobrien    int			toindex;
20650397Sobrien    struct rawarc	rawarc;
20750397Sobrien
20850397Sobrien    moncontrol(0);
20950397Sobrien    fd = creat( "gmon.out" , 0666 );
21050397Sobrien    if ( fd < 0 ) {
21150397Sobrien	perror( "mcount: gmon.out" );
21250397Sobrien	return;
21350397Sobrien    }
21450397Sobrien#   ifdef DEBUG
21550397Sobrien	fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz );
216169689Skan#   endif /* DEBUG */
21750397Sobrien
21850397Sobrien    write( fd , sbuf , ssiz );
21950397Sobrien    endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
22050397Sobrien    for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
22150397Sobrien	if ( froms[fromindex] == 0 ) {
22250397Sobrien	    continue;
22350397Sobrien	}
22450397Sobrien	frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
22550397Sobrien	for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
22650397Sobrien#	    ifdef DEBUG
22750397Sobrien		fprintf( stderr ,
22850397Sobrien			"[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" ,
22950397Sobrien			frompc , tos[toindex].selfpc , tos[toindex].count );
230169689Skan#	    endif /* DEBUG */
23150397Sobrien	    rawarc.raw_frompc = (unsigned long) frompc;
23250397Sobrien	    rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc;
23350397Sobrien	    rawarc.raw_count = tos[toindex].count;
23450397Sobrien	    write( fd , &rawarc , sizeof rawarc );
23550397Sobrien	}
23650397Sobrien    }
23750397Sobrien    close( fd );
23850397Sobrien}
23950397Sobrien
24050397Sobrien/* Solaris 2 libraries use _mcount.  */
24150397Sobrienasm(".globl _mcount; _mcount: jmp internal_mcount");
24250397Sobrien/* This is for compatibility with old versions of gcc which used mcount.  */
24350397Sobrienasm(".globl mcount; mcount: jmp internal_mcount");
24450397Sobrien
245169689Skanvoid
246169689Skaninternal_mcount (void)
24750397Sobrien{
24850397Sobrien	register char			*selfpc;
24950397Sobrien	register unsigned short		*frompcindex;
25050397Sobrien	register struct tostruct	*top;
25150397Sobrien	register struct tostruct	*prevtop;
25250397Sobrien	register long			toindex;
25350397Sobrien	static char already_setup;
25450397Sobrien
25550397Sobrien	/*
25650397Sobrien	 *	find the return address for mcount,
25750397Sobrien	 *	and the return address for mcount's caller.
25850397Sobrien	 */
25950397Sobrien
26050397Sobrien	/* selfpc = pc pushed by mcount call.
26150397Sobrien	   This identifies the function that was just entered.  */
26250397Sobrien	selfpc = (void *) __builtin_return_address (0);
26350397Sobrien	/* frompcindex = pc in preceding frame.
26450397Sobrien	   This identifies the caller of the function just entered.  */
26550397Sobrien	frompcindex = (void *) __builtin_return_address (1);
26650397Sobrien
26750397Sobrien	if(!already_setup) {
268169689Skan          extern char etext[];
26950397Sobrien	  already_setup = 1;
27050397Sobrien/*	  monstartup(0, etext); */
271169689Skan	  monstartup((char*)0x08040000, etext);
27250397Sobrien#ifdef USE_ONEXIT
27350397Sobrien	  on_exit(_mcleanup, 0);
27450397Sobrien#else
27550397Sobrien	  atexit(_mcleanup);
27650397Sobrien#endif
27750397Sobrien	}
27850397Sobrien	/*
27950397Sobrien	 *	check that we are profiling
28050397Sobrien	 *	and that we aren't recursively invoked.
28150397Sobrien	 */
28250397Sobrien	if (profiling) {
28350397Sobrien		goto out;
28450397Sobrien	}
28550397Sobrien	profiling++;
28650397Sobrien	/*
28750397Sobrien	 *	check that frompcindex is a reasonable pc value.
28850397Sobrien	 *	for example:	signal catchers get called from the stack,
28950397Sobrien	 *			not from text space.  too bad.
29050397Sobrien	 */
29150397Sobrien	frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc);
29250397Sobrien	if ((unsigned long)frompcindex > s_textsize) {
29350397Sobrien		goto done;
29450397Sobrien	}
29550397Sobrien	frompcindex =
29650397Sobrien	    &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))];
29750397Sobrien	toindex = *frompcindex;
29850397Sobrien	if (toindex == 0) {
29950397Sobrien		/*
30050397Sobrien		 *	first time traversing this arc
30150397Sobrien		 */
30250397Sobrien		toindex = ++tos[0].link;
30350397Sobrien		if (toindex >= tolimit) {
30450397Sobrien			goto overflow;
30550397Sobrien		}
30650397Sobrien		*frompcindex = toindex;
30750397Sobrien		top = &tos[toindex];
30850397Sobrien		top->selfpc = selfpc;
30950397Sobrien		top->count = 1;
31050397Sobrien		top->link = 0;
31150397Sobrien		goto done;
31250397Sobrien	}
31350397Sobrien	top = &tos[toindex];
31450397Sobrien	if (top->selfpc == selfpc) {
31550397Sobrien		/*
31650397Sobrien		 *	arc at front of chain; usual case.
31750397Sobrien		 */
31850397Sobrien		top->count++;
31950397Sobrien		goto done;
32050397Sobrien	}
32150397Sobrien	/*
32250397Sobrien	 *	have to go looking down chain for it.
32350397Sobrien	 *	top points to what we are looking at,
32450397Sobrien	 *	prevtop points to previous top.
32550397Sobrien	 *	we know it is not at the head of the chain.
32650397Sobrien	 */
32750397Sobrien	for (; /* goto done */; ) {
32850397Sobrien		if (top->link == 0) {
32950397Sobrien			/*
33050397Sobrien			 *	top is end of the chain and none of the chain
33150397Sobrien			 *	had top->selfpc == selfpc.
33250397Sobrien			 *	so we allocate a new tostruct
33350397Sobrien			 *	and link it to the head of the chain.
33450397Sobrien			 */
33550397Sobrien			toindex = ++tos[0].link;
33650397Sobrien			if (toindex >= tolimit) {
33750397Sobrien				goto overflow;
33850397Sobrien			}
33950397Sobrien			top = &tos[toindex];
34050397Sobrien			top->selfpc = selfpc;
34150397Sobrien			top->count = 1;
34250397Sobrien			top->link = *frompcindex;
34350397Sobrien			*frompcindex = toindex;
34450397Sobrien			goto done;
34550397Sobrien		}
34650397Sobrien		/*
34750397Sobrien		 *	otherwise, check the next arc on the chain.
34850397Sobrien		 */
34950397Sobrien		prevtop = top;
35050397Sobrien		top = &tos[top->link];
35150397Sobrien		if (top->selfpc == selfpc) {
35250397Sobrien			/*
35350397Sobrien			 *	there it is.
35450397Sobrien			 *	increment its count
35550397Sobrien			 *	move it to the head of the chain.
35650397Sobrien			 */
35750397Sobrien			top->count++;
35850397Sobrien			toindex = prevtop->link;
35950397Sobrien			prevtop->link = top->link;
36050397Sobrien			top->link = *frompcindex;
36150397Sobrien			*frompcindex = toindex;
36250397Sobrien			goto done;
36350397Sobrien		}
36450397Sobrien
36550397Sobrien	}
36650397Sobriendone:
36750397Sobrien	profiling--;
36850397Sobrien	/* and fall through */
36950397Sobrienout:
37050397Sobrien	return;		/* normal return restores saved registers */
37150397Sobrien
37250397Sobrienoverflow:
37350397Sobrien	profiling++; /* halt further profiling */
37450397Sobrien#   define	TOLIMIT	"mcount: tos overflow\n"
37550397Sobrien	write(2, TOLIMIT, sizeof(TOLIMIT));
37650397Sobrien	goto out;
37750397Sobrien}
37850397Sobrien
37950397Sobrien/*
38050397Sobrien * Control profiling
38150397Sobrien *	profiling is what mcount checks to see if
38250397Sobrien *	all the data structures are ready.
38350397Sobrien */
384169689Skanstatic void
385169689Skanmoncontrol(int mode)
38650397Sobrien{
38750397Sobrien    if (mode)
38850397Sobrien    {
38950397Sobrien      /* start */
39050397Sobrien      profil((unsigned short *)(sbuf + sizeof(struct phdr)),
39150397Sobrien	     ssiz - sizeof(struct phdr),
392169689Skan	     (long)s_lowpc, s_scale);
39350397Sobrien
39450397Sobrien      profiling = 0;
39550397Sobrien    } else {
39650397Sobrien      /* stop */
39750397Sobrien      profil((unsigned short *)0, 0, 0, 0);
39850397Sobrien      profiling = 3;
39950397Sobrien    }
40050397Sobrien}
401