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
31117395Skan/* Mangled into a form that works on SPARC Solaris 2 by Mark Eichin
3250397Sobrien * for Cygnus Support, July 1992.
3350397Sobrien */
3450397Sobrien
35132718Skan#include "tconfig.h"
36132718Skan#include "tsystem.h"
37132718Skan#include <fcntl.h> /* for creat() */
38132718Skan#include "coretypes.h"
39132718Skan#include "tm.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
91132718Skanstatic void moncontrol (int);
92132718Skanextern void monstartup (char *, char *);
93132718Skanextern void _mcleanup (void);
9450397Sobrien
95132718Skanvoid monstartup(char *lowpc, char *highpc)
9650397Sobrien{
9750397Sobrien    int			monsize;
9850397Sobrien    char		*buffer;
9950397Sobrien    register int	o;
10050397Sobrien
10150397Sobrien	/*
10250397Sobrien	 *	round lowpc and highpc to multiples of the density we're using
10350397Sobrien	 *	so the rest of the scaling (here and in gprof) stays in ints.
10450397Sobrien	 */
10550397Sobrien    lowpc = (char *)
10690075Sobrien	    ROUNDDOWN((unsigned long)lowpc, HISTFRACTION*sizeof(HISTCOUNTER));
10750397Sobrien    s_lowpc = lowpc;
10850397Sobrien    highpc = (char *)
10990075Sobrien	    ROUNDUP((unsigned long)highpc, HISTFRACTION*sizeof(HISTCOUNTER));
11050397Sobrien    s_highpc = highpc;
11150397Sobrien    s_textsize = highpc - lowpc;
11250397Sobrien    monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr);
11350397Sobrien    buffer = sbrk( monsize );
11450397Sobrien    if ( buffer == (char *) -1 ) {
11550397Sobrien	write( 2 , MSG , sizeof(MSG) );
11650397Sobrien	return;
11750397Sobrien    }
11850397Sobrien    froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION );
11950397Sobrien    if ( froms == (unsigned short *) -1 ) {
12050397Sobrien	write( 2 , MSG , sizeof(MSG) );
12150397Sobrien	froms = 0;
12250397Sobrien	return;
12350397Sobrien    }
12450397Sobrien    tolimit = s_textsize * ARCDENSITY / 100;
12550397Sobrien    if ( tolimit < MINARCS ) {
12650397Sobrien	tolimit = MINARCS;
12750397Sobrien    } else if ( tolimit > 65534 ) {
12850397Sobrien	tolimit = 65534;
12950397Sobrien    }
13050397Sobrien    tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) );
13150397Sobrien    if ( tos == (struct tostruct *) -1 ) {
13250397Sobrien	write( 2 , MSG , sizeof(MSG) );
13350397Sobrien	froms = 0;
13450397Sobrien	tos = 0;
13550397Sobrien	return;
13650397Sobrien    }
13750397Sobrien    minbrk = sbrk(0);
13850397Sobrien    tos[0].link = 0;
13950397Sobrien    sbuf = buffer;
14050397Sobrien    ssiz = monsize;
14150397Sobrien    ( (struct phdr *) buffer ) -> lpc = lowpc;
14250397Sobrien    ( (struct phdr *) buffer ) -> hpc = highpc;
14350397Sobrien    ( (struct phdr *) buffer ) -> ncnt = ssiz;
14450397Sobrien    monsize -= sizeof(struct phdr);
14550397Sobrien    if ( monsize <= 0 )
14650397Sobrien	return;
14750397Sobrien    o = highpc - lowpc;
14850397Sobrien    if( monsize < o )
14950397Sobrien#ifndef hp300
15050397Sobrien	s_scale = ( (float) monsize / o ) * SCALE_1_TO_1;
15150397Sobrien#else /* avoid floating point */
15250397Sobrien    {
15350397Sobrien	int quot = o / monsize;
15450397Sobrien
15550397Sobrien	if (quot >= 0x10000)
15650397Sobrien		s_scale = 1;
15750397Sobrien	else if (quot >= 0x100)
15850397Sobrien		s_scale = 0x10000 / quot;
15950397Sobrien	else if (o >= 0x800000)
16050397Sobrien		s_scale = 0x1000000 / (o / (monsize >> 8));
16150397Sobrien	else
16250397Sobrien		s_scale = 0x1000000 / ((o << 8) / monsize);
16350397Sobrien    }
16450397Sobrien#endif
16550397Sobrien    else
16650397Sobrien	s_scale = SCALE_1_TO_1;
16750397Sobrien    moncontrol(1);
16850397Sobrien}
16950397Sobrien
17050397Sobrienvoid
171132718Skan_mcleanup(void)
17250397Sobrien{
17350397Sobrien    int			fd;
17450397Sobrien    int			fromindex;
17550397Sobrien    int			endfrom;
17650397Sobrien    char		*frompc;
17750397Sobrien    int			toindex;
17850397Sobrien    struct rawarc	rawarc;
17950397Sobrien    char		*profdir;
18052284Sobrien    const char		*proffile;
18150397Sobrien    char		*progname;
18250397Sobrien    char		 buf[PATH_MAX];
18350397Sobrien    extern char	       **___Argv;
18450397Sobrien
18550397Sobrien    moncontrol(0);
18650397Sobrien
18750397Sobrien    if ((profdir = getenv("PROFDIR")) != NULL) {
18850397Sobrien	/* If PROFDIR contains a null value, no profiling output is produced */
18950397Sobrien	if (*profdir == '\0') {
19050397Sobrien	    return;
19150397Sobrien	}
19250397Sobrien
19350397Sobrien	progname=strrchr(___Argv[0], '/');
19450397Sobrien	if (progname == NULL)
19550397Sobrien	    progname=___Argv[0];
19650397Sobrien	else
19750397Sobrien	    progname++;
19850397Sobrien
19990075Sobrien	sprintf(buf, "%s/%ld.%s", profdir, (long) getpid(), progname);
20050397Sobrien	proffile = buf;
20150397Sobrien    } else {
20250397Sobrien	proffile = "gmon.out";
20350397Sobrien    }
20450397Sobrien
20550397Sobrien    fd = creat( proffile, 0666 );
20650397Sobrien    if ( fd < 0 ) {
20750397Sobrien	perror( proffile );
20850397Sobrien	return;
20950397Sobrien    }
21050397Sobrien#   ifdef DEBUG
21150397Sobrien	fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz );
21290075Sobrien#   endif /* DEBUG */
21350397Sobrien    write( fd , sbuf , ssiz );
21450397Sobrien    endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
21550397Sobrien    for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
21650397Sobrien	if ( froms[fromindex] == 0 ) {
21750397Sobrien	    continue;
21850397Sobrien	}
21950397Sobrien	frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
22050397Sobrien	for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
22150397Sobrien#	    ifdef DEBUG
22250397Sobrien		fprintf( stderr ,
22350397Sobrien			"[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" ,
22450397Sobrien			frompc , tos[toindex].selfpc , tos[toindex].count );
22590075Sobrien#	    endif /* DEBUG */
22650397Sobrien	    rawarc.raw_frompc = (unsigned long) frompc;
22750397Sobrien	    rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc;
22850397Sobrien	    rawarc.raw_count = tos[toindex].count;
22950397Sobrien	    write( fd , &rawarc , sizeof rawarc );
23050397Sobrien	}
23150397Sobrien    }
23250397Sobrien    close( fd );
23350397Sobrien}
23450397Sobrien
23550397Sobrien/*
236117395Skan * The SPARC stack frame is only held together by the frame pointers
23750397Sobrien * in the register windows. According to the SVR4 SPARC ABI
23850397Sobrien * Supplement, Low Level System Information/Operating System
23950397Sobrien * Interface/Software Trap Types, a type 3 trap will flush all of the
24050397Sobrien * register windows to the stack, which will make it possible to walk
24150397Sobrien * the frames and find the return addresses.
24250397Sobrien * 	However, it seems awfully expensive to incur a trap (system
24350397Sobrien * call) for every function call. It turns out that "call" simply puts
24450397Sobrien * the return address in %o7 expecting the "save" in the procedure to
24550397Sobrien * shift it into %i7; this means that before the "save" occurs, %o7
24650397Sobrien * contains the address of the call to mcount, and %i7 still contains
24750397Sobrien * the caller above that. The asm mcount here simply saves those
24850397Sobrien * registers in argument registers and branches to internal_mcount,
24950397Sobrien * simulating a call with arguments.
25050397Sobrien * 	Kludges:
25150397Sobrien * 	1) the branch to internal_mcount is hard coded; it should be
25250397Sobrien * possible to tell asm to use the assembler-name of a symbol.
25350397Sobrien * 	2) in theory, the function calling mcount could have saved %i7
25450397Sobrien * somewhere and reused the register; in practice, I *think* this will
25550397Sobrien * break longjmp (and maybe the debugger) but I'm not certain. (I take
25650397Sobrien * some comfort in the knowledge that it will break the native mcount
25750397Sobrien * as well.)
25850397Sobrien * 	3) if builtin_return_address worked, this could be portable.
25950397Sobrien * However, it would really have to be optimized for arguments of 0
26050397Sobrien * and 1 and do something like what we have here in order to avoid the
26150397Sobrien * trap per function call performance hit.
26250397Sobrien * 	4) the atexit and monsetup calls prevent this from simply
26350397Sobrien * being a leaf routine that doesn't do a "save" (and would thus have
26450397Sobrien * access to %o7 and %i7 directly) but the call to write() at the end
26550397Sobrien * would have also prevented this.
26650397Sobrien *
26750397Sobrien * -- [eichin:19920702.1107EST]
26850397Sobrien */
26950397Sobrien
270161651Skanstatic void internal_mcount (char *, unsigned short *) __attribute__ ((used));
27152284Sobrien
27250397Sobrien/* i7 == last ret, -> frompcindex */
27350397Sobrien/* o7 == current ret, -> selfpc */
27450397Sobrien/* Solaris 2 libraries use _mcount.  */
27550397Sobrienasm(".global _mcount; _mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
27650397Sobrien/* This is for compatibility with old versions of gcc which used mcount.  */
27750397Sobrienasm(".global mcount; mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
27850397Sobrien
279132718Skanstatic void internal_mcount(char *selfpc, unsigned short *frompcindex)
28050397Sobrien{
28150397Sobrien	register struct tostruct	*top;
28250397Sobrien	register struct tostruct	*prevtop;
28350397Sobrien	register long			toindex;
28450397Sobrien	static char already_setup;
28550397Sobrien
28650397Sobrien	/*
28750397Sobrien	 *	find the return address for mcount,
28850397Sobrien	 *	and the return address for mcount's caller.
28950397Sobrien	 */
29050397Sobrien
29150397Sobrien	if(!already_setup) {
29252284Sobrien          extern char etext[];
29396263Sobrien	  extern char _start[];
29496263Sobrien	  extern char _init[];
29550397Sobrien	  already_setup = 1;
29696263Sobrien	  monstartup(_start < _init ? _start : _init, etext);
29750397Sobrien#ifdef USE_ONEXIT
29850397Sobrien	  on_exit(_mcleanup, 0);
29950397Sobrien#else
30050397Sobrien	  atexit(_mcleanup);
30150397Sobrien#endif
30250397Sobrien	}
30350397Sobrien	/*
30450397Sobrien	 *	check that we are profiling
30550397Sobrien	 *	and that we aren't recursively invoked.
30650397Sobrien	 */
30750397Sobrien	if (profiling) {
30850397Sobrien		goto out;
30950397Sobrien	}
31050397Sobrien	profiling++;
31150397Sobrien	/*
31250397Sobrien	 *	check that frompcindex is a reasonable pc value.
31350397Sobrien	 *	for example:	signal catchers get called from the stack,
31450397Sobrien	 *			not from text space.  too bad.
31550397Sobrien	 */
31650397Sobrien	frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc);
31750397Sobrien	if ((unsigned long)frompcindex > s_textsize) {
31850397Sobrien		goto done;
31950397Sobrien	}
32050397Sobrien	frompcindex =
32150397Sobrien	    &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))];
32250397Sobrien	toindex = *frompcindex;
32350397Sobrien	if (toindex == 0) {
32450397Sobrien		/*
32550397Sobrien		 *	first time traversing this arc
32650397Sobrien		 */
32750397Sobrien		toindex = ++tos[0].link;
32850397Sobrien		if (toindex >= tolimit) {
32950397Sobrien			goto overflow;
33050397Sobrien		}
33150397Sobrien		*frompcindex = toindex;
33250397Sobrien		top = &tos[toindex];
33350397Sobrien		top->selfpc = selfpc;
33450397Sobrien		top->count = 1;
33550397Sobrien		top->link = 0;
33650397Sobrien		goto done;
33750397Sobrien	}
33850397Sobrien	top = &tos[toindex];
33950397Sobrien	if (top->selfpc == selfpc) {
34050397Sobrien		/*
34150397Sobrien		 *	arc at front of chain; usual case.
34250397Sobrien		 */
34350397Sobrien		top->count++;
34450397Sobrien		goto done;
34550397Sobrien	}
34650397Sobrien	/*
34750397Sobrien	 *	have to go looking down chain for it.
34850397Sobrien	 *	top points to what we are looking at,
34950397Sobrien	 *	prevtop points to previous top.
35050397Sobrien	 *	we know it is not at the head of the chain.
35150397Sobrien	 */
35250397Sobrien	for (; /* goto done */; ) {
35350397Sobrien		if (top->link == 0) {
35450397Sobrien			/*
35550397Sobrien			 *	top is end of the chain and none of the chain
35650397Sobrien			 *	had top->selfpc == selfpc.
35750397Sobrien			 *	so we allocate a new tostruct
35850397Sobrien			 *	and link it to the head of the chain.
35950397Sobrien			 */
36050397Sobrien			toindex = ++tos[0].link;
36150397Sobrien			if (toindex >= tolimit) {
36250397Sobrien				goto overflow;
36350397Sobrien			}
36450397Sobrien			top = &tos[toindex];
36550397Sobrien			top->selfpc = selfpc;
36650397Sobrien			top->count = 1;
36750397Sobrien			top->link = *frompcindex;
36850397Sobrien			*frompcindex = toindex;
36950397Sobrien			goto done;
37050397Sobrien		}
37150397Sobrien		/*
37250397Sobrien		 *	otherwise, check the next arc on the chain.
37350397Sobrien		 */
37450397Sobrien		prevtop = top;
37550397Sobrien		top = &tos[top->link];
37650397Sobrien		if (top->selfpc == selfpc) {
37750397Sobrien			/*
37850397Sobrien			 *	there it is.
37950397Sobrien			 *	increment its count
38050397Sobrien			 *	move it to the head of the chain.
38150397Sobrien			 */
38250397Sobrien			top->count++;
38350397Sobrien			toindex = prevtop->link;
38450397Sobrien			prevtop->link = top->link;
38550397Sobrien			top->link = *frompcindex;
38650397Sobrien			*frompcindex = toindex;
38750397Sobrien			goto done;
38850397Sobrien		}
38950397Sobrien
39050397Sobrien	}
39150397Sobriendone:
39250397Sobrien	profiling--;
39350397Sobrien	/* and fall through */
39450397Sobrienout:
39550397Sobrien	return;		/* normal return restores saved registers */
39650397Sobrien
39750397Sobrienoverflow:
39850397Sobrien	profiling++; /* halt further profiling */
39950397Sobrien#   define	TOLIMIT	"mcount: tos overflow\n"
40050397Sobrien	write(2, TOLIMIT, sizeof(TOLIMIT));
40150397Sobrien	goto out;
40250397Sobrien}
40350397Sobrien
40450397Sobrien/*
40550397Sobrien * Control profiling
40650397Sobrien *	profiling is what mcount checks to see if
40750397Sobrien *	all the data structures are ready.
40850397Sobrien */
409132718Skanstatic void moncontrol(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