412 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			412 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
/*-
 | 
						|
 * Copyright (c) 1991, 1998, 2001 The Regents of the University of California.
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 * 3. [rescinded 22 July 1999]
 | 
						|
 * 4. Neither the name of the University nor the names of its contributors
 | 
						|
 *    may be used to endorse or promote products derived from this software
 | 
						|
 *    without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef lint
 | 
						|
static char sccsid[] = "@(#)gmon.c	5.3 (Berkeley) 5/22/91";
 | 
						|
#endif /* not lint */
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#define GMON_PTR_SIZE 4
 | 
						|
 | 
						|
struct phdr
 | 
						|
  {
 | 
						|
    char *lpc;			/* base pc address of sample buffer */
 | 
						|
    char *hpc;			/* max pc address of sampled buffer */
 | 
						|
    int ncnt;			/* size of sample buffer (plus this header) */
 | 
						|
 | 
						|
    char version[4];		/* version number */
 | 
						|
    char profrate[4];		/* profiling clock rate */
 | 
						|
    char spare[3*4];		/* reserved */
 | 
						|
  };
 | 
						|
 | 
						|
#define GMONVERSION     0x00051879
 | 
						|
 | 
						|
/*
 | 
						|
 * Histogram counters are unsigned shorts:
 | 
						|
 */
 | 
						|
#define	HISTCOUNTER unsigned short
 | 
						|
 | 
						|
/*
 | 
						|
 * Fraction of text space to allocate for histogram counters here, 1/2:
 | 
						|
 */
 | 
						|
#define	HISTFRACTION	2
 | 
						|
 | 
						|
/*
 | 
						|
 * Fraction of text space to allocate for from hash buckets.  The
 | 
						|
 * value of HASHFRACTION is based on the minimum number of bytes of
 | 
						|
 * separation between two subroutine call points in the object code.
 | 
						|
 * Given MIN_SUBR_SEPARATION bytes of separation the value of
 | 
						|
 * HASHFRACTION is calculated as:
 | 
						|
 *
 | 
						|
 *      HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
 | 
						|
 *
 | 
						|
 * For the VAX, the shortest two call sequence is:
 | 
						|
 *
 | 
						|
 *      calls   $0,(r0)
 | 
						|
 *      calls   $0,(r0)
 | 
						|
 *
 | 
						|
 * which is separated by only three bytes, thus HASHFRACTION is
 | 
						|
 * calculated as:
 | 
						|
 *
 | 
						|
 *      HASHFRACTION = 3 / (2 * 2 - 1) = 1
 | 
						|
 *
 | 
						|
 * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
 | 
						|
 * is less than three, this algorithm will not work!
 | 
						|
 */
 | 
						|
#define	HASHFRACTION 1
 | 
						|
 | 
						|
/*
 | 
						|
 * Percent of text space to allocate for tostructs with a minimum:
 | 
						|
 */
 | 
						|
#define ARCDENSITY	2
 | 
						|
#define MINARCS		50
 | 
						|
 | 
						|
struct tostruct
 | 
						|
  {
 | 
						|
    char *selfpc;
 | 
						|
    int count;
 | 
						|
    unsigned short link;
 | 
						|
  };
 | 
						|
 | 
						|
/*
 | 
						|
 * A raw arc, with pointers to the calling site and the called site
 | 
						|
 * and a count.  Everything is defined in terms of characters so
 | 
						|
 * as to get a packed representation (otherwise, different compilers
 | 
						|
 * might introduce different padding):
 | 
						|
 */
 | 
						|
struct rawarc
 | 
						|
  {
 | 
						|
    unsigned long raw_frompc;
 | 
						|
    unsigned long raw_selfpc;
 | 
						|
    int raw_count;
 | 
						|
  };
 | 
						|
 | 
						|
/*
 | 
						|
 * General rounding functions:
 | 
						|
 */
 | 
						|
#define ROUNDDOWN(x,y)	(((x)/(y))*(y))
 | 
						|
#define ROUNDUP(x,y)	((((x)+(y)-1)/(y))*(y))
 | 
						|
 | 
						|
 | 
						|
#ifdef __alpha
 | 
						|
extern char *sbrk ();
 | 
						|
#endif
 | 
						|
 | 
						|
    /*
 | 
						|
     *  froms is actually a bunch of unsigned shorts indexing tos
 | 
						|
     */
 | 
						|
static int profiling = 3;
 | 
						|
static unsigned short *froms;
 | 
						|
static struct tostruct *tos = 0;
 | 
						|
static long tolimit = 0;
 | 
						|
static char *s_lowpc = 0;
 | 
						|
static char *s_highpc = 0;
 | 
						|
static unsigned long s_textsize = 0;
 | 
						|
 | 
						|
static int ssiz;
 | 
						|
static char *sbuf;
 | 
						|
static int s_scale;
 | 
						|
    /* see profil(2) where this is describe (incorrectly) */
 | 
						|
#define		SCALE_1_TO_1	0x10000L
 | 
						|
 | 
						|
#define	MSG "No space for profiling buffer(s)\n"
 | 
						|
 | 
						|
#ifndef O_BINARY
 | 
						|
#define O_BINARY 0
 | 
						|
#endif
 | 
						|
 | 
						|
static void
 | 
						|
moncleanup ()
 | 
						|
{
 | 
						|
  int fd;
 | 
						|
  int fromindex;
 | 
						|
  int endfrom;
 | 
						|
  char *frompc;
 | 
						|
  int toindex;
 | 
						|
  struct rawarc rawarc;
 | 
						|
 | 
						|
  profiling = 1;
 | 
						|
  fd = open ("gmon.out", O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0666);
 | 
						|
  if (fd < 0)
 | 
						|
    {
 | 
						|
      perror ("mcount: gmon.out");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "[mcleanup] sbuf 0x%x ssiz %d\n", sbuf, ssiz);
 | 
						|
#endif /* DEBUG */
 | 
						|
  write (fd, sbuf, ssiz);
 | 
						|
  endfrom = s_textsize / (HASHFRACTION * sizeof (*froms));
 | 
						|
  for (fromindex = 0; fromindex < endfrom; fromindex++)
 | 
						|
    {
 | 
						|
      if (froms[fromindex] == 0)
 | 
						|
	{
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof (*froms));
 | 
						|
      for (toindex = froms[fromindex]; toindex != 0;
 | 
						|
	   toindex = tos[toindex].link)
 | 
						|
	{
 | 
						|
#ifdef DEBUG
 | 
						|
	  fprintf (stderr,
 | 
						|
		   "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n",
 | 
						|
		   frompc, tos[toindex].selfpc, tos[toindex].count);
 | 
						|
#endif /* DEBUG */
 | 
						|
	  rawarc.raw_frompc = (unsigned long) frompc;
 | 
						|
	  rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc;
 | 
						|
	  rawarc.raw_count = tos[toindex].count;
 | 
						|
	  write (fd, &rawarc, sizeof rawarc);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  close (fd);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
monstartup (lowpc, highpc)
 | 
						|
     char *lowpc;
 | 
						|
     char *highpc;
 | 
						|
{
 | 
						|
  int monsize;
 | 
						|
  char *buffer;
 | 
						|
  register int o;
 | 
						|
 | 
						|
  atexit (moncleanup);
 | 
						|
 | 
						|
  /*
 | 
						|
   *    round lowpc and highpc to multiples of the density we're using
 | 
						|
   *    so the rest of the scaling (here and in gprof) stays in ints.
 | 
						|
   */
 | 
						|
  lowpc = (char *)
 | 
						|
    ROUNDDOWN ((unsigned) lowpc, HISTFRACTION * sizeof (HISTCOUNTER));
 | 
						|
  s_lowpc = lowpc;
 | 
						|
  highpc = (char *)
 | 
						|
    ROUNDUP ((unsigned) highpc, HISTFRACTION * sizeof (HISTCOUNTER));
 | 
						|
  s_highpc = highpc;
 | 
						|
  s_textsize = highpc - lowpc;
 | 
						|
  monsize = (s_textsize / HISTFRACTION) + sizeof (struct phdr);
 | 
						|
  buffer = sbrk (monsize);
 | 
						|
  if (buffer == (char *) -1)
 | 
						|
    {
 | 
						|
      write (2, MSG, sizeof (MSG));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  froms = (unsigned short *) sbrk (s_textsize / HASHFRACTION);
 | 
						|
  if (froms == (unsigned short *) -1)
 | 
						|
    {
 | 
						|
      write (2, MSG, sizeof (MSG));
 | 
						|
      froms = 0;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  tolimit = s_textsize * ARCDENSITY / 100;
 | 
						|
  if (tolimit < MINARCS)
 | 
						|
    {
 | 
						|
      tolimit = MINARCS;
 | 
						|
    }
 | 
						|
  else if (tolimit > 65534)
 | 
						|
    {
 | 
						|
      tolimit = 65534;
 | 
						|
    }
 | 
						|
  tos = (struct tostruct *) sbrk (tolimit * sizeof (struct tostruct));
 | 
						|
  if (tos == (struct tostruct *) -1)
 | 
						|
    {
 | 
						|
      write (2, MSG, sizeof (MSG));
 | 
						|
      froms = 0;
 | 
						|
      tos = 0;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  tos[0].link = 0;
 | 
						|
  sbuf = buffer;
 | 
						|
  ssiz = monsize;
 | 
						|
  ((struct phdr *) buffer)->lpc = lowpc;
 | 
						|
  ((struct phdr *) buffer)->hpc = highpc;
 | 
						|
  ((struct phdr *) buffer)->ncnt = ssiz;
 | 
						|
  monsize -= sizeof (struct phdr);
 | 
						|
  if (monsize <= 0)
 | 
						|
    return;
 | 
						|
  o = highpc - lowpc;
 | 
						|
  if (monsize < o)
 | 
						|
#if 0
 | 
						|
    s_scale = ((float) monsize / o) * SCALE_1_TO_1;
 | 
						|
#else /* avoid floating point */
 | 
						|
    {
 | 
						|
      int quot = o / monsize;
 | 
						|
 | 
						|
      if (quot >= 0x10000)
 | 
						|
	s_scale = 1;
 | 
						|
      else if (quot >= 0x100)
 | 
						|
	s_scale = 0x10000 / quot;
 | 
						|
      else if (o >= 0x800000)
 | 
						|
	s_scale = 0x1000000 / (o / (monsize >> 8));
 | 
						|
      else
 | 
						|
	s_scale = 0x1000000 / ((o << 8) / monsize);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  else
 | 
						|
    s_scale = SCALE_1_TO_1;
 | 
						|
  profiling = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* These happen to be in the right place because of how crt0 works */
 | 
						|
extern char __attribute__((far)) _start;
 | 
						|
extern char __attribute__((far)) _etext;
 | 
						|
 | 
						|
void
 | 
						|
__mep_mcount_2 (selfpc, frompcindex)
 | 
						|
     char *selfpc;
 | 
						|
     unsigned short *frompcindex;
 | 
						|
{
 | 
						|
  struct tostruct *top;
 | 
						|
  struct tostruct *prevtop;
 | 
						|
  long toindex;
 | 
						|
  static int initialized = 0;
 | 
						|
 | 
						|
  if (!initialized)
 | 
						|
    {
 | 
						|
      initialized = 1;
 | 
						|
      monstartup (&_start, &_etext);
 | 
						|
    }
 | 
						|
 | 
						|
  /*
 | 
						|
   *    check that we are profiling
 | 
						|
   *    and that we aren't recursively invoked.
 | 
						|
   */
 | 
						|
  if (profiling)
 | 
						|
    {
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
  profiling++;
 | 
						|
  /*
 | 
						|
   *    check that frompcindex is a reasonable pc value.
 | 
						|
   *    for example:    signal catchers get called from the stack,
 | 
						|
   *                    not from text space.  too bad.
 | 
						|
   */
 | 
						|
  frompcindex = (unsigned short *) ((long) frompcindex - (long) s_lowpc);
 | 
						|
  if ((unsigned long) frompcindex > s_textsize)
 | 
						|
    {
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
  frompcindex =
 | 
						|
    &froms[((long) frompcindex) / (HASHFRACTION * sizeof (*froms))];
 | 
						|
  toindex = *frompcindex;
 | 
						|
  if (toindex == 0)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
       *  first time traversing this arc
 | 
						|
       */
 | 
						|
      toindex = ++tos[0].link;
 | 
						|
      if (toindex >= tolimit)
 | 
						|
	{
 | 
						|
	  goto overflow;
 | 
						|
	}
 | 
						|
      *frompcindex = toindex;
 | 
						|
      top = &tos[toindex];
 | 
						|
      top->selfpc = selfpc;
 | 
						|
      top->count = 1;
 | 
						|
      top->link = 0;
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
  top = &tos[toindex];
 | 
						|
  if (top->selfpc == selfpc)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
       *  arc at front of chain; usual case.
 | 
						|
       */
 | 
						|
      top->count++;
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
  /*
 | 
						|
   *    have to go looking down chain for it.
 | 
						|
   *    top points to what we are looking at,
 | 
						|
   *    prevtop points to previous top.
 | 
						|
   *    we know it is not at the head of the chain.
 | 
						|
   */
 | 
						|
  for (; /* goto done */ ;)
 | 
						|
    {
 | 
						|
      if (top->link == 0)
 | 
						|
	{
 | 
						|
	  /*
 | 
						|
	   *        top is end of the chain and none of the chain
 | 
						|
	   *        had top->selfpc == selfpc.
 | 
						|
	   *        so we allocate a new tostruct
 | 
						|
	   *        and link it to the head of the chain.
 | 
						|
	   */
 | 
						|
	  toindex = ++tos[0].link;
 | 
						|
	  if (toindex >= tolimit)
 | 
						|
	    {
 | 
						|
	      goto overflow;
 | 
						|
	    }
 | 
						|
	  top = &tos[toindex];
 | 
						|
	  top->selfpc = selfpc;
 | 
						|
	  top->count = 1;
 | 
						|
	  top->link = *frompcindex;
 | 
						|
	  *frompcindex = toindex;
 | 
						|
	  goto done;
 | 
						|
	}
 | 
						|
      /*
 | 
						|
       *  otherwise, check the next arc on the chain.
 | 
						|
       */
 | 
						|
      prevtop = top;
 | 
						|
      top = &tos[top->link];
 | 
						|
      if (top->selfpc == selfpc)
 | 
						|
	{
 | 
						|
	  /*
 | 
						|
	   *        there it is.
 | 
						|
	   *        increment its count
 | 
						|
	   *        move it to the head of the chain.
 | 
						|
	   */
 | 
						|
	  top->count++;
 | 
						|
	  toindex = prevtop->link;
 | 
						|
	  prevtop->link = top->link;
 | 
						|
	  top->link = *frompcindex;
 | 
						|
	  *frompcindex = toindex;
 | 
						|
	  goto done;
 | 
						|
	}
 | 
						|
 | 
						|
    }
 | 
						|
done:
 | 
						|
  profiling--;
 | 
						|
  /* and fall through */
 | 
						|
out:
 | 
						|
  return;			/* normal return restores saved registers */
 | 
						|
 | 
						|
overflow:
 | 
						|
  profiling++;			/* halt further profiling */
 | 
						|
#   define	TOLIMIT	"mcount: tos overflow\n"
 | 
						|
  write (2, TOLIMIT, sizeof (TOLIMIT));
 | 
						|
  goto out;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 |