(struct bb): Add flags field.
authorRichard Kenner <kenner@gcc.gnu.org>
Sun, 24 Dec 1995 22:42:24 +0000 (17:42 -0500)
committerRichard Kenner <kenner@gcc.gnu.org>
Sun, 24 Dec 1995 22:42:24 +0000 (17:42 -0500)
(HAVE_POPEN): Test new define.
(struct __bb, struct bb_{edge,func}): New structs.
(__bb_init_{prg,file},__bb_{init,exit}_trace_func,__bb_trace_ret,
(__bb_trace_func{,_ret},gopen,gclose): New functions.

From-SVN: r10850

gcc/libgcc2.c

index 1dd16f9..b7458ef 100644 (file)
@@ -1450,6 +1450,7 @@ struct bb
   const char **functions;
   const long *line_nums;
   const char **filenames;
+  char *flags;
 };
 
 #ifdef BLOCK_PROFILER_CODE
@@ -1513,11 +1514,11 @@ __bb_exit_func (void)
 
       /* 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)
+        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);
+      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
@@ -1632,6 +1633,735 @@ __bb_init_func (struct bb *blocks)
   bb_head = blocks;
 }
 
+#ifndef MACHINE_STATE_SAVE
+#define MACHINE_STATE_SAVE(ID)
+#endif
+#ifndef MACHINE_STATE_RESTORE
+#define MACHINE_STATE_RESTORE(ID)
+#endif
+
+#include <string.h>
+
+/* Number of buckets in hashtable of basic block addresses. */
+
+#define BB_BUCKETS 311
+
+/* Maximum length of string in file bb.in. */
+
+#define BBINBUFSIZE 500
+
+/* BBINBUFSIZE-1 with double quotes. We could use #BBINBUFSIZE or
+   "BBINBUFSIZE" but want to avoid trouble with preprocessors. */
+
+#define BBINBUFSIZESTR "499"
+
+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 (fn, mode)
+     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 (f)
+     FILE *f;
+{
+  struct stat buf;
+
+  if (f != NULL)
+    {
+      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 ()
+{
+  FILE *file = fopen ("bb.out", "a");
+  struct bb_func *f;
+  struct bb_edge *e;
+  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 %*d 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 ()
+{
+
+  FILE *file;
+  char buf[BBINBUFSIZE];
+  const char *p;
+  const char *pos;
+  enum bb_func_mode m;
+
+#ifdef ON_EXIT
+  /* Initialize destructor.  */
+  ON_EXIT (__bb_exit_func, 0);
+#endif
+
+  if (!(file = fopen ("bb.in", "r")))
+    return;
+
+  while(fscanf (file, " %" BBINBUFSIZESTR "s ", buf) != EOF)
+    {
+      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)
+        bzero (bb_hashbuckets, BB_BUCKETS);
+    }
+
+  if (bb_mode & 12)
+    {
+      bb_stacksize = 10;
+      bb_stack = (unsigned long *) malloc (bb_stacksize * sizeof (*bb_stack));
+    }
+
+#ifdef ON_EXIT
+      /* Initialize destructor.  */
+      ON_EXIT (__bb_exit_trace_func, 0);
+#endif
+
+}
+
+/* Called upon entering a basic block. */
+
+void
+__bb_trace_func ()
+{
+  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 ()
+{
+  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 (blocks)
+     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 ()
+{
+
+  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 (blocks, blockno)
+     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")
+}
+
 #endif /* not inhibit_libc */
 #endif /* not BLOCK_PROFILER_CODE */
 #endif /* L_bb */