/*--------------------------------------------------------------------*/ /*--- 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 ---*/ /*--------------------------------------------------------------------*/