mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
264 lines
7.6 KiB
C
264 lines
7.6 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Store and compare stack backtraces m_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 "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_profile.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;
|
|
/* 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 m_execontext.c ---*/
|
|
/*--------------------------------------------------------------------*/
|