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