423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /*-
 | |
|  * Copyright (c) 1983, 1992, 1993
 | |
|  *	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.
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| /* This file implements a subset of the profiling support functions.
 | |
|    It has been copied and adapted from mcount.c, gmon.c and gmon.h in
 | |
|    the glibc sources.
 | |
|    Since we do not have access to a timer interrupt in the simulator
 | |
|    the histogram and basic block information is not generated.  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| 
 | |
| /* 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 example, on 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!
 | |
| 
 | |
|    In practice, however, call instructions are rarely at a minimal
 | |
|    distance.  Hence, we will define HASHFRACTION to be 2 across all
 | |
|    architectures.  This saves a reasonable amount of space for
 | |
|    profiling data structures without (in practice) sacrificing
 | |
|    any granularity.  */
 | |
| #define	HASHFRACTION	2
 | |
| 
 | |
| /* Percent of text space to allocate for tostructs.
 | |
|    This is a heuristic; we will fail with a warning when profiling
 | |
|    programs with a very large number of very small functions, but
 | |
|    that's normally OK.
 | |
|    2 is probably still a good value for normal programs.
 | |
|    Profiling a test case with 64000 small functions will work if
 | |
|    you raise this value to 3 and link statically (which bloats the
 | |
|    text size, thus raising the number of arcs expected by the heuristic).  */
 | |
| #define ARCDENSITY	3
 | |
| 
 | |
| /* Always allocate at least this many tostructs.  This hides the
 | |
|    inadequacy of the ARCDENSITY heuristic, at least for small programs.  */
 | |
| #define MINARCS		50
 | |
| 
 | |
| /* Maximum number of arcs we want to allow.
 | |
|    Used to be max representable value of ARCINDEX minus 2, but now
 | |
|    that ARCINDEX is a long, that's too large; we don't really want
 | |
|    to allow a 48 gigabyte table.
 | |
|    The old value of 1<<16 wasn't high enough in practice for large C++
 | |
|    programs; will 1<<20 be adequate for long?  FIXME  */
 | |
| #define MAXARCS		(1L << 20)
 | |
| 
 | |
| #define	SCALE_1_TO_1	0x10000L
 | |
| 
 | |
| #define	GMON_MAGIC	"gmon"	/* Magic cookie.  */
 | |
| #define GMON_VERSION	1	/* Version number.  */
 | |
| 
 | |
| 
 | |
| /* General rounding functions.  */
 | |
| #define ROUNDDOWN(x ,y)	(((x) / (y)) * (y))
 | |
| #define ROUNDUP(x, y)	((((x) + (y) - 1) / (y)) * (y))
 | |
| 
 | |
| struct tostruct
 | |
| {
 | |
|   unsigned long selfpc;
 | |
|   unsigned long	count;
 | |
|   unsigned long	link;
 | |
| };
 | |
| 
 | |
| /* Possible states of profiling.  */
 | |
| enum profiling_state
 | |
| {
 | |
|   GMON_PROF_OFF,
 | |
|   GMON_PROF_ON,
 | |
|   GMON_PROF_BUSY,
 | |
|   GMON_PROF_ERROR
 | |
| };
 | |
| 
 | |
| /* The profiling data structures are housed in this structure.  */
 | |
| struct gmonparam
 | |
| {
 | |
|   enum profiling_state state;
 | |
|   unsigned short *     kcount;
 | |
|   unsigned long	       kcountsize;
 | |
|   unsigned long	*      froms;
 | |
|   unsigned long	       fromssize;
 | |
|   struct tostruct *    tos;
 | |
|   unsigned long	       tossize;
 | |
|   long		       tolimit;
 | |
|   unsigned long	       lowpc;
 | |
|   unsigned long	       highpc;
 | |
|   unsigned long	       textsize;
 | |
|   unsigned long	       hashfraction;
 | |
|   long		       log_hashfraction;
 | |
| };
 | |
| 
 | |
| /* Raw header as it appears in the gmon.out file (without padding).
 | |
|    This header always comes first and is then followed by a series
 | |
|    records defined below.  */
 | |
| struct gmon_hdr
 | |
| {
 | |
|   char cookie[4];
 | |
|   char version[4];
 | |
|   char spare[3 * 4];
 | |
| };
 | |
| 
 | |
| /* Types of records in this file.  */
 | |
| typedef enum
 | |
| {
 | |
|   GMON_TAG_TIME_HIST = 0,
 | |
|   GMON_TAG_CG_ARC = 1,
 | |
| } GMON_Record_Tag;
 | |
| 
 | |
| struct gmon_cg_arc_record
 | |
| {
 | |
|   char tag;                             /* Set to GMON_TAG_CG_ARC.  */
 | |
|   char from_pc[sizeof (char *)];	/* Address within caller's body.  */
 | |
|   char self_pc[sizeof (char *)];	/* Address within callee's body.  */
 | |
|   char count[4];			/* Number of arc traversals.  */
 | |
| };
 | |
| 
 | |
| 
 | |
| /* Forward declarations.  */
 | |
| void _mcount_internal (unsigned long);
 | |
| void _monstartup (unsigned long, unsigned long);
 | |
| void _mcleanup (void);
 | |
| 
 | |
| static struct gmonparam _gmonparam;
 | |
| 
 | |
| void
 | |
| _mcount_internal (unsigned long frompc)
 | |
| {
 | |
|   unsigned long      selfpc = frompc;
 | |
|   unsigned long *    frompcindex;
 | |
|   struct tostruct *  top;
 | |
|   struct tostruct *  prevtop;
 | |
|   struct gmonparam * p;
 | |
|   unsigned long      toindex;
 | |
|   int                i;
 | |
| 
 | |
|   p = & _gmonparam;
 | |
| 
 | |
|   /* Check that we are profiling and that we aren't recursively invoked.
 | |
|      NB/ This version is not thread-safe.  */
 | |
|   if (p->state != GMON_PROF_ON)
 | |
|     return;
 | |
|   p->state = GMON_PROF_BUSY;
 | |
| 
 | |
|   /* Check that frompcindex is a reasonable pc value.
 | |
|      For example: signal catchers get called from the stack,
 | |
|      not from text space.  Too bad.  */
 | |
|   frompc -= p->lowpc;
 | |
|   if (frompc > p->textsize)
 | |
|     goto done;
 | |
| 
 | |
|   i = frompc >> p->log_hashfraction;
 | |
| 
 | |
|   frompcindex = p->froms + i;
 | |
|   toindex = * frompcindex;
 | |
| 
 | |
|   if (toindex == 0)
 | |
|     {
 | |
|       /* First time traversing this arc.  */
 | |
|       toindex = ++ p->tos[0].link;
 | |
|       if (toindex >= p->tolimit)
 | |
| 	/* Halt further profiling.  */
 | |
| 	goto overflow;
 | |
| 
 | |
|       * frompcindex = toindex;
 | |
|       top = p->tos + toindex;
 | |
|       top->selfpc = selfpc;
 | |
|       top->count = 1;
 | |
|       top->link = 0;
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|   top = p->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 (;;)
 | |
|     {
 | |
|       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 = ++ p->tos[0].link;
 | |
| 	  if (toindex >= p->tolimit)
 | |
| 	    goto overflow;
 | |
| 
 | |
| 	  top = p->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 = p->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:
 | |
|   p->state = GMON_PROF_ON;
 | |
|   return;
 | |
| 
 | |
|  overflow:
 | |
|   p->state = GMON_PROF_ERROR;
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void
 | |
| _monstartup (unsigned long lowpc, unsigned long highpc)
 | |
| {
 | |
|   char * cp;
 | |
|   struct gmonparam * p = & _gmonparam;
 | |
| 
 | |
|   /* If the calloc() function has been instrumented we must make sure
 | |
|      that it is not profiled until we are ready.  */
 | |
|   p->state = GMON_PROF_BUSY;
 | |
| 
 | |
|   /* 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.  */
 | |
|   p->lowpc            = ROUNDDOWN (lowpc, HISTFRACTION * sizeof (* p->kcount));
 | |
|   p->highpc           = ROUNDUP (highpc, HISTFRACTION * sizeof (* p->kcount));
 | |
|   p->textsize         = p->highpc - p->lowpc;
 | |
|   p->kcountsize       = ROUNDUP (p->textsize / HISTFRACTION, sizeof (*p->froms));
 | |
|   p->hashfraction     = HASHFRACTION;
 | |
|   p->log_hashfraction = -1;
 | |
|   p->log_hashfraction = ffs (p->hashfraction * sizeof (*p->froms)) - 1;
 | |
|   p->fromssize        = p->textsize / HASHFRACTION;
 | |
|   p->tolimit          = p->textsize * ARCDENSITY / 100;
 | |
| 
 | |
|   if (p->tolimit < MINARCS)
 | |
|     p->tolimit = MINARCS;
 | |
|   else if (p->tolimit > MAXARCS)
 | |
|     p->tolimit = MAXARCS;
 | |
| 
 | |
|   p->tossize          = p->tolimit * sizeof (struct tostruct);
 | |
| 
 | |
|   cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
 | |
|   if (cp == NULL)
 | |
|     {
 | |
|       write (2, "monstartup: out of memory\n", 26);
 | |
|       p->tos = NULL;
 | |
|       p->state = GMON_PROF_ERROR;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   p->tos = (struct tostruct *) cp;
 | |
|   cp += p->tossize;
 | |
|   p->kcount = (unsigned short *) cp;
 | |
|   cp += p->kcountsize;
 | |
|   p->froms = (unsigned long *) cp;
 | |
| 
 | |
|   p->tos[0].link = 0;
 | |
|   p->state = GMON_PROF_ON;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| write_call_graph (int fd)
 | |
| {
 | |
| #define NARCS_PER_WRITE	32
 | |
| 
 | |
|   struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITE]
 | |
|     __attribute__ ((aligned (__alignof__ (char *))));
 | |
|   unsigned long from_index;
 | |
|   unsigned long to_index;
 | |
|   unsigned long from_len;
 | |
|   unsigned long frompc;
 | |
|   int nfilled;
 | |
| 
 | |
|   for (nfilled = 0; nfilled < NARCS_PER_WRITE; ++ nfilled)
 | |
|     raw_arc[nfilled].tag = GMON_TAG_CG_ARC;
 | |
| 
 | |
|   nfilled = 0;
 | |
|   from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
 | |
| 
 | |
|   for (from_index = 0; from_index < from_len; ++from_index)
 | |
|     {
 | |
|       if (_gmonparam.froms[from_index] == 0)
 | |
| 	continue;
 | |
| 
 | |
|       frompc = _gmonparam.lowpc;
 | |
|       frompc += (from_index * _gmonparam.hashfraction
 | |
| 		 * sizeof (*_gmonparam.froms));
 | |
| 
 | |
|       for (to_index = _gmonparam.froms[from_index];
 | |
| 	   to_index != 0;
 | |
| 	   to_index = _gmonparam.tos[to_index].link)
 | |
| 	{
 | |
| 	  struct gmon_cg_arc_record * arc = raw_arc + nfilled;
 | |
| 
 | |
| 	  memcpy (arc->from_pc, & frompc, sizeof (arc->from_pc));
 | |
| 	  memcpy (arc->self_pc, & _gmonparam.tos[to_index].selfpc, sizeof (arc->self_pc));
 | |
| 	  memcpy (arc->count,   & _gmonparam.tos[to_index].count, sizeof (arc->count));
 | |
| 
 | |
| 	  if (++ nfilled == NARCS_PER_WRITE)
 | |
| 	    {
 | |
| 	      write (fd, raw_arc, sizeof raw_arc);
 | |
| 	      nfilled = 0;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (nfilled > 0)
 | |
|     write (fd, raw_arc, nfilled * sizeof (raw_arc[0]));
 | |
| }
 | |
| 
 | |
| #include <errno.h>
 | |
| 
 | |
| static void
 | |
| write_gmon (void)
 | |
| {
 | |
|   struct gmon_hdr ghdr __attribute__ ((aligned (__alignof__ (int))));
 | |
|   int fd;
 | |
| 
 | |
|   fd = open ("gmon.out", O_CREAT|O_TRUNC|O_WRONLY, 0666);
 | |
|   if (fd < 0)
 | |
|     {
 | |
|       write (2, "_mcleanup: could not create gmon.out\n", 37);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   /* Write gmon.out header: */
 | |
|   memset (& ghdr, '\0', sizeof (ghdr));
 | |
|   memcpy (ghdr.cookie, GMON_MAGIC, sizeof (ghdr.cookie));
 | |
|   * (unsigned long *) ghdr.version = GMON_VERSION;
 | |
|   write (fd, & ghdr, sizeof (ghdr));
 | |
| 
 | |
|   /* We do not have histogram or basic block information,
 | |
|      so we do not generate these parts of the gmon.out file.  */
 | |
| 
 | |
|   /* Write call-graph.  */
 | |
|   write_call_graph (fd);
 | |
| 
 | |
|   close (fd);
 | |
| }
 | |
| 
 | |
| void
 | |
| _mcleanup (void)
 | |
| {
 | |
|   if (_gmonparam.state != GMON_PROF_ERROR)
 | |
|     {
 | |
|       _gmonparam.state = GMON_PROF_OFF;
 | |
|       write_gmon ();
 | |
|     }
 | |
| 
 | |
|   /* Free the memory.  */
 | |
|   if (_gmonparam.tos != NULL)
 | |
|     {
 | |
|       free (_gmonparam.tos);
 | |
|       _gmonparam.tos = NULL;
 | |
|     }
 | |
| }
 |