mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
ThreadSanitizer. - Modified DRD's error reporting code such that it does no longer let the Valgrind core print the Valgrind thread ID but that it now prints the DRD thread ID and name. Updated expected output files where necessary. - Modified drd/test/Makefile.am such that the tests using gcc's built-in functions for atomic memory access such that these are only compiled when the gcc version in use supports these built-in functions. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10186
448 lines
14 KiB
C
448 lines
14 KiB
C
/* -*- mode: C; c-basic-offset: 3; -*- */
|
|
/*
|
|
This file is part of drd, a thread error detector.
|
|
|
|
Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
|
|
|
|
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 "drd_clientobj.h" /* struct mutex_info */
|
|
#include "drd_error.h"
|
|
#include "drd_malloc_wrappers.h"
|
|
#include "drd_mutex.h"
|
|
#include "drd_suppression.h" /* drd_start_suppression() */
|
|
#include "pub_drd_bitmap.h" /* LHS_W, ... */
|
|
#include "pub_tool_vki.h"
|
|
#include "pub_tool_basics.h"
|
|
#include "pub_tool_libcassert.h" /* tl_assert() */
|
|
#include "pub_tool_libcbase.h" /* strlen() */
|
|
#include "pub_tool_libcfile.h" /* VG_(get_startup_wd)() */
|
|
#include "pub_tool_libcprint.h" /* VG_(printf)() */
|
|
#include "pub_tool_machine.h"
|
|
#include "pub_tool_mallocfree.h" /* VG_(malloc), VG_(free) */
|
|
#include "pub_tool_threadstate.h" /* VG_(get_pthread_id)() */
|
|
#include "pub_tool_tooliface.h" /* VG_(needs_tool_errors)() */
|
|
|
|
|
|
/* Local variables. */
|
|
|
|
static Bool s_show_conflicting_segments = True;
|
|
|
|
|
|
void DRD_(set_show_conflicting_segments)(const Bool scs)
|
|
{
|
|
s_show_conflicting_segments = scs;
|
|
}
|
|
|
|
/**
|
|
* Describe a data address range [a,a+len[ as good as possible, for error
|
|
* messages, putting the result in ai.
|
|
*/
|
|
static
|
|
void describe_malloced_addr(Addr const a, SizeT const len, AddrInfo* const ai)
|
|
{
|
|
Addr data;
|
|
|
|
if (DRD_(heap_addrinfo)(a, &data, &ai->size, &ai->lastchange))
|
|
{
|
|
ai->akind = eMallocd;
|
|
ai->rwoffset = a - data;
|
|
}
|
|
else
|
|
{
|
|
ai->akind = eUnknown;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report where an object has been observed for the first time. The printed
|
|
* call stack will either refer to a pthread_*_init() or a pthread_*lock()
|
|
* call.
|
|
*/
|
|
static void first_observed(const Addr obj)
|
|
{
|
|
DrdClientobj* cl;
|
|
|
|
cl = DRD_(clientobj_get_any)(obj);
|
|
if (cl)
|
|
{
|
|
tl_assert(cl->any.first_observed_at);
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s 0x%lx was first observed at:",
|
|
DRD_(clientobj_type_name)(cl->any.type),
|
|
obj);
|
|
VG_(pp_ExeContext)(cl->any.first_observed_at);
|
|
}
|
|
}
|
|
|
|
static
|
|
void drd_report_data_race(Error* const err, const DataRaceErrInfo* const dri)
|
|
{
|
|
AddrInfo ai;
|
|
const unsigned descr_size = 256;
|
|
Char* descr1 = VG_(malloc)("drd.error.drdr2.1", descr_size);
|
|
Char* descr2 = VG_(malloc)("drd.error.drdr2.2", descr_size);
|
|
|
|
tl_assert(dri);
|
|
tl_assert(dri->addr);
|
|
tl_assert(dri->size > 0);
|
|
tl_assert(descr1);
|
|
tl_assert(descr2);
|
|
|
|
descr1[0] = 0;
|
|
descr2[0] = 0;
|
|
VG_(get_data_description)(descr1, descr2, descr_size, dri->addr);
|
|
if (descr1[0] == 0)
|
|
{
|
|
describe_malloced_addr(dri->addr, dri->size, &ai);
|
|
}
|
|
VG_(message)(Vg_UserMsg,
|
|
"Conflicting %s by thread %d/%d at 0x%08lx size %ld",
|
|
dri->access_type == eStore ? "store" : "load",
|
|
DRD_(DrdThreadIdToVgThreadId)(dri->tid),
|
|
dri->tid,
|
|
dri->addr,
|
|
dri->size);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(err));
|
|
if (descr1[0])
|
|
{
|
|
VG_(message)(Vg_UserMsg, "%s", descr1);
|
|
VG_(message)(Vg_UserMsg, "%s", descr2);
|
|
}
|
|
else if (ai.akind == eMallocd && ai.lastchange)
|
|
{
|
|
VG_(message)(Vg_UserMsg,
|
|
"Address 0x%lx is at offset %ld from 0x%lx."
|
|
" Allocation context:",
|
|
dri->addr, ai.rwoffset, dri->addr - ai.rwoffset);
|
|
VG_(pp_ExeContext)(ai.lastchange);
|
|
}
|
|
else
|
|
{
|
|
char sect_name[64];
|
|
VgSectKind sect_kind;
|
|
|
|
sect_kind = VG_(seginfo_sect_kind)(sect_name, sizeof(sect_name),
|
|
dri->addr);
|
|
if (sect_kind != Vg_SectUnknown)
|
|
{
|
|
VG_(message)(Vg_UserMsg,
|
|
"Allocation context: %s section of %s",
|
|
VG_(pp_SectKind)(sect_kind),
|
|
sect_name);
|
|
}
|
|
else
|
|
{
|
|
VG_(message)(Vg_UserMsg, "Allocation context: unknown.");
|
|
}
|
|
}
|
|
if (s_show_conflicting_segments)
|
|
{
|
|
DRD_(thread_report_conflicting_segments)(dri->tid,
|
|
dri->addr, dri->size,
|
|
dri->access_type);
|
|
}
|
|
|
|
VG_(free)(descr2);
|
|
VG_(free)(descr1);
|
|
}
|
|
|
|
static Bool drd_tool_error_eq(VgRes res, Error* e1, Error* e2)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
static void drd_tool_error_pp(Error* const e)
|
|
{
|
|
static DrdThreadId s_last_tid_printed = 1;
|
|
DrdThreadId* err_extra;
|
|
|
|
err_extra = VG_(get_error_extra)(e);
|
|
|
|
if (err_extra && *err_extra != s_last_tid_printed)
|
|
{
|
|
VG_UMSG("%s:", DRD_(thread_get_name)(*err_extra));
|
|
s_last_tid_printed = *err_extra;
|
|
}
|
|
|
|
switch (VG_(get_error_kind)(e))
|
|
{
|
|
case DataRaceErr: {
|
|
drd_report_data_race(e, VG_(get_error_extra)(e));
|
|
break;
|
|
}
|
|
case MutexErr: {
|
|
MutexErrInfo* p = (MutexErrInfo*)(VG_(get_error_extra)(e));
|
|
tl_assert(p);
|
|
if (p->recursion_count >= 0)
|
|
{
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: mutex 0x%lx, recursion count %d, owner %d.",
|
|
VG_(get_error_string)(e),
|
|
p->mutex,
|
|
p->recursion_count,
|
|
p->owner);
|
|
}
|
|
else
|
|
{
|
|
VG_(message)(Vg_UserMsg,
|
|
"The object at address 0x%lx is not a mutex.",
|
|
p->mutex);
|
|
}
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(p->mutex);
|
|
break;
|
|
}
|
|
case CondErr: {
|
|
CondErrInfo* cdei =(CondErrInfo*)(VG_(get_error_extra)(e));
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: cond 0x%lx",
|
|
VG_(get_error_string)(e),
|
|
cdei->cond);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(cdei->cond);
|
|
break;
|
|
}
|
|
case CondDestrErr: {
|
|
CondDestrErrInfo* cdi = (CondDestrErrInfo*)(VG_(get_error_extra)(e));
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: cond 0x%lx, mutex 0x%lx locked by thread %d/%d",
|
|
VG_(get_error_string)(e),
|
|
cdi->cond, cdi->mutex,
|
|
DRD_(DrdThreadIdToVgThreadId)(cdi->owner), cdi->owner);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(cdi->mutex);
|
|
break;
|
|
}
|
|
case CondRaceErr: {
|
|
CondRaceErrInfo* cei = (CondRaceErrInfo*)(VG_(get_error_extra)(e));
|
|
VG_(message)(Vg_UserMsg,
|
|
"Probably a race condition: condition variable 0x%lx has"
|
|
" been signaled but the associated mutex 0x%lx is not"
|
|
" locked by the signalling thread.",
|
|
cei->cond, cei->mutex);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(cei->cond);
|
|
first_observed(cei->mutex);
|
|
break;
|
|
}
|
|
case CondWaitErr: {
|
|
CondWaitErrInfo* cwei = (CondWaitErrInfo*)(VG_(get_error_extra)(e));
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: condition variable 0x%lx, mutexes 0x%lx and 0x%lx",
|
|
VG_(get_error_string)(e),
|
|
cwei->cond,
|
|
cwei->mutex1,
|
|
cwei->mutex2);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(cwei->cond);
|
|
first_observed(cwei->mutex1);
|
|
first_observed(cwei->mutex2);
|
|
break;
|
|
}
|
|
case SemaphoreErr: {
|
|
SemaphoreErrInfo* sei = (SemaphoreErrInfo*)(VG_(get_error_extra)(e));
|
|
tl_assert(sei);
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: semaphore 0x%lx",
|
|
VG_(get_error_string)(e),
|
|
sei->semaphore);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(sei->semaphore);
|
|
break;
|
|
}
|
|
case BarrierErr: {
|
|
BarrierErrInfo* bei = (BarrierErrInfo*)(VG_(get_error_extra)(e));
|
|
tl_assert(bei);
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: barrier 0x%lx",
|
|
VG_(get_error_string)(e),
|
|
bei->barrier);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
if (bei->other_context)
|
|
{
|
|
VG_(message)(Vg_UserMsg,
|
|
"Conflicting wait call by thread %d/%d:",
|
|
DRD_(DrdThreadIdToVgThreadId)(bei->other_tid),
|
|
bei->other_tid);
|
|
VG_(pp_ExeContext)(bei->other_context);
|
|
}
|
|
first_observed(bei->barrier);
|
|
break;
|
|
}
|
|
case RwlockErr: {
|
|
RwlockErrInfo* p = (RwlockErrInfo*)(VG_(get_error_extra)(e));
|
|
tl_assert(p);
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s: rwlock 0x%lx.",
|
|
VG_(get_error_string)(e),
|
|
p->rwlock);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(p->rwlock);
|
|
break;
|
|
}
|
|
case HoldtimeErr: {
|
|
HoldtimeErrInfo* p =(HoldtimeErrInfo*)(VG_(get_error_extra)(e));
|
|
tl_assert(p);
|
|
tl_assert(p->acquired_at);
|
|
VG_(message)(Vg_UserMsg, "Acquired at:");
|
|
VG_(pp_ExeContext)(p->acquired_at);
|
|
VG_(message)(Vg_UserMsg,
|
|
"Lock on %s 0x%lx was held during %d ms (threshold: %d ms).",
|
|
VG_(get_error_string)(e),
|
|
p->synchronization_object,
|
|
p->hold_time_ms,
|
|
p->threshold_ms);
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
first_observed(p->synchronization_object);
|
|
break;
|
|
}
|
|
case GenericErr: {
|
|
//GenericErrInfo* gei =(GenericErrInfo*)(VG_(get_error_extra)(e));
|
|
VG_(message)(Vg_UserMsg, "%s", VG_(get_error_string)(e));
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
break;
|
|
}
|
|
default:
|
|
VG_(message)(Vg_UserMsg,
|
|
"%s",
|
|
VG_(get_error_string)(e));
|
|
VG_(pp_ExeContext)(VG_(get_error_where)(e));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static UInt drd_tool_error_update_extra(Error* e)
|
|
{
|
|
switch (VG_(get_error_kind)(e))
|
|
{
|
|
case DataRaceErr:
|
|
return sizeof(DataRaceErrInfo);
|
|
case MutexErr:
|
|
return sizeof(MutexErrInfo);
|
|
case CondErr:
|
|
return sizeof(CondErrInfo);
|
|
case CondDestrErr:
|
|
return sizeof(CondDestrErrInfo);
|
|
case CondRaceErr:
|
|
return sizeof(CondRaceErrInfo);
|
|
case CondWaitErr:
|
|
return sizeof(CondWaitErrInfo);
|
|
case SemaphoreErr:
|
|
return sizeof(SemaphoreErrInfo);
|
|
case BarrierErr:
|
|
return sizeof(BarrierErrInfo);
|
|
case RwlockErr:
|
|
return sizeof(RwlockErrInfo);
|
|
case HoldtimeErr:
|
|
return sizeof(HoldtimeErrInfo);
|
|
case GenericErr:
|
|
return sizeof(GenericErrInfo);
|
|
default:
|
|
tl_assert(False);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Bool drd_tool_error_recog(Char* const name, Supp* const supp)
|
|
{
|
|
SuppKind skind = 0;
|
|
|
|
if (VG_(strcmp)(name, STR_DataRaceErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_MutexErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_CondErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_CondDestrErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_CondRaceErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_CondWaitErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_SemaphoreErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_BarrierErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_RwlockErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_HoldtimeErr) == 0)
|
|
;
|
|
else if (VG_(strcmp)(name, STR_GenericErr) == 0)
|
|
;
|
|
else
|
|
return False;
|
|
|
|
VG_(set_supp_kind)(supp, skind);
|
|
return True;
|
|
}
|
|
|
|
static
|
|
Bool drd_tool_error_read_extra(Int fd, Char* buf, Int nBuf, Supp* supp)
|
|
{
|
|
return True;
|
|
}
|
|
|
|
static Bool drd_tool_error_matches(Error* const e, Supp* const supp)
|
|
{
|
|
switch (VG_(get_supp_kind)(supp))
|
|
{
|
|
}
|
|
return True;
|
|
}
|
|
|
|
static Char* drd_tool_error_name(Error* e)
|
|
{
|
|
switch (VG_(get_error_kind)(e))
|
|
{
|
|
case DataRaceErr: return VGAPPEND(STR_, DataRaceErr);
|
|
case MutexErr: return VGAPPEND(STR_, MutexErr);
|
|
case CondErr: return VGAPPEND(STR_, CondErr);
|
|
case CondDestrErr: return VGAPPEND(STR_, CondDestrErr);
|
|
case CondRaceErr: return VGAPPEND(STR_, CondRaceErr);
|
|
case CondWaitErr: return VGAPPEND(STR_, CondWaitErr);
|
|
case SemaphoreErr: return VGAPPEND(STR_, SemaphoreErr);
|
|
case BarrierErr: return VGAPPEND(STR_, BarrierErr);
|
|
case RwlockErr: return VGAPPEND(STR_, RwlockErr);
|
|
case HoldtimeErr: return VGAPPEND(STR_, HoldtimeErr);
|
|
case GenericErr: return VGAPPEND(STR_, GenericErr);
|
|
default:
|
|
tl_assert(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void drd_tool_error_print_extra(Error* e)
|
|
{ }
|
|
|
|
void DRD_(register_error_handlers)(void)
|
|
{
|
|
// Tool error reporting.
|
|
VG_(needs_tool_errors)(drd_tool_error_eq,
|
|
drd_tool_error_pp,
|
|
False,
|
|
drd_tool_error_update_extra,
|
|
drd_tool_error_recog,
|
|
drd_tool_error_read_extra,
|
|
drd_tool_error_matches,
|
|
drd_tool_error_name,
|
|
drd_tool_error_print_extra);
|
|
}
|