* mep: New target directory.
        * README: Add MeP.
        * configure.in: Add support for MeP.
        * configure: Regenerated.
        * mep/configure.in: New file.
        * mep/configure: Ditto.
        * mep/Makefile.in: Ditto.
        * mep/aclocal.m4: Ditto.
        * mep/crt0.S: Ditto.
        * mep/crtn.S: Ditto.
        * mep/sim-crt0.S: Ditto.
        * mep/sim-crtn.S: Ditto.
        * mep/fmax.ld: Ditto.
        * mep/gcov-io.h: Ditto.
        * mep/gmap_default.ld: Ditto.
        * mep/handlers.c: Ditto.
        * mep/h_reset.c: Ditto.
        * mep/isatty.c: Ditto.
        * mep/mep-bb.c: Ditto.
        * mep/mep-gmon.c: Ditto.
        * mep/min.ld: Ditto.
        * mep/read.c: Ditto.
        * mep/sbrk.c: Ditto.
        * mep/sdram-crt0.S: Ditto.
        * mep/sdram-crtn.S: Ditto.
        * mep/simnovec-crt0.S: Ditto.
        * mep/simple.ld: Ditto.
        * mep/simsdran-crt0.S: Ditto.
        * mep/syscalls.S: Ditto.
        * mep/write.c: Ditto.
		
	
		
			
				
	
	
		
			1072 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1072 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2000-2001  Red Hat, Inc. All rights reserved.
 | 
						|
 *
 | 
						|
 * This copyrighted material is made available to anyone wishing to use, modify,
 | 
						|
 * copy, or redistribute it subject to the terms and conditions of the BSD 
 | 
						|
 * License.  This program is distributed in the hope that it will be useful, 
 | 
						|
 * but WITHOUT ANY WARRANTY expressed or implied, including the implied 
 | 
						|
 * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  A copy 
 | 
						|
 * of this license is available at http://www.opensource.org/licenses. Any 
 | 
						|
 * Red Hat trademarks that are incorporated in the source code or documentation
 | 
						|
 * are not subject to the BSD License and may only be used or replicated with 
 | 
						|
 * the express permission of Red Hat, Inc.
 | 
						|
 */
 | 
						|
 | 
						|
/* Structure emitted by -a  */
 | 
						|
struct bb
 | 
						|
{
 | 
						|
  long zero_word;
 | 
						|
  const char *filename;
 | 
						|
  long *counts;
 | 
						|
  long ncounts;
 | 
						|
  struct bb *next;
 | 
						|
  const unsigned long *addresses;
 | 
						|
 | 
						|
  /* Older GCC's did not emit these fields.  */
 | 
						|
  long nwords;
 | 
						|
  const char **functions;
 | 
						|
  const long *line_nums;
 | 
						|
  const char **filenames;
 | 
						|
  char *flags;
 | 
						|
};
 | 
						|
 | 
						|
/* Simple minded basic block profiling output dumper for
 | 
						|
   systems that don't provide tcov support.  At present,
 | 
						|
   it requires atexit and stdio.  */
 | 
						|
 | 
						|
#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
 | 
						|
#include <stdio.h>
 | 
						|
#include <time.h>
 | 
						|
char *ctime (const time_t *);
 | 
						|
 | 
						|
/*#include "gbl-ctors.h"*/
 | 
						|
#include "gcov-io.h"
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
static struct bb *bb_head;
 | 
						|
 | 
						|
static int num_digits (long value, int base) __attribute__ ((const));
 | 
						|
 | 
						|
/* Return the number of digits needed to print a value */
 | 
						|
/* __inline__ */ static int num_digits (long value, int base)
 | 
						|
{
 | 
						|
  int minus = (value < 0 && base != 16);
 | 
						|
  unsigned long v = (minus) ? -value : value;
 | 
						|
  int ret = minus;
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      v /= base;
 | 
						|
      ret++;
 | 
						|
    }
 | 
						|
  while (v);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
__bb_exit_func (void)
 | 
						|
{
 | 
						|
  FILE *da_file, *file;
 | 
						|
  long time_value;
 | 
						|
  int i;
 | 
						|
 | 
						|
  if (bb_head == 0)
 | 
						|
    return;
 | 
						|
 | 
						|
  i = strlen (bb_head->filename) - 3;
 | 
						|
 | 
						|
  if (!strcmp (bb_head->filename+i, ".da"))
 | 
						|
    {
 | 
						|
      /* Must be -fprofile-arcs not -a.
 | 
						|
	 Dump data in a form that gcov expects.  */
 | 
						|
 | 
						|
      struct bb *ptr;
 | 
						|
 | 
						|
      for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
 | 
						|
	{
 | 
						|
	  int firstchar;
 | 
						|
 | 
						|
	  /* Make sure the output file exists -
 | 
						|
	     but don't clobber exiting data.  */
 | 
						|
	  if ((da_file = fopen (ptr->filename, "a")) != 0)
 | 
						|
	    fclose (da_file);
 | 
						|
 | 
						|
	  /* Need to re-open in order to be able to write from the start.  */
 | 
						|
	  da_file = fopen (ptr->filename, "r+b");
 | 
						|
	  /* Some old systems might not allow the 'b' mode modifier.
 | 
						|
	     Therefore, try to open without it.  This can lead to a race
 | 
						|
	     condition so that when you delete and re-create the file, the
 | 
						|
	     file might be opened in text mode, but then, you shouldn't
 | 
						|
	     delete the file in the first place.  */
 | 
						|
	  if (da_file == 0)
 | 
						|
	    da_file = fopen (ptr->filename, "r+");
 | 
						|
	  if (da_file == 0)
 | 
						|
	    {
 | 
						|
	      fprintf (stderr, "arc profiling: Can't open output file %s.\n",
 | 
						|
		       ptr->filename);
 | 
						|
	      continue;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* After a fork, another process might try to read and/or write
 | 
						|
	     the same file simultanously.  So if we can, lock the file to
 | 
						|
	     avoid race conditions.  */
 | 
						|
 | 
						|
	  /* If the file is not empty, and the number of counts in it is the
 | 
						|
	     same, then merge them in.  */
 | 
						|
	  firstchar = fgetc (da_file);
 | 
						|
	  if (firstchar == EOF)
 | 
						|
	    {
 | 
						|
	      if (ferror (da_file))
 | 
						|
		{
 | 
						|
		  fprintf (stderr, "arc profiling: Can't read output file ");
 | 
						|
		  perror (ptr->filename);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      long n_counts = 0;
 | 
						|
	      
 | 
						|
	      if (ungetc (firstchar, da_file) == EOF)
 | 
						|
		rewind (da_file);
 | 
						|
	      if (__read_long (&n_counts, da_file, 8) != 0)
 | 
						|
		{
 | 
						|
		  fprintf (stderr, "arc profiling: Can't read output file %s.\n",
 | 
						|
			   ptr->filename);
 | 
						|
		  continue;
 | 
						|
		}
 | 
						|
 | 
						|
	      if (n_counts == ptr->ncounts)
 | 
						|
		{
 | 
						|
		  int i;
 | 
						|
 | 
						|
		  for (i = 0; i < n_counts; i++)
 | 
						|
		    {
 | 
						|
		      long v = 0;
 | 
						|
 | 
						|
		      if (__read_long (&v, da_file, 8) != 0)
 | 
						|
			{
 | 
						|
			  fprintf (stderr, "arc profiling: Can't read output file %s.\n",
 | 
						|
				   ptr->filename);
 | 
						|
			  break;
 | 
						|
			}
 | 
						|
		      ptr->counts[i] += v;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
 | 
						|
	    }
 | 
						|
 | 
						|
	  rewind (da_file);
 | 
						|
 | 
						|
	  /* ??? Should first write a header to the file.  Preferably, a 4 byte
 | 
						|
	     magic number, 4 bytes containing the time the program was
 | 
						|
	     compiled, 4 bytes containing the last modification time of the
 | 
						|
	     source file, and 4 bytes indicating the compiler options used.
 | 
						|
 | 
						|
	     That way we can easily verify that the proper source/executable/
 | 
						|
	     data file combination is being used from gcov.  */
 | 
						|
 | 
						|
	  if (__write_long (ptr->ncounts, da_file, 8) != 0)
 | 
						|
	    {
 | 
						|
	      
 | 
						|
	      fprintf (stderr, "arc profiling: Error writing output file %s.\n",
 | 
						|
		       ptr->filename);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      int j;
 | 
						|
	      long *count_ptr = ptr->counts;
 | 
						|
	      int ret = 0;
 | 
						|
	      for (j = ptr->ncounts; j > 0; j--)
 | 
						|
		{
 | 
						|
		  if (__write_long (*count_ptr, da_file, 8) != 0)
 | 
						|
		    {
 | 
						|
		      ret=1;
 | 
						|
		      break;
 | 
						|
		    }
 | 
						|
		  count_ptr++;
 | 
						|
		}
 | 
						|
	      if (ret)
 | 
						|
		fprintf (stderr, "arc profiling: Error writing output file %s.\n",
 | 
						|
			 ptr->filename);
 | 
						|
	    }
 | 
						|
	  
 | 
						|
	  if (fclose (da_file) == EOF)
 | 
						|
	    fprintf (stderr, "arc profiling: Error closing output file %s.\n",
 | 
						|
		     ptr->filename);
 | 
						|
	}
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Must be basic block profiling.  Emit a human readable output file.  */
 | 
						|
 | 
						|
  file = fopen ("bb.out", "a");
 | 
						|
 | 
						|
  if (!file)
 | 
						|
    perror ("bb.out");
 | 
						|
 | 
						|
  else
 | 
						|
    {
 | 
						|
      struct bb *ptr;
 | 
						|
 | 
						|
      /* This is somewhat type incorrect, but it avoids worrying about
 | 
						|
	 exactly where time.h is included from.  It should be ok unless
 | 
						|
	 a void * differs from other pointer formats, or if sizeof (long)
 | 
						|
	 is < sizeof (time_t).  It would be nice if we could assume the
 | 
						|
	 use of rationale standards here.  */
 | 
						|
 | 
						|
      time ((void *) &time_value);
 | 
						|
      fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value));
 | 
						|
 | 
						|
      /* We check the length field explicitly in order to allow compatibility
 | 
						|
	 with older GCC's which did not provide it.  */
 | 
						|
 | 
						|
      for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
 | 
						|
	{
 | 
						|
	  int i;
 | 
						|
	  int func_p	= (ptr->nwords >= (long) sizeof (struct bb)
 | 
						|
			   && ptr->nwords <= 1000
 | 
						|
			   && ptr->functions);
 | 
						|
	  int line_p	= (func_p && ptr->line_nums);
 | 
						|
	  int file_p	= (func_p && ptr->filenames);
 | 
						|
	  int addr_p	= (ptr->addresses != 0);
 | 
						|
	  long ncounts	= ptr->ncounts;
 | 
						|
	  long cnt_max  = 0;
 | 
						|
	  long line_max = 0;
 | 
						|
	  long addr_max = 0;
 | 
						|
	  int file_len	= 0;
 | 
						|
	  int func_len	= 0;
 | 
						|
	  int blk_len	= num_digits (ncounts, 10);
 | 
						|
	  int cnt_len;
 | 
						|
	  int line_len;
 | 
						|
	  int addr_len;
 | 
						|
 | 
						|
	  fprintf (file, "File %s, %ld basic blocks \n\n",
 | 
						|
		   ptr->filename, ncounts);
 | 
						|
 | 
						|
	  /* Get max values for each field.  */
 | 
						|
	  for (i = 0; i < ncounts; i++)
 | 
						|
	    {
 | 
						|
	      const char *p;
 | 
						|
	      int len;
 | 
						|
 | 
						|
	      if (cnt_max < ptr->counts[i])
 | 
						|
		cnt_max = ptr->counts[i];
 | 
						|
 | 
						|
	      if (addr_p && (unsigned long) addr_max < ptr->addresses[i])
 | 
						|
		addr_max = ptr->addresses[i];
 | 
						|
 | 
						|
	      if (line_p && line_max < ptr->line_nums[i])
 | 
						|
		line_max = ptr->line_nums[i];
 | 
						|
 | 
						|
	      if (func_p)
 | 
						|
		{
 | 
						|
		  p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>";
 | 
						|
		  len = strlen (p);
 | 
						|
		  if (func_len < len)
 | 
						|
		    func_len = len;
 | 
						|
		}
 | 
						|
 | 
						|
	      if (file_p)
 | 
						|
		{
 | 
						|
		  p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>";
 | 
						|
		  len = strlen (p);
 | 
						|
		  if (file_len < len)
 | 
						|
		    file_len = len;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	  addr_len = num_digits (addr_max, 16);
 | 
						|
	  cnt_len  = num_digits (cnt_max, 10);
 | 
						|
	  line_len = num_digits (line_max, 10);
 | 
						|
 | 
						|
	  /* Now print out the basic block information.  */
 | 
						|
	  for (i = 0; i < ncounts; i++)
 | 
						|
	    {
 | 
						|
	      fprintf (file,
 | 
						|
		       "    Block #%*d: executed %*ld time(s)",
 | 
						|
		       blk_len, i+1,
 | 
						|
		       cnt_len, ptr->counts[i]);
 | 
						|
 | 
						|
	      if (addr_p)
 | 
						|
		fprintf (file, " address= 0x%.*lx", addr_len,
 | 
						|
			 ptr->addresses[i]);
 | 
						|
 | 
						|
	      if (func_p)
 | 
						|
		fprintf (file, " function= %-*s", func_len,
 | 
						|
			 (ptr->functions[i]) ? ptr->functions[i] : "<none>");
 | 
						|
 | 
						|
	      if (line_p)
 | 
						|
		fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]);
 | 
						|
 | 
						|
	      if (file_p)
 | 
						|
		fprintf (file, " file= %s",
 | 
						|
			 (ptr->filenames[i]) ? ptr->filenames[i] : "<none>");
 | 
						|
 | 
						|
	      fprintf (file, "\n");
 | 
						|
	    }
 | 
						|
 | 
						|
	  fprintf (file, "\n");
 | 
						|
	  fflush (file);
 | 
						|
	}
 | 
						|
 | 
						|
      fprintf (file, "\n\n");
 | 
						|
      fclose (file);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
__bb_init_func (struct bb *blocks)
 | 
						|
{
 | 
						|
  /* User is supposed to check whether the first word is non-0,
 | 
						|
     but just in case....  */
 | 
						|
 | 
						|
  if (blocks->zero_word)
 | 
						|
    return;
 | 
						|
 | 
						|
  /* Initialize destructor.  */
 | 
						|
  if (!bb_head)
 | 
						|
    atexit (__bb_exit_func);
 | 
						|
 | 
						|
  /* Set up linked list.  */
 | 
						|
  blocks->zero_word = 1;
 | 
						|
  blocks->next = bb_head;
 | 
						|
  bb_head = blocks;
 | 
						|
}
 | 
						|
 | 
						|
/* Called before fork or exec - write out profile information gathered so
 | 
						|
   far and reset it to zero.  This avoids duplication or loss of the
 | 
						|
   profile information gathered so far.  */
 | 
						|
void
 | 
						|
__bb_fork_func (void)
 | 
						|
{
 | 
						|
  struct bb *ptr;
 | 
						|
 | 
						|
  __bb_exit_func ();
 | 
						|
  for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
 | 
						|
    {
 | 
						|
      long i;
 | 
						|
      for (i = ptr->ncounts - 1; i >= 0; i--)
 | 
						|
	ptr->counts[i] = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#ifndef MACHINE_STATE_SAVE
 | 
						|
#define MACHINE_STATE_SAVE(ID)
 | 
						|
#endif
 | 
						|
#ifndef MACHINE_STATE_RESTORE
 | 
						|
#define MACHINE_STATE_RESTORE(ID)
 | 
						|
#endif
 | 
						|
 | 
						|
/* Number of buckets in hashtable of basic block addresses.  */
 | 
						|
 | 
						|
#define BB_BUCKETS 311
 | 
						|
 | 
						|
/* Maximum length of string in file bb.in.  */
 | 
						|
 | 
						|
#define BBINBUFSIZE 500
 | 
						|
 | 
						|
struct bb_edge
 | 
						|
{
 | 
						|
  struct bb_edge *next;
 | 
						|
  unsigned long src_addr;
 | 
						|
  unsigned long dst_addr;
 | 
						|
  unsigned long count;
 | 
						|
};
 | 
						|
 | 
						|
enum bb_func_mode
 | 
						|
{
 | 
						|
  TRACE_KEEP = 0, TRACE_ON = 1, TRACE_OFF = 2
 | 
						|
};
 | 
						|
 | 
						|
struct bb_func
 | 
						|
{
 | 
						|
  struct bb_func *next;
 | 
						|
  char *funcname;
 | 
						|
  char *filename;
 | 
						|
  enum bb_func_mode mode;
 | 
						|
};
 | 
						|
 | 
						|
/* This is the connection to the outside world.
 | 
						|
   The BLOCK_PROFILER macro must set __bb.blocks
 | 
						|
   and __bb.blockno.  */
 | 
						|
 | 
						|
struct {
 | 
						|
  unsigned long blockno;
 | 
						|
  struct bb *blocks;
 | 
						|
} __bb;
 | 
						|
 | 
						|
/* Vars to store addrs of source and destination basic blocks 
 | 
						|
   of a jump.  */
 | 
						|
 | 
						|
static unsigned long bb_src = 0;
 | 
						|
static unsigned long bb_dst = 0;
 | 
						|
 | 
						|
static FILE *bb_tracefile = (FILE *) 0;
 | 
						|
static struct bb_edge **bb_hashbuckets = (struct bb_edge **) 0;
 | 
						|
static struct bb_func *bb_func_head = (struct bb_func *) 0;
 | 
						|
static unsigned long bb_callcount = 0;
 | 
						|
static int bb_mode = 0;
 | 
						|
 | 
						|
static unsigned long *bb_stack = (unsigned long *) 0;
 | 
						|
static size_t bb_stacksize = 0;
 | 
						|
 | 
						|
static int reported = 0;
 | 
						|
 | 
						|
/* Trace modes:
 | 
						|
Always             :   Print execution frequencies of basic blocks
 | 
						|
                       to file bb.out.
 | 
						|
bb_mode & 1 != 0   :   Dump trace of basic blocks to file bbtrace[.gz]
 | 
						|
bb_mode & 2 != 0   :   Print jump frequencies to file bb.out.
 | 
						|
bb_mode & 4 != 0   :   Cut call instructions from basic block flow.
 | 
						|
bb_mode & 8 != 0   :   Insert return instructions in basic block flow.
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef HAVE_POPEN
 | 
						|
 | 
						|
/*#include <sys/types.h>*/
 | 
						|
#include <sys/stat.h>
 | 
						|
/*#include <malloc.h>*/
 | 
						|
 | 
						|
/* Commands executed by gopen.  */
 | 
						|
 | 
						|
#define GOPENDECOMPRESS "gzip -cd "
 | 
						|
#define GOPENCOMPRESS "gzip -c >"
 | 
						|
 | 
						|
/* Like fopen but pipes through gzip.  mode may only be "r" or "w".
 | 
						|
   If it does not compile, simply replace gopen by fopen and delete
 | 
						|
   '.gz' from any first parameter to gopen.  */
 | 
						|
 | 
						|
static FILE *
 | 
						|
gopen (char *fn, char *mode)
 | 
						|
{
 | 
						|
  int use_gzip;
 | 
						|
  char *p;
 | 
						|
 | 
						|
  if (mode[1])
 | 
						|
    return (FILE *) 0;
 | 
						|
 | 
						|
  if (mode[0] != 'r' && mode[0] != 'w') 
 | 
						|
    return (FILE *) 0;
 | 
						|
 | 
						|
  p = fn + strlen (fn)-1;
 | 
						|
  use_gzip = ((p[-1] == '.' && (p[0] == 'Z' || p[0] == 'z'))
 | 
						|
	      || (p[-2] == '.' && p[-1] == 'g' && p[0] == 'z'));
 | 
						|
 | 
						|
  if (use_gzip)
 | 
						|
    {
 | 
						|
      if (mode[0]=='r')
 | 
						|
        {
 | 
						|
          FILE *f;
 | 
						|
          char *s = (char *) malloc (sizeof (char) * strlen (fn)
 | 
						|
				     + sizeof (GOPENDECOMPRESS));
 | 
						|
          strcpy (s, GOPENDECOMPRESS);
 | 
						|
          strcpy (s + (sizeof (GOPENDECOMPRESS)-1), fn);
 | 
						|
          f = popen (s, mode);
 | 
						|
          free (s);
 | 
						|
          return f;
 | 
						|
        }
 | 
						|
 | 
						|
      else
 | 
						|
        {
 | 
						|
          FILE *f;
 | 
						|
          char *s = (char *) malloc (sizeof (char) * strlen (fn)
 | 
						|
				     + sizeof (GOPENCOMPRESS));
 | 
						|
          strcpy (s, GOPENCOMPRESS);
 | 
						|
          strcpy (s + (sizeof (GOPENCOMPRESS)-1), fn);
 | 
						|
          if (!(f = popen (s, mode)))
 | 
						|
            f = fopen (s, mode);
 | 
						|
          free (s);
 | 
						|
          return f;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  else
 | 
						|
    return fopen (fn, mode);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
gclose (FILE *f)
 | 
						|
{
 | 
						|
  struct stat buf;
 | 
						|
 | 
						|
  if (f != 0)
 | 
						|
    {
 | 
						|
      if (!fstat (fileno (f), &buf) && S_ISFIFO (buf.st_mode))
 | 
						|
        return pclose (f);
 | 
						|
 | 
						|
      return fclose (f);
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* HAVE_POPEN */
 | 
						|
 | 
						|
/* Called once per program.  */
 | 
						|
 | 
						|
static void
 | 
						|
__bb_exit_trace_func (void)
 | 
						|
{
 | 
						|
  FILE *file = fopen ("bb.out", "a");
 | 
						|
  struct bb_func *f;
 | 
						|
  struct bb *b;
 | 
						|
        
 | 
						|
  if (!file)
 | 
						|
    perror ("bb.out");
 | 
						|
 | 
						|
  if (bb_mode & 1)
 | 
						|
    {
 | 
						|
      if (!bb_tracefile)
 | 
						|
        perror ("bbtrace");
 | 
						|
      else
 | 
						|
#ifdef HAVE_POPEN
 | 
						|
        gclose (bb_tracefile);
 | 
						|
#else
 | 
						|
        fclose (bb_tracefile);
 | 
						|
#endif /* HAVE_POPEN */
 | 
						|
    }
 | 
						|
 | 
						|
  /* Check functions in `bb.in'.  */
 | 
						|
 | 
						|
  if (file)
 | 
						|
    {
 | 
						|
      long time_value;
 | 
						|
      const struct bb_func *p;
 | 
						|
      int printed_something = 0;
 | 
						|
      struct bb *ptr;
 | 
						|
      long blk;
 | 
						|
 | 
						|
      /* This is somewhat type incorrect.  */
 | 
						|
      time ((void *) &time_value);
 | 
						|
 | 
						|
      for (p = bb_func_head; p != (struct bb_func *) 0; p = p->next)
 | 
						|
        {
 | 
						|
          for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
 | 
						|
            {
 | 
						|
              if (!ptr->filename || (p->filename != (char *) 0 && strcmp (p->filename, ptr->filename)))
 | 
						|
                continue;
 | 
						|
              for (blk = 0; blk < ptr->ncounts; blk++)
 | 
						|
                {
 | 
						|
                  if (!strcmp (p->funcname, ptr->functions[blk]))
 | 
						|
                    goto found;
 | 
						|
                }
 | 
						|
            }
 | 
						|
  
 | 
						|
          if (!printed_something)
 | 
						|
            {
 | 
						|
              fprintf (file, "Functions in `bb.in' not executed during basic block profiling on %s\n", ctime ((void *) &time_value));
 | 
						|
              printed_something = 1;
 | 
						|
            }
 | 
						|
 | 
						|
          fprintf (file, "\tFunction %s", p->funcname);
 | 
						|
          if (p->filename)
 | 
						|
              fprintf (file, " of file %s", p->filename);
 | 
						|
          fprintf (file, "\n" );
 | 
						|
  
 | 
						|
found:        ;
 | 
						|
        }
 | 
						|
 | 
						|
      if (printed_something)
 | 
						|
       fprintf (file, "\n");
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
  if (bb_mode & 2)
 | 
						|
    {
 | 
						|
      if (!bb_hashbuckets)
 | 
						|
        {
 | 
						|
          if (!reported)
 | 
						|
            {
 | 
						|
              fprintf (stderr, "Profiler: out of memory\n");
 | 
						|
              reported = 1;
 | 
						|
            }
 | 
						|
          return;
 | 
						|
        }
 | 
						|
    
 | 
						|
      else if (file)
 | 
						|
        {
 | 
						|
          long time_value;
 | 
						|
          int i;
 | 
						|
          unsigned long addr_max = 0;
 | 
						|
          unsigned long cnt_max  = 0;
 | 
						|
          int cnt_len;
 | 
						|
          int addr_len;
 | 
						|
    
 | 
						|
          /* This is somewhat type incorrect, but it avoids worrying about
 | 
						|
             exactly where time.h is included from.  It should be ok unless
 | 
						|
             a void * differs from other pointer formats, or if sizeof (long)
 | 
						|
             is < sizeof (time_t).  It would be nice if we could assume the
 | 
						|
             use of rationale standards here.  */
 | 
						|
    
 | 
						|
          time ((void *) &time_value);
 | 
						|
          fprintf (file, "Basic block jump tracing");
 | 
						|
 | 
						|
          switch (bb_mode & 12)
 | 
						|
            {
 | 
						|
              case 0:
 | 
						|
                fprintf (file, " (with call)");
 | 
						|
              break;
 | 
						|
 | 
						|
              case 4:
 | 
						|
		/* Print nothing.  */
 | 
						|
              break;
 | 
						|
 | 
						|
              case 8:
 | 
						|
                fprintf (file, " (with call & ret)");
 | 
						|
              break;
 | 
						|
 | 
						|
              case 12:
 | 
						|
                fprintf (file, " (with ret)");
 | 
						|
              break;
 | 
						|
            }
 | 
						|
 | 
						|
          fprintf (file, " finished on %s\n", ctime ((void *) &time_value));
 | 
						|
    
 | 
						|
          for (i = 0; i < BB_BUCKETS; i++)
 | 
						|
            {
 | 
						|
               struct bb_edge *bucket = bb_hashbuckets[i];
 | 
						|
               for ( ; bucket; bucket = bucket->next )
 | 
						|
                 {
 | 
						|
                   if (addr_max < bucket->src_addr) 
 | 
						|
                     addr_max = bucket->src_addr;
 | 
						|
                   if (addr_max < bucket->dst_addr) 
 | 
						|
                     addr_max = bucket->dst_addr;
 | 
						|
                   if (cnt_max < bucket->count) 
 | 
						|
                     cnt_max = bucket->count;
 | 
						|
                 }
 | 
						|
            }
 | 
						|
          addr_len = num_digits (addr_max, 16);
 | 
						|
          cnt_len  = num_digits (cnt_max, 10);
 | 
						|
    
 | 
						|
          for ( i = 0; i < BB_BUCKETS; i++)
 | 
						|
            {
 | 
						|
               struct bb_edge *bucket = bb_hashbuckets[i];
 | 
						|
               for ( ; bucket; bucket = bucket->next )
 | 
						|
                 {
 | 
						|
                   fprintf (file,
 | 
						|
	"Jump from block 0x%.*lx to block 0x%.*lx executed %*lu time(s)\n", 
 | 
						|
                            addr_len, bucket->src_addr, 
 | 
						|
                            addr_len, bucket->dst_addr, 
 | 
						|
                            cnt_len, bucket->count);
 | 
						|
                 }
 | 
						|
            }
 | 
						|
  
 | 
						|
          fprintf (file, "\n");
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
   if (file)
 | 
						|
     fclose (file);
 | 
						|
 | 
						|
   /* Free allocated memory.  */
 | 
						|
 | 
						|
   f = bb_func_head;
 | 
						|
   while (f)
 | 
						|
     {
 | 
						|
       struct bb_func *old = f;
 | 
						|
 | 
						|
       f = f->next;
 | 
						|
       if (old->funcname) free (old->funcname);
 | 
						|
       if (old->filename) free (old->filename);
 | 
						|
       free (old);
 | 
						|
     }
 | 
						|
 | 
						|
   if (bb_stack)
 | 
						|
     free (bb_stack);
 | 
						|
 | 
						|
   if (bb_hashbuckets)
 | 
						|
     {
 | 
						|
       int i;
 | 
						|
 | 
						|
       for (i = 0; i < BB_BUCKETS; i++)
 | 
						|
         {
 | 
						|
           struct bb_edge *old, *bucket = bb_hashbuckets[i];
 | 
						|
 | 
						|
           while (bucket)
 | 
						|
             {
 | 
						|
               old = bucket;
 | 
						|
               bucket = bucket->next;
 | 
						|
               free (old);
 | 
						|
             }
 | 
						|
         }
 | 
						|
       free (bb_hashbuckets);
 | 
						|
     }
 | 
						|
 | 
						|
   for (b = bb_head; b; b = b->next)
 | 
						|
     if (b->flags) free (b->flags);
 | 
						|
}
 | 
						|
 | 
						|
/* Called once per program.  */
 | 
						|
 | 
						|
static void
 | 
						|
__bb_init_prg (void)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  char buf[BBINBUFSIZE];
 | 
						|
  const char *p;
 | 
						|
  const char *pos;
 | 
						|
  enum bb_func_mode m;
 | 
						|
  int i;
 | 
						|
 | 
						|
  /* Initialize destructor.  */
 | 
						|
  atexit (__bb_exit_func);
 | 
						|
 | 
						|
  if (!(file = fopen ("bb.in", "r")))
 | 
						|
    return;
 | 
						|
 | 
						|
  while(fgets (buf, BBINBUFSIZE, file) != 0)
 | 
						|
    {
 | 
						|
      i = strlen (buf);
 | 
						|
      if (buf[i-1] == '\n')
 | 
						|
	buf[--i] = '\0';
 | 
						|
 | 
						|
      p = buf;
 | 
						|
      if (*p == '-') 
 | 
						|
        { 
 | 
						|
          m = TRACE_OFF; 
 | 
						|
          p++; 
 | 
						|
        }
 | 
						|
      else 
 | 
						|
        { 
 | 
						|
          m = TRACE_ON; 
 | 
						|
        }
 | 
						|
      if (!strcmp (p, "__bb_trace__"))
 | 
						|
        bb_mode |= 1;
 | 
						|
      else if (!strcmp (p, "__bb_jumps__"))
 | 
						|
        bb_mode |= 2;
 | 
						|
      else if (!strcmp (p, "__bb_hidecall__"))
 | 
						|
        bb_mode |= 4;
 | 
						|
      else if (!strcmp (p, "__bb_showret__"))
 | 
						|
        bb_mode |= 8;
 | 
						|
      else 
 | 
						|
        {
 | 
						|
          struct bb_func *f = (struct bb_func *) malloc (sizeof (struct bb_func));
 | 
						|
          if (f)
 | 
						|
            {
 | 
						|
              unsigned long l;
 | 
						|
              f->next = bb_func_head;
 | 
						|
              if ((pos = strchr (p, ':')))
 | 
						|
                {
 | 
						|
                  if (!(f->funcname = (char *) malloc (strlen (pos+1)+1)))
 | 
						|
                    continue;
 | 
						|
                  strcpy (f->funcname, pos+1);
 | 
						|
                  l = pos-p;
 | 
						|
                  if ((f->filename = (char *) malloc (l+1)))
 | 
						|
                    {
 | 
						|
                      strncpy (f->filename, p, l);
 | 
						|
                      f->filename[l] = '\0';
 | 
						|
                    }
 | 
						|
                  else
 | 
						|
                    f->filename = (char *) 0;
 | 
						|
                }
 | 
						|
              else
 | 
						|
                {
 | 
						|
                  if (!(f->funcname = (char *) malloc (strlen (p)+1)))
 | 
						|
                    continue;
 | 
						|
                  strcpy (f->funcname, p);
 | 
						|
                  f->filename = (char *) 0;
 | 
						|
                }
 | 
						|
              f->mode = m;
 | 
						|
              bb_func_head = f;
 | 
						|
	    }
 | 
						|
         }
 | 
						|
    }
 | 
						|
  fclose (file);
 | 
						|
 | 
						|
#ifdef HAVE_POPEN 
 | 
						|
 | 
						|
  if (bb_mode & 1)
 | 
						|
      bb_tracefile = gopen ("bbtrace.gz", "w");
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
  if (bb_mode & 1)
 | 
						|
      bb_tracefile = fopen ("bbtrace", "w");
 | 
						|
 | 
						|
#endif /* HAVE_POPEN */
 | 
						|
 | 
						|
  if (bb_mode & 2)
 | 
						|
    {
 | 
						|
      bb_hashbuckets = (struct bb_edge **) 
 | 
						|
                   malloc (BB_BUCKETS * sizeof (struct bb_edge *));
 | 
						|
      if (bb_hashbuckets)
 | 
						|
	/* Use a loop here rather than calling bzero to avoid having to
 | 
						|
	   conditionalize its existance.  */
 | 
						|
	for (i = 0; i < BB_BUCKETS; i++)
 | 
						|
	  bb_hashbuckets[i] = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  if (bb_mode & 12)
 | 
						|
    {
 | 
						|
      bb_stacksize = 10;
 | 
						|
      bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack));
 | 
						|
    }
 | 
						|
 | 
						|
  /* Initialize destructor.  */
 | 
						|
  atexit (__bb_exit_trace_func);
 | 
						|
}
 | 
						|
 | 
						|
/* Called upon entering a basic block.  */
 | 
						|
 | 
						|
void
 | 
						|
__bb_trace_func (void)
 | 
						|
{
 | 
						|
  struct bb_edge *bucket;
 | 
						|
 | 
						|
  MACHINE_STATE_SAVE("1")
 | 
						|
 | 
						|
  if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF)))
 | 
						|
    goto skip;
 | 
						|
 | 
						|
  bb_dst = __bb.blocks->addresses[__bb.blockno];
 | 
						|
  __bb.blocks->counts[__bb.blockno]++;
 | 
						|
 | 
						|
  if (bb_tracefile)
 | 
						|
    {
 | 
						|
      fwrite (&bb_dst, sizeof (unsigned long), 1, bb_tracefile);
 | 
						|
    }
 | 
						|
 | 
						|
  if (bb_hashbuckets)
 | 
						|
    {
 | 
						|
      struct bb_edge **startbucket, **oldnext;
 | 
						|
 | 
						|
      oldnext = startbucket
 | 
						|
	= & bb_hashbuckets[ (((int) bb_src*8) ^ (int) bb_dst) % BB_BUCKETS ];
 | 
						|
      bucket = *startbucket;
 | 
						|
 | 
						|
      for (bucket = *startbucket; bucket; 
 | 
						|
           oldnext = &(bucket->next), bucket = *oldnext)
 | 
						|
        {
 | 
						|
          if (bucket->src_addr == bb_src
 | 
						|
	      && bucket->dst_addr == bb_dst)
 | 
						|
            {
 | 
						|
              bucket->count++;
 | 
						|
              *oldnext = bucket->next;
 | 
						|
              bucket->next = *startbucket;
 | 
						|
              *startbucket = bucket;
 | 
						|
              goto ret;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge));
 | 
						|
 | 
						|
      if (!bucket)
 | 
						|
        {
 | 
						|
          if (!reported)
 | 
						|
            {
 | 
						|
              fprintf (stderr, "Profiler: out of memory\n");
 | 
						|
              reported = 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      else
 | 
						|
        {
 | 
						|
          bucket->src_addr = bb_src;
 | 
						|
          bucket->dst_addr = bb_dst;
 | 
						|
          bucket->next = *startbucket;
 | 
						|
          *startbucket = bucket;
 | 
						|
          bucket->count = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
ret:
 | 
						|
  bb_src = bb_dst;
 | 
						|
 | 
						|
skip:
 | 
						|
  ;
 | 
						|
 | 
						|
  MACHINE_STATE_RESTORE("1")
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Called when returning from a function and `__bb_showret__' is set.  */
 | 
						|
 | 
						|
static void
 | 
						|
__bb_trace_func_ret (void)
 | 
						|
{
 | 
						|
  struct bb_edge *bucket;
 | 
						|
 | 
						|
  if (!bb_callcount || (__bb.blocks->flags && (__bb.blocks->flags[__bb.blockno] & TRACE_OFF)))
 | 
						|
    goto skip;
 | 
						|
 | 
						|
  if (bb_hashbuckets)
 | 
						|
    {
 | 
						|
      struct bb_edge **startbucket, **oldnext;
 | 
						|
 | 
						|
      oldnext = startbucket
 | 
						|
	= & bb_hashbuckets[ (((int) bb_dst * 8) ^ (int) bb_src) % BB_BUCKETS ];
 | 
						|
      bucket = *startbucket;
 | 
						|
 | 
						|
      for (bucket = *startbucket; bucket; 
 | 
						|
           oldnext = &(bucket->next), bucket = *oldnext)
 | 
						|
        {
 | 
						|
          if (bucket->src_addr == bb_dst
 | 
						|
	       && bucket->dst_addr == bb_src)
 | 
						|
            {
 | 
						|
              bucket->count++;
 | 
						|
              *oldnext = bucket->next;
 | 
						|
              bucket->next = *startbucket;
 | 
						|
              *startbucket = bucket;
 | 
						|
              goto ret;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      bucket = (struct bb_edge *) malloc (sizeof (struct bb_edge));
 | 
						|
 | 
						|
      if (!bucket)
 | 
						|
        {
 | 
						|
          if (!reported)
 | 
						|
            {
 | 
						|
              fprintf (stderr, "Profiler: out of memory\n");
 | 
						|
              reported = 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
      else
 | 
						|
        {
 | 
						|
          bucket->src_addr = bb_dst;
 | 
						|
          bucket->dst_addr = bb_src;
 | 
						|
          bucket->next = *startbucket;
 | 
						|
          *startbucket = bucket;
 | 
						|
          bucket->count = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
ret:
 | 
						|
  bb_dst = bb_src;
 | 
						|
 | 
						|
skip:
 | 
						|
  ;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Called upon entering the first function of a file.  */
 | 
						|
 | 
						|
static void
 | 
						|
__bb_init_file (struct bb *blocks)
 | 
						|
{
 | 
						|
 | 
						|
  const struct bb_func *p;
 | 
						|
  long blk, ncounts = blocks->ncounts;
 | 
						|
  const char **functions = blocks->functions;
 | 
						|
 | 
						|
  /* Set up linked list.  */
 | 
						|
  blocks->zero_word = 1;
 | 
						|
  blocks->next = bb_head;
 | 
						|
  bb_head = blocks;
 | 
						|
 | 
						|
  blocks->flags = 0;
 | 
						|
  if (!bb_func_head
 | 
						|
      || !(blocks->flags = (char *) malloc (sizeof (char) * blocks->ncounts)))
 | 
						|
    return;
 | 
						|
 | 
						|
  for (blk = 0; blk < ncounts; blk++)
 | 
						|
    blocks->flags[blk] = 0;
 | 
						|
 | 
						|
  for (blk = 0; blk < ncounts; blk++)
 | 
						|
    {
 | 
						|
      for (p = bb_func_head; p; p = p->next)
 | 
						|
        {
 | 
						|
          if (!strcmp (p->funcname, functions[blk])
 | 
						|
	      && (!p->filename || !strcmp (p->filename, blocks->filename)))
 | 
						|
            {
 | 
						|
              blocks->flags[blk] |= p->mode;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Called when exiting from a function.  */
 | 
						|
 | 
						|
void
 | 
						|
__bb_trace_ret (void)
 | 
						|
{
 | 
						|
 | 
						|
  MACHINE_STATE_SAVE("2")
 | 
						|
 | 
						|
  if (bb_callcount)
 | 
						|
    {
 | 
						|
      if ((bb_mode & 12) && bb_stacksize > bb_callcount)
 | 
						|
        {
 | 
						|
          bb_src = bb_stack[bb_callcount];
 | 
						|
          if (bb_mode & 8)
 | 
						|
            __bb_trace_func_ret ();
 | 
						|
        }
 | 
						|
 | 
						|
      bb_callcount -= 1;
 | 
						|
    }
 | 
						|
 | 
						|
  MACHINE_STATE_RESTORE("2")
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Called when entering a function.  */
 | 
						|
 | 
						|
void
 | 
						|
__bb_init_trace_func (struct bb *blocks, unsigned long blockno)
 | 
						|
{
 | 
						|
  static int trace_init = 0;
 | 
						|
 | 
						|
  MACHINE_STATE_SAVE("3")
 | 
						|
 | 
						|
  if (!blocks->zero_word)
 | 
						|
    { 
 | 
						|
      if (!trace_init)
 | 
						|
        { 
 | 
						|
          trace_init = 1;
 | 
						|
          __bb_init_prg ();
 | 
						|
        }
 | 
						|
      __bb_init_file (blocks);
 | 
						|
    }
 | 
						|
 | 
						|
  if (bb_callcount)
 | 
						|
    {
 | 
						|
 | 
						|
      bb_callcount += 1;
 | 
						|
 | 
						|
      if (bb_mode & 12)
 | 
						|
        {
 | 
						|
          if (bb_callcount >= bb_stacksize)
 | 
						|
            {
 | 
						|
              size_t newsize = bb_callcount + 100;
 | 
						|
 | 
						|
              bb_stack = (unsigned long *) realloc (bb_stack, newsize);
 | 
						|
              if (! bb_stack)
 | 
						|
                {
 | 
						|
                  if (!reported)
 | 
						|
                    {
 | 
						|
                      fprintf (stderr, "Profiler: out of memory\n");
 | 
						|
                      reported = 1;
 | 
						|
                    }
 | 
						|
                  bb_stacksize = 0;
 | 
						|
                  goto stack_overflow;
 | 
						|
                }
 | 
						|
	      bb_stacksize = newsize;
 | 
						|
            }
 | 
						|
          bb_stack[bb_callcount] = bb_src;
 | 
						|
 | 
						|
          if (bb_mode & 4)
 | 
						|
            bb_src = 0;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
stack_overflow:;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
  else if (blocks->flags && (blocks->flags[blockno] & TRACE_ON))
 | 
						|
    {
 | 
						|
      bb_callcount = 1;
 | 
						|
      bb_src = 0;
 | 
						|
 | 
						|
      if (bb_stack)
 | 
						|
          bb_stack[bb_callcount] = bb_src;
 | 
						|
    }
 | 
						|
 | 
						|
  MACHINE_STATE_RESTORE("3")
 | 
						|
}
 | 
						|
 |