summaryrefslogtreecommitdiff
path: root/coregrind/.svn/text-base/m_execontext.c.svn-base
diff options
context:
space:
mode:
Diffstat (limited to 'coregrind/.svn/text-base/m_execontext.c.svn-base')
-rw-r--r--coregrind/.svn/text-base/m_execontext.c.svn-base479
1 files changed, 479 insertions, 0 deletions
diff --git a/coregrind/.svn/text-base/m_execontext.c.svn-base b/coregrind/.svn/text-base/m_execontext.c.svn-base
new file mode 100644
index 0000000..bc5d8ce
--- /dev/null
+++ b/coregrind/.svn/text-base/m_execontext.c.svn-base
@@ -0,0 +1,479 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Store and compare stack backtraces m_execontext.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2009 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_debuglog.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"
+#include "pub_core_machine.h" // VG_(get_IP)
+#include "pub_core_vki.h" // To keep pub_core_threadstate.h happy
+#include "pub_core_threadstate.h" // VG_(is_valid_tid)
+#include "pub_core_execontext.h" // self
+
+/*------------------------------------------------------------*/
+/*--- 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).
+
+ The contexts are stored in a traditional chained hash table, so as
+ to allow quick determination of whether a new context already
+ exists. The hash table starts small and expands dynamically, so as
+ to keep the load factor below 1.0.
+
+ The idea is only to ever store any one context once, so as to save
+ space and make exact comparisons faster. */
+
+
+/* Primes for the hash table */
+
+#define N_EC_PRIMES 18
+
+static SizeT ec_primes[N_EC_PRIMES] = {
+ 769UL, 1543UL, 3079UL, 6151UL,
+ 12289UL, 24593UL, 49157UL, 98317UL,
+ 196613UL, 393241UL, 786433UL, 1572869UL,
+ 3145739UL, 6291469UL, 12582917UL, 25165843UL,
+ 50331653UL, 100663319UL
+};
+
+
+/* Each element is present in a hash chain, and also contains a
+ variable length array of guest code addresses (the useful part). */
+
+struct _ExeContext {
+ struct _ExeContext* chain;
+ /* A 32-bit unsigned integer that uniquely identifies this
+ ExeContext. Memcheck uses these for origin tracking. Values
+ must be nonzero (else Memcheck's origin tracking is hosed), must
+ be a multiple of four, and must be unique. Hence they start at
+ 4. */
+ UInt ecu;
+ /* Variable-length array. The size is 'n_ips'; at
+ least 1, at most VG_DEEPEST_BACKTRACE. [0] is the current IP,
+ [1] is its caller, [2] is the caller of [1], etc. */
+ UInt n_ips;
+ Addr ips[0];
+};
+
+
+/* This is the dynamically expanding hash table. */
+static ExeContext** ec_htab; /* array [ec_htab_size] of ExeContext* */
+static SizeT ec_htab_size; /* one of the values in ec_primes */
+static SizeT ec_htab_size_idx; /* 0 .. N_EC_PRIMES-1 */
+
+/* ECU serial number */
+static UInt ec_next_ecu = 4; /* We must never issue zero */
+
+
+/* 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 (LIKELY(init_done))
+ return;
+ ec_searchreqs = 0;
+ ec_searchcmps = 0;
+ ec_totstored = 0;
+ ec_cmp2s = 0;
+ ec_cmp4s = 0;
+ ec_cmpAlls = 0;
+
+ ec_htab_size_idx = 0;
+ ec_htab_size = ec_primes[ec_htab_size_idx];
+ ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.iEs1",
+ sizeof(ExeContext*) * ec_htab_size);
+ for (i = 0; i < ec_htab_size; i++)
+ ec_htab[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)",
+ ec_htab_size, ec_totstored, ec_totstored / (ULong)ec_htab_size
+ );
+ VG_(message)(Vg_DebugMsg,
+ " exectx: %'llu searches, %'llu full compares (%'llu per 1000)",
+ ec_searchreqs, ec_searchcmps,
+ ec_searchreqs == 0
+ ? 0ULL
+ : ( (ec_searchcmps * 1000ULL) / 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 )
+{
+ VG_(pp_StackTrace)( ec->ips, ec->n_ips );
+}
+
+
+/* Compare two ExeContexts, comparing all callers. */
+Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 )
+{
+ Int i;
+
+ if (e1 == NULL || e2 == NULL)
+ return False;
+
+ // Must be at least one address in each trace.
+ tl_assert(e1->n_ips >= 1 && e2->n_ips >= 1);
+
+ switch (res) {
+ case Vg_LowRes:
+ /* Just compare the top two callers. */
+ ec_cmp2s++;
+ for (i = 0; i < 2; i++) {
+ if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
+ if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
+ if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
+ if (e1->ips[i] != e2->ips[i]) return False;
+ }
+ return True;
+
+ case Vg_MedRes:
+ /* Just compare the top four callers. */
+ ec_cmp4s++;
+ for (i = 0; i < 4; i++) {
+ if ( (e1->n_ips <= i) && (e2->n_ips <= i)) return True;
+ if ( (e1->n_ips <= i) && !(e2->n_ips <= i)) return False;
+ if (!(e1->n_ips <= i) && (e2->n_ips <= i)) return False;
+ if (e1->ips[i] != e2->ips[i]) 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");
+ }
+}
+
+/* VG_(record_ExeContext) 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.
+
+ Also checks whether the hash table needs expanding, and expands it
+ if so. */
+
+static inline UWord ROLW ( UWord w, Int n )
+{
+ Int bpw = 8 * sizeof(UWord);
+ w = (w << n) | (w >> (bpw-n));
+ return w;
+}
+
+static UWord calc_hash ( Addr* ips, UInt n_ips, UWord htab_sz )
+{
+ UInt i;
+ UWord hash = 0;
+ vg_assert(htab_sz > 0);
+ for (i = 0; i < n_ips; i++) {
+ hash ^= ips[i];
+ hash = ROLW(hash, 19);
+ }
+ return hash % htab_sz;
+}
+
+static void resize_ec_htab ( void )
+{
+ SizeT i;
+ SizeT new_size;
+ ExeContext** new_ec_htab;
+
+ vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
+ if (ec_htab_size_idx == N_EC_PRIMES-1)
+ return; /* out of primes - can't resize further */
+
+ new_size = ec_primes[ec_htab_size_idx + 1];
+ new_ec_htab = VG_(arena_malloc)(VG_AR_EXECTXT, "execontext.reh1",
+ sizeof(ExeContext*) * new_size);
+
+ VG_(debugLog)(
+ 1, "execontext",
+ "resizing htab from size %lu to %lu (idx %lu) Total#ECs=%llu\n",
+ ec_htab_size, new_size, ec_htab_size_idx + 1, ec_totstored);
+
+ for (i = 0; i < new_size; i++)
+ new_ec_htab[i] = NULL;
+
+ for (i = 0; i < ec_htab_size; i++) {
+ ExeContext* cur = ec_htab[i];
+ while (cur) {
+ ExeContext* next = cur->chain;
+ UWord hash = calc_hash(cur->ips, cur->n_ips, new_size);
+ vg_assert(hash < new_size);
+ cur->chain = new_ec_htab[hash];
+ new_ec_htab[hash] = cur;
+ cur = next;
+ }
+ }
+
+ VG_(arena_free)(VG_AR_EXECTXT, ec_htab);
+ ec_htab = new_ec_htab;
+ ec_htab_size = new_size;
+ ec_htab_size_idx++;
+}
+
+/* Do the first part of getting a stack trace: actually unwind the
+ stack, and hand the results off to the duplicate-trace-finder
+ (_wrk2). */
+static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips ); /*fwds*/
+static ExeContext* record_ExeContext_wrk ( ThreadId tid, Word first_ip_delta,
+ Bool first_ip_only )
+{
+ Addr ips[VG_DEEPEST_BACKTRACE];
+ UInt n_ips;
+
+ init_ExeContext_storage();
+
+ vg_assert(sizeof(void*) == sizeof(UWord));
+ vg_assert(sizeof(void*) == sizeof(Addr));
+
+ vg_assert(VG_(is_valid_tid)(tid));
+
+ vg_assert(VG_(clo_backtrace_size) >= 1 &&
+ VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE);
+
+ if (first_ip_only) {
+ n_ips = 1;
+ ips[0] = VG_(get_IP)(tid);
+ } else {
+ n_ips = VG_(get_StackTrace)( tid, ips, VG_(clo_backtrace_size),
+ NULL/*array to dump SP values in*/,
+ NULL/*array to dump FP values in*/,
+ first_ip_delta );
+ }
+
+ return record_ExeContext_wrk2 ( &ips[0], n_ips );
+}
+
+/* Do the second part of getting a stack trace: ips[0 .. n_ips-1]
+ holds a proposed trace. Find or allocate a suitable ExeContext.
+ Note that callers must have done init_ExeContext_storage() before
+ getting to this point. */
+static ExeContext* record_ExeContext_wrk2 ( Addr* ips, UInt n_ips )
+{
+ Int i;
+ Bool same;
+ UWord hash;
+ ExeContext* new_ec;
+ ExeContext* list;
+ ExeContext *prev2, *prev;
+
+ static UInt ctr = 0;
+
+ tl_assert(n_ips >= 1 && n_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 = calc_hash( ips, n_ips, ec_htab_size );
+
+ /* And (the expensive bit) look a for matching entry in the list. */
+
+ ec_searchreqs++;
+
+ prev2 = NULL;
+ prev = NULL;
+ list = ec_htab[hash];
+
+ while (True) {
+ if (list == NULL) break;
+ ec_searchcmps++;
+ same = True;
+ for (i = 0; i < n_ips; i++) {
+ if (list->ips[i] != ips[i]) {
+ same = False;
+ break;
+ }
+ }
+ if (same) break;
+ prev2 = prev;
+ prev = list;
+ list = list->chain;
+ }
+
+ if (list != NULL) {
+ /* Yay! We found it. Once every 8 searches, move it one step
+ closer to the start of the list to make future searches
+ cheaper. */
+ if (0 == ((ctr++) & 7)) {
+ if (prev2 != NULL && prev != NULL) {
+ /* Found at 3rd or later position in the chain. */
+ vg_assert(prev2->chain == prev);
+ vg_assert(prev->chain == list);
+ prev2->chain = list;
+ prev->chain = list->chain;
+ list->chain = prev;
+ }
+ else if (prev2 == NULL && prev != NULL) {
+ /* Found at 2nd position in the chain. */
+ vg_assert(ec_htab[hash] == prev);
+ vg_assert(prev->chain == list);
+ prev->chain = list->chain;
+ list->chain = prev;
+ ec_htab[hash] = list;
+ }
+ }
+ return list;
+ }
+
+ /* Bummer. We have to allocate a new context record. */
+ ec_totstored++;
+
+ new_ec = VG_(arena_malloc)( VG_AR_EXECTXT, "execontext.rEw2.2",
+ sizeof(struct _ExeContext)
+ + n_ips * sizeof(Addr) );
+
+ for (i = 0; i < n_ips; i++)
+ new_ec->ips[i] = ips[i];
+
+ vg_assert(VG_(is_plausible_ECU)(ec_next_ecu));
+ new_ec->ecu = ec_next_ecu;
+ ec_next_ecu += 4;
+ if (ec_next_ecu == 0) {
+ /* Urr. Now we're hosed; we emitted 2^30 ExeContexts already
+ and have run out of numbers. Not sure what to do. */
+ VG_(core_panic)("m_execontext: more than 2^30 ExeContexts created");
+ }
+
+ new_ec->n_ips = n_ips;
+ new_ec->chain = ec_htab[hash];
+ ec_htab[hash] = new_ec;
+
+ /* Resize the hash table, maybe? */
+ if ( ((ULong)ec_totstored) > ((ULong)ec_htab_size) ) {
+ vg_assert(ec_htab_size_idx >= 0 && ec_htab_size_idx < N_EC_PRIMES);
+ if (ec_htab_size_idx < N_EC_PRIMES-1)
+ resize_ec_htab();
+ }
+
+ return new_ec;
+}
+
+ExeContext* VG_(record_ExeContext)( ThreadId tid, Word first_ip_delta ) {
+ return record_ExeContext_wrk( tid, first_ip_delta,
+ False/*!first_ip_only*/ );
+}
+
+ExeContext* VG_(record_depth_1_ExeContext)( ThreadId tid ) {
+ return record_ExeContext_wrk( tid, 0/*first_ip_delta*/,
+ True/*first_ip_only*/ );
+}
+
+ExeContext* VG_(make_depth_1_ExeContext_from_Addr)( Addr a ) {
+ init_ExeContext_storage();
+ return record_ExeContext_wrk2( &a, 1 );
+}
+
+StackTrace VG_(get_ExeContext_StackTrace) ( ExeContext* e ) {
+ return e->ips;
+}
+
+UInt VG_(get_ECU_from_ExeContext)( ExeContext* e ) {
+ vg_assert(VG_(is_plausible_ECU)(e->ecu));
+ return e->ecu;
+}
+
+Int VG_(get_ExeContext_n_ips)( ExeContext* e ) {
+ vg_assert(e->n_ips >= 1);
+ return e->n_ips;
+}
+
+ExeContext* VG_(get_ExeContext_from_ECU)( UInt ecu )
+{
+ UWord i;
+ ExeContext* ec;
+ vg_assert(VG_(is_plausible_ECU)(ecu));
+ vg_assert(ec_htab_size > 0);
+ for (i = 0; i < ec_htab_size; i++) {
+ for (ec = ec_htab[i]; ec; ec = ec->chain) {
+ if (ec->ecu == ecu)
+ return ec;
+ }
+ }
+ return NULL;
+}
+
+ExeContext* VG_(make_ExeContext_from_StackTrace)( Addr* ips, UInt n_ips )
+{
+ return record_ExeContext_wrk2(ips, n_ips);
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end m_execontext.c ---*/
+/*--------------------------------------------------------------------*/