
/*--------------------------------------------------------------------*/
/*--- Store and compare stack backtraces            m_execontext.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2000-2007 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 "pub_core_basics.h"
#include "pub_core_execontext.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"     // For VG_(message)()
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_stacktrace.h"

/*------------------------------------------------------------*/
/*--- Low-level ExeContext storage.                        ---*/
/*------------------------------------------------------------*/

/* The first 4 IP values are used in comparisons to remove duplicate errors,
   and for comparing against suppression specifications.  The rest are
   purely informational (but often important). */

struct _ExeContext {
   struct _ExeContext * next; /* Hash list pointer */
   struct _ExeContext * parent;  /* Stack traceback pointer */
   Addr  ip;  /* Current address */
};

/* Number of lists in which we keep track of ExeContexts.  Should be
   prime. */
#define N_EC_LISTS 6291469 /*30011*/ /*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 ULong ec_searchreqs;

/* Stats only: the number of full context comparisons done. */
static ULong ec_searchcmps;

/* Stats only: total number of stored contexts. */
static ULong ec_totstored;

/* Number of 2, 4 and (fast) full cmps done. */
static ULong ec_cmp2s;
static ULong ec_cmp4s;
static ULong 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: %,lu lists, %,llu contexts (avg %,llu per list)",
      N_EC_LISTS, ec_totstored, ec_totstored / N_EC_LISTS 
   );
   VG_(message)(Vg_DebugMsg, 
      "   exectx: %,llu searches, %,llu full compares (%,llu per 1000)",
      ec_searchreqs, ec_searchcmps, 
      ec_searchreqs == 0 
         ? 0L 
         : ( (ec_searchcmps * 1000) / ec_searchreqs ) 
   );
   VG_(message)(Vg_DebugMsg, 
      "   exectx: %,llu cmp2, %,llu cmp4, %,llu cmpAll",
      ec_cmp2s, ec_cmp4s, ec_cmpAlls 
   );
}


/* Print an ExeContext. */
void VG_(pp_ExeContext) ( ExeContext * ec )
{
   Addr ips[VG_DEEPEST_BACKTRACE];
   UInt n_ips;
   VG_(extract_StackTrace)(ec, ips, &n_ips);
   VG_(pp_StackTrace)(ips, n_ips);
}


/* Compare two ExeContexts, comparing all callers. */
Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
{
   return e1 == e2;
   tl_assert(0);
}

/* 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.  
*/
static inline UWord ROLW ( UWord w, Int n )
{
   Int bpw = 8 * sizeof(UWord);
   w = (w << n) | (w >> (bpw-n));
   return w;
}

static ExeContext * locate_or_create_context(Addr ips[], UInt n_ips)
{
   Int i;
   Bool same;
   ExeContext * list;
   ExeContext * cur;
   ExeContext * new_ec;
   UWord hash = 0;
   
//    VG_(printf) ("Called locc with ips: %p and n_ips: %u\n", ips, n_ips);
   
   if (n_ips == 0) return NULL;

   for (i = 0; i < n_ips; i++) {
      hash ^= ips[i];
      hash = ROLW(hash, 19);
   }
   if (0) VG_(printf)("hash  0x%lx  ", hash);
   hash = hash % N_EC_LISTS;
   if (0) VG_(printf)("%lu\n", hash);

   /* 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;
      cur = list;
      for (i = 0; i < n_ips; i++) {
         if (!cur || cur->ip != ips[i]) {
            same = False;
            break; 
         }
         cur = cur->parent;
      }
      if (same) break;
      list  = list->next;
   }

   if (list != NULL) {
      return list;
   }

   /* Bummer.  We have to allocate a new context record. */
   ec_totstored++;
   
   new_ec = VG_(arena_malloc)(VG_AR_EXECTXT, sizeof(struct _ExeContext));

   new_ec->ip = ips[0];
   new_ec->parent = locate_or_create_context(ips + 1, n_ips - 1);
   new_ec->next  = ec_list[hash];
   ec_list[hash] = new_ec;

//    VG_(printf) ("New context: %p   Parent: %p   IP: %p  Next: %p\n", new_ec, new_ec->parent,
//     new_ec->ip, new_ec->next);

   return new_ec;
}

ExeContext* VG_(record_ExeContext) ( ThreadId tid )
{
   Int         i;
   Addr        ips[VG_DEEPEST_BACKTRACE];
   Bool        same;
   UWord       hash;
   ExeContext* new_ec;
   ExeContext* list;
   ExeContext* cur;
   UInt        n_ips;
   ExeContext  *prev2, *prev;

   static UInt ctr = 0;

   vg_assert(sizeof(void*) == sizeof(UWord));
   vg_assert(sizeof(void*) == sizeof(Addr));

   init_ExeContext_storage();
   vg_assert(VG_(clo_backtrace_size) >= 1 &&
             VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);

   n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size) );
   tl_assert(n_ips >= 1);

   return locate_or_create_context(ips, n_ips);
}

void VG_(extract_StackTrace) (ExeContext * e, Addr ips[], UInt * n_ips)
{   
   *n_ips = 0;                               
   while (e) {
//       VG_(printf) ("E ist: %p  Count: %u\n", e, *n_ips);
      ips[*n_ips] = e->ip;
      (*n_ips)++;
      e = e->parent;
   }
}  

/*--------------------------------------------------------------------*/
/*--- end                                           m_execontext.c ---*/
/*--------------------------------------------------------------------*/
