A modularisation + refactoring commit.  vg_execontext.c has been split into
two halves: stacktrace.c, which deals with getting, traversing and printing
stack traces;  and execontext.c, which deals with storing stack traces
permanently in a way that avoids duplicates, and comparing them.

One nice outcome:  previously we were often creating ExeContexts, which live
forever, even when they were only needed temporarily.  Ie. this was a memory
leak, which has been removed.

As part of this, new headers have been created, carved off core.h and
tool.h.  Lots of function names have changed, too.

In Massif, I also changed a lot of "eip" names to "ip" to make them less
x86-specific.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3429 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/execontext.c b/coregrind/execontext.c
new file mode 100644
index 0000000..5de1dbe
--- /dev/null
+++ b/coregrind/execontext.c
@@ -0,0 +1,256 @@
+/*--------------------------------------------------------------------*/
+/*---                                                 execontext.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2000-2005 Julian Seward 
+      jseward@acm.org
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "core.h"
+#include "pub_core_execontext.h"
+
+/*------------------------------------------------------------*/
+/*--- Low-level ExeContext storage.                        ---*/
+/*------------------------------------------------------------*/
+
+/* The first 4 IP values are used in comparisons do remove duplicate errors,
+   and for comparing against suppression specifications.  The rest are
+   purely informational (but often important). */
+
+struct _ExeContext {
+   struct _ExeContext * next;
+   /* Variable-length array.  The size is VG_(clo_backtrace_size); at
+      least 1, at most VG_DEEPEST_BACKTRACE.  [0] is the current IP,
+      [1] is its caller, [2] is the caller of [1], etc. */
+   Addr ips[0];
+};
+
+/* Number of lists in which we keep track of ExeContexts.  Should be
+   prime. */
+#define N_EC_LISTS 4999 /* a prime number */
+
+/* The idea is only to ever store any one context once, so as to save
+   space and make exact comparisons faster. */
+
+static ExeContext* ec_list[N_EC_LISTS];
+
+/* Stats only: the number of times the system was searched to locate a
+   context. */
+static UInt ec_searchreqs;
+
+/* Stats only: the number of full context comparisons done. */
+static UInt ec_searchcmps;
+
+/* Stats only: total number of stored contexts. */
+static UInt ec_totstored;
+
+/* Number of 2, 4 and (fast) full cmps done. */
+static UInt ec_cmp2s;
+static UInt ec_cmp4s;
+static UInt ec_cmpAlls;
+
+
+/*------------------------------------------------------------*/
+/*--- Exported functions.                                  ---*/
+/*------------------------------------------------------------*/
+
+
+/* Initialise this subsystem. */
+static void init_ExeContext_storage ( void )
+{
+   Int i;
+   static Bool init_done = False;
+   if (init_done)
+      return;
+   ec_searchreqs = 0;
+   ec_searchcmps = 0;
+   ec_totstored = 0;
+   ec_cmp2s = 0;
+   ec_cmp4s = 0;
+   ec_cmpAlls = 0;
+   for (i = 0; i < N_EC_LISTS; i++)
+      ec_list[i] = NULL;
+   init_done = True;
+}
+
+
+/* Print stats. */
+void VG_(print_ExeContext_stats) ( void )
+{
+   init_ExeContext_storage();
+   VG_(message)(Vg_DebugMsg, 
+      "   exectx: %d lists, %d contexts (avg %d per list)",
+      N_EC_LISTS, ec_totstored, 
+      ec_totstored / N_EC_LISTS 
+   );
+   VG_(message)(Vg_DebugMsg, 
+      "   exectx: %d searches, %d full compares (%d per 1000)",
+      ec_searchreqs, ec_searchcmps, 
+      ec_searchreqs == 0 
+         ? 0 
+         : (UInt)( (((ULong)ec_searchcmps) * 1000) 
+           / ((ULong)ec_searchreqs )) 
+   );
+   VG_(message)(Vg_DebugMsg, 
+      "   exectx: %d cmp2, %d cmp4, %d cmpAll",
+      ec_cmp2s, ec_cmp4s, ec_cmpAlls 
+   );
+}
+
+
+/* Print an ExeContext. */
+void VG_(pp_ExeContext) ( ExeContext* ec )
+{
+   VG_(pp_StackTrace)( ec->ips, VG_(clo_backtrace_size) );
+}
+
+
+/* Compare two ExeContexts, comparing all callers. */
+Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
+{
+   if (e1 == NULL || e2 == NULL) 
+      return False;
+   switch (res) {
+   case Vg_LowRes:
+      /* Just compare the top two callers. */
+      ec_cmp2s++;
+      if (e1->ips[0] != e2->ips[0]) return False;
+
+      if (VG_(clo_backtrace_size) < 2) return True;
+      if (e1->ips[1] != e2->ips[1]) return False;
+      return True;
+
+   case Vg_MedRes:
+      /* Just compare the top four callers. */
+      ec_cmp4s++;
+      if (e1->ips[0] != e2->ips[0]) return False;
+
+      if (VG_(clo_backtrace_size) < 2) return True;
+      if (e1->ips[1] != e2->ips[1]) return False;
+
+      if (VG_(clo_backtrace_size) < 3) return True;
+      if (e1->ips[2] != e2->ips[2]) return False;
+
+      if (VG_(clo_backtrace_size) < 4) return True;
+      if (e1->ips[3] != e2->ips[3]) return False;
+      return True;
+
+   case Vg_HighRes:
+      ec_cmpAlls++;
+      /* Compare them all -- just do pointer comparison. */
+      if (e1 != e2) return False;
+      return True;
+
+   default:
+      VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes");
+   }
+}
+
+/* This guy is the head honcho here.  Take a snapshot of the client's
+   stack.  Search our collection of ExeContexts to see if we already
+   have it, and if not, allocate a new one.  Either way, return a
+   pointer to the context.  If there is a matching context we
+   guarantee to not allocate a new one.  Thus we never store
+   duplicates, and so exact equality can be quickly done as equality
+   on the returned ExeContext* values themselves.  Inspired by Hugs's
+   Text type.  
+*/
+ExeContext* VG_(record_ExeContext) ( ThreadId tid )
+{
+   Int         i;
+   Addr        ips[VG_DEEPEST_BACKTRACE];
+   Bool        same;
+   UWord       hash;
+   ExeContext* new_ec;
+   ExeContext* list;
+
+   VGP_PUSHCC(VgpExeContext);
+
+   init_ExeContext_storage();
+   vg_assert(VG_(clo_backtrace_size) >= 1 
+             && VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
+
+   VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size) );
+
+   /* Now figure out if we've seen this one before.  First hash it so
+      as to determine the list number. */
+
+   hash = 0;
+   for (i = 0; i < VG_(clo_backtrace_size); i++) {
+      hash ^= ips[i];
+      hash = (hash << 29) | (hash >> 3);
+   }
+   hash = hash % N_EC_LISTS;
+
+   /* And (the expensive bit) look a matching entry in the list. */
+
+   ec_searchreqs++;
+
+   list = ec_list[hash];
+
+   while (True) {
+      if (list == NULL) break;
+      ec_searchcmps++;
+      same = True;
+      for (i = 0; i < VG_(clo_backtrace_size); i++) {
+         if (list->ips[i] != ips[i]) {
+            same = False;
+            break; 
+         }
+      }
+      if (same) break;
+      list = list->next;
+   }
+
+   if (list != NULL) {
+      /* Yay!  We found it.  */
+      VGP_POPCC(VgpExeContext);
+      return list;
+   }
+
+   /* Bummer.  We have to allocate a new context record. */
+   ec_totstored++;
+
+   new_ec = VG_(arena_malloc)( VG_AR_EXECTXT, 
+                               sizeof(struct _ExeContext *) 
+                               + VG_(clo_backtrace_size) * sizeof(Addr) );
+
+   for (i = 0; i < VG_(clo_backtrace_size); i++)
+      new_ec->ips[i] = ips[i];
+
+   new_ec->next = ec_list[hash];
+   ec_list[hash] = new_ec;
+
+   VGP_POPCC(VgpExeContext);
+   return new_ec;
+}
+
+StackTrace VG_(extract_StackTrace) ( ExeContext* e )
+{                                  
+   return e->ips;
+}  
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/