blob: a219cec6930ccbec7e3917a60df76fac901e5742 [file] [log] [blame]
/*
This file is part of drd, a data race 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_bitmap.h"
#include "drd_thread_bitmap.h"
#include "drd_vc.h" /* DRD_(vc_snprint)() */
/* Include several source files here in order to allow the compiler to */
/* do more inlining. */
#include "drd_bitmap.c"
#include "drd_load_store.h"
#include "drd_segment.c"
#include "drd_thread.c"
#include "drd_vc.c"
#include "libvex_guest_offsets.h"
/* STACK_POINTER_OFFSET: VEX register offset for the stack pointer register. */
#if defined(VGA_x86)
#define STACK_POINTER_OFFSET OFFSET_x86_ESP
#elif defined(VGA_amd64)
#define STACK_POINTER_OFFSET OFFSET_amd64_RSP
#elif defined(VGA_ppc32)
#define STACK_POINTER_OFFSET ((OFFSET_ppc32_GPR0 + OFFSET_ppc32_GPR2) / 2)
#elif defined(VGA_ppc64)
#define STACK_POINTER_OFFSET ((OFFSET_ppc64_GPR0 + OFFSET_ppc64_GPR2) / 2)
#else
#error Unknown architecture.
#endif
/* Local variables. */
static Bool s_drd_check_stack_accesses = False;
/* Function definitions. */
Bool DRD_(get_check_stack_accesses)()
{
return s_drd_check_stack_accesses;
}
void DRD_(set_check_stack_accesses)(const Bool c)
{
tl_assert(c == False || c == True);
s_drd_check_stack_accesses = c;
}
void DRD_(trace_mem_access)(const Addr addr, const SizeT size,
const BmAccessTypeT access_type)
{
if (DRD_(is_any_traced)(addr, addr + size))
{
char vc[80];
DRD_(vc_snprint)(vc, sizeof(vc), thread_get_vc(thread_get_running_tid()));
VG_(message)(Vg_UserMsg,
"%s 0x%lx size %ld (vg %d / drd %d / vc %s)",
access_type == eLoad
? "load "
: access_type == eStore
? "store"
: access_type == eStart
? "start"
: access_type == eEnd
? "end "
: "????",
addr,
size,
VG_(get_running_tid)(),
thread_get_running_tid(),
vc);
VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(),
VG_(clo_backtrace_size));
tl_assert(DrdThreadIdToVgThreadId(thread_get_running_tid())
== VG_(get_running_tid)());
}
}
static VG_REGPARM(2) void drd_trace_mem_load(const Addr addr, const SizeT size)
{
return DRD_(trace_mem_access)(addr, size, eLoad);
}
static VG_REGPARM(2) void drd_trace_mem_store(const Addr addr,const SizeT size)
{
return DRD_(trace_mem_access)(addr, size, eStore);
}
static void drd_report_race(const Addr addr, const SizeT size,
const BmAccessTypeT access_type)
{
DataRaceErrInfo drei;
drei.tid = thread_get_running_tid();
drei.addr = addr;
drei.size = size;
drei.access_type = access_type;
VG_(maybe_record_error)(VG_(get_running_tid)(),
DataRaceErr,
VG_(get_IP)(VG_(get_running_tid)()),
"Conflicting accesses",
&drei);
}
VG_REGPARM(2) void drd_trace_load(Addr addr, SizeT size)
{
#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
/* The assert below has been commented out because of performance reasons.*/
tl_assert(thread_get_running_tid()
== VgThreadIdToDrdThreadId(VG_(get_running_tid())));
#endif
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_load_triggers_conflict(addr, addr + size)
&& ! DRD_(is_suppressed)(addr, addr + size))
{
drd_report_race(addr, size, eLoad);
}
}
static VG_REGPARM(1) void drd_trace_load_1(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_load_1_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 1))
{
drd_report_race(addr, 1, eLoad);
}
}
static VG_REGPARM(1) void drd_trace_load_2(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_load_2_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 2))
{
drd_report_race(addr, 2, eLoad);
}
}
static VG_REGPARM(1) void drd_trace_load_4(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_load_4_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 4))
{
drd_report_race(addr, 4, eLoad);
}
}
static VG_REGPARM(1) void drd_trace_load_8(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_load_8_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 8))
{
drd_report_race(addr, 8, eLoad);
}
}
VG_REGPARM(2) void drd_trace_store(Addr addr, SizeT size)
{
#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
/* The assert below has been commented out because of performance reasons.*/
tl_assert(thread_get_running_tid()
== VgThreadIdToDrdThreadId(VG_(get_running_tid())));
#endif
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_store_triggers_conflict(addr, addr + size)
&& ! DRD_(is_suppressed)(addr, addr + size))
{
drd_report_race(addr, size, eStore);
}
}
static VG_REGPARM(1) void drd_trace_store_1(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_store_1_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 1))
{
drd_report_race(addr, 1, eStore);
}
}
static VG_REGPARM(1) void drd_trace_store_2(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_store_2_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 2))
{
drd_report_race(addr, 2, eStore);
}
}
static VG_REGPARM(1) void drd_trace_store_4(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_store_4_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 4))
{
drd_report_race(addr, 4, eStore);
}
}
static VG_REGPARM(1) void drd_trace_store_8(Addr addr)
{
if (running_thread_is_recording()
&& (s_drd_check_stack_accesses || ! thread_address_on_stack(addr))
&& bm_access_store_8_triggers_conflict(addr)
&& ! DRD_(is_suppressed)(addr, addr + 8))
{
drd_report_race(addr, 8, eStore);
}
}
/**
* Return true if and only if addr_expr matches the pattern (SP) or
* <offset>(SP).
*/
static Bool is_stack_access(IRSB* const bb, IRExpr* const addr_expr)
{
Bool result = False;
if (addr_expr->tag == Iex_RdTmp)
{
int i;
for (i = 0; i < bb->stmts_size; i++)
{
if (bb->stmts[i]
&& bb->stmts[i]->tag == Ist_WrTmp
&& bb->stmts[i]->Ist.WrTmp.tmp == addr_expr->Iex.RdTmp.tmp)
{
IRExpr* e = bb->stmts[i]->Ist.WrTmp.data;
if (e->tag == Iex_Get && e->Iex.Get.offset == STACK_POINTER_OFFSET)
{
result = True;
}
//ppIRExpr(e);
//VG_(printf)(" (%s)\n", result ? "True" : "False");
break;
}
}
}
return result;
}
static void instrument_load(IRSB* const bb,
IRExpr* const addr_expr,
const HWord size)
{
IRExpr* size_expr;
IRExpr** argv;
IRDirty* di;
if (UNLIKELY(DRD_(any_address_is_traced)()))
{
addStmtToIRSB(bb,
IRStmt_Dirty(
unsafeIRDirty_0_N(/*regparms*/2,
"drd_trace_load",
VG_(fnptr_to_fnentry)
(drd_trace_mem_load),
mkIRExprVec_2(addr_expr,
mkIRExpr_HWord(size)))));
}
if (! s_drd_check_stack_accesses && is_stack_access(bb, addr_expr))
return;
switch (size)
{
case 1:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_load_1",
VG_(fnptr_to_fnentry)(drd_trace_load_1),
argv);
break;
case 2:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_load_2",
VG_(fnptr_to_fnentry)(drd_trace_load_2),
argv);
break;
case 4:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_load_4",
VG_(fnptr_to_fnentry)(drd_trace_load_4),
argv);
break;
case 8:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_load_8",
VG_(fnptr_to_fnentry)(drd_trace_load_8),
argv);
break;
default:
size_expr = mkIRExpr_HWord(size);
argv = mkIRExprVec_2(addr_expr, size_expr);
di = unsafeIRDirty_0_N(/*regparms*/2,
"drd_trace_load",
VG_(fnptr_to_fnentry)(drd_trace_load),
argv);
break;
}
addStmtToIRSB(bb, IRStmt_Dirty(di));
}
static void instrument_store(IRSB* const bb,
IRExpr* const addr_expr,
const HWord size)
{
IRExpr* size_expr;
IRExpr** argv;
IRDirty* di;
if (UNLIKELY(DRD_(any_address_is_traced)()))
{
addStmtToIRSB(bb,
IRStmt_Dirty(
unsafeIRDirty_0_N(/*regparms*/2,
"drd_trace_store",
VG_(fnptr_to_fnentry)
(drd_trace_mem_store),
mkIRExprVec_2(addr_expr,
mkIRExpr_HWord(size)))));
}
if (! s_drd_check_stack_accesses && is_stack_access(bb, addr_expr))
return;
switch (size)
{
case 1:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_store_1",
VG_(fnptr_to_fnentry)(drd_trace_store_1),
argv);
break;
case 2:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_store_2",
VG_(fnptr_to_fnentry)(drd_trace_store_2),
argv);
break;
case 4:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_store_4",
VG_(fnptr_to_fnentry)(drd_trace_store_4),
argv);
break;
case 8:
argv = mkIRExprVec_1(addr_expr);
di = unsafeIRDirty_0_N(/*regparms*/1,
"drd_trace_store_8",
VG_(fnptr_to_fnentry)(drd_trace_store_8),
argv);
break;
default:
size_expr = mkIRExpr_HWord(size);
argv = mkIRExprVec_2(addr_expr, size_expr);
di = unsafeIRDirty_0_N(/*regparms*/2,
"drd_trace_store",
VG_(fnptr_to_fnentry)(drd_trace_store),
argv);
break;
}
addStmtToIRSB(bb, IRStmt_Dirty(di));
}
IRSB* DRD_(instrument)(VgCallbackClosure* const closure,
IRSB* const bb_in,
VexGuestLayout* const layout,
VexGuestExtents* const vge,
IRType const gWordTy,
IRType const hWordTy)
{
IRDirty* di;
Int i;
IRSB* bb;
IRExpr** argv;
Bool instrument = True;
Bool bus_locked = False;
/* Set up BB */
bb = emptyIRSB();
bb->tyenv = deepCopyIRTypeEnv(bb_in->tyenv);
bb->next = deepCopyIRExpr(bb_in->next);
bb->jumpkind = bb_in->jumpkind;
for (i = 0; i < bb_in->stmts_used; i++)
{
IRStmt* const st = bb_in->stmts[i];
tl_assert(st);
if (st->tag == Ist_NoOp)
continue;
switch (st->tag)
{
/* Note: the code for not instrumenting the code in .plt */
/* sections is only necessary on CentOS 3.0 x86 (kernel 2.4.21 */
/* + glibc 2.3.2 + NPTL 0.60 + binutils 2.14.90.0.4). */
/* This is because on this platform dynamic library symbols are */
/* relocated in another way than by later binutils versions. The */
/* linker e.g. does not generate .got.plt sections on CentOS 3.0. */
case Ist_IMark:
instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr)
!= Vg_SectPLT;
addStmtToIRSB(bb, st);
break;
case Ist_MBE:
switch (st->Ist.MBE.event)
{
case Imbe_Fence:
break; /* not interesting */
case Imbe_BusLock:
case Imbe_SnoopedStoreBegin:
tl_assert(! bus_locked);
bus_locked = True;
break;
case Imbe_BusUnlock:
case Imbe_SnoopedStoreEnd:
tl_assert(bus_locked);
bus_locked = False;
break;
default:
tl_assert(0);
}
addStmtToIRSB(bb, st);
break;
case Ist_Store:
if (instrument && ! bus_locked)
{
instrument_store(bb,
st->Ist.Store.addr,
sizeofIRType(typeOfIRExpr(bb->tyenv,
st->Ist.Store.data)));
}
addStmtToIRSB(bb, st);
break;
case Ist_WrTmp:
if (instrument)
{
const IRExpr* const data = st->Ist.WrTmp.data;
if (data->tag == Iex_Load)
{
instrument_load(bb,
data->Iex.Load.addr,
sizeofIRType(data->Iex.Load.ty));
}
}
addStmtToIRSB(bb, st);
break;
case Ist_Dirty:
if (instrument)
{
IRDirty* d = st->Ist.Dirty.details;
IREffect const mFx = d->mFx;
switch (mFx) {
case Ifx_None:
break;
case Ifx_Read:
case Ifx_Write:
case Ifx_Modify:
tl_assert(d->mAddr);
tl_assert(d->mSize > 0);
argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize));
if (mFx == Ifx_Read || mFx == Ifx_Modify) {
di = unsafeIRDirty_0_N(
/*regparms*/2,
"drd_trace_load",
VG_(fnptr_to_fnentry)(drd_trace_load),
argv);
addStmtToIRSB(bb, IRStmt_Dirty(di));
}
if ((mFx == Ifx_Write || mFx == Ifx_Modify)
&& ! bus_locked)
{
di = unsafeIRDirty_0_N(
/*regparms*/2,
"drd_trace_store",
VG_(fnptr_to_fnentry)(drd_trace_store),
argv);
addStmtToIRSB(bb, IRStmt_Dirty(di));
}
break;
default:
tl_assert(0);
}
}
addStmtToIRSB(bb, st);
break;
default:
addStmtToIRSB(bb, st);
break;
}
}
tl_assert(! bus_locked);
return bb;
}