From: Julian Seward Date: Sun, 25 Nov 2007 14:01:38 +0000 (+0000) Subject: Add DRD as an experimental tool. Bart Van Assche is the maintainer. X-Git-Tag: svn/VALGRIND_3_3_0~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc1dbea9371e08db31c20990f10c74220b952cc7;p=thirdparty%2Fvalgrind.git Add DRD as an experimental tool. Bart Van Assche is the maintainer. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7211 --- diff --git a/exp-drd/Makefile.am b/exp-drd/Makefile.am new file mode 100644 index 0000000000..228ee54bbe --- /dev/null +++ b/exp-drd/Makefile.am @@ -0,0 +1,139 @@ +include $(top_srcdir)/Makefile.tool.am + +noinst_PROGRAMS = +if VGP_X86_LINUX + noinst_PROGRAMS += exp-drd-x86-linux vgpreload_exp-drd-x86-linux.so +endif +if VGP_AMD64_LINUX + noinst_PROGRAMS += exp-drd-amd64-linux vgpreload_exp-drd-amd64-linux.so +endif +if VGP_PPC32_LINUX + noinst_PROGRAMS += exp-drd-ppc32-linux vgpreload_exp-drd-ppc32-linux.so +endif +if VGP_PPC64_LINUX + noinst_PROGRAMS += exp-drd-ppc64-linux vgpreload_exp-drd-ppc64-linux.so +endif +if VGP_PPC32_AIX5 + noinst_PROGRAMS += exp-drd-ppc32-aix5 vgpreload_exp-drd-ppc32-aix5.so +endif +if VGP_PPC64_AIX5 + noinst_PROGRAMS += exp-drd-ppc64-aix5 vgpreload_exp-drd-ppc64-aix5.so +endif + +VGPRELOAD_DRD_SOURCES_COMMON = drd_preloaded.c + +vgpreload_exp_drd_x86_linux_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_x86_linux_so_CPPFLAGS = $(AM_CPPFLAGS_X86_LINUX) +vgpreload_exp_drd_x86_linux_so_CFLAGS = $(AM_CFLAGS_X86_LINUX) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_x86_linux_so_CCASFLAGS = $(AM_CCASFLAGS_X86_LINUX) +vgpreload_exp_drd_x86_linux_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_LINUX) +vgpreload_exp_drd_x86_linux_so_LDFLAGS = $(PRELOAD_LDFLAGS_X86_LINUX)\ + $(LIBREPLACEMALLOC_LDFLAGS_X86_LINUX) + +vgpreload_exp_drd_amd64_linux_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_amd64_linux_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_LINUX) +vgpreload_exp_drd_amd64_linux_so_CFLAGS = $(AM_CFLAGS_AMD64_LINUX) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_amd64_linux_so_CCASFLAGS = $(AM_CCASFLAGS_AMD64_LINUX) +vgpreload_exp_drd_amd64_linux_so_DEPENDENCIES = +vgpreload_exp_drd_amd64_linux_so_LDFLAGS = $(PRELOAD_LDFLAGS_AMD64_LINUX) + +vgpreload_exp_drd_ppc32_linux_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_ppc32_linux_so_CPPFLAGS = $(AM_CPPFLAGS_PPC32_LINUX) +vgpreload_exp_drd_ppc32_linux_so_CFLAGS = $(AM_CFLAGS_PPC32_LINUX) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_ppc32_linux_so_CCASFLAGS = $(AM_CCASFLAGS_PPC32_LINUX) +vgpreload_exp_drd_ppc32_linux_so_DEPENDENCIES = +vgpreload_exp_drd_ppc32_linux_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC32_LINUX) + +vgpreload_exp_drd_ppc64_linux_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_ppc64_linux_so_CPPFLAGS = $(AM_CPPFLAGS_PPC64_LINUX) +vgpreload_exp_drd_ppc64_linux_so_CFLAGS = $(AM_CFLAGS_PPC64_LINUX) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_ppc64_linux_so_CCASFLAGS = $(AM_CCASFLAGS_PPC64_LINUX) +vgpreload_exp_drd_ppc64_linux_so_DEPENDENCIES = +vgpreload_exp_drd_ppc64_linux_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC64_LINUX) + +vgpreload_exp_drd_ppc32_aix5_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_ppc32_aix5_so_CPPFLAGS = $(AM_CPPFLAGS_PPC32_AIX5) +vgpreload_exp_drd_ppc32_aix5_so_CFLAGS = $(AM_CFLAGS_PPC32_AIX5) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_ppc32_aix5_so_CCASFLAGS = $(AM_CCASFLAGS_PPC32_AIX5) +vgpreload_exp_drd_ppc32_aix5_so_DEPENDENCIES = +vgpreload_exp_drd_ppc32_aix5_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC32_AIX5) + +vgpreload_exp_drd_ppc64_aix5_so_SOURCES = $(VGPRELOAD_DRD_SOURCES_COMMON) +vgpreload_exp_drd_ppc64_aix5_so_CPPFLAGS = $(AM_CPPFLAGS_PPC64_AIX5) +vgpreload_exp_drd_ppc64_aix5_so_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) $(AM_CFLAGS_PIC) +vgpreload_exp_drd_ppc64_aix5_so_CCASFLAGS = $(AM_CCASFLAGS_PPC64_AIX5) +vgpreload_exp_drd_ppc64_aix5_so_DEPENDENCIES = +vgpreload_exp_drd_ppc64_aix5_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC64_AIX5) + + +DRD_SOURCES_COMMON = \ + drd_bitmap.c \ + drd_clientreq.c \ + drd_cond.c \ + drd_error.c \ + drd_main.c \ + drd_malloc_wrappers.c \ + drd_mutex.c \ + drd_segment.c \ + drd_suppression.c \ + drd_thread.c \ + drd_vc.c + +AM_CFLAGS_X86_LINUX += -I$(top_srcdir)/coregrind +AM_CFLAGS_AMD64_LINUX += -I$(top_srcdir)/coregrind +AM_CFLAGS_PPC32_LINUX += -I$(top_srcdir)/coregrind +AM_CFLAGS_PPC64_LINUX += -I$(top_srcdir)/coregrind +AM_CFLAGS_PPC32_AIX5 += -I$(top_srcdir)/coregrind +AM_CFLAGS_PPC64_AIX5 += -I$(top_srcdir)/coregrind + +exp_drd_x86_linux_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_x86_linux_CPPFLAGS = $(AM_CPPFLAGS_X86_LINUX) +exp_drd_x86_linux_CFLAGS = $(AM_CFLAGS_X86_LINUX) +exp_drd_x86_linux_DEPENDENCIES = $(COREGRIND_LIBS_X86_LINUX) +exp_drd_x86_linux_LDADD = $(TOOL_LDADD_X86_LINUX) +exp_drd_x86_linux_LDFLAGS = $(TOOL_LDFLAGS_X86_LINUX) + +exp_drd_amd64_linux_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_amd64_linux_CPPFLAGS = $(AM_CPPFLAGS_AMD64_LINUX) +exp_drd_amd64_linux_CFLAGS = $(AM_CFLAGS_AMD64_LINUX) +exp_drd_amd64_linux_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_LINUX) +exp_drd_amd64_linux_LDADD = $(TOOL_LDADD_AMD64_LINUX) +exp_drd_amd64_linux_LDFLAGS = $(TOOL_LDFLAGS_AMD64_LINUX) + +exp_drd_ppc32_linux_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_ppc32_linux_CPPFLAGS = $(AM_CPPFLAGS_PPC32_LINUX) +exp_drd_ppc32_linux_CFLAGS = $(AM_CFLAGS_PPC32_LINUX) +exp_drd_ppc32_linux_DEPENDENCIES = $(COREGRIND_LIBS_PPC32_LINUX) +exp_drd_ppc32_linux_LDADD = $(TOOL_LDADD_PPC32_LINUX) +exp_drd_ppc32_linux_LDFLAGS = $(TOOL_LDFLAGS_PPC32_LINUX) + +exp_drd_ppc64_linux_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_ppc64_linux_CPPFLAGS = $(AM_CPPFLAGS_PPC64_LINUX) +exp_drd_ppc64_linux_CFLAGS = $(AM_CFLAGS_PPC64_LINUX) +exp_drd_ppc64_linux_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_LINUX) +exp_drd_ppc64_linux_LDADD = $(TOOL_LDADD_PPC64_LINUX) +exp_drd_ppc64_linux_LDFLAGS = $(TOOL_LDFLAGS_PPC64_LINUX) + +exp_drd_ppc32_aix5_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_ppc32_aix5_CPPFLAGS = $(AM_CPPFLAGS_PPC32_AIX5) +exp_drd_ppc32_aix5_CFLAGS = $(AM_CFLAGS_PPC32_AIX5) +exp_drd_ppc32_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC32_AIX5) +exp_drd_ppc32_aix5_LDADD = $(TOOL_LDADD_PPC32_AIX5) +exp_drd_ppc32_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC32_AIX5) + +exp_drd_ppc64_aix5_SOURCES = $(DRD_SOURCES_COMMON) +exp_drd_ppc64_aix5_CPPFLAGS = $(AM_CPPFLAGS_PPC64_AIX5) +exp_drd_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) +exp_drd_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) +exp_drd_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) +exp_drd_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + + +#all-local: +# for f in $(noinst_PROGRAMS); do \ +# p=`echo $$f | sed -e 's/^[^-]*-[^-]*-//' -e 's/\..*$$//'`; \ +# n=`echo $$f | sed -e 's/^\([^-]*-[^-]*\)-[^-]*-[^-]*/\1/'`; \ +# mkdir -p $(inplacedir)/$$p; \ +# rm -f $(inplacedir)/$$p/$$n; \ +# ln -f -s ../../$(subdir)/$$f $(inplacedir)/$$p/$$n; \ +# done diff --git a/exp-drd/TODO.txt b/exp-drd/TODO.txt new file mode 100644 index 0000000000..02a135c3fc --- /dev/null +++ b/exp-drd/TODO.txt @@ -0,0 +1,57 @@ +Last updated February 22, 2006 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Data-race detection algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Implement segment merging, such that the number of segments per thread + remains limited even when there is no synchronization between threads. +- Find out why a race is reported on std::string::string(std::string const&) + (stc test case 16). +- Make sure that drd supports more than 256 mutexes. +- Performance testing and tuning. +- pthread rwlock state tracking and support. +- pthread barrier state tracking and support. +- mutexes: support for pthread_mutex_timedlock() (recently added to the POSIX + spec, and present in glibc). See also + http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_timedlock.html +- testing on PPC and AIX (current implementation is only tested on X86 and + AMD64). +- Change s_threadinfo[] from an array into an OSet or VgHashTable, in order to + make ThreadId <> DrdThreadId <> pthread_t conversions faster. +- Write a configure test that figures out sizeof(pthread_mutex_t) etc. +- [AMD64] Find out why removing 'write(1, "", 0)' in drd_preloaded.c triggers + a crash on AMD64. Is this a drd or a VEX bug ? +- Reintroduce the const keyword in the function declarations of the OSet + implementation in the core where appropriate. + +Testing +~~~~~~~ +- testing with more complex multithreaded test programs. +- test drd's performance with the SPLASH-2 software, e.g. fft + (http://www-flash.stanford.edu/apps/SPLASH/). +- Add helgrind's unit tests to drd's unit test set by adding soft links + under drd/tests to the respective helgrind unit tests. + + +Documentation +~~~~~~~~~~~~~ +- Document the code. +- Document how to use the tool. + + +Known bugs +~~~~~~~~~~ +- Gets killed by the OOM handler for some applications, e.g. knode and + OpenOffice. +- [AMD64] Reports "Allocation context: unknown" for BSS symbols on AMD64 + (works fine on X86). This is a bug in Valgrind's debug info reader + -- VG_(find_seginfo)() returns NULL for BSS symbols on AMD64. Not yet in + the KDE bug tracking system. +- False positives are reported when a signal is sent via pthread_kill() from + one thread to another (bug 152728). +- Crashes (cause not known): VALGRIND_LIB=$PWD/.in_place coregrind/valgrind --tool=exp-drd --trace-mem=yes /bin/ls + +Known performance issues: +- According to cachegrind, VG_(OSet_Next)() is taking up most CPU cycles. + Probably due to the bitmap implementation. diff --git a/exp-drd/docs/Makefile.am b/exp-drd/docs/Makefile.am new file mode 100644 index 0000000000..9d6f5f4778 --- /dev/null +++ b/exp-drd/docs/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = drd-manual.xml diff --git a/exp-drd/drd_bitmap.c b/exp-drd/drd_bitmap.c new file mode 100644 index 0000000000..21d9bb377b --- /dev/null +++ b/exp-drd/drd_bitmap.c @@ -0,0 +1,776 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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 "pub_tool_basics.h" // Addr, SizeT +#include "pub_tool_debuginfo.h" // VG_(get_objname)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcbase.h" // VG_(memset) +#include "pub_tool_libcprint.h" // VG_(printf) +#include "pub_tool_machine.h" // VG_(get_IP)() +#include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free) +#include "pub_drd_bitmap.h" +#include "drd_bitmap.h" +#include "drd_error.h" +#include "drd_suppression.h" + + +// Local constants. + +static ULong s_bitmap_creation_count; + + +// Local function declarations. + +static void bm2_merge(struct bitmap2* const bm2l, + const struct bitmap2* const bm2r); + + +// Function definitions. + +struct bitmap* bm_new() +{ + struct bitmap* bm; + + // If this assert fails, fix the definition of BITS_PER_BITS_PER_UWORD + // in drd_bitmap.h. + tl_assert((1 << BITS_PER_BITS_PER_UWORD) == BITS_PER_UWORD); + + bm = VG_(malloc)(sizeof(*bm)); + tl_assert(bm); + bm->oset = VG_(OSetGen_Create)(0, 0, VG_(malloc), VG_(free)); + + s_bitmap_creation_count++; + + return bm; +} + +void bm_delete(struct bitmap* const bm) +{ + tl_assert(bm); + VG_(OSetGen_Destroy)(bm->oset); + VG_(free)(bm); +} + +/** + * Record an access of type access_type at addresses a in bitmap bm. + */ +static +__inline__ +void bm_access_1(struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + struct bitmap2* p2; + struct bitmap1* p1; + UWord* p0; + SPLIT_ADDRESS(a); + + tl_assert(bm); + + p2 = bm2_lookup_or_insert(bm, a1); + p1 = &p2->bm1; + p0 = (access_type == eLoad) ? p1->bm0_r : p1->bm0_w; + bm0_set(p0, a0); +} + +static +void bm_access_4_nonaligned(struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + bm_access_1(bm, a + 0, access_type); + bm_access_1(bm, a + 1, access_type); + bm_access_1(bm, a + 2, access_type); + bm_access_1(bm, a + 3, access_type); +} + +static +__inline__ +void bm_access_4_aligned(struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + struct bitmap2* p2; + struct bitmap1* p1; + UWord* p0; + SPLIT_ADDRESS(a); + + tl_assert(bm); + + p2 = bm2_lookup_or_insert(bm, a1); + p1 = &p2->bm1; + p0 = (access_type == eLoad) ? p1->bm0_r : p1->bm0_w; + bm0_set(p0, a0+0); + bm0_set(p0, a0+1); + bm0_set(p0, a0+2); + bm0_set(p0, a0+3); +} + +/** + * Record an access of type access_type at addresses a .. a + 3 in bitmap bm. + */ +void bm_access_4(struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + tl_assert(bm); + if ((a & 3) != 0) + { + bm_access_4_nonaligned(bm, a, access_type); + } + else + { + bm_access_4_aligned(bm, a, access_type); + } +} + +/** + * Record an access of type access_type at addresses a .. a + size - 1 in + * bitmap bm. + */ +void bm_access_range(struct bitmap* const bm, + const Addr a, + const SizeT size, + const BmAccessTypeT access_type) +{ + tl_assert(bm); + tl_assert(size > 0); + + if (size == 4) + bm_access_4(bm, a, access_type); + else if (size == 1) + bm_access_1(bm, a, access_type); + else + { + Addr b; + for (b = a; b != a + size; b++) + { + bm_access_1(bm, b, access_type); + } + } +} + +Bool bm_has(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type) +{ + Addr b; + for (b = a1; b < a2; b++) + { + if (! bm_has_1(bm, b, access_type)) + { + return False; + } + } + return True; +} + +Bool bm_has_any(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type) +{ + Addr b; + + tl_assert(bm); + + for (b = a1; b < a2; b++) + { + if (bm_has_1(bm, b, access_type)) + { + return True; + } + } + return False; +} + +/* Return a non-zero value if there is a read access, write access or both */ +/* to any of the addresses in the range [ a1, a2 [ in bitmap bm. */ +UWord bm_has_any_access(const struct bitmap* const bm, + const Addr a1, + const Addr a2) +{ + Addr b, b_next; + + tl_assert(bm); + + for (b = a1; b < a2; b = b_next) + { + struct bitmap2* bm2 = bm_lookup(bm, b); + + b_next = (b & ~ADDR0_MASK) + ADDR0_COUNT; + if (b_next > a2) + { + b_next = a2; + } + + if (bm2) + { + Addr b_start; + Addr b_end; + UWord b0; + + if ((bm2->addr << ADDR0_BITS) < a1) + b_start = a1; + else + if ((bm2->addr << ADDR0_BITS) < a2) + b_start = (bm2->addr << ADDR0_BITS); + else + break; + tl_assert(a1 <= b_start && b_start <= a2); + + if ((bm2->addr << ADDR0_BITS) + ADDR0_COUNT < a2) + b_end = (bm2->addr << ADDR0_BITS) + ADDR0_COUNT; + else + b_end = a2; +#if 0 + VG_(message)(Vg_DebugMsg, + "in 0x%lx 0x%lx / cur 0x%lx 0x%lx / out 0x%lx 0x%lx", + a1, a2, + (bm2->addr << ADDR0_BITS), + (bm2->addr << ADDR0_BITS) + ADDR0_COUNT, + b_start, b_end); +#endif + tl_assert(a1 <= b_end && b_end <= a2); + tl_assert(b_start < b_end); + tl_assert((b_start & ADDR0_MASK) <= ((b_end - 1) & ADDR0_MASK)); + + for (b0 = b_start & ADDR0_MASK; b0 <= ((b_end - 1) & ADDR0_MASK); b0++) + { + const struct bitmap1* const p1 = &bm2->bm1; + const UWord mask + = bm0_is_set(p1->bm0_r, b0) | bm0_is_set(p1->bm0_w, b0); + if (mask) + { + return mask; + } + } + } + } + return 0; +} + +/** + * Report whether an access of type access_type at address a is recorded in + * bitmap bm. + * @return != 0 means true, and == 0 means false + */ +UWord bm_has_1(const struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + struct bitmap2* p2; + struct bitmap1* p1; + UWord* p0; + const UWord a0 = a & ADDR0_MASK; + + tl_assert(bm); + + p2 = bm_lookup(bm, a); + if (p2) + { + p1 = &p2->bm1; + p0 = (access_type == eLoad) ? p1->bm0_r : p1->bm0_w; + return bm0_is_set(p0, a0); + } + return 0; +} + +static __inline__ +void bm1_clear(struct bitmap1* const bm1, const Addr a1, const Addr a2) +{ + UWord idx; + UWord mask; + +#if 0 + // Commented out the assert statements below because of performance reasons. + tl_assert(a1); + tl_assert(a1 <= a2); + tl_assert(UWORD_MSB(a1) == UWORD_MSB(a2) + || UWORD_MSB(a1) == UWORD_MSB(a2 - 1)); +#endif + + idx = (a1 & ADDR0_MASK) >> BITS_PER_BITS_PER_UWORD; + /* mask: a contiguous series of one bits. The first bit set is bit */ + /* UWORD_LSB(a2-1), and the last bit set is UWORD_LSB(a1). */ + mask = UWORD_LSB(a2) ? bm0_mask(a2) - bm0_mask(a1) : - bm0_mask(a1); + bm1->bm0_r[idx] &= ~mask; + bm1->bm0_w[idx] &= ~mask; +} + +void bm_clear_all(const struct bitmap* const bm) +{ + struct bitmap2* bm2; + + VG_(OSetGen_ResetIter)(bm->oset); + + for ( ; (bm2 = VG_(OSetGen_Next)(bm->oset)) != 0; ) + { + struct bitmap1* const bm1 = &bm2->bm1; + tl_assert(bm1); + VG_(memset)(&bm1->bm0_r[0], 0, sizeof(bm1->bm0_r)); + VG_(memset)(&bm1->bm0_w[0], 0, sizeof(bm1->bm0_w)); + } +} + +#if 1 +// New and fast implementation. +void bm_clear(const struct bitmap* const bm, + const Addr a1, + const Addr a2) +{ + Addr b, b_next; + + tl_assert(bm); + tl_assert(a1); + tl_assert(a1 <= a2); + + for (b = a1; b < a2; b = b_next) + { + struct bitmap2* const p2 = bm_lookup(bm, b); + + b_next = (b & ~ADDR0_MASK) + ADDR0_COUNT; + if (b_next > a2) + { + b_next = a2; + } + + if (p2) + { + Addr c = b; + if (UWORD_LSB(c)) + { + Addr c_next = UWORD_MSB(c) + BITS_PER_UWORD; + if (c_next > b_next) + c_next = b_next; + bm1_clear(&p2->bm1, c, c_next); + c = c_next; + } + if (UWORD_LSB(c) == 0) + { + const Addr c_next = UWORD_MSB(b_next); + tl_assert(UWORD_LSB(c) == 0); + tl_assert(UWORD_LSB(c_next) == 0); + tl_assert(c_next <= b_next); + tl_assert(c <= c_next); + if (c_next > c) + { + UWord idx = (c & ADDR0_MASK) >> BITS_PER_BITS_PER_UWORD; + VG_(memset)(&p2->bm1.bm0_r[idx], 0, (c_next - c) / 8); + VG_(memset)(&p2->bm1.bm0_w[idx], 0, (c_next - c) / 8); + c = c_next; + } + } + if (c != b_next) + { + bm1_clear(&p2->bm1, c, b_next); + } + } + } +} +#else +// Old and slow implementation +void bm_clear(const struct bitmap* const bm, + const Addr a1, + const Addr a2) +{ + Addr b, b_next, c; + + tl_assert(bm); + tl_assert(a1); + tl_assert(a1 <= a2); + + for (b = a1; b < a2; b = b_next) + { + struct bitmap2* const p2 = bm_lookup(bm, b); + + b_next = (b & ~ADDR0_MASK) + ADDR0_COUNT; + if (b_next > a2) + { + b_next = a2; + } + + if (p2) + { + for (c = b; c < b_next; c++) + { + const UWord c0 = c & ADDR0_MASK; + + p2->bm1.bm0_r[c0 / (8*sizeof(UWord))] + &= ~(1UL << (c0 % (8*sizeof(UWord)))); + p2->bm1.bm0_w[c0 / (8*sizeof(UWord))] + &= ~(1UL << (c0 % (8*sizeof(UWord)))); + } + } + } +} +#endif + +static +__inline__ +UWord bm_has_conflict_with_1(const struct bitmap* const bm, + const Addr a, + const BmAccessTypeT access_type) +{ + struct bitmap2* p2; + const UWord a0 = a & ADDR0_MASK; + + tl_assert(bm); + + p2 = bm_lookup(bm, a); + if (p2) + { + if (access_type == eLoad) + { + return bm0_is_set(p2->bm1.bm0_w, a0); + } + else + { + tl_assert(access_type == eStore); + return (bm0_is_set(p2->bm1.bm0_r, a0) + | bm0_is_set(p2->bm1.bm0_w, a0)); + } + } + return False; +} + +/** + * Return true if the access to [a,a+size[ of type access_type conflicts with + * any access stored in bitmap bm. + */ +Bool bm_has_conflict_with(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type) +{ + Addr b; + for (b = a1; b != a2; b++) + { + if (bm_has_conflict_with_1(bm, b, access_type)) + { + return True; + } + } + return False; +} + +void bm_swap(struct bitmap* const bm1, struct bitmap* const bm2) +{ + OSet* const tmp = bm1->oset; + bm1->oset = bm2->oset; + bm2->oset = tmp; +} + +void bm_merge2(struct bitmap* const lhs, + const struct bitmap* const rhs) +{ + struct bitmap2* bm2l; + const struct bitmap2* bm2r; + + // First step: allocate any missing bitmaps in *lhs. + VG_(OSetGen_ResetIter)(rhs->oset); + for ( ; (bm2r = VG_(OSetGen_Next)(rhs->oset)) != 0; ) + { + bm2_lookup_or_insert(lhs, bm2r->addr); + } + + VG_(OSetGen_ResetIter)(lhs->oset); + VG_(OSetGen_ResetIter)(rhs->oset); + + for ( ; (bm2r = VG_(OSetGen_Next)(rhs->oset)) != 0; ) + { + do + { + bm2l = VG_(OSetGen_Next)(lhs->oset); + //VG_(message)(Vg_DebugMsg, "0x%x 0x%x", bm2l->addr, bm2r->addr); + } while (bm2l->addr < bm2r->addr); + + tl_assert(bm2l->addr == bm2r->addr); + + bm2_merge(bm2l, bm2r); + } +} + +/** + * Report whether there are any RW / WR / WW patterns in lhs and rhs. + * @param lhs First bitmap. + * @param rhs Bitmap to be compared with lhs. + * @return !=0 if there are data races, == 0 if there are none. + */ +int bm_has_races(const struct bitmap* const lhs, + const struct bitmap* const rhs) +{ + VG_(OSetGen_ResetIter)(lhs->oset); + VG_(OSetGen_ResetIter)(rhs->oset); + + for (;;) + { + const struct bitmap2* bm2l = VG_(OSetGen_Next)(lhs->oset); + const struct bitmap2* bm2r = VG_(OSetGen_Next)(rhs->oset); + const struct bitmap1* bm1l; + const struct bitmap1* bm1r; + unsigned k; + + while (bm2l && bm2r && bm2l->addr != bm2r->addr) + { + if (bm2l->addr < bm2r->addr) + bm2l = VG_(OSetGen_Next)(lhs->oset); + else + bm2r = VG_(OSetGen_Next)(rhs->oset); + } + if (bm2l == 0 || bm2r == 0) + break; + + bm1l = &bm2l->bm1; + bm1r = &bm2r->bm1; + + for (k = 0; k < BITMAP1_UWORD_COUNT; k++) + { + unsigned b; + for (b = 0; b < BITS_PER_UWORD; b++) + { + UWord const access + = ((bm1l->bm0_r[k] & bm0_mask(b)) ? LHS_R : 0) + | ((bm1l->bm0_w[k] & bm0_mask(b)) ? LHS_W : 0) + | ((bm1r->bm0_r[k] & bm0_mask(b)) ? RHS_R : 0) + | ((bm1r->bm0_w[k] & bm0_mask(b)) ? RHS_W : 0); + Addr const a = MAKE_ADDRESS(bm2l->addr, k * BITS_PER_UWORD | b); + if (HAS_RACE(access) && ! drd_is_suppressed(a, a + 1)) + { + return 1; + } + } + } + } + return 0; +} + +#ifdef OLD_RACE_DETECTION_ALGORITHM +/** + * Report RW / WR / WW patterns between lhs and rhs. + * @param tid1 Thread ID of lhs. + * @param tid2 Thread ID of rhs. + * @param lhs First bitmap. + * @param rhs Bitmap to be compared with lhs. + * @return Number of reported ranges with data races. + */ +void bm_report_races(const ThreadId tid1, + const ThreadId tid2, + const struct bitmap* const lhs, + const struct bitmap* const rhs) +{ + Addr range_begin = 0; + Addr range_end = 0; + UWord range_access = 0; + + VG_(message)(Vg_UserMsg, "Data addresses accessed by both segments:"); + + VG_(OSetGen_ResetIter)(lhs->oset); + VG_(OSetGen_ResetIter)(rhs->oset); + + for (;;) + { + const struct bitmap2* bm2l = VG_(OSetGen_Next)(lhs->oset); + const struct bitmap2* bm2r = VG_(OSetGen_Next)(rhs->oset); + const struct bitmap1* bm1l; + const struct bitmap1* bm1r; + unsigned k; + + while (bm2l && bm2r && bm2l->addr != bm2r->addr) + { + if (bm2l->addr < bm2r->addr) + bm2l = VG_(OSetGen_Next)(lhs->oset); + else + bm2r = VG_(OSetGen_Next)(rhs->oset); + } + if (bm2l == 0 || bm2r == 0) + break; + + bm1l = &bm2l->bm1; + bm1r = &bm2r->bm1; + + for (k = 0; k < BITMAP1_UWORD_COUNT; k++) + { + unsigned b; + for (b = 0; b < BITS_PER_UWORD; b++) + { + UWord const access + = ((bm1l->bm0_r[k] & bm0_mask(b)) ? LHS_R : 0) + | ((bm1l->bm0_w[k] & bm0_mask(b)) ? LHS_W : 0) + | ((bm1r->bm0_r[k] & bm0_mask(b)) ? RHS_R : 0) + | ((bm1r->bm0_w[k] & bm0_mask(b)) ? RHS_W : 0); + Addr const a = MAKE_ADDRESS(bm2l->addr, k * BITS_PER_UWORD | b); + if (access == range_access) + range_end = a + 1; + else + { + tl_assert(range_begin < range_end); + if (HAS_RACE(range_access) + && ! drd_is_suppressed(range_begin, range_end)) + { + DataRaceInfo dri; + dri.tid1 = tid1; + dri.tid2 = tid2; + dri.range_begin = range_begin; + dri.range_end = range_end; + dri.range_access = range_access; + tl_assert(dri.range_begin < dri.range_end); +#if 0 + VG_(maybe_record_error)(tid1, + DataRaceErr, + VG_(get_IP)(tid1), // where + "data race", + &dri); +#else + drd_report_data_race(&dri); +#endif + } + range_access = access; + range_begin = a; + range_end = a + 1; + } + } + } + } +} +#endif + +void bm_print(const struct bitmap* const bm) +{ + struct bitmap2* bm2; + + VG_(OSetGen_ResetIter)(bm->oset); + + for ( ; (bm2 = VG_(OSetGen_Next)(bm->oset)) != 0; ) + { + const struct bitmap1* const bm1 = &bm2->bm1; + unsigned k; + for (k = 0; k < BITMAP1_UWORD_COUNT; k++) + { + unsigned b; + for (b = 0; b < BITS_PER_UWORD; b++) + { + int const r = bm1->bm0_r[k] & bm0_mask(b); + int const w = bm1->bm0_w[k] & bm0_mask(b); + Addr const a = MAKE_ADDRESS(bm2->addr, k * BITS_PER_UWORD | b); + if (r || w) + { + VG_(printf)("0x%08lx %c %c\n", + (Addr)(a), + w ? 'W' : ' ', r ? 'R' : ' '); + } + } + } + } +} + +ULong bm_get_bitmap_creation_count(void) +{ + return s_bitmap_creation_count; +} + +ULong bm_get_bitmap2_creation_count(void) +{ + return s_bitmap2_creation_count; +} + +static void bm2_merge(struct bitmap2* const bm2l, + const struct bitmap2* const bm2r) +{ + unsigned k; + + tl_assert(bm2l->addr == bm2r->addr); + + for (k = 0; k < BITMAP1_UWORD_COUNT; k++) + { + bm2l->bm1.bm0_r[k] |= bm2r->bm1.bm0_r[k]; + } + for (k = 0; k < BITMAP1_UWORD_COUNT; k++) + { + bm2l->bm1.bm0_w[k] |= bm2r->bm1.bm0_w[k]; + } +} + +#if 0 + +/* Unit test */ +static +struct { Addr address; SizeT size; BmAccessTypeT access_type; } + s_args[] = { + { 0, 1, eLoad }, + { 666, 4, eLoad }, + { 667, 2, eStore }, + { 1024, 1, eStore }, + { 0x0000ffff, 1, eLoad }, + { 0x0001ffff, 1, eLoad }, + { 0x00ffffff, 1, eLoad }, + { 0xffffffff, 1, eStore }, + }; + +void bm_test(void) +{ + struct bitmap* bm; + struct bitmap* bm2; + int i, j; + + VG_(printf)("Start of DRD BM unit test.\n"); + + bm = bm_new(); + + for (i = 0; i < sizeof(s_args)/sizeof(s_args[0]); i++) + { + bm_access_range(bm, s_args[i].address, + s_args[i].size, s_args[i].access_type); + } + + VG_(printf)("Map contents -- should contain 10 addresses:\n"); + bm_print(bm); + + for (i = 0; i < sizeof(s_args)/sizeof(s_args[0]); i++) + { + for (j = 0; j < s_args[i].size; j++) + { + tl_assert(bm_has_1(bm, s_args[i].address + j, s_args[i].access_type)); + } + } + + VG_(printf)("Merge result:\n"); + bm2 = bm_merge(bm, bm); + bm_print(bm); + + bm_delete(bm); + bm_delete(bm2); + + VG_(printf)("End of DRD BM unit test.\n"); +} +#endif + + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_bitmap.h b/exp-drd/drd_bitmap.h new file mode 100644 index 0000000000..248a5d29c9 --- /dev/null +++ b/exp-drd/drd_bitmap.h @@ -0,0 +1,167 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +#ifndef __DRD_BITMAP3_H +#define __DRD_BITMAP3_H + + +#include "pub_tool_oset.h" + + +/* + Bitmap representation. A bitmap is a data structure in which two bits are + reserved per 32 bit address: one bit that indicates that the data at the + specified address has been read, and one bit that indicates that the data has + been written to. +*/ + + +/* Macro definitions. */ + +#define ADDR0_BITS 12 + +#define ADDR0_COUNT (1UL << ADDR0_BITS) + +#define ADDR0_MASK (ADDR0_COUNT - 1) + +#define SPLIT_ADDRESS(a) \ + UWord a##0 = ((a) & ADDR0_MASK); \ + UWord a##1 = ((a) >> ADDR0_BITS); + +// Assumption: sizeof(Addr) == sizeof(UWord). +#define MAKE_ADDRESS(a1, a0) \ + (Addr)(((UWord)(a1) << (ADDR0_BITS)) | ((UWord)(a0))) + +#define BITS_PER_UWORD (8UL*sizeof(UWord)) +#if defined(VGA_x86) || defined(VGA_ppc32) +#define BITS_PER_BITS_PER_UWORD 5 +#elif defined(VGA_amd64) || defined(VGA_ppc64) +#define BITS_PER_BITS_PER_UWORD 6 +#else +#error Unknown platform. +#endif + +#define BITMAP1_UWORD_COUNT (ADDR0_COUNT >> BITS_PER_BITS_PER_UWORD) + +/* Highest bits of an address that fit into the same UWord of bm0[]. */ +#define UWORD_MSB(a) ((a) & ~(BITS_PER_UWORD - 1)) + +/* Lowest bits of an address that fit into the same UWord of bm0[]. */ +#define UWORD_LSB(a) ((a) & (BITS_PER_UWORD - 1)) + +/* Highest address that fits in the same UWord as a. */ +#define UWORD_HIGHEST_ADDRESS(a) ((a) | (BITS_PER_UWORD - 1)) + + +// Local functions. + +// Similar to const_cast<> in C++. +static __inline__ OSet* const_to_non_const_oset(const OSet* os) +{ return (OSet*)os; } + + +// Local constants. + +static ULong s_bitmap2_creation_count; + + +/* Lowest level, corresponding to the lowest ADDR0_BITS of an address. */ +struct bitmap1 +{ + UWord bm0_r[BITMAP1_UWORD_COUNT]; + UWord bm0_w[BITMAP1_UWORD_COUNT]; +}; + +static __inline__ UWord bm0_mask(const Addr a) +{ + return (1UL << UWORD_LSB(a)); +} + +static __inline__ void bm0_set(UWord* bm0, const Addr a) +{ + //tl_assert(a < ADDR0_COUNT); + bm0[a >> BITS_PER_BITS_PER_UWORD] |= 1UL << UWORD_LSB(a); +} + +static __inline__ void bm0_clear(UWord* bm0, const Addr a) +{ + //tl_assert(a < ADDR0_COUNT); + bm0[a >> BITS_PER_BITS_PER_UWORD] &= ~(1UL << UWORD_LSB(a)); +} + +static __inline__ UWord bm0_is_set(const UWord* bm0, const Addr a) +{ + //tl_assert(a < ADDR0_COUNT); + return (bm0[a >> BITS_PER_BITS_PER_UWORD] & (1UL << UWORD_LSB(a))); +} + + +struct bitmap2 +{ + Addr addr; ///< address >> ADDR0_BITS + struct bitmap1 bm1; +}; + +/* Complete bitmap. */ +struct bitmap +{ + OSet* oset; +}; + +static __inline__ +struct bitmap2* bm_lookup(const struct bitmap* const bm, const Addr a) +{ + const UWord a1 = a >> ADDR0_BITS; + return VG_(OSetGen_Lookup)(const_to_non_const_oset(bm->oset), (void*)&a1); +} + +static __inline__ +struct bitmap2* bm2_insert(const struct bitmap* const bm, + const UWord a1) +{ + struct bitmap2* const node = VG_(OSetGen_AllocNode)(bm->oset, sizeof(*node)); + node->addr = a1; + VG_(memset)(&node->bm1, 0, sizeof(node->bm1)); + VG_(OSetGen_Insert)(bm->oset, node); + + s_bitmap2_creation_count++; + + return node; +} + +static __inline__ +struct bitmap2* bm2_lookup_or_insert(const struct bitmap* const bm, + const UWord a1) +{ + struct bitmap2* p2 = VG_(OSetGen_Lookup)(const_to_non_const_oset(bm->oset), (void*)&a1); + if (p2 == 0) + { + p2 = bm2_insert(bm, a1); + } + return p2; +} + + +#endif /* __DRD_BITMAP3_H */ diff --git a/exp-drd/drd_clientreq.c b/exp-drd/drd_clientreq.c new file mode 100644 index 0000000000..194d31f241 --- /dev/null +++ b/exp-drd/drd_clientreq.c @@ -0,0 +1,175 @@ +#include "drd_clientreq.h" +#include "drd_cond.h" +#include "drd_mutex.h" +#include "drd_suppression.h" // drd_start_suppression() +#include "drd_thread.h" +#include "drd_track.h" +#include "pthread_object_size.h" +#include "pub_core_tooliface.h" // VG_TRACK() +#include "pub_tool_basics.h" // Bool +#include "pub_tool_libcassert.h" +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcprint.h" // VG_(message)() +#include "pub_tool_machine.h" // VG_(get_SP)() +#include "pub_tool_threadstate.h" +#include "pub_tool_tooliface.h" // VG_(needs_...)() + + +static void drd_spin_init_or_unlock(const Addr spinlock, const SizeT size) +{ + struct mutex_info* mutex_p = mutex_get(spinlock); + if (mutex_p) + { + mutex_unlock(spinlock); + } + else + { + mutex_init(spinlock, size); + } +} + +static void drd_pre_cond_wait(const Addr cond, const Addr mutex) +{ + mutex_unlock(mutex); + cond_pre_wait(cond, mutex); +} + +static void drd_post_cond_wait(const Addr cond, const Addr mutex) +{ + cond_post_wait(cond); + mutex_lock(mutex, PTHREAD_MUTEX_SIZE); +} + +static void drd_pre_cond_signal(const Addr cond) +{ + cond_pre_signal(cond); +} + +static void drd_pre_cond_broadcast(const Addr cond) +{ + cond_pre_broadcast(cond); +} + +static Bool drd_handle_client_request(ThreadId tid, UWord* arg, UWord* ret) +{ + UWord result = 0; + + switch (arg[0]) + { + case VG_USERREQ__GET_THREAD_SELF: + result = tid; + break; + + case VG_USERREQ__SET_THREAD_NAME: + thread_set_name_fmt(VgThreadIdToDrdThreadId(VG_(get_running_tid)()), + (char*)arg[1], arg[2]); + break; + + case VG_USERREQ__DRD_START_SUPPRESSION: + drd_start_suppression(arg[1], arg[1] + arg[2], "client"); + break; + + case VG_USERREQ__DRD_FINISH_SUPPRESSION: + drd_finish_suppression(arg[1], arg[1] + arg[2]); + break; + + case VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK: + thread_set_stack_startup(thread_get_running_tid(), + VG_(get_SP)(VG_(get_running_tid)())); + break; + + case VG_USERREQ__DRD_START_NEW_SEGMENT: + thread_new_segment(PtThreadIdToDrdThreadId(arg[1])); + break; + + case VG_USERREQ__DRD_START_RECORDING: + thread_start_recording(PtThreadIdToDrdThreadId(arg[1])); + break; + + case VG_USERREQ__DRD_STOP_RECORDING: + thread_stop_recording(PtThreadIdToDrdThreadId(arg[1])); + break; + + case VG_USERREQ__SET_PTHREADID: + thread_set_pthreadid(thread_get_running_tid(), arg[1]); + break; + + case VG_USERREQ__SET_JOINABLE: + thread_set_joinable(PtThreadIdToDrdThreadId(arg[1]), (Bool)arg[2]); + break; + + case VG_USERREQ__POST_THREAD_JOIN: + tl_assert(arg[1]); + drd_post_thread_join(thread_get_running_tid(), + PtThreadIdToDrdThreadId(arg[1])); + break; + + case VG_USERREQ__PRE_MUTEX_INIT: + drd_pre_mutex_init(arg[1], arg[2]); + break; + + case VG_USERREQ__POST_MUTEX_DESTROY: + drd_post_mutex_destroy(arg[1], arg[2]); + break; + + case VG_USERREQ__PRE_PTHREAD_MUTEX_LOCK: + drd_pre_mutex_lock(thread_get_running_tid(), arg[1], arg[2]); + break; + + case VG_USERREQ__POST_PTHREAD_MUTEX_LOCK: + drd_post_mutex_lock(thread_get_running_tid(), arg[1], arg[2]); + break; + + case VG_USERREQ__PRE_PTHREAD_MUTEX_UNLOCK: + drd_pre_mutex_unlock(thread_get_running_tid(), arg[1]); + break; + + case VG_USERREQ__SPIN_INIT_OR_UNLOCK: + drd_spin_init_or_unlock(arg[1], arg[2]); + break; + + case VG_USERREQ__POST_PTHREAD_COND_INIT: + drd_post_cond_init(arg[1], arg[2]); + break; + + case VG_USERREQ__PRE_PTHREAD_COND_DESTROY: + drd_pre_cond_destroy(arg[1], arg[2]); + break; + + case VG_USERREQ__PRE_PTHREAD_COND_WAIT: + drd_pre_cond_wait(arg[1], arg[2]); + break; + + case VG_USERREQ__POST_PTHREAD_COND_WAIT: + drd_post_cond_wait(arg[1], arg[2]); + break; + + case VG_USERREQ__PRE_PTHREAD_COND_SIGNAL: + drd_pre_cond_signal(arg[1]); + break; + + case VG_USERREQ__PRE_PTHREAD_COND_BROADCAST: + drd_pre_cond_broadcast(arg[1]); + break; + + default: + VG_(message)(Vg_DebugMsg, "Unrecognized client request 0x%lx 0x%lx", + arg[0], arg[1]); + tl_assert(0); + return False; + } + + *ret = result; + return True; +} + +void drd_clientreq_init(void) +{ + VG_(needs_client_requests)(drd_handle_client_request); +} + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_clientreq.h b/exp-drd/drd_clientreq.h new file mode 100644 index 0000000000..57baee0918 --- /dev/null +++ b/exp-drd/drd_clientreq.h @@ -0,0 +1,94 @@ +#ifndef __DRD_CLIENTREQ_H +#define __DRD_CLIENTREQ_H + + +#include "valgrind.h" // VG_USERREQ_TOOL_BASE() + + +enum { + /* Ask the core the thread ID assigned by Valgrind. */ + VG_USERREQ__GET_THREAD_SELF = VG_USERREQ_TOOL_BASE('D', 'R'), + /* args: none. */ + /* Set the name of the thread that performs this client request. */ + VG_USERREQ__SET_THREAD_NAME, + /* args: null-terminated character string. */ + + /* To tell the drd tool to suppress data race detection on the specified */ + /* address range. */ + VG_USERREQ__DRD_START_SUPPRESSION, + /* args: start address, size in bytes */ + /* To tell the drd tool no longer to suppress data race detection on the */ + /* specified address range. */ + VG_USERREQ__DRD_FINISH_SUPPRESSION, + /* args: start address, size in bytes */ + /* Ask drd to suppress data race reports on all currently allocated stack */ + /* data of the current thread. */ + VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK, + /* args: none */ + /* To ask the drd tool to start a new segment in the specified thread. */ + VG_USERREQ__DRD_START_NEW_SEGMENT, + /* args: POSIX thread ID. */ + + /* To tell the drd tool to start again recording memory accesses for the */ + /* specified thread. */ + VG_USERREQ__DRD_START_RECORDING, + /* args: POSIX thread ID. */ + /* To tell the drd tool to stop recording memory accesses for the */ + /* specified thread. */ + VG_USERREQ__DRD_STOP_RECORDING, + /* args: POSIX thread ID. */ + + /* Tell the core the pthread_t of the running thread */ + VG_USERREQ__SET_PTHREADID, + /* args: pthread_t. */ + /* Ask the core that a the thread's state transition from */ + /* VgTs_Zombie to VgTs_Empty is delayed until */ + /* VG_USERREQ__POST_THREAD_JOIN is performed. */ + VG_USERREQ__SET_JOINABLE, + /* args: pthread_t, Bool */ + + /* To notify drd that a thread finished because */ + /* pthread_thread_join() was called on it. */ + VG_USERREQ__POST_THREAD_JOIN, + /* args: pthread_t (joinee) */ + + /* To notify the core of a pthread_mutex_init call */ + VG_USERREQ__PRE_MUTEX_INIT, + /* args: Addr, SizeT */ + /* To notify the core of a pthread_mutex_destroy call */ + VG_USERREQ__POST_MUTEX_DESTROY, + /* args: Addr, SizeT */ + /* To notify the core of pthread_mutex_lock calls */ + VG_USERREQ__PRE_PTHREAD_MUTEX_LOCK, + /* args: Addr, SizeT */ + /* To notify the core of pthread_mutex_lock calls */ + VG_USERREQ__POST_PTHREAD_MUTEX_LOCK, + /* args: Addr, SizeT */ + /* To notify the core of pthread_mutex_unlock calls */ + VG_USERREQ__PRE_PTHREAD_MUTEX_UNLOCK, + /* args: Addr */ + VG_USERREQ__SPIN_INIT_OR_UNLOCK, + /* args: Addr spinlock, SizeT size */ + + + /* To notify the core of a pthread_cond_init call */ + VG_USERREQ__POST_PTHREAD_COND_INIT, + /* args: Addr, SizeT */ + /* To notify the core of a pthread_cond_destroy call */ + VG_USERREQ__PRE_PTHREAD_COND_DESTROY, + /* args: Addr, SizeT */ + VG_USERREQ__PRE_PTHREAD_COND_WAIT, + /* args: Addr cond, Addr mutex */ + VG_USERREQ__POST_PTHREAD_COND_WAIT, + /* args: Addr cond, Addr mutex */ + VG_USERREQ__PRE_PTHREAD_COND_SIGNAL, + /* args: Addr cond */ + VG_USERREQ__PRE_PTHREAD_COND_BROADCAST, + /* args: Addr cond */ + +}; + +void drd_clientreq_init(void); + + +#endif // __DRD_CLIENTREQ_H diff --git a/exp-drd/drd_cond.c b/exp-drd/drd_cond.c new file mode 100644 index 0000000000..0323fa0c4d --- /dev/null +++ b/exp-drd/drd_cond.c @@ -0,0 +1,201 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_cond.h" +#include "drd_error.h" +#include "drd_mutex.h" +#include "drd_suppression.h" +#include "pthread_object_size.h" +#include "pub_tool_errormgr.h" // VG_(maybe_record_error)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcprint.h" // VG_(printf)() +#include "pub_tool_machine.h" // VG_(get_IP)() +#include "pub_tool_threadstate.h" // VG_(get_running_tid)() +#include "pub_core_options.h" // VG_(clo_backtrace_size) + + +static struct cond_info s_cond[256]; +static Bool s_trace_cond; + + +void cond_set_trace(const Bool trace_cond) +{ + s_trace_cond = trace_cond; +} + +static +void cond_initialize(struct cond_info* const p, const Addr cond) +{ + tl_assert(cond != 0); + + p->cond = cond; + p->waiter_count = 0; + p->mutex = 0; +} + +static struct cond_info* cond_get_or_allocate(const Addr cond) +{ + int i; + for (i = 0; i < sizeof(s_cond)/sizeof(s_cond[0]); i++) + if (s_cond[i].cond == cond) + return &s_cond[i]; + for (i = 0; i < sizeof(s_cond)/sizeof(s_cond[0]); i++) + { + if (s_cond[i].cond == 0) + { + cond_initialize(&s_cond[i], cond); + /* TO DO: replace the constant below by a symbolic constant referring */ + /* to sizeof(pthread_cond_t). */ + drd_start_suppression(cond, cond + PTHREAD_COND_SIZE, "cond"); + return &s_cond[i]; + } + } + tl_assert(0); + return 0; +} + +void cond_init(const Addr cond) +{ + if (s_trace_cond) + { + VG_(message)(Vg_UserMsg, "Initializing condition variable 0x%lx", cond); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + } + tl_assert(cond_get(cond) == 0); + cond_get_or_allocate(cond); +} + +void cond_destroy(struct cond_info* const p) +{ + if (s_trace_cond) + { + VG_(message)(Vg_UserMsg, "Destroying condition variable 0x%lx", p->cond); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + } + + // TO DO: print a proper error message if waiter_count != 0. + tl_assert(p->waiter_count == 0); + + drd_finish_suppression(p->cond, p->cond + PTHREAD_COND_SIZE); + + p->cond = 0; + p->waiter_count = 0; + p->mutex = 0; +} + +struct cond_info* cond_get(const Addr cond) +{ + int i; + for (i = 0; i < sizeof(s_cond)/sizeof(s_cond[0]); i++) + if (s_cond[i].cond == cond) + return &s_cond[i]; + return 0; +} + +int cond_pre_wait(const Addr cond, const Addr mutex) +{ + struct cond_info* p; + + p = cond_get_or_allocate(cond); + if (p->waiter_count == 0) + { + p->mutex = mutex; + } + else + { + // TO DO: print a proper error message if two different threads call + // pthread_cond_*wait() on the same condition variable but with a different + // mutex argument. + tl_assert(p->mutex == mutex); + } + return ++p->waiter_count; +} + +int cond_post_wait(const Addr cond) +{ + struct cond_info* p; + + p = cond_get(cond); + tl_assert(p); + tl_assert(p->waiter_count > 0); + tl_assert(p->mutex); + if (--p->waiter_count == 0) + { + p->mutex = 0; + } + return p->waiter_count; +} + +void cond_pre_signal(Addr const cond) +{ + const ThreadId vg_tid = VG_(get_running_tid)(); + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(vg_tid); + struct cond_info* const cond_p = cond_get(cond); +#if 0 + VG_(message)(Vg_DebugMsg, "cond_pre_signal cond %d, w.c. %d, mutex %d", + cond, + cond_p ? cond_p->waiter_count : 0, + cond_p ? cond_p->mutex : 0); +#endif + if (cond_p && cond_p->waiter_count > 0) + { + if (! mutex_is_locked_by(cond_p->mutex, drd_tid)) + { + CondRaceErrInfo cei; + cei.cond = cond; + cei.mutex = cond_p->mutex; + VG_(maybe_record_error)(vg_tid, + CondRaceErr, + VG_(get_IP)(vg_tid), + "CondErr", + &cei); + } + } + else + { + /* No other thread is waiting for the signal, hence the signal will be */ + /* lost. This is normal in a POSIX threads application. */ + } +} + +void cond_pre_broadcast(Addr const cond) +{ + cond_pre_signal(cond); +} + +void cond_stop_using_mem(const Addr a1, const Addr a2) +{ + unsigned i; + for (i = 0; i < sizeof(s_cond)/sizeof(s_cond[0]); i++) + { + if (a1 <= s_cond[i].cond && s_cond[i].cond < a2) + { + tl_assert(s_cond[i].cond + PTHREAD_COND_SIZE <= a2); + cond_destroy(&s_cond[i]); + } + } +} diff --git a/exp-drd/drd_cond.h b/exp-drd/drd_cond.h new file mode 100644 index 0000000000..2a3b94570b --- /dev/null +++ b/exp-drd/drd_cond.h @@ -0,0 +1,58 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +// Condition variable state information: mutex specified in pthread_cond_wait() +// call. + + +#ifndef __COND_H +#define __COND_H + + +#include "pub_tool_basics.h" // Addr, SizeT +#include "drd_vc.h" +#include "drd_thread.h" // DrdThreadId + + +struct cond_info +{ + Addr cond; // Pointer to client condition variable. + int waiter_count; + Addr mutex; // Client mutex specified in pthread_cond_wait() call, and null + // if no client threads are currently waiting on this cond.var. +}; + +void cond_set_trace(const Bool trace_cond); +void cond_init(const Addr cond); +void cond_destroy(struct cond_info* const p); +struct cond_info* cond_get(Addr const mutex); +int cond_pre_wait(const Addr cond, const Addr mutex); +int cond_post_wait(const Addr cond); +void cond_pre_signal(Addr const cond); +void cond_pre_broadcast(Addr const cond); +void cond_stop_using_mem(const Addr a1, const Addr a2); + + +#endif /* __COND_H */ diff --git a/exp-drd/drd_error.c b/exp-drd/drd_error.c new file mode 100644 index 0000000000..6ec58e6f7f --- /dev/null +++ b/exp-drd/drd_error.c @@ -0,0 +1,412 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_error.h" +#include "drd_malloc_wrappers.h" +#include "drd_mutex.h" // struct mutex_info +#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_threadstate.h" // VG_(get_pthread_id)() +#include "pub_tool_tooliface.h" // VG_(needs_tool_errors)() + + +typedef enum { + ConflictingAccessSupp +} DRD_SuppKind; + + +static void make_path_relative(Char* const path) +{ + int offset = 0; + Char cwd[512]; + + if (! VG_(get_startup_wd)(cwd, sizeof(cwd))) + tl_assert(False); + if (VG_(strncmp)(path + offset, cwd, VG_(strlen)(cwd)) == 0) + { + offset += VG_(strlen)(cwd); + if (path[offset] == '/') + { + offset++; + } + } + VG_(memmove)(path, path + offset, VG_(strlen)(path + offset) + 1); +} + + +/* Describe a data address range [a,a+len[ as good as you can, for error */ +/* messages, putting the result in ai. */ +void describe_addr(Addr const a, SizeT const len, AddrInfo* const ai) +{ + Addr stack_min, stack_max; + SegInfo* sg; + + /* Perhaps it's on a thread's stack? */ + ai->stack_tid = thread_lookup_stackaddr(a, &stack_min, &stack_max); + if (ai->stack_tid != DRD_INVALID_THREADID) + { + ai->akind = eStack; + ai->size = len; + ai->rwoffset = a - stack_max; + tl_assert(a + ai->size <= stack_max); + tl_assert(ai->rwoffset < 0); + return; + } + + /* Perhaps it's in a mapped segment ? */ + sg = VG_(find_seginfo)(a); + if (sg) + { + int i, n; + + ai->akind = eSegment; + ai->seginfo = sg; + ai->name[0] = 0; + ai->size = 1; + ai->rwoffset = 0; + + n = VG_(seginfo_syms_howmany)(sg); + for (i = 0; i < n; i++) + { + Addr addr; + Addr tocptr; + UInt size; + HChar* name; + Char filename[256]; + Int linenum; + + VG_(seginfo_syms_getidx)(sg, i, &addr, &tocptr, &size, &name); + if (addr <= a && a < addr + size) + { + ai->size = size; + ai->rwoffset = a - addr; + tl_assert(name && name[0]); + VG_(snprintf)(ai->name, sizeof(ai->name), "%s", name); + if (VG_(get_filename_linenum)(addr, + filename, sizeof(filename), + 0, 0, 0, + &linenum)) + { + make_path_relative(filename); + VG_(snprintf)(ai->descr, sizeof(ai->descr), + " in %s:%d", filename, linenum); + } + else + { + i = n; + } + break; + } + } + if (i == n) + { + Char filename[512]; + Char soname[512]; + Char sect_kind_name[16]; + + VG_(seginfo_sect_kind_name)(a, sect_kind_name, + sizeof(sect_kind_name)); + VG_(strncpy)(filename, VG_(seginfo_filename)(sg), sizeof(filename)); + filename[sizeof(filename) - 1] = 0; + make_path_relative(filename); + VG_(strncpy)(soname, VG_(seginfo_soname)(sg), sizeof(soname)); + soname[sizeof(soname) - 1] = 0; + make_path_relative(soname); + VG_(snprintf)(ai->descr, sizeof(ai->descr), + "%s, %s:%s", + filename, + soname, + sect_kind_name); + } + return; + } + + /* Search for a currently malloc'd block which might bracket it. */ + { + Addr data; + if (drd_heap_addrinfo(a, &data, &ai->size, &ai->lastchange)) + { + ai->akind = eMallocd; + ai->rwoffset = a - data; + return; + } + } + + /* Clueless ... */ + ai->akind = eUnknown; + return; +} + +/** + * Generate a description string for the data residing at address a. + */ +Char* describe_addr_text(Addr const a, SizeT const len, AddrInfo* const ai, + Char* const buf, UInt const n_buf) +{ + tl_assert(a); + tl_assert(ai); + tl_assert(buf); + + describe_addr(a, len, ai); + + switch (ai->akind) + { + case eStack: { + VG_(snprintf)(buf, n_buf, + "stack of %s, offset %d", + thread_get_name(ai->stack_tid), ai->rwoffset); + break; + } + case eSegment: { + if (ai->name[0]) + { + VG_(snprintf)(buf, n_buf, + "%s (offset %ld, size %ld) in %s", + ai->name, ai->rwoffset, ai->size, ai->descr); + } + else + { + VG_(snprintf)(buf, n_buf, + "%s", + ai->descr); + } + break; + } + case eMallocd: { + VG_(snprintf)(buf, n_buf, "heap"); + VG_(snprintf)(buf + VG_(strlen)(buf), n_buf - VG_(strlen)(buf), + ", offset %ld in block at 0x%lx of size %ld", + ai->rwoffset, a - ai->rwoffset, ai->size); + break; + } + case eUnknown: + VG_(snprintf)(buf, n_buf, "unknown"); + break; + default: + tl_assert(0); + } + return buf; +} + +#ifdef OLD_RACE_DETECTION_ALGORITHM +void drd_report_data_race(const DataRaceInfo* const dri) +{ + AddrInfo ai; + Char descr[256]; + + tl_assert(dri); + tl_assert(dri->range_begin < dri->range_end); + describe_addr_text(dri->range_begin, dri->range_end - dri->range_begin, + &ai, descr, sizeof(descr)); + VG_(message)(Vg_UserMsg, + "0x%08lx sz %ld %c %c (%s)", + dri->range_begin, + dri->range_end - dri->range_begin, + dri->range_access & LHS_W ? 'W' : 'R', + dri->range_access & RHS_W ? 'W' : 'R', + descr); + if (ai.akind == eMallocd && ai.lastchange) + { + VG_(message)(Vg_UserMsg, "Allocation context:"); + VG_(pp_ExeContext)(ai.lastchange); + } + // Note: for stack and heap variables suppression should be + // stopped automatically as soon as the specified memory + // range has been freed. + tl_assert(dri->range_begin < dri->range_end); + drd_start_suppression(dri->range_begin, dri->range_end, "detected race"); +} +#endif + +static +void drd_report_data_race2(Error* const err, const DataRaceErrInfo* const dri) +{ + AddrInfo ai; + Char descr[256]; + + tl_assert(dri); + tl_assert(dri->addr); + tl_assert(dri->size > 0); + describe_addr_text(dri->addr, dri->size, + &ai, descr, sizeof(descr)); + VG_(message)(Vg_UserMsg, + "Conflicting %s by %s at 0x%08lx size %ld", + dri->access_type == eStore ? "store" : "load", + thread_get_name(VgThreadIdToDrdThreadId(dri->tid)), + dri->addr, + dri->size); + VG_(pp_ExeContext)(VG_(get_error_where)(err)); + VG_(message)(Vg_UserMsg, "Allocation context: %s", descr); + if (ai.akind == eMallocd && ai.lastchange) + { + VG_(pp_ExeContext)(ai.lastchange); + } + thread_report_conflicting_segments(VgThreadIdToDrdThreadId(dri->tid), + dri->addr, dri->size, dri->access_type); +} + +static Bool drd_tool_error_eq(VgRes res, Error* e1, Error* e2) +{ + return False; +} + +static void drd_tool_error_pp(Error* const e) +{ + switch (VG_(get_error_kind)(e)) + { + case DataRaceErr: { + drd_report_data_race2(e, VG_(get_error_extra)(e)); + break; + } + case MutexErr: { + MutexErrInfo* p = (MutexErrInfo*)(VG_(get_error_extra)(e)); + 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); + VG_(pp_ExeContext)(VG_(get_error_where)(e)); + break; + } + case CondRaceErr: { + CondRaceErrInfo* cei = (CondRaceErrInfo*)(VG_(get_error_extra)(e)); + VG_(message)(Vg_UserMsg, + "Race condition: condition variable 0x%lx has been signalled" + " 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)); + break; + } + case CondErr: { + CondErrInfo* cdei =(CondErrInfo*)(VG_(get_error_extra)(e)); + VG_(message)(Vg_UserMsg, + "cond 0x%lx: %s", + cdei->cond, + 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 CondRaceErr: + return sizeof(CondRaceErrInfo); + case CondErr: + return sizeof(CondErrInfo); + default: + tl_assert(False); + break; + } +} + +static Bool drd_tool_error_recog(Char* const name, Supp* const supp) +{ + SuppKind skind; + + if (VG_(strcmp)(name, "ConflictingAccess") == 0) + skind = ConflictingAccessSupp; + 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 "ConflictingAccess"; + case MutexErr: return "MutexErr"; + case CondRaceErr: return "CondRaceErr"; + default: + tl_assert(0); + } + return 0; +} + +static void drd_tool_error_print_extra(Error* e) +{ + switch (VG_(get_error_kind)(e)) + { + // VG_(printf)(" %s\n", VG_(get_error_string)(err)); + } +} + +void drd_register_error_handlers(void) +{ + // Tool error reporting. + VG_(needs_tool_errors)(drd_tool_error_eq, + drd_tool_error_pp, + True, + 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); +} + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_error.h b/exp-drd/drd_error.h new file mode 100644 index 0000000000..dc9ed1c276 --- /dev/null +++ b/exp-drd/drd_error.h @@ -0,0 +1,125 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +#ifndef __DRD_ERROR_H +#define __DRD_ERROR_H + + +#include "pub_drd_bitmap.h" // BmAccessTypeT +#include "drd_thread.h" // DrdThreadId +#include "pub_tool_basics.h" // SizeT +#include "pub_tool_debuginfo.h" // SegInfo +#include "pub_tool_errormgr.h" // ExeContext + + +/* DRD error types. */ + +typedef enum { + DataRaceErr = 1, + MutexErr = 2, + CondRaceErr = 3, + CondErr = 4, +} DrdErrorKind; + +/* The classification of a faulting address. */ +typedef +enum { + //Undescribed, // as-yet unclassified + eStack, + eUnknown, // classification yielded nothing useful + //Freed, + eMallocd, + eSegment, // in a segment (as defined in pub_tool_debuginfo.h) + //UserG, // in a user-defined block + //Mempool, // in a mempool + //Register, // in a register; for Param errors only +} + AddrKind; + +/* Records info about a faulting address. */ +typedef +struct { // Used by: + AddrKind akind; // ALL + SizeT size; // ALL + OffT rwoffset; // ALL + ExeContext* lastchange; // Mallocd + DrdThreadId stack_tid; // Stack + SegInfo* seginfo; // Segment + Char name[256]; // Segment + Char descr[256]; // Segment +} + AddrInfo; + +#ifdef OLD_RACE_DETECTION_ALGORITHM +/* Records info about a data race. */ +typedef struct { + ThreadId tid1; // Thread ID of first thread involved in the data race. + ThreadId tid2; // Thread ID of second thread involved in the data race. + Addr range_begin; // Start address of range involved. + Addr range_end; // Last address (exclusive) of range involved. + UInt range_access; // How the range was accessed (LHS_[RW] | RHS_[RW]). +} DataRaceInfo; +#endif + +typedef struct { + ThreadId tid; // Thread ID of the running thread. + Addr addr; // Conflicting address in current thread. + SizeT size; // Size in bytes of conflicting operation. + BmAccessTypeT access_type; // Access type: load or store. +} DataRaceErrInfo; + +typedef struct { + Addr mutex; + Int recursion_count; + DrdThreadId owner; +} MutexErrInfo; + +typedef struct { + Addr cond; + Addr mutex; +} CondRaceErrInfo; + +typedef struct { + Addr cond; +} CondErrInfo; + +void describe_addr(Addr const a, SizeT const len, AddrInfo* const ai); +Char* describe_addr_text(Addr const a, SizeT const len, AddrInfo* const ai, + Char* const buf, UInt const n_buf); +#ifdef OLD_RACE_DETECTION_ALGORITHM +void drd_report_data_race(const DataRaceInfo* const dri); +#endif +//void drd_report_data_race2(const DataRaceErrInfo* const dri); +void drd_register_error_handlers(void); + + +#endif /* __DRD_ERROR_H */ + + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_main.c b/exp-drd/drd_main.c new file mode 100644 index 0000000000..117b3085db --- /dev/null +++ b/exp-drd/drd_main.c @@ -0,0 +1,819 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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 "pub_drd_bitmap.h" +#include "drd_clientreq.h" +#include "drd_cond.h" +#include "drd_error.h" +#include "drd_malloc_wrappers.h" +#include "drd_mutex.h" +#include "drd_segment.h" +#include "drd_suppression.h" +#include "drd_thread.h" +#include "drd_track.h" +#include "drd_vc.h" +#include "pthread_object_size.h" +#include "pub_core_mallocfree.h" +#include "pub_core_options.h" +#include "pub_tool_vki.h" +#include "pub_tool_basics.h" +#include "pub_tool_debuginfo.h" // VG_(describe_IP)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcbase.h" // VG_(strcmp) +#include "pub_tool_libcprint.h" // VG_(printf) +#include "pub_tool_libcproc.h" +#include "pub_tool_machine.h" +#include "pub_tool_options.h" // command line options +#include "pub_tool_threadstate.h" // VG_(get_running_tid) +#include "pub_tool_tooliface.h" + + +// Type definitions. + +#if 0 +typedef struct +{ + const Char* const soname; + const Char* const symbol; +} SuppressedSymbol; +#endif + + +// Function declarations. + +static void drd_start_client_code(const ThreadId tid, const ULong bbs_done); +static void drd_set_running_tid(const ThreadId tid); + + + +// Local variables. + +static Bool drd_print_stats = False; +static Bool drd_trace_mem = False; +static Bool drd_trace_fork_join = False; +static Addr drd_trace_address = 0; +#if 0 +// Note: using the information below for suppressing data races is only +// possible when the client and the shared libraries it uses contain +// debug information. Not every Linux distribution includes debug information +// in shared libraries. +static const SuppressedSymbol s_suppressed_symbols[] = + { + { "ld-linux.so.2", "_rtld_local" }, + { "libpthread.so.0", "__nptl_nthreads" }, + { "libpthread.so.0", "stack_cache" }, + { "libpthread.so.0", "stack_cache_actsize" }, + { "libpthread.so.0", "stack_cache_lock" }, + { "libpthread.so.0", "stack_used" }, + { "libpthread.so.0", "libgcc_s_forcedunwind" }, + { "libpthread.so.0", "libgcc_s_getcfa" }, + }; +#endif + + +// +// Implement the needs_command_line_options for drd. +// + +static Bool drd_process_cmd_line_option(Char* arg) +{ + Bool trace_cond = False; + Bool trace_mutex = False; + Bool trace_segment = False; + Bool trace_suppression = False; + Char* trace_address = 0; + + VG_BOOL_CLO (arg, "--drd-stats", drd_print_stats) + else VG_BOOL_CLO(arg, "--trace-cond", trace_cond) + else VG_BOOL_CLO(arg, "--trace-fork-join", drd_trace_fork_join) + else VG_BOOL_CLO(arg, "--trace-mem", drd_trace_mem) + else VG_BOOL_CLO(arg, "--trace-mutex", trace_mutex) + else VG_BOOL_CLO(arg, "--trace-segment", trace_segment) + else VG_BOOL_CLO(arg, "--trace-suppression", trace_suppression) + else VG_STR_CLO (arg, "--trace-address", trace_address) + else + return False; + + if (trace_address) + drd_trace_address = VG_(strtoll16)(trace_address, 0); + if (trace_cond) + cond_set_trace(trace_cond); + if (trace_mutex) + mutex_set_trace(trace_mutex); + if (trace_segment) + sg_set_trace(trace_segment); + if (trace_suppression) + suppression_set_trace(trace_suppression); + + return True; +} + +static void drd_print_usage(void) +{ + VG_(printf)(" --trace-mem=no|yes Trace all memory accesses to stdout[no]" + "\n" + " --trace-fork-join=no|yes Trace all thread creation and join" + " activity\n" + " --trace-mutex=no|yes Trace all mutex activity\n" + " --trace-segment=no|yes Trace segment actions\n" + ); +} + +static void drd_print_debug_usage(void) +{ +} + + +// +// Implements the thread-related core callbacks. +// + +static +VG_REGPARM(2) void drd_trace_load(Addr addr, SizeT size) +{ + Segment* sg; + + if (! thread_is_recording(thread_get_running_tid())) + return; + +#if 1 + if (drd_trace_mem || (addr == drd_trace_address)) + { + VG_(message)(Vg_UserMsg, "load 0x%lx size %ld %s (vg %d / drd %d)", + addr, + size, + thread_get_name(thread_get_running_tid()), + VG_(get_running_tid)(), + thread_get_running_tid()); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + tl_assert(DrdThreadIdToVgThreadId(thread_get_running_tid()) + == VG_(get_running_tid)()); + } +#endif + sg = thread_get_segment(thread_get_running_tid()); + bm_access_range(sg->bm, addr, size, eLoad); + if (thread_conflicting_access(addr, size, eLoad)) + { + DataRaceErrInfo drei; + drei.tid = VG_(get_running_tid)(); + drei.addr = addr; + drei.size = size; + drei.access_type = eLoad; + VG_(maybe_record_error)(VG_(get_running_tid)(), + DataRaceErr, + VG_(get_IP)(VG_(get_running_tid)()), + "Conflicting accesses", + &drei); + } +} + +static +VG_REGPARM(2) void drd_trace_store(Addr addr, SizeT size) +{ + Segment* sg; + + if (! thread_is_recording(thread_get_running_tid())) + return; + +#if 1 + if (drd_trace_mem || (addr == drd_trace_address)) + { + VG_(message)(Vg_UserMsg, "store 0x%lx size %ld %s (vg %d / drd %d / off %d)", + addr, + size, + thread_get_name(thread_get_running_tid()), + VG_(get_running_tid)(), + thread_get_running_tid(), + addr - thread_get_stack_min(thread_get_running_tid())); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + tl_assert(DrdThreadIdToVgThreadId(thread_get_running_tid()) + == VG_(get_running_tid)()); + } +#endif + sg = thread_get_segment(thread_get_running_tid()); + bm_access_range(sg->bm, addr, size, eStore); + if (thread_conflicting_access(addr, size, eStore)) + { + DataRaceErrInfo drei; + drei.tid = VG_(get_running_tid)(); + drei.addr = addr; + drei.size = size; + drei.access_type = eStore; + VG_(maybe_record_error)(VG_(get_running_tid)(), + DataRaceErr, + VG_(get_IP)(VG_(get_running_tid)()), + "Conflicting accesses", + &drei); + } +} + +static void drd_pre_mem_read(const CorePart part, + const ThreadId tid, + Char* const s, + const Addr a, + const SizeT size) +{ + const ThreadId running_tid = VG_(get_running_tid)(); + + if (size == 0) + return; + + if (tid != running_tid) + { + if (VgThreadIdToDrdThreadId(tid) != DRD_INVALID_THREADID) + { + drd_set_running_tid(tid); + drd_trace_load(a, size); + drd_set_running_tid(running_tid); + } + else + { + VG_(message)(Vg_DebugMsg, + "drd_pre_mem_read() was called before" + " drd_post_thread_create() for thread ID %d", + tid); + tl_assert(0); + } + } + else + { + drd_trace_load(a, size); + } +} + +static void drd_post_mem_write(const CorePart part, + const ThreadId tid, + const Addr a, + const SizeT size) +{ + const ThreadId running_tid = VG_(get_running_tid)(); + + if (size == 0) + return; + + if (tid != running_tid) + { + if (VgThreadIdToDrdThreadId(tid) != DRD_INVALID_THREADID) + { + drd_set_running_tid(tid); + drd_trace_store(a, size); + drd_set_running_tid(running_tid); + } + else + { +#if 1 + VG_(message)(Vg_DebugMsg, + "drd_pre_mem_write() was called before" + " drd_post_thread_create() for thread ID %d", + tid); + tl_assert(0); +#endif + } + } + else + { + drd_trace_store(a, size); + } +} + +static void drd_start_using_mem(const Addr a1, const Addr a2) +{ + if (a1 <= drd_trace_address && drd_trace_address < a2 + && thread_is_recording(thread_get_running_tid())) + { + VG_(message)(Vg_UserMsg, "start 0x%lx size %ld %s (tracing 0x%lx)", + a1, a2 - a1, thread_get_name(thread_get_running_tid()), + drd_trace_address); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + } +} + +static void drd_stop_using_mem(const Addr a1, const Addr a2) +{ + if (a1 <= drd_trace_address && drd_trace_address < a2 + && thread_is_recording(thread_get_running_tid())) + { + VG_(message)(Vg_UserMsg, "end 0x%lx size %ld %s (tracing 0x%lx)", + a1, a2 - a1, thread_get_name(thread_get_running_tid()), + drd_trace_address); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + } + thread_stop_using_mem(a1, a2); + mutex_stop_using_mem(a1, a2); + cond_stop_using_mem(a1, a2); + drd_suppression_stop_using_mem(a1, a2); +} + +static VG_REGPARM(2) + void drd_make_stack_uninit(const Addr base, const UWord len) +{ +#if 0 + VG_(message)(Vg_DebugMsg, "make_stack_uninit(0x%lx, %ld)", base, len); +#endif + drd_stop_using_mem(base, base + len); +} + +/* Called by the core when the stack of a thread grows, to indicate that */ +/* the addresses in range [ a, a + len [ may now be used by the client. */ +/* Assumption: stacks grow downward. */ +static void drd_start_using_mem_stack(const Addr a, const SizeT len) +{ + thread_set_stack_min(thread_get_running_tid(), a - VG_STACK_REDZONE_SZB); + drd_start_using_mem(a - VG_STACK_REDZONE_SZB, + a - VG_STACK_REDZONE_SZB + len); +} + +/* Called by the core when the stack of a thread shrinks, to indicate that */ +/* the addresses [ a, a + len [ are no longer accessible for the client. */ +/* Assumption: stacks grow downward. */ +static void drd_stop_using_mem_stack(const Addr a, const SizeT len) +{ +#if 0 + VG_(message)(Vg_DebugMsg, "stop_using_mem_stack(0x%lx, %ld)", a, len); +#endif + thread_set_stack_min(thread_get_running_tid(), + a + len - VG_STACK_REDZONE_SZB); + drd_stop_using_mem(a - VG_STACK_REDZONE_SZB, + a + len - VG_STACK_REDZONE_SZB); +} + +static void drd_start_using_mem_mmap(Addr a, SizeT len, + Bool rr, Bool ww, Bool xx) +{ + drd_start_using_mem(a, a + len); +} + +static void drd_stop_using_mem_munmap(Addr a, SizeT len) +{ + drd_stop_using_mem(a, a + len); +} + +static +void drd_pre_thread_create(const ThreadId creator, const ThreadId created) +{ + const DrdThreadId drd_creator = VgThreadIdToDrdThreadId(creator); + tl_assert(created != VG_INVALID_THREADID); + thread_pre_create(drd_creator, created); +#if 1 + // Hack: compensation for code missing in coregrind/m_main.c. + if (created == 1) + { + extern ThreadId VG_(running_tid); + tl_assert(VG_(running_tid) == VG_INVALID_THREADID); + VG_(running_tid) = 1; + drd_start_client_code(VG_(running_tid), 0); + VG_(running_tid) = VG_INVALID_THREADID; + } +#endif + if (IsValidDrdThreadId(drd_creator)) + { + thread_new_segment(drd_creator); + } + if (drd_trace_fork_join) + { + VG_(message)(Vg_DebugMsg, + "drd_pre_thread_create creator = %d/%d, created = %d", + creator, drd_creator, created); + } +} + +/* Called by Valgrind's core before any loads or stores are performed on */ +/* the context of thread "created". At startup, this function is called */ +/* with arguments (0,1). */ +static +void drd_post_thread_create(const ThreadId created) +{ + const DrdThreadId drd_created = thread_post_create(created); + tl_assert(created != VG_INVALID_THREADID); + if (drd_trace_fork_join) + { + VG_(message)(Vg_DebugMsg, + "drd_post_thread_create created = %d/%d", + created, drd_created); + } +} + +/* Process VG_USERREQ__POST_THREAD_JOIN. This client request is invoked just */ +/* after thread drd_joiner joined thread drd_joinee. */ +void drd_post_thread_join(DrdThreadId drd_joiner, DrdThreadId drd_joinee) +{ + tl_assert(IsValidDrdThreadId(drd_joiner)); + tl_assert(IsValidDrdThreadId(drd_joinee)); + thread_new_segment(drd_joinee); + thread_combine_vc(drd_joiner, drd_joinee); + thread_new_segment(drd_joiner); + + if (drd_trace_fork_join) + { + char msg[256]; + const ThreadId joiner = DrdThreadIdToVgThreadId(drd_joiner); + const ThreadId joinee = DrdThreadIdToVgThreadId(drd_joinee); + VG_(snprintf)(msg, sizeof(msg), + "drd_post_thread_join joiner = %d/%d, joinee = %d/%d", + joiner, drd_joiner, joinee, drd_joinee); + if (joiner) + { + VG_(snprintf)(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + ", new vc: "); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + thread_get_vc(drd_joiner)); + } + VG_(message)(Vg_DebugMsg, msg); + } + + thread_delete(drd_joinee); + mutex_thread_delete(drd_joinee); +} + +/* Called after a thread has performed its last memory access. */ +static void drd_thread_finished(ThreadId tid) +{ + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(tid); + if (drd_trace_fork_join) + { + VG_(message)(Vg_DebugMsg, + "drd_thread_finished tid = %d/%d%s", + tid, + drd_tid, + thread_get_joinable(drd_tid) + ? "" + : " (which is a detached thread)"); + + } + thread_finished(drd_tid); +} + +void drd_pre_mutex_init(Addr mutex, SizeT size) +{ + mutex_init(mutex, size); +} + +void drd_post_mutex_destroy(Addr mutex, SizeT size) +{ + struct mutex_info* p; + + p = mutex_get(mutex); + if (p) + { + // TO DO: report an error in case the recursion count is not zero + // before asserting. + tl_assert(mutex_get_recursion_count(mutex) == 0); + mutex_destroy(p); + } +} + +void drd_pre_mutex_lock(const DrdThreadId drd_tid, + const Addr mutex, + const SizeT size) +{ + if (mutex_get(mutex) == 0) + { + mutex_init(mutex, size); + } +} + +void drd_post_mutex_lock(const DrdThreadId drd_tid, + const Addr mutex, + const SizeT size) +{ + mutex_lock(mutex, size); +} + +void drd_pre_mutex_unlock(const DrdThreadId drd_tid, Addr mutex) +{ + mutex_unlock(mutex); +} + +void drd_post_cond_init(Addr cond, SizeT s) +{ + tl_assert(s == PTHREAD_COND_SIZE); + if (cond_get(cond)) + { + CondErrInfo cei = { .cond = cond }; + VG_(maybe_record_error)(VG_(get_running_tid)(), + CondErr, + VG_(get_IP)(VG_(get_running_tid)()), + "initialized twice", + &cei); + } + cond_init(cond); +} + +void drd_pre_cond_destroy(Addr cond, SizeT s) +{ + struct cond_info* cond_p; + + tl_assert(s == PTHREAD_COND_SIZE); + cond_p = cond_get(cond); + if (cond_p) + { + cond_destroy(cond_p); + } + else + { + CondErrInfo cei = { .cond = cond }; + VG_(maybe_record_error)(VG_(get_running_tid)(), + CondErr, + VG_(get_IP)(VG_(get_running_tid)()), + "destroy requested but not initialized", + &cei); + } +} + + +// +// Implementation of the tool interface. +// + +static +void drd_post_clo_init(void) +{ } + +static +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; + IRExpr* addr_expr; + IRExpr* size_expr; + Bool instrument = True; + + /* 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]; + if (!st || st->tag == Ist_NoOp) + continue; + + switch (st->tag) + { + case Ist_IMark: + instrument = VG_(seginfo_sect_kind)(st->Ist.IMark.addr) != Vg_SectPLT; + break; + + case Ist_AbiHint: + addStmtToIRSB(bb, + IRStmt_Dirty( + unsafeIRDirty_0_N( + /*regparms*/2, + "drd_make_stack_uninit", + VG_(fnptr_to_fnentry)(drd_make_stack_uninit), + mkIRExprVec_2(st->Ist.AbiHint.base, + mkIRExpr_HWord((UInt)st->Ist.AbiHint.len)) + ) + ) + ); + break; + + case Ist_Store: + if (instrument) + { + addr_expr = st->Ist.Store.addr; + size_expr = mkIRExpr_HWord( + sizeofIRType(typeOfIRExpr(bb->tyenv, st->Ist.Store.data))); + argv = mkIRExprVec_2(addr_expr, size_expr); + di = unsafeIRDirty_0_N(/*regparms*/2, + "drd_trace_store", + VG_(fnptr_to_fnentry)(drd_trace_store), + argv); + addStmtToIRSB(bb, IRStmt_Dirty(di)); + } + addStmtToIRSB(bb, st); + break; + + case Ist_WrTmp: + if (instrument) + { + const IRExpr* const data = st->Ist.WrTmp.data; + if (data->tag == Iex_Load) + { + addr_expr = data->Iex.Load.addr; + size_expr = mkIRExpr_HWord(sizeofIRType(data->Iex.Load.ty)); + argv = mkIRExprVec_2(addr_expr, size_expr); + di = unsafeIRDirty_0_N(/*regparms*/2, + "drd_trace_load", + VG_(fnptr_to_fnentry)(drd_trace_load), + argv); + addStmtToIRSB(bb, IRStmt_Dirty(di)); + } + } + 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) { + 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; + } + } + + return bb; +} + +static void drd_set_running_tid(const ThreadId tid) +{ + static ThreadId s_last_tid = VG_INVALID_THREADID; + if (tid != s_last_tid) + { + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(tid); + tl_assert(drd_tid != DRD_INVALID_THREADID); + s_last_tid = tid; + if (drd_trace_fork_join) + { + VG_(message)(Vg_DebugMsg, + "drd_track_thread_run tid = %d / drd tid %d", + tid, drd_tid); + } + thread_set_running_tid(drd_tid); + } +} + +static void drd_start_client_code(const ThreadId tid, const ULong bbs_done) +{ + drd_set_running_tid(tid); +} + +static +void drd_fini(Int exitcode) +{ + // thread_print_all(); +#ifdef OLD_RACE_DETECTION_ALGORITHM + thread_report_all_races(); +#endif + if (VG_(clo_verbosity) > 1 || drd_print_stats) + { + VG_(message)(Vg_DebugMsg, + " thread: %lld context switches" + " / %lld updates of the danger set", + thread_get_context_switch_count(), + thread_get_update_danger_set_count()); +#ifdef OLD_RACE_DETECTION_ALGORITHM + VG_(message)(Vg_DebugMsg, + " analysis: %lld data race analysis points", + thread_get_report_races_count()); +#endif + VG_(message)(Vg_DebugMsg, + " segments: %lld total, %lld max, %lld discard points", + sg_get_segments_created_count(), + sg_get_max_segments_alive_count(), + thread_get_discard_ordered_segments_count()); + VG_(message)(Vg_DebugMsg, + " bitmaps: %lld / %lld bitmaps were allocated" + " and %lld / %lld for danger set updates", + bm_get_bitmap_creation_count(), + bm_get_bitmap2_creation_count(), + thread_get_danger_set_bitmap_creation_count(), + thread_get_danger_set_bitmap2_creation_count()); + VG_(message)(Vg_DebugMsg, + " mutex: %lld non-recursive lock/unlock events", + get_mutex_lock_count()); + drd_print_malloc_stats(); + } +} + +static void drd_load_suppression_file(void) +{ + tl_assert(VG_(clo_n_suppressions) < VG_CLO_MAX_SFILES - 1); + { + /* If we haven't reached the max number of suppression files, load + the drd suppression patterns file. */ + static const Char drd_supp[] = "glibc-2.X-drd.supp"; + const Int len = VG_(strlen)(VG_(libdir)) + 1 + sizeof(drd_supp); + Char* const buf = VG_(arena_malloc)(VG_AR_CORE, len); + VG_(sprintf)(buf, "%s/%s", VG_(libdir), drd_supp); + VG_(clo_suppressions)[VG_(clo_n_suppressions)] = buf; + VG_(clo_n_suppressions)++; + } +} + +static +void drd_pre_clo_init(void) +{ + // Basic tool stuff. + + VG_(details_name) ("exp-drd"); + VG_(details_version) (NULL); + VG_(details_description) ("a data race detector"); + VG_(details_copyright_author)("Copyright (C) 2006-2007, and GNU GPL'd," + " by Bart Van Assche."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + + VG_(basic_tool_funcs) (drd_post_clo_init, + drd_instrument, + drd_fini); + + // Command line stuff. + VG_(needs_command_line_options)(drd_process_cmd_line_option, + drd_print_usage, + drd_print_debug_usage); + + drd_register_error_handlers(); + + // Core event tracking. + VG_(track_pre_mem_read) (drd_pre_mem_read); + VG_(track_post_mem_write) (drd_post_mem_write); + VG_(track_new_mem_stack) (drd_start_using_mem_stack); + VG_(track_die_mem_stack) (drd_stop_using_mem_stack); + VG_(track_new_mem_mmap) (drd_start_using_mem_mmap); + VG_(track_die_mem_munmap) (drd_stop_using_mem_munmap); + VG_(track_start_client_code) (drd_start_client_code); + VG_(track_pre_thread_ll_create) (drd_pre_thread_create); + VG_(track_pre_thread_first_insn)(drd_post_thread_create); + VG_(track_pre_thread_ll_exit) (drd_thread_finished); + + // Other stuff. + VG_(needs_data_syms)(); + + drd_register_malloc_wrappers(drd_start_using_mem, drd_stop_using_mem); + + drd_clientreq_init(); + + drd_suppression_init(); + + drd_load_suppression_file(); +} + + +VG_DETERMINE_INTERFACE_VERSION(drd_pre_clo_init) + + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_malloc_wrappers.c b/exp-drd/drd_malloc_wrappers.c new file mode 100644 index 0000000000..205f473bcd --- /dev/null +++ b/exp-drd/drd_malloc_wrappers.c @@ -0,0 +1,347 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_malloc_wrappers.h" +#include "drd_thread.h" +#include "pub_tool_basics.h" +#include "pub_tool_execontext.h" +#include "pub_tool_hashtable.h" +#include "pub_tool_libcassert.h" +#include "pub_tool_libcbase.h" +#include "pub_tool_libcprint.h" +#include "pub_tool_mallocfree.h" +#include "pub_tool_options.h" +#include "pub_tool_replacemalloc.h" +#include "pub_tool_threadstate.h" +#include "pub_tool_tooliface.h" + + +/*------------------------------------------------------------*/ +/*--- Defns ---*/ +/*------------------------------------------------------------*/ + + +typedef struct _DRD_Chunk { + struct _DRD_Chunk* next; + Addr data; // ptr to actual block + SizeT size : (sizeof(UWord)*8)-2; //size requested; 30 or 62 bits + ExeContext* where; // where it was allocated +} DRD_Chunk; + +static StartUsingMem s_start_using_mem_callback; +static StopUsingMem s_stop_using_mem_callback; +/* Stats ... */ +static SizeT cmalloc_n_mallocs = 0; +static SizeT cmalloc_n_frees = 0; +static SizeT cmalloc_bs_mallocd = 0; + + +/*------------------------------------------------------------*/ +/*--- Tracking malloc'd and free'd blocks ---*/ +/*------------------------------------------------------------*/ + +/* Record malloc'd blocks. */ +static VgHashTable drd_malloc_list = NULL; + + +/* Allocate its shadow chunk, put it on the appropriate list. */ +static +DRD_Chunk* create_DRD_Chunk(ThreadId tid, Addr p, SizeT size) +{ + DRD_Chunk* mc = VG_(malloc)(sizeof(DRD_Chunk)); + mc->data = p; + mc->size = size; + mc->where = VG_(record_ExeContext)(tid, 0); + + return mc; +} + +/*------------------------------------------------------------*/ +/*--- client_malloc(), etc ---*/ +/*------------------------------------------------------------*/ + +/* Allocate memory and note change in memory available */ +static +__inline__ +void* drd_new_block(ThreadId tid, + SizeT size, SizeT align, + Bool is_zeroed) +{ + Addr p; + + cmalloc_n_mallocs ++; + + // Allocate and zero + p = (Addr)VG_(cli_malloc)(align, size); + if (!p) { + return NULL; + } + if (is_zeroed) VG_(memset)((void*)p, 0, size); + s_start_using_mem_callback(p, p + size); + + // Only update this stat if allocation succeeded. + cmalloc_bs_mallocd += size; + + VG_(HT_add_node)(drd_malloc_list, create_DRD_Chunk(tid, p, size)); + + return (void*)p; +} + +static +void* drd_malloc(ThreadId tid, SizeT n) +{ + return drd_new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); +} + +static +void* drd_memalign(ThreadId tid, SizeT align, SizeT n) +{ + return drd_new_block(tid, n, align, /*is_zeroed*/False); +} + +static +void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1) +{ + return drd_new_block(tid, nmemb*size1, VG_(clo_alignment), + /*is_zeroed*/True); +} + +static +__inline__ +void drd_handle_free(ThreadId tid, Addr p) +{ + DRD_Chunk* mc; + + cmalloc_n_frees++; + + mc = VG_(HT_remove)(drd_malloc_list, (UWord)p); + if (mc == NULL) + { + tl_assert(0); + } + else + { + s_stop_using_mem_callback(mc->data, mc->data + mc->size); + VG_(free)(mc); + } +} + +static +void drd_free(ThreadId tid, void* p) +{ + drd_handle_free(tid, (Addr)p); +} + +static +void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size) +{ + DRD_Chunk* mc; + void* p_new; + SizeT old_size; + + cmalloc_n_frees ++; + cmalloc_n_mallocs ++; + cmalloc_bs_mallocd += new_size; + + /* Remove the old block */ + mc = VG_(HT_remove)(drd_malloc_list, (UWord)p_old); + if (mc == NULL) { + tl_assert(0); + return NULL; + } + + old_size = mc->size; + + if (old_size == new_size) + { + /* size unchanged */ + mc->where = VG_(record_ExeContext)(tid, 0); + p_new = p_old; + + } + else if (old_size > new_size) + { + /* new size is smaller */ + s_stop_using_mem_callback(mc->data + new_size, mc->data + old_size); + mc->size = new_size; + mc->where = VG_(record_ExeContext)(tid, 0); + p_new = p_old; + + } + else + { + /* new size is bigger */ + /* Get new memory */ + const Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size); + + if (a_new) + { + /* Copy from old to new */ + VG_(memcpy)((void*)a_new, p_old, mc->size); + + /* Free old memory */ + s_stop_using_mem_callback(mc->data, mc->data + mc->size); + VG_(free)(mc); + + // Allocate a new chunk. + mc = create_DRD_Chunk(tid, a_new, new_size); + s_start_using_mem_callback(a_new, a_new + new_size); + } + else + { + /* Allocation failed -- leave original block untouched. */ + } + + p_new = (void*)a_new; + } + + // Now insert the new mc (with a possibly new 'data' field) into + // malloc_list. If this realloc() did not increase the memory size, we + // will have removed and then re-added mc unnecessarily. But that's ok + // because shrinking a block with realloc() is (presumably) much rarer + // than growing it, and this way simplifies the growing case. + VG_(HT_add_node)(drd_malloc_list, mc); + + return p_new; +} + +static +void* drd___builtin_new(ThreadId tid, SizeT n) +{ + void* const result = drd_new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); + //VG_(message)(Vg_DebugMsg, "__builtin_new(%d, %d) = %p", tid, n, result); + return result; +} + +static +void drd___builtin_delete(ThreadId tid, void* p) +{ + //VG_(message)(Vg_DebugMsg, "__builtin_delete(%d, %p)", tid, p); + drd_handle_free(tid, (Addr)p); +} + +static +void* drd___builtin_vec_new(ThreadId tid, SizeT n) +{ + return drd_new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); +} + +static +void drd___builtin_vec_delete(ThreadId tid, void* p) +{ + drd_handle_free(tid, (Addr)p); +} + +void drd_register_malloc_wrappers(const StartUsingMem start_using_mem_callback, + const StopUsingMem stop_using_mem_callback) +{ + tl_assert(drd_malloc_list == 0); + drd_malloc_list = VG_(HT_construct)("drd_malloc_list"); // a big prime + tl_assert(drd_malloc_list != 0); + tl_assert(stop_using_mem_callback); + + s_start_using_mem_callback = start_using_mem_callback; + s_stop_using_mem_callback = stop_using_mem_callback; + + VG_(needs_malloc_replacement)(drd_malloc, + drd___builtin_new, + drd___builtin_vec_new, + drd_memalign, + drd_calloc, + drd_free, + drd___builtin_delete, + drd___builtin_vec_delete, + drd_realloc, + 0); +} + +Bool drd_heap_addrinfo(Addr const a, + Addr* const data, + SizeT* const size, + ExeContext** const where) +{ + DRD_Chunk* mc; + + tl_assert(data); + tl_assert(size); + tl_assert(where); + + VG_(HT_ResetIter)(drd_malloc_list); + while ((mc = VG_(HT_Next)(drd_malloc_list))) + { + if (mc->data <= a && a < mc->data + mc->size) + { + *data = mc->data; + *size = mc->size; + *where = mc->where; + return True; + } + } + return False; +} + +/*------------------------------------------------------------*/ +/*--- Statistics printing ---*/ +/*------------------------------------------------------------*/ + +void drd_print_malloc_stats(void) +{ + DRD_Chunk* mc; + SizeT nblocks = 0; + SizeT nbytes = 0; + + if (VG_(clo_verbosity) == 0) + return; + if (VG_(clo_xml)) + return; + + /* Count memory still in use. */ + VG_(HT_ResetIter)(drd_malloc_list); + while ((mc = VG_(HT_Next)(drd_malloc_list))) + { + nblocks++; + nbytes += mc->size; + } + + VG_(message)(Vg_DebugMsg, + "malloc/free: in use at exit: %lu bytes in %lu blocks.", + nbytes, nblocks); + VG_(message)(Vg_DebugMsg, + "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.", + cmalloc_n_mallocs, + cmalloc_n_frees, cmalloc_bs_mallocd); + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, " "); +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_malloc_wrappers.h b/exp-drd/drd_malloc_wrappers.h new file mode 100644 index 0000000000..8c9978ebf0 --- /dev/null +++ b/exp-drd/drd_malloc_wrappers.h @@ -0,0 +1,46 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + +#ifndef __MALLOC_WRAPPERS_H +#define __MALLOC_WRAPPERS_H + + +#include "pub_tool_basics.h" // Bool +#include "pub_tool_execontext.h" // ExeContext + + +typedef void (*StartUsingMem)(const Addr a1, const Addr a2); +typedef void (*StopUsingMem)(const Addr a1, const Addr a2); + + +void drd_register_malloc_wrappers(const StartUsingMem start_using_mem_callback, + const StopUsingMem stop_using_mem_callback); +Bool drd_heap_addrinfo(Addr const a, + Addr* const data, + SizeT* const size, + ExeContext** const where); +void drd_print_malloc_stats(void); + + +#endif // __MALLOC_WRAPPERS_H diff --git a/exp-drd/drd_mutex.c b/exp-drd/drd_mutex.c new file mode 100644 index 0000000000..25b4ef13b5 --- /dev/null +++ b/exp-drd/drd_mutex.c @@ -0,0 +1,331 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_error.h" +#include "drd_mutex.h" +#include "drd_suppression.h" +#include "pthread_object_size.h" +#include "pub_tool_errormgr.h" // VG_(maybe_record_error)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcprint.h" // VG_(printf)() +#include "pub_tool_machine.h" // VG_(get_IP)() +#include "pub_tool_threadstate.h" // VG_(get_running_tid)() + + +// Type definitions. + +struct mutex_info +{ + Addr mutex; // Pointer to client mutex. + SizeT size; // Size in bytes of client-side object. + int recursion_count; // 0 if free, >= 1 if locked. + DrdThreadId owner; // owner if locked, last owner if free. + VectorClock vc; // vector clock associated with last unlock. +}; + + +// Local variables. + +static Bool s_trace_mutex; +static ULong s_mutex_lock_count; +struct mutex_info s_mutex[256]; + + +// Function definitions. + +void mutex_set_trace(const Bool trace_mutex) +{ + tl_assert(!! trace_mutex == trace_mutex); + s_trace_mutex = trace_mutex; +} + +static +void mutex_initialize(struct mutex_info* const p, + const Addr mutex, + const SizeT size) +{ + tl_assert(mutex != 0); + tl_assert(size > 0); + + p->mutex = mutex; + p->size = size; + p->recursion_count = 0; + p->owner = DRD_INVALID_THREADID; + vc_init(&p->vc, 0, 0); +} + +static +struct mutex_info* mutex_get_or_allocate(const Addr mutex, const SizeT size) +{ + int i; + for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++) + if (s_mutex[i].mutex == mutex) + return &s_mutex[i]; + for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++) + { + if (s_mutex[i].mutex == 0) + { + mutex_initialize(&s_mutex[i], mutex, size); + drd_start_suppression(mutex, mutex + size, + mutex_get_typename(&s_mutex[i])); + return &s_mutex[i]; + } + } + tl_assert(0); + return 0; +} + +struct mutex_info* mutex_init(const Addr mutex, const SizeT size) +{ + struct mutex_info* mutex_p; + + tl_assert(mutex_get(mutex) == 0); + mutex_p = mutex_get_or_allocate(mutex, size); + + if (s_trace_mutex) + { + const ThreadId vg_tid = VG_(get_running_tid)(); + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(vg_tid); + VG_(message)(Vg_DebugMsg, + "drd_post_mutex_init tid = %d/%d, %s 0x%lx", + vg_tid, drd_tid, + mutex_get_typename(mutex_p), + mutex); + } + + return mutex_p; +} + +void mutex_destroy(struct mutex_info* const p) +{ + if (s_trace_mutex) + { + const ThreadId vg_tid = VG_(get_running_tid)(); + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(vg_tid); + VG_(message)(Vg_DebugMsg, + "drd_pre_mutex_destroy tid = %d/%d, %s 0x%lx", + vg_tid, drd_tid, + mutex_get_typename(p), + p->mutex); + } + + drd_finish_suppression(p->mutex, p->mutex + p->size); + + vc_cleanup(&p->vc); + p->mutex = 0; +} + +struct mutex_info* mutex_get(const Addr mutex) +{ + int i; + for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++) + if (s_mutex[i].mutex == mutex) + return &s_mutex[i]; + return 0; +} + +/** + * Update mutex_info state when locking the pthread_mutex_t mutex. + * Note: this function must be called after pthread_mutex_lock() has been + * called, or a race condition is triggered ! + */ +int mutex_lock(const Addr mutex, const SizeT size) +{ + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(VG_(get_running_tid)()); + struct mutex_info* const p = mutex_get_or_allocate(mutex, size); + const DrdThreadId last_owner = p->owner; + + if (s_trace_mutex) + { + const ThreadId tid = DrdThreadIdToVgThreadId(drd_tid); + VG_(message)(Vg_DebugMsg, + "drd_post_mutex_lock tid = %d/%d, %s 0x%lx rc %d owner %d", + tid, + drd_tid, + mutex_get_typename(p), + mutex, + p ? p->recursion_count : 0, + p ? p->owner : VG_INVALID_THREADID); + } + + if (p->recursion_count >= 1 && p->size == PTHREAD_SPINLOCK_SIZE) + { + // TO DO: tell the user in a more friendly way that it is not allowed to + // lock spinlocks recursively. + tl_assert(0); + } + + if (p->recursion_count == 0) + { + p->owner = drd_tid; + s_mutex_lock_count++; + } + else if (p->owner != drd_tid) + { + VG_(message)(Vg_DebugMsg, + "The impossible happened: mutex 0x%lx is locked" + " simultaneously by two threads (recursion count %d," + " owners %d and %d) !", + p->mutex, p->recursion_count, p->owner, drd_tid); + tl_assert(0); + } + p->recursion_count++; + + if (p->recursion_count == 1) + { + if (last_owner != drd_tid && last_owner != DRD_INVALID_THREADID) + thread_combine_vc2(drd_tid, mutex_get_last_vc(mutex)); + thread_new_segment(drd_tid); + } + + return p->recursion_count; +} + +/** + * Update mutex_info state when unlocking the pthread_mutex_t mutex. + * Note: this function must be called before pthread_mutex_unlock() is called, + * or a race condition is triggered ! + * @param mutex Pointer to pthread_mutex_t data structure in the client space. + * @param tid ThreadId of the thread calling pthread_mutex_unlock(). + * @param vc Pointer to the current vector clock of thread tid. + */ +int mutex_unlock(const Addr mutex) +{ + const DrdThreadId drd_tid = VgThreadIdToDrdThreadId(VG_(get_running_tid)()); + const ThreadId vg_tid = DrdThreadIdToVgThreadId(drd_tid); + const VectorClock* const vc = thread_get_vc(drd_tid); + struct mutex_info* const p = mutex_get(mutex); + + if (s_trace_mutex) + { + VG_(message)(Vg_DebugMsg, + "drd_pre_mutex_unlock tid = %d/%d, %s 0x%lx rc %d", + vg_tid, drd_tid, + mutex_get_typename(p), + mutex, + p->recursion_count, + p->owner); + } + + tl_assert(p); + tl_assert(p->owner != DRD_INVALID_THREADID); + if (p->owner != drd_tid) + { + MutexErrInfo MEI = { p->mutex, p->recursion_count, p->owner }; + VG_(maybe_record_error)(vg_tid, + MutexErr, + VG_(get_IP)(vg_tid), + "Mutex not unlocked by owner thread", + &MEI); + } + p->recursion_count--; + tl_assert(p->recursion_count >= 0); + if (p->recursion_count == 0) + { + /* This pthread_mutex_unlock() call really unlocks the mutex. Save the */ + /* current vector clock of the thread such that it is available when */ + /* this mutex is locked again. */ + vc_copy(&p->vc, vc); + + thread_new_segment(drd_tid); + } + return p->recursion_count; +} + +const char* mutex_get_typename(struct mutex_info* const p) +{ + tl_assert(p); + switch (p->size) + { + case PTHREAD_MUTEX_SIZE: + return "mutex"; + case PTHREAD_SPINLOCK_SIZE: + return "spinlock"; + default: + tl_assert(0); + } + return "?"; +} + +Bool mutex_is_locked_by(const Addr mutex, const DrdThreadId tid) +{ + struct mutex_info* const p = mutex_get(mutex); + tl_assert(p); + if (p) + { + return (p->recursion_count > 0 && p->owner == tid); + } + return False; +} + +const VectorClock* mutex_get_last_vc(const Addr mutex) +{ + struct mutex_info* const p = mutex_get(mutex); + return p ? &p->vc : 0; +} + +int mutex_get_recursion_count(const Addr mutex) +{ + struct mutex_info* const p = mutex_get(mutex); + tl_assert(p); + return p->recursion_count; +} + +/** + * Call this function when thread threadid stops to exist, such that the + * "last owner" field can be cleared if it still refers to that thread. + * TO DO: print an error message if a thread exits while it still has some + * mutexes locked. + */ +void mutex_thread_delete(const DrdThreadId threadid) +{ + int i; + for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++) + { + struct mutex_info* const p = &s_mutex[i]; + if (p->mutex && p->owner == threadid) + { + p->owner = VG_INVALID_THREADID; + } + } +} + +void mutex_stop_using_mem(const Addr a1, const Addr a2) +{ + unsigned i; + for (i = 0; i < sizeof(s_mutex)/sizeof(s_mutex[0]); i++) + { + if (a1 <= s_mutex[i].mutex && s_mutex[i].mutex < a2) + { + tl_assert(s_mutex[i].mutex + s_mutex[i].size <= a2); + mutex_destroy(&s_mutex[i]); + } + } +} + +ULong get_mutex_lock_count(void) +{ + return s_mutex_lock_count; +} diff --git a/exp-drd/drd_mutex.h b/exp-drd/drd_mutex.h new file mode 100644 index 0000000000..03d2994b03 --- /dev/null +++ b/exp-drd/drd_mutex.h @@ -0,0 +1,56 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +// Mutex state information: owner thread and recursion count. + + +#ifndef __MUTEX_H +#define __MUTEX_H + + +#include "pub_tool_basics.h" // Addr, SizeT +#include "drd_vc.h" +#include "drd_thread.h" // DrdThreadId + + +struct mutex_info; + + +void mutex_set_trace(const Bool trace_mutex); +struct mutex_info* mutex_init(const Addr mutex, const SizeT size); +void mutex_destroy(struct mutex_info* const p); +struct mutex_info* mutex_get(const Addr mutex); +int mutex_lock(const Addr mutex, const SizeT size); +int mutex_unlock(const Addr mutex); +const char* mutex_get_typename(struct mutex_info* const p); +Bool mutex_is_locked_by(const Addr mutex, const DrdThreadId tid); +const VectorClock* mutex_get_last_vc(const Addr mutex); +int mutex_get_recursion_count(const Addr mutex); +void mutex_thread_delete(const DrdThreadId threadid); +void mutex_stop_using_mem(const Addr a1, const Addr a2); +ULong get_mutex_lock_count(void); + + +#endif /* __MUTEX_H */ diff --git a/exp-drd/drd_preloaded.c b/exp-drd/drd_preloaded.c new file mode 100644 index 0000000000..8c9028845c --- /dev/null +++ b/exp-drd/drd_preloaded.c @@ -0,0 +1,532 @@ + +/*--------------------------------------------------------------------*/ +/*--- Client-space code for drd. drd_preloaded.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + +/* --------------------------------------------------------------------- + ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU. + + These functions are not called directly - they're the targets of code + redirection or load notifications (see pub_core_redir.h for info). + They're named weirdly so that the intercept code can find them when the + shared object is initially loaded. + + Note that this filename has the "drd_" prefix because it can appear + in stack traces, and the "drd_" makes it a little clearer that it + originates from Valgrind. + ------------------------------------------------------------------ */ + +// Make sure pthread_spinlock_t is available on glibc 2.3.2 systems. +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include // uintptr_t +#include +#include +#include +#include "drd_clientreq.h" +#include "pub_core_basics.h" +#include "pub_core_clreq.h" +#include "pub_core_debuginfo.h" // Needed for pub_core_redir.h +#include "pub_core_redir.h" // For VG_NOTIFY_ON_LOAD +#include "pub_tool_threadstate.h"// VG_N_THREADS +#include "pthread_object_size.h" // PTHREAD_MUTEX_SIZE etc. + + +// Defines. + +#define PTH_FUNC(ret_ty, f, args...) \ + ret_ty VG_WRAP_FUNCTION_ZZ(libpthreadZdsoZd0,f)(args); \ + ret_ty VG_WRAP_FUNCTION_ZZ(libpthreadZdsoZd0,f)(args) + + +// Local data structures. + +typedef struct +{ + void* (*start)(void*); + void* arg; + int detachstate; +#if 0 + pthread_mutex_t mutex; + pthread_cond_t cond; +#else + int wrapper_started; +#endif +} VgPosixThreadArgs; + + +// Local variables. + +static int vg_main_thread_state_is_set = 0; + + +// Function definitions. + +static void vg_start_suppression(const void* const p, size_t const size) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_SUPPRESSION, + p, size, 0, 0, 0); +} + +#if 0 +static void vg_finish_suppression(const void* const p, size_t const size) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_FINISH_SUPPRESSION, + p, size, 0, 0, 0); +} +#endif + +static void vg_start_recording(void) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_RECORDING, + pthread_self(), 0, 0, 0, 0); +} + +static void vg_stop_recording(void) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_STOP_RECORDING, + pthread_self(), 0, 0, 0, 0); +} + +static void vg_set_joinable(const pthread_t tid, const int joinable) +{ + int res; + assert(joinable == 0 || joinable == 1); +#if 0 + printf("vg_set_joinable(%ld, %d)\n", tid, joinable); +#endif + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__SET_JOINABLE, + tid, joinable, 0, 0, 0); +} + +static void* vg_thread_wrapper(void* arg) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK, + 0, 0, 0, 0, 0); + + { + VgPosixThreadArgs* const arg_ptr = (VgPosixThreadArgs*)arg; + VgPosixThreadArgs const arg_copy = *arg_ptr; + void* result; + +#if 0 + pthread_mutex_lock(arg_ptr->mutex); + pthread_cond_signal(arg_ptr->cond); + pthread_mutex_unlock(arg_ptr->mutex); +#else + arg_ptr->wrapper_started = 1; +#endif + + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID, + pthread_self(), 0, 0, 0, 0); + vg_set_joinable(pthread_self(), + arg_copy.detachstate == PTHREAD_CREATE_JOINABLE); + result = (arg_copy.start)(arg_copy.arg); + return result; + } +} + +static void vg_set_main_thread_state(void) +{ + int res; + + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__DRD_SUPPRESS_CURRENT_STACK, + 0, 0, 0, 0, 0); + + // Sanity checks. + assert(sizeof(pthread_mutex_t) == PTHREAD_MUTEX_SIZE); + assert(sizeof(pthread_spinlock_t) == PTHREAD_SPINLOCK_SIZE); + + // Make sure that DRD knows about the main thread's POSIX thread ID. + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SET_PTHREADID, + pthread_self(), 0, 0, 0, 0); + +} + +// pthread_create +PTH_FUNC(int, pthreadZucreateZAZa, // pthread_create@* + pthread_t *thread, const pthread_attr_t *attr, + void *(*start) (void *), void *arg) +{ + int ret; + OrigFn fn; + VgPosixThreadArgs vgargs; + + VALGRIND_GET_ORIG_FN(fn); + + if (vg_main_thread_state_is_set == 0) + { + vg_set_main_thread_state(); + vg_main_thread_state_is_set = 1; + } + vg_start_suppression(&vgargs.wrapper_started, + sizeof(vgargs.wrapper_started)); + vgargs.start = start; + vgargs.arg = arg; + vgargs.wrapper_started = 0; + vgargs.detachstate = PTHREAD_CREATE_JOINABLE; + if (attr) + { + if (pthread_attr_getdetachstate(attr, &vgargs.detachstate) != 0) + { + assert(0); + } +#if 0 + printf("[%ld] Requested detach state for new thread: %d\n", + pthread_self(), vgargs.detachstate); +#endif + } + assert(vgargs.detachstate == PTHREAD_CREATE_JOINABLE + || vgargs.detachstate == PTHREAD_CREATE_DETACHED); +#if 0 + pthread_mutex_init(&vgargs.mutex, 0); + pthread_cond_init(&vgargs.cond, 0); + pthread_mutex_lock(&vgargs.mutex); +#endif + vg_stop_recording(); + CALL_FN_W_WWWW(ret, fn, thread, attr, vg_thread_wrapper, &vgargs); + vg_start_recording(); +#if 0 + pthread_cond_wait(&vgargs.cond, &vgargs.mutex); + pthread_mutex_unlock(&vgargs.mutex); + pthread_cond_destroy(&vgargs.cond); + pthread_mutex_destroy(&vgargs.mutex); +#else + // Yes, you see it correctly, busy waiting ... The problem is that + // POSIX threads functions cannot be called here -- the functions defined + // in this file (vg_preloaded.c) would be called instead of those in + // libpthread.so. This loop is necessary because vgargs is allocated on the + // stack, and the created thread reads it. + while (! vgargs.wrapper_started) + { + sched_yield(); + } +#endif + return ret; +} + +// pthread_join +PTH_FUNC(int, pthreadZujoin, // pthread_join + pthread_t pt_joinee, void **thread_return) +{ + int ret; + int res; + OrigFn fn; + + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_WW(ret, fn, pt_joinee, thread_return); + if (ret == 0) + { + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_THREAD_JOIN, + pt_joinee, 0, 0, 0, 0); + } + return ret; +} + +// pthread_detach +PTH_FUNC(int, pthreadZudetach, pthread_t pt_thread) +{ + int ret; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + { + CALL_FN_W_W(ret, fn, pt_thread); + if (ret == 0) + { + vg_set_joinable(pt_thread, 0); + } + } + return ret; +} + +// pthread_mutex_init +PTH_FUNC(int, pthreadZumutexZuinit, + pthread_mutex_t *mutex, + const pthread_mutexattr_t* attr) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_MUTEX_INIT, + mutex, sizeof(*mutex), 0, 0, 0); + CALL_FN_W_WW(ret, fn, mutex, attr); + return ret; +} + +// pthread_mutex_destroy +PTH_FUNC(int, pthreadZumutexZudestroy, + pthread_mutex_t *mutex) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(ret, fn, mutex); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_DESTROY, + mutex, sizeof(*mutex), 0, 0, 0); + return ret; +} + +// pthread_mutex_lock +PTH_FUNC(int, pthreadZumutexZulock, // pthread_mutex_lock + pthread_mutex_t *mutex) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__PRE_PTHREAD_MUTEX_LOCK, + mutex, sizeof(*mutex), 0, 0, 0); +#if 1 + // The only purpose of the system call below is to make drd work on AMD64 + // systems. Without this system call, clients crash (SIGSEGV) in + // std::locale::locale(). + write(1, "", 0); +#endif + CALL_FN_W_W(ret, fn, mutex); + if (ret == 0) + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__POST_PTHREAD_MUTEX_LOCK, + mutex, sizeof(*mutex), 0, 0, 0); + return ret; +} + +// pthread_mutex_trylock +PTH_FUNC(int, pthreadZumutexZutrylock, // pthread_mutex_trylock + pthread_mutex_t *mutex) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(ret, fn, mutex); + if (ret == 0) + { + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_MUTEX_LOCK, + mutex, sizeof(*mutex), 0, 0, 0); + } + return ret; +} + +// pthread_mutex_unlock +PTH_FUNC(int, pthreadZumutexZuunlock, // pthread_mutex_unlock + pthread_mutex_t *mutex) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, + VG_USERREQ__PRE_PTHREAD_MUTEX_UNLOCK, + mutex, sizeof(*mutex), 0, 0, 0); + CALL_FN_W_W(ret, fn, mutex); + return ret; +} + +// pthread_cond_init +PTH_FUNC(int, pthreadZucondZuinitZAZa, // pthread_cond_init@* + pthread_cond_t* cond, + const pthread_condattr_t* attr) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_WW(ret, fn, cond, attr); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_COND_INIT, + cond, sizeof(*cond), 0, 0, 0); + return ret; +} + +// pthread_cond_destroy +PTH_FUNC(int, pthreadZucondZudestroyZAZa, // pthread_cond_destroy@* + pthread_cond_t* cond) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_PTHREAD_COND_DESTROY, + cond, sizeof(*cond), 0, 0, 0); + CALL_FN_W_W(ret, fn, cond); + return ret; +} + +// pthread_cond_wait +PTH_FUNC(int, pthreadZucondZuwaitZAZa, // pthread_cond_wait@* + pthread_cond_t *cond, + pthread_mutex_t *mutex) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_PTHREAD_COND_WAIT, + cond, mutex, 0, 0, 0); + CALL_FN_W_WW(ret, fn, cond, mutex); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_COND_WAIT, + cond, mutex, 0, 0, 0); + return ret; +} + +// pthread_cond_timedwait +PTH_FUNC(int, pthreadZucondZutimedwaitZAZa, // pthread_cond_timedwait@* + pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec* abstime) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_PTHREAD_COND_WAIT, + cond, mutex, 0, 0, 0); + CALL_FN_W_WWW(ret, fn, cond, mutex, abstime); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_COND_WAIT, + cond, mutex, 0, 0, 0); + return ret; +} + +// pthread_cond_signal +PTH_FUNC(int, pthreadZucondZusignalZAZa, // pthread_cond_signal@* + pthread_cond_t* cond) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_PTHREAD_COND_SIGNAL, + cond, 0, 0, 0, 0); + CALL_FN_W_W(ret, fn, cond); + return ret; +} + +// pthread_cond_broadcast +PTH_FUNC(int, pthreadZucondZubroadcastZAZa, // pthread_cond_broadcast@* + pthread_cond_t* cond) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__PRE_PTHREAD_COND_BROADCAST, + cond, 0, 0, 0, 0); + CALL_FN_W_W(ret, fn, cond); + return ret; +} + + +// pthread_spin_init +PTH_FUNC(int, pthreadZuspinZuinit, // pthread_spin_init + pthread_spinlock_t *spinlock, + int pshared) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SPIN_INIT_OR_UNLOCK, + spinlock, sizeof(*spinlock), 0, 0, 0); + CALL_FN_W_WW(ret, fn, spinlock, pshared); + return ret; +} + +// pthread_spin_destroy +PTH_FUNC(int, pthreadZuspinZudestroy, // pthread_spin_destroy + pthread_spinlock_t *spinlock) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(ret, fn, spinlock); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_MUTEX_DESTROY, + spinlock, sizeof(*spinlock), 0, 0, 0); + return ret; +} + +// pthread_spin_lock +PTH_FUNC(int, pthreadZuspinZulock, // pthread_spin_lock + pthread_spinlock_t *spinlock) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(ret, fn, spinlock); + if (ret == 0) + { + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_MUTEX_LOCK, + spinlock, sizeof(*spinlock), 0, 0, 0); + } + return ret; +} + +// pthread_spin_trylock +PTH_FUNC(int, pthreadZuspinZutrylock, // pthread_spin_trylock + pthread_spinlock_t *spinlock) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + CALL_FN_W_W(ret, fn, spinlock); + if (ret == 0) + { + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__POST_PTHREAD_MUTEX_LOCK, + spinlock, sizeof(*spinlock), 0, 0, 0); + } + return ret; +} + +// pthread_spin_unlock +PTH_FUNC(int, pthreadZuspinZuunlock, // pthread_spin_unlock + pthread_spinlock_t *spinlock) +{ + int ret; + int res; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + VALGRIND_DO_CLIENT_REQUEST(res, -1, VG_USERREQ__SPIN_INIT_OR_UNLOCK, + spinlock, sizeof(*spinlock), 0, 0, 0); + CALL_FN_W_W(ret, fn, spinlock); + return ret; +} + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_segment.c b/exp-drd/drd_segment.c new file mode 100644 index 0000000000..cb4b979ce9 --- /dev/null +++ b/exp-drd/drd_segment.c @@ -0,0 +1,146 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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 "pub_tool_basics.h" // Addr, SizeT +#include "pub_tool_errormgr.h" // VG_(unique_error)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcbase.h" // VG_(strlen)() +#include "pub_tool_libcprint.h" // VG_(printf)() +#include "pub_tool_mallocfree.h" // VG_(malloc)(), VG_(free)() +#include "drd_error.h" +#include "drd_segment.h" +#include "drd_thread.h" + + +// Local variables. + +static ULong s_segments_created_count; +static ULong s_segments_alive_count; +static ULong s_max_segments_alive_count; +static Bool drd_trace_segment = False; + + +// Function definitions. + +/** + * Note: creator and created may be equal. + */ +void sg_init(Segment* const sg, + DrdThreadId const creator, + DrdThreadId const created) +{ + Segment* creator_sg; + + tl_assert(sg); + tl_assert(creator == DRD_INVALID_THREADID || IsValidDrdThreadId(creator)); + + creator_sg = (creator != DRD_INVALID_THREADID + ? thread_get_segment(creator) : 0); + + sg->next = 0; + sg->prev = 0; + + sg->stacktrace = VG_(record_ExeContext)(created, 0); + + if (creator_sg) + vc_copy(&sg->vc, &creator_sg->vc); + else + vc_init(&sg->vc, 0, 0); + vc_increment(&sg->vc, created); + sg->bm = bm_new(); + + if (drd_trace_segment) + { + char msg[256]; + VG_(snprintf)(msg, sizeof(msg), + "New segment for thread %d with vc ", + creator); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + &sg->vc); + VG_(message)(Vg_DebugMsg, "%s", msg); + } +} + +void sg_cleanup(Segment* const sg) +{ + tl_assert(sg); + vc_cleanup(&sg->vc); + bm_delete(sg->bm); + sg->bm = 0; +} + +Segment* sg_new(ThreadId const creator, ThreadId const created) +{ + Segment* sg; + + s_segments_created_count++; + s_segments_alive_count++; + if (s_max_segments_alive_count < s_segments_alive_count) + s_max_segments_alive_count = s_segments_alive_count; + + sg = VG_(malloc)(sizeof(*sg)); + tl_assert(sg); + sg_init(sg, creator, created); + return sg; +} + +void sg_delete(Segment* const sg) +{ + s_segments_alive_count--; + + tl_assert(sg); + sg_cleanup(sg); + VG_(free)(sg); +} + +void sg_print(const Segment* const sg) +{ + tl_assert(sg); + VG_(printf)("vc: "); + vc_print(&sg->vc); + VG_(printf)("\n"); + bm_print(sg->bm); +} + +Bool sg_get_trace(void) +{ + return drd_trace_segment; +} + +void sg_set_trace(Bool const trace_segment) +{ + tl_assert(trace_segment == False || trace_segment == True); + drd_trace_segment = trace_segment; +} + +ULong sg_get_segments_created_count(void) +{ + return s_segments_created_count; +} + +ULong sg_get_max_segments_alive_count(void) +{ + return s_max_segments_alive_count; +} diff --git a/exp-drd/drd_segment.h b/exp-drd/drd_segment.h new file mode 100644 index 0000000000..209ed160e4 --- /dev/null +++ b/exp-drd/drd_segment.h @@ -0,0 +1,63 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +#ifndef __SEGMENT_H +#define __SEGMENT_H + + +// Segments and segment lists. A segment represents information about +// a contiguous group of statements of a specific thread. There is a vector +// clock associated with each segment. + + +#include "drd_vc.h" +#include "pub_drd_bitmap.h" +#include "pub_tool_execontext.h" // ExeContext +#include "pub_tool_stacktrace.h" // StackTrace + + +typedef struct segment +{ + struct segment* next; + struct segment* prev; + ExeContext* stacktrace; + VectorClock vc; + struct bitmap* bm; +} Segment; + +void sg_init(Segment* const sg, + ThreadId const creator, + ThreadId const created); +void sg_cleanup(Segment* const sg); +Segment* sg_new(ThreadId const creator, ThreadId const created); +void sg_delete(Segment* const sg); +void sg_print(const Segment* const sg); +Bool sg_get_trace(void); +void sg_set_trace(Bool const trace_segment); +ULong sg_get_segments_created_count(void); +ULong sg_get_max_segments_alive_count(void); + + +#endif // __SEGMENT_H diff --git a/exp-drd/drd_suppression.c b/exp-drd/drd_suppression.c new file mode 100644 index 0000000000..5fe25f7e99 --- /dev/null +++ b/exp-drd/drd_suppression.c @@ -0,0 +1,135 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_suppression.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcprint.h" +#include "pub_core_options.h" // VG_(clo_backtrace_size) +#include "pub_drd_bitmap.h" +#include "pub_tool_stacktrace.h" // VG_(get_and_pp_StackTrace)() +#include "pub_tool_threadstate.h" // VG_(get_running_tid)() + + +// Local variables. + +static struct bitmap* s_suppressed; +static Bool s_trace_suppression; + + +// Function definitions. + +void suppression_set_trace(const Bool trace_suppression) +{ + s_trace_suppression = trace_suppression; +} + +void drd_suppression_init(void) +{ + tl_assert(s_suppressed == 0); + s_suppressed = bm_new(); + tl_assert(s_suppressed); +} + +void drd_start_suppression(const Addr a1, const Addr a2, + const char* const reason) +{ + if (s_trace_suppression) + { + VG_(message)(Vg_DebugMsg, "start suppression of 0x%lx sz %ld (%s)", + a1, a2 - a1, reason); + } + + tl_assert(a1 < a2); + tl_assert(! drd_is_any_suppressed(a1, a2)); + bm_access_range(s_suppressed, a1, a2 - a1, eStore); +} + +void drd_finish_suppression(const Addr a1, const Addr a2) +{ + if (s_trace_suppression) + { + VG_(message)(Vg_DebugMsg, "finish suppression of 0x%lx sz %ld", + a1, a2 - a1); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + } + + tl_assert(a1 < a2); + if (! drd_is_suppressed(a1, a2)) + { + VG_(message)(Vg_DebugMsg, "?? not suppressed ??"); + VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), + VG_(clo_backtrace_size)); + tl_assert(False); + } + bm_clear(s_suppressed, a1, a2); +} + +/** + * Return true if data race detection suppression has been requested for all + * bytes in the range a1 .. a2 - 1 inclusive. Return false in case the range + * is only partially suppressed or not suppressed at all. + */ +Bool drd_is_suppressed(const Addr a1, const Addr a2) +{ + return bm_has(s_suppressed, a1, a2, eStore); +} + +/** + * Return true if data race detection suppression has been requested for any + * of the bytes in the range a1 .. a2 - 1 inclusive. Return false in case none + * of the bytes in the specified range is suppressed. + */ +Bool drd_is_any_suppressed(const Addr a1, const Addr a2) +{ + return bm_has_any(s_suppressed, a1, a2, eStore); +} + +void drd_suppression_stop_using_mem(const Addr a1, const Addr a2) +{ + if (s_trace_suppression) + { + Addr b; + for (b = a1; b < a2; b++) + { + if (bm_has_1(s_suppressed, b, eStore)) + { + VG_(message)(Vg_DebugMsg, + "stop_using_mem(0x%lx, %ld) finish suppression of 0x%lx", + a1, a2 - a1, b); + //VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(), VG_(clo_backtrace_size)); + } + } + } + tl_assert(a1); + tl_assert(a1 < a2); + bm_clear(s_suppressed, a1, a2); +} + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_suppression.h b/exp-drd/drd_suppression.h new file mode 100644 index 0000000000..ab018b91af --- /dev/null +++ b/exp-drd/drd_suppression.h @@ -0,0 +1,17 @@ +#ifndef __PUB_CORE_DRD_H +#define __PUB_CORE_DRD_H + + +#include "pub_tool_basics.h" + +void suppression_set_trace(const Bool trace_suppression); +void drd_suppression_init(void); +void drd_start_suppression(const Addr a1, const Addr a2, + const char* const reason); +void drd_finish_suppression(const Addr a1, const Addr a2); +Bool drd_is_suppressed(const Addr a1, const Addr a2); +Bool drd_is_any_suppressed(const Addr a1, const Addr a2); +void drd_suppression_stop_using_mem(const Addr a1, const Addr a2); + + +#endif // __PUB_CORE_DRD_H diff --git a/exp-drd/drd_thread.c b/exp-drd/drd_thread.c new file mode 100644 index 0000000000..72a92dc02b --- /dev/null +++ b/exp-drd/drd_thread.c @@ -0,0 +1,1062 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_error.h" +#include "drd_segment.h" +#include "drd_suppression.h" +#include "drd_thread.h" +#include "pthread_object_size.h" +#include "pub_core_options.h" // VG_(clo_backtrace_size) +#include "pub_tool_basics.h" // Addr, SizeT +#include "pub_tool_errormgr.h" // VG_(unique_error)() +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcbase.h" // VG_(strlen)() +#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)() + + +// Defines. + +#define DRD_N_THREADS VG_N_THREADS + + +// Type definitions. + +typedef struct +{ + Segment* first; + Segment* last; + ThreadId vg_threadid; + PThreadId pt_threadid; + Addr stack_min_min; + Addr stack_min; + Addr stack_startup; + Addr stack_max; + char name[32]; + /// Indicates whether the Valgrind core knows about this thread. + Bool vg_thread_exists; + /// Indicates whether there is an associated POSIX thread ID. + Bool posix_thread_exists; + /// If true, indicates that there is a corresponding POSIX thread ID and + /// a corresponding OS thread that is detached. + Bool detached_posix_thread; + Bool is_recording; +} ThreadInfo; + + +// Local functions. + +static void thread_append_segment(const DrdThreadId tid, + Segment* const sg); +static void thread_update_danger_set(const DrdThreadId tid); + + +// Local variables. + +static ULong s_context_switch_count; +static ULong s_discard_ordered_segments_count; +#ifdef OLD_RACE_DETECTION_ALGORITHM +static ULong s_report_races_count; +#endif +static ULong s_update_danger_set_count; +static ULong s_danger_set_bitmap_creation_count; +static ULong s_danger_set_bitmap2_creation_count; +static DrdThreadId s_running_tid = DRD_INVALID_THREADID; +static ThreadInfo s_threadinfo[DRD_N_THREADS]; +static struct bitmap* s_danger_set; + + +// Function definitions. + +__inline__ Bool IsValidDrdThreadId(const DrdThreadId tid) +{ + return (0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID + && ! (s_threadinfo[tid].vg_thread_exists == False + && s_threadinfo[tid].posix_thread_exists == False + && s_threadinfo[tid].detached_posix_thread == False)); +} + +/** + * Convert Valgrind's ThreadId into a DrdThreadId. Report failure if + * Valgrind's ThreadId does not yet exist. + **/ +DrdThreadId VgThreadIdToDrdThreadId(const ThreadId tid) +{ + int i; + + if (tid == VG_INVALID_THREADID) + return DRD_INVALID_THREADID; + + for (i = 1; i < DRD_N_THREADS; i++) + { + if (s_threadinfo[i].vg_thread_exists == True + && s_threadinfo[i].vg_threadid == tid) + { + return i; + } + } + + return DRD_INVALID_THREADID; +} + +static +DrdThreadId VgThreadIdToNewDrdThreadId(const ThreadId tid) +{ + int i; + + tl_assert(VgThreadIdToDrdThreadId(tid) == DRD_INVALID_THREADID); + + for (i = 1; i < DRD_N_THREADS; i++) + { + if (s_threadinfo[i].vg_thread_exists == False + && s_threadinfo[i].posix_thread_exists == False + && s_threadinfo[i].detached_posix_thread == False) + { + s_threadinfo[i].vg_thread_exists = True; + s_threadinfo[i].vg_threadid = tid; + s_threadinfo[i].pt_threadid = INVALID_POSIX_THREADID; + s_threadinfo[i].stack_min_min = 0; + s_threadinfo[i].stack_min = 0; + s_threadinfo[i].stack_startup = 0; + s_threadinfo[i].stack_max = 0; + VG_(snprintf)(s_threadinfo[i].name, sizeof(s_threadinfo[i].name), + "thread %d", tid); + s_threadinfo[i].name[sizeof(s_threadinfo[i].name) - 1] = 0; + s_threadinfo[i].is_recording = True; + if (s_threadinfo[i].first != 0) + VG_(printf)("drd thread id = %d\n", i); + tl_assert(s_threadinfo[i].first == 0); + tl_assert(s_threadinfo[i].last == 0); + return i; + } + } + + tl_assert(False); + + return DRD_INVALID_THREADID; +} + +DrdThreadId PtThreadIdToDrdThreadId(const PThreadId tid) +{ + int i; + + tl_assert(tid != INVALID_POSIX_THREADID); + + for (i = 1; i < DRD_N_THREADS; i++) + { + if (s_threadinfo[i].posix_thread_exists + && s_threadinfo[i].pt_threadid == tid) + { + return i; + } + } + return DRD_INVALID_THREADID; +} + +ThreadId DrdThreadIdToVgThreadId(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + return (s_threadinfo[tid].vg_thread_exists + ? s_threadinfo[tid].vg_threadid + : VG_INVALID_THREADID); +} + +/** + * Sanity check of the doubly linked list of segments referenced by a ThreadInfo struct. + * @return True if sane, False if not. + */ +static Bool sane_ThreadInfo(const ThreadInfo* const ti) +{ + Segment* p; + for (p = ti->first; p; p = p->next) { + if (p->next && p->next->prev != p) + return False; + if (p->next == 0 && p != ti->last) + return False; + } + for (p = ti->last; p; p = p->prev) { + if (p->prev && p->prev->next != p) + return False; + if (p->prev == 0 && p != ti->first) + return False; + } + return True; +} + +DrdThreadId thread_pre_create(const DrdThreadId creator, + const ThreadId vg_created) +{ + DrdThreadId created; + + tl_assert(VgThreadIdToDrdThreadId(vg_created) == DRD_INVALID_THREADID); + created = VgThreadIdToNewDrdThreadId(vg_created); + tl_assert(0 <= created && created < DRD_N_THREADS + && created != DRD_INVALID_THREADID); + + tl_assert(s_threadinfo[created].first == 0); + tl_assert(s_threadinfo[created].last == 0); + thread_append_segment(created, sg_new(creator, created)); + + return created; +} + +/** + * Allocate the first segment for a thread. Call this just after + * pthread_create(). + */ +DrdThreadId thread_post_create(const ThreadId vg_created) +{ + const DrdThreadId created = VgThreadIdToDrdThreadId(vg_created); + + tl_assert(0 <= created && created < DRD_N_THREADS + && created != DRD_INVALID_THREADID); + + s_threadinfo[created].stack_max = VG_(thread_get_stack_max)(vg_created); + s_threadinfo[created].stack_startup = s_threadinfo[created].stack_max; + s_threadinfo[created].stack_min = s_threadinfo[created].stack_max; + s_threadinfo[created].stack_min_min = s_threadinfo[created].stack_max; + tl_assert(s_threadinfo[created].stack_max != 0); + + return created; +} + +/* NPTL hack: NPTL allocates the 'struct pthread' on top of the stack, */ +/* and accesses this data structure from multiple threads without locking. */ +/* Any conflicting accesses in the range stack_startup..stack_max will be */ +/* ignored. */ +void thread_set_stack_startup(const DrdThreadId tid, const Addr stack_startup) +{ +#if 0 + VG_(message)(Vg_DebugMsg, "thread_set_stack_startup: thread %d (%d)" + " stack 0x%x .. 0x%lx (size %d)", + s_threadinfo[tid].vg_threadid, tid, + stack_startup, + s_threadinfo[tid].stack_max, + s_threadinfo[tid].stack_max - stack_startup); +#endif + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[tid].stack_min <= stack_startup); + tl_assert(stack_startup <= s_threadinfo[tid].stack_max); + s_threadinfo[tid].stack_startup = stack_startup; +} + +Addr thread_get_stack_min(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + return s_threadinfo[tid].stack_min; +} + +void thread_set_stack_min(const DrdThreadId tid, const Addr stack_min) +{ +#if 0 + VG_(message)(Vg_DebugMsg, "thread %d (%d) stack_min = 0x%x" + " (size %d, max %d, delta %d)", + s_threadinfo[tid].vg_threadid, tid, + stack_min, + s_threadinfo[tid].stack_max - stack_min, + s_threadinfo[tid].stack_max - s_threadinfo[tid].stack_min_min, + s_threadinfo[tid].stack_min - stack_min); +#endif + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + if (s_threadinfo[tid].stack_max) + { + s_threadinfo[tid].stack_min = stack_min; + if (stack_min < s_threadinfo[tid].stack_min_min) + { + s_threadinfo[tid].stack_min_min = stack_min; + } + tl_assert(s_threadinfo[tid].stack_min_min + <= s_threadinfo[tid].stack_min); + tl_assert(s_threadinfo[tid].stack_min < s_threadinfo[tid].stack_max); + } +} + +DrdThreadId thread_lookup_stackaddr(const Addr a, + Addr* const stack_min, + Addr* const stack_max) +{ + unsigned i; + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + if (s_threadinfo[i].stack_min <= a && a <= s_threadinfo[i].stack_max) + { + *stack_min = s_threadinfo[i].stack_min; + *stack_max = s_threadinfo[i].stack_max; + return i; + } + } + return DRD_INVALID_THREADID; +} + +/** + * Clean up thread-specific data structures. Call this just after + * pthread_join(). + */ +void thread_delete(const DrdThreadId tid) +{ + Segment* sg; + Segment* sg_prev; + + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + for (sg = s_threadinfo[tid].last; sg; sg = sg_prev) + { + sg_prev = sg->prev; + sg_delete(sg); + } + s_threadinfo[tid].vg_thread_exists = False; + s_threadinfo[tid].posix_thread_exists = False; + tl_assert(s_threadinfo[tid].detached_posix_thread == False); + s_threadinfo[tid].first = 0; + s_threadinfo[tid].last = 0; +} + +/* Called after a thread performed its last memory access and before */ +/* thread_delete() is called. Note: thread_delete() is only called for */ +/* joinable threads, not for detached threads. */ +void thread_finished(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + + thread_stop_using_mem(s_threadinfo[tid].stack_min, + s_threadinfo[tid].stack_max); + + s_threadinfo[tid].vg_thread_exists = False; + + if (s_threadinfo[tid].detached_posix_thread) + { + /* Once a detached thread has finished, its stack is deallocated and */ + /* should no longer be taken into account when computing the danger set*/ + s_threadinfo[tid].stack_min = s_threadinfo[tid].stack_max; + + /* For a detached thread, calling pthread_exit() invalidates the */ + /* POSIX thread ID associated with the detached thread. For joinable */ + /* POSIX threads however, the POSIX thread ID remains live after the */ + /* pthread_exit() call until pthread_join() is called. */ + s_threadinfo[tid].posix_thread_exists = False; + } +} + +void thread_set_pthreadid(const DrdThreadId tid, const PThreadId ptid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[tid].pt_threadid == INVALID_POSIX_THREADID); + tl_assert(ptid != INVALID_POSIX_THREADID); + s_threadinfo[tid].posix_thread_exists = True; + s_threadinfo[tid].pt_threadid = ptid; +} + +Bool thread_get_joinable(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + return ! s_threadinfo[tid].detached_posix_thread; +} + +void thread_set_joinable(const DrdThreadId tid, const Bool joinable) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(!! joinable == joinable); + tl_assert(s_threadinfo[tid].pt_threadid != INVALID_POSIX_THREADID); +#if 0 + VG_(message)(Vg_DebugMsg, + "thread_set_joinable(%d/%d, %s)", + tid, + s_threadinfo[tid].vg_threadid, + joinable ? "joinable" : "detached"); +#endif + s_threadinfo[tid].detached_posix_thread = ! joinable; +} + +const char* thread_get_name(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + return s_threadinfo[tid].name; +} + +void thread_set_name(const DrdThreadId tid, const char* const name) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + VG_(strncpy)(s_threadinfo[tid].name, name, + sizeof(s_threadinfo[tid].name)); + s_threadinfo[tid].name[sizeof(s_threadinfo[tid].name) - 1] = 0; +} + +void thread_set_name_fmt(const DrdThreadId tid, const char* const fmt, + const UWord arg) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + VG_(snprintf)(s_threadinfo[tid].name, sizeof(s_threadinfo[tid].name), + fmt, arg); + s_threadinfo[tid].name[sizeof(s_threadinfo[tid].name) - 1] = 0; +} +DrdThreadId thread_get_running_tid(void) +{ + tl_assert(s_running_tid != DRD_INVALID_THREADID); + return s_running_tid; +} + +void thread_set_running_tid(const DrdThreadId tid) +{ + s_running_tid = tid; + thread_update_danger_set(tid); + s_context_switch_count++; +} + +/** + * Return a pointer to the latest segment for the specified thread. + */ +Segment* thread_get_segment(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + if (s_threadinfo[tid].last == 0) + { + VG_(message)(Vg_DebugMsg, "threadid = %d", tid); + thread_print_all(); + } + tl_assert(s_threadinfo[tid].last); + return s_threadinfo[tid].last; +} + +/** + * Insert a new segment at the end of the segment list. + */ +static void thread_append_segment(const DrdThreadId tid, + Segment* const sg) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(sane_ThreadInfo(&s_threadinfo[tid])); + sg->prev = s_threadinfo[tid].last; + sg->next = 0; + if (s_threadinfo[tid].last) + s_threadinfo[tid].last->next = sg; + s_threadinfo[tid].last = sg; + if (s_threadinfo[tid].first == 0) + s_threadinfo[tid].first = sg; + tl_assert(sane_ThreadInfo(&s_threadinfo[tid])); +} + +/** + * Remove a segment from the segment list of thread threadid, and free the + * associated memory. + */ +static void thread_discard_segment(const DrdThreadId tid, + Segment* const sg) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(sane_ThreadInfo(&s_threadinfo[tid])); + if (sg->prev) + sg->prev->next = sg->next; + if (sg->next) + sg->next->prev = sg->prev; + if (sg == s_threadinfo[tid].first) + s_threadinfo[tid].first = sg->next; + if (sg == s_threadinfo[tid].last) + s_threadinfo[tid].last = sg->prev; + sg_delete(sg); + tl_assert(sane_ThreadInfo(&s_threadinfo[tid])); +} + +VectorClock* thread_get_vc(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[tid].last); + return &s_threadinfo[tid].last->vc; +} + +/** + * Compute the minimum of all latest vector clocks of all threads + * (Michiel Ronsse calls this "clock snooping" in his papers about DIOTA). + * @param vc pointer to a vectorclock, holds result upon return. + */ +static void thread_compute_minimum_vc(VectorClock* vc) +{ + int i; + Bool first; + Segment* latest_sg; + + first = True; + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + latest_sg = s_threadinfo[i].last; + if (latest_sg) + { + if (first) + { + vc_cleanup(vc); + vc_copy(vc, &latest_sg->vc); + } + else + vc_min(vc, &latest_sg->vc); + first = False; + } + } +} + +static void thread_compute_maximum_vc(VectorClock* vc) +{ + int i; + Bool first; + Segment* latest_sg; + + first = True; + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + latest_sg = s_threadinfo[i].last; + if (latest_sg) + { + if (first) + { + vc_cleanup(vc); + vc_copy(vc, &latest_sg->vc); + } + else + vc_combine(vc, &latest_sg->vc); + first = False; + } + } +} + +/** + * Discard all segments that have a defined ordered against the latest vector + * clock of every thread -- these segments can no longer be involved in a + * data race. + */ +static void thread_discard_ordered_segments(void) +{ + VectorClock thread_vc_min; + int i; + + s_discard_ordered_segments_count++; + + vc_init(&thread_vc_min, 0, 0); + thread_compute_minimum_vc(&thread_vc_min); + if (sg_get_trace()) + { + char msg[256]; + VectorClock thread_vc_max; + + vc_init(&thread_vc_max, 0, 0); + thread_compute_maximum_vc(&thread_vc_max); + VG_(snprintf)(msg, sizeof(msg), + "Discarding ordered segments -- min vc is "); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + &thread_vc_min); + VG_(snprintf)(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + ", max vc is "); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + &thread_vc_max); + VG_(message)(Vg_DebugMsg, "%s", msg); + vc_cleanup(&thread_vc_max); + } + + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + Segment* sg; + Segment* sg_next; + for (sg = s_threadinfo[i].first; + sg && (sg_next = sg->next) && vc_lte(&sg->vc, &thread_vc_min); + sg = sg_next) + { +#if 0 + VG_(printf)("Discarding a segment of thread %d: ", i); + vc_print(&sg->vc); + VG_(printf)("\n"); +#endif + thread_discard_segment(i, sg); + } + } + vc_cleanup(&thread_vc_min); +} + +/** + * Create a new segment for the specified thread, and report all data races + * of the most recent thread segment with other threads. + */ +void thread_new_segment(const DrdThreadId tid) +{ + //static int s_calls_since_last_discard = 0; + Segment* sg; + + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + +#ifdef OLD_RACE_DETECTION_ALGORITHM + if (s_threadinfo[tid].last) + { + thread_report_races_segment(tid, s_threadinfo[tid].last); + } +#endif + + sg = sg_new(tid, tid); + thread_append_segment(tid, sg); + + thread_discard_ordered_segments(); +} + +void thread_combine_vc(DrdThreadId joiner, DrdThreadId joinee) +{ + tl_assert(joiner != joinee); + tl_assert(0 <= joiner && joiner < DRD_N_THREADS + && joiner != DRD_INVALID_THREADID); + tl_assert(0 <= joinee && joinee < DRD_N_THREADS + && joinee != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[joiner].last); + tl_assert(s_threadinfo[joinee].last); + vc_combine(&s_threadinfo[joiner].last->vc, &s_threadinfo[joinee].last->vc); + thread_discard_ordered_segments(); + + if (joiner == s_running_tid) + { + thread_update_danger_set(joiner); + } +} + +void thread_combine_vc2(DrdThreadId tid, const VectorClock* const vc) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[tid].last); + tl_assert(vc); + vc_combine(&s_threadinfo[tid].last->vc, vc); + thread_discard_ordered_segments(); +} + +void thread_stop_using_mem(const Addr a1, const Addr a2) +{ + DrdThreadId other_user = DRD_INVALID_THREADID; + + /* For all threads, mark the range [a,a+size[ as no longer in use. */ + + unsigned i; + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + Segment* p; + for (p = s_threadinfo[i].first; p; p = p->next) + { + if (other_user == DRD_INVALID_THREADID + && i != s_running_tid + && bm_has_any_access(p->bm, a1, a2)) + { + other_user = i; + } + bm_clear(p->bm, a1, a2); + } + } + + /* If any other thread had accessed memory in [a,a+size[, update the */ + /* danger set. */ + if (other_user != DRD_INVALID_THREADID + && bm_has_any_access(s_danger_set, a1, a2)) + { +#if 0 + VG_(message)(Vg_DebugMsg, + "recalculating danger set because thread %d / %d stopped" + " using memory at 0x%x sz %d", + other_user, + s_threadinfo[other_user].vg_threadid, + a1, + a2 - a1); +#endif + thread_update_danger_set(thread_get_running_tid()); + } +} + +void thread_start_recording(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + tl_assert(! s_threadinfo[tid].is_recording); + s_threadinfo[tid].is_recording = True; +} + +void thread_stop_recording(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + tl_assert(s_threadinfo[tid].is_recording); + s_threadinfo[tid].is_recording = False; +} + +Bool thread_is_recording(const DrdThreadId tid) +{ + tl_assert(0 <= tid && tid < DRD_N_THREADS && tid != DRD_INVALID_THREADID); + return s_threadinfo[tid].is_recording; +} + +void thread_print_all(void) +{ + unsigned i; + Segment* p; + + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + if (s_threadinfo[i].first) + { + VG_(printf)("**************\n" + "* thread %3d (%d/%d/%d/0x%x/%d/%s) *\n" + "**************\n", + i, + s_threadinfo[i].vg_thread_exists, + s_threadinfo[i].vg_threadid, + s_threadinfo[i].posix_thread_exists, + s_threadinfo[i].pt_threadid, + s_threadinfo[i].detached_posix_thread, + s_threadinfo[i].name); + for (p = s_threadinfo[i].first; p; p = p->next) + { + sg_print(p); + } + } + } +} + +static void show_call_stack(const DrdThreadId tid, + const Char* const msg, + ExeContext* const callstack) +{ + const ThreadId vg_tid = DrdThreadIdToVgThreadId(tid); + + VG_(message)(Vg_UserMsg, + "%s (%s)", + msg, + thread_get_name(tid)); + + if (vg_tid != VG_INVALID_THREADID) + { + if (callstack) + { + VG_(pp_ExeContext)(callstack); + } + else + { + VG_(get_and_pp_StackTrace)(vg_tid, VG_(clo_backtrace_size)); + } + } + else + { + VG_(message)(Vg_UserMsg, + " (thread finished, call stack no longer available)"); + } +} + +#ifdef OLD_RACE_DETECTION_ALGORITHM +void thread_report_races(const DrdThreadId threadid) +{ + Segment* p; + + s_report_races_count++; + + tl_assert(0 <= threadid && threadid < DRD_N_THREADS + && threadid != DRD_INVALID_THREADID); + + for (p = s_threadinfo[threadid].first; p; p = p->next) + { + thread_report_races_segment(threadid, p); + } +} + +/** + * Report all data races for segment p of thread threadid against other + * threads. + */ +void thread_report_races_segment(const DrdThreadId threadid, + Segment* const p) +{ + unsigned i; + + tl_assert(0 <= threadid && threadid < DRD_N_THREADS + && threadid != DRD_INVALID_THREADID); + tl_assert(p); + + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + if (i != threadid) + { + Segment* q; + for (q = s_threadinfo[i].last; q; q = q->prev) + { +#if 0 + char msg[256]; + VG_(snprintf)(msg, sizeof(msg), "Examining thread %d (vc ", threadid); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + &p->vc); + VG_(snprintf)(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + ") versus thread %d (vc ", i); + vc_snprint(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + &q->vc); + VG_(snprintf)(msg + VG_(strlen)(msg), sizeof(msg) - VG_(strlen)(msg), + ") %d %d", + vc_lte(&p->vc, &q->vc), vc_lte(&q->vc, &p->vc)); + VG_(message)(Vg_DebugMsg, "%s", msg); +#endif + // Since q iterates over the segments of thread i in order of + // decreasing vector clocks, if q->vc <= p->vc, then + // q->next->vc <= p->vc will also hold. Hence, break out of the + // loop once this condition is met. + if (vc_lte(&q->vc, &p->vc)) + break; + if (! vc_lte(&p->vc, &q->vc)) + { + if (bm_has_races(p->bm, q->bm)) + { + VG_(message)(Vg_UserMsg, "----------------------------------------------------------------------"); + tl_assert(p->stacktrace); + show_call_stack(threadid, "1st segment start", + p->stacktrace); + show_call_stack(threadid, "1st segment end", + p->next ? p->next->stacktrace : 0); + tl_assert(q->stacktrace); + show_call_stack(i, "2nd segment start", + q->stacktrace); + show_call_stack(i, "2nd segment end", + q->next ? q->next->stacktrace : 0); + bm_report_races(threadid, i, p->bm, q->bm); + } + } + } + } + } +} + +/** + * Report all detected data races for all threads. + */ +void thread_report_all_races(void) +{ + unsigned i; + + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + if (s_threadinfo[i].last) + { + thread_report_races(i); + } + } +} +#else +static void +thread_report_conflicting_segments_segment(const DrdThreadId tid, + const Addr addr, + const SizeT size, + const BmAccessTypeT access_type, + const Segment* const p) +{ + unsigned i; + + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(p); + + for (i = 0; i < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); i++) + { + if (i != tid) + { + Segment* q; + for (q = s_threadinfo[i].last; q; q = q->prev) + { + // Since q iterates over the segments of thread i in order of + // decreasing vector clocks, if q->vc <= p->vc, then + // q->next->vc <= p->vc will also hold. Hence, break out of the + // loop once this condition is met. + if (vc_lte(&q->vc, &p->vc)) + break; + if (! vc_lte(&p->vc, &q->vc)) + { + if (bm_has_conflict_with(q->bm, addr, addr + size, access_type)) + { + tl_assert(q->stacktrace); + show_call_stack(i, "Other segment start", + q->stacktrace); + show_call_stack(i, "Other segment end", + q->next ? q->next->stacktrace : 0); + } + } + } + } + } +} + +void thread_report_conflicting_segments(const DrdThreadId tid, + const Addr addr, + const SizeT size, + const BmAccessTypeT access_type) +{ + Segment* p; + + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + + for (p = s_threadinfo[tid].first; p; p = p->next) + { + if (bm_has(p->bm, addr, addr + size, access_type)) + { + thread_report_conflicting_segments_segment(tid, addr, size, + access_type, p); + } + } +} +#endif + +/** + * Compute a bitmap that represents the union of all memory accesses of all + * segments that are unordered to the current segment of the thread tid. + */ +static void thread_update_danger_set(const DrdThreadId tid) +{ + Segment* p; + + tl_assert(0 <= tid && tid < DRD_N_THREADS + && tid != DRD_INVALID_THREADID); + tl_assert(tid == s_running_tid); + + s_update_danger_set_count++; + s_danger_set_bitmap_creation_count -= bm_get_bitmap_creation_count(); + s_danger_set_bitmap2_creation_count -= bm_get_bitmap2_creation_count(); + +#if 0 + if (s_danger_set) + { + bm_delete(s_danger_set); + s_danger_set = 0; + } + s_danger_set = bm_new(); +#else + // Marginally faster than the above code. + if (s_danger_set) + { + bm_clear_all(s_danger_set); + } + else + { + s_danger_set = bm_new(); + } +#endif + + for (p = s_threadinfo[tid].first; p; p = p->next) + { + unsigned j; + + for (j = 0; j < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); j++) + { + if (IsValidDrdThreadId(j)) + { + const Segment* const q = s_threadinfo[j].last; + if (j != tid && q != 0 + && ! vc_lte(&q->vc, &p->vc) && ! vc_lte(&p->vc, &q->vc)) + { + bm_merge2(s_danger_set, q->bm); + } + + } + } + + for (j = 0; j < sizeof(s_threadinfo) / sizeof(s_threadinfo[0]); j++) + { + if (IsValidDrdThreadId(j)) + { + // NPTL hack: don't report data races on sizeof(struct pthread) + // bytes at the top of the stack, since the NPTL functions access + // this data without locking. + if (s_threadinfo[j].stack_min != 0) + { + tl_assert(s_threadinfo[j].stack_startup != 0); + if (s_threadinfo[j].stack_min < s_threadinfo[j].stack_startup) + { + bm_clear(s_danger_set, + s_threadinfo[j].stack_min, + s_threadinfo[j].stack_startup); + } + } + } + } + } + + s_danger_set_bitmap_creation_count += bm_get_bitmap_creation_count(); + s_danger_set_bitmap2_creation_count += bm_get_bitmap2_creation_count(); + +#if 0 + VG_(message)(Vg_DebugMsg, "[%d] new danger set:", tid); + bm_print(s_danger_set); + VG_(message)(Vg_DebugMsg, "[%d] end of new danger set.", tid); +#endif +} + +Bool thread_conflicting_access(const Addr a, + const SizeT size, + const BmAccessTypeT access_type) +{ + tl_assert(s_danger_set); + return (bm_has_conflict_with(s_danger_set, a, a + size, access_type) + && ! drd_is_suppressed(a, a + size)); +} + +ULong thread_get_context_switch_count(void) +{ + return s_context_switch_count; +} + +#ifdef OLD_RACE_DETECTION_ALGORITHM +ULong thread_get_report_races_count(void) +{ + return s_report_races_count; +} +#endif + +ULong thread_get_discard_ordered_segments_count(void) +{ + return s_discard_ordered_segments_count; +} + +ULong thread_get_update_danger_set_count(void) +{ + return s_update_danger_set_count; +} + +ULong thread_get_danger_set_bitmap_creation_count(void) +{ + return s_danger_set_bitmap_creation_count; +} + +ULong thread_get_danger_set_bitmap2_creation_count(void) +{ + return s_danger_set_bitmap2_creation_count; +} + +/* + * Local variables: + * c-basic-offset: 3 + * End: + */ diff --git a/exp-drd/drd_thread.h b/exp-drd/drd_thread.h new file mode 100644 index 0000000000..174648aec1 --- /dev/null +++ b/exp-drd/drd_thread.h @@ -0,0 +1,103 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +#ifndef __THREAD_H +#define __THREAD_H + + +#include "drd_segment.h" +#include "pub_tool_stacktrace.h" // StackTrace + + +#define DRD_INVALID_THREADID 0 + +/* Note: the PThreadId typedef and the INVALID_POSIX_THREADID depend on the */ +/* operating system and threading library in use. PThreadId must contain at */ +/* least the same number of bits as pthread_t, and INVALID_POSIX_THREADID */ +/* must be a value that will never be returned by pthread_self(). */ + +#define INVALID_POSIX_THREADID ((PThreadId)0) + + +typedef UInt DrdThreadId; +typedef UWord PThreadId; + + +Bool IsValidDrdThreadId(const DrdThreadId tid); + +DrdThreadId VgThreadIdToDrdThreadId(const ThreadId tid); +DrdThreadId NewVgThreadIdToDrdThreadId(const ThreadId tid); +DrdThreadId PtThreadIdToDrdThreadId(const PThreadId tid); +ThreadId DrdThreadIdToVgThreadId(const DrdThreadId tid); +DrdThreadId thread_pre_create(const DrdThreadId creator, + const ThreadId vg_created); +DrdThreadId thread_post_create(const ThreadId vg_created); +void thread_delete(const DrdThreadId tid); +void thread_finished(const DrdThreadId tid); +void thread_set_stack_startup(const DrdThreadId tid, const Addr stack_startup); +Addr thread_get_stack_min(const DrdThreadId tid); +void thread_set_stack_min(const DrdThreadId tid, const Addr stack_min); +DrdThreadId thread_lookup_stackaddr(const Addr a, + Addr* const stack_min, + Addr* const stack_max); +void thread_set_pthreadid(const DrdThreadId tid, const PThreadId ptid); +Bool thread_get_joinable(const DrdThreadId tid); +void thread_set_joinable(const DrdThreadId tid, const Bool joinable); +const char* thread_get_name(const DrdThreadId tid); +void thread_set_name(const DrdThreadId tid, const char* const name); +void thread_set_name_fmt(const DrdThreadId tid, const char* const name, + const UWord arg); +DrdThreadId thread_get_running_tid(void); +void thread_set_running_tid(const DrdThreadId tid); +Segment* thread_get_segment(const DrdThreadId tid); +void thread_new_segment(const DrdThreadId tid); +VectorClock* thread_get_vc(const DrdThreadId tid); +void thread_combine_vc(const DrdThreadId joiner, const DrdThreadId joinee); +void thread_combine_vc2(const DrdThreadId tid, const VectorClock* const vc); +void thread_stop_using_mem(const Addr a1, const Addr a2); +void thread_start_recording(const DrdThreadId tid); +void thread_stop_recording(const DrdThreadId tid); +Bool thread_is_recording(const DrdThreadId tid); +void thread_print_all(void); +void thread_report_races(const DrdThreadId tid); +void thread_report_races_segment(const DrdThreadId tid, + const Segment* const p); +void thread_report_all_races(void); +Bool thread_conflicting_access(Addr const a, + SizeT const size, + BmAccessTypeT access_type); +void thread_report_conflicting_segments(const DrdThreadId tid, + const Addr addr, + const SizeT size, + const BmAccessTypeT access_type); +ULong thread_get_context_switch_count(void); +ULong thread_get_report_races_count(void); +ULong thread_get_discard_ordered_segments_count(void); +ULong thread_get_update_danger_set_count(void); +ULong thread_get_danger_set_bitmap_creation_count(void); +ULong thread_get_danger_set_bitmap2_creation_count(void); + + +#endif // __THREAD_H diff --git a/exp-drd/drd_track.h b/exp-drd/drd_track.h new file mode 100644 index 0000000000..8d56222c54 --- /dev/null +++ b/exp-drd/drd_track.h @@ -0,0 +1,8 @@ +void drd_post_thread_join(DrdThreadId joiner, DrdThreadId joinee); +void drd_pre_mutex_init(Addr mutex, SizeT size); +void drd_post_mutex_destroy(Addr mutex, SizeT size); +void drd_pre_mutex_lock(DrdThreadId tid, Addr mutex, const SizeT size); +void drd_post_mutex_lock(DrdThreadId tid, Addr mutex, const SizeT size); +void drd_pre_mutex_unlock(DrdThreadId tid, Addr mutex); +void drd_post_cond_init(Addr cond, SizeT s); +void drd_pre_cond_destroy(Addr cond, SizeT s); diff --git a/exp-drd/drd_vc.c b/exp-drd/drd_vc.c new file mode 100644 index 0000000000..92f0474c8f --- /dev/null +++ b/exp-drd/drd_vc.c @@ -0,0 +1,392 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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_vc.h" +#include "pub_tool_basics.h" // Addr, SizeT +#include "pub_tool_libcassert.h" // tl_assert() +#include "pub_tool_libcbase.h" // VG_(memset), VG_(memmove) +#include "pub_tool_libcprint.h" // VG_(printf) +#include "pub_tool_mallocfree.h" // VG_(malloc), VG_(free) +#include "pub_tool_threadstate.h" // VG_(get_running_tid) + + +static +void vc_reserve(VectorClock* const vc, const unsigned new_capacity); + + +void vc_init(VectorClock* const vc, + const VCElem* const vcelem, + const unsigned size) +{ + tl_assert(vc); + vc->size = 0; + vc->capacity = 0; + vc->vc = 0; + vc_reserve(vc, size); + tl_assert(size == 0 || vc->vc != 0); + if (vcelem) + { + VG_(memcpy)(vc->vc, vcelem, size * sizeof(vcelem[0])); + vc->size = size; + } +} + +void vc_cleanup(VectorClock* const vc) +{ + vc_reserve(vc, 0); +} + +/** + * Copy constructor -- initializes 'new'. + */ +void vc_copy(VectorClock* const new, + const VectorClock* const rhs) +{ + vc_init(new, rhs->vc, rhs->size); +} + +void vc_increment(VectorClock* const vc, ThreadId const threadid) +{ + unsigned i; + for (i = 0; i < vc->size; i++) + { + if (vc->vc[i].threadid == threadid) + { + typeof(vc->vc[i].count) const oldcount = vc->vc[i].count; + vc->vc[i].count++; + // Check for integer overflow. + tl_assert(oldcount < vc->vc[i].count); + return; + } + } + + // The specified thread ID does not yet exist in the vector clock + // -- insert it. + { + VCElem vcelem = { threadid, 1 }; + VectorClock vc2; + vc_init(&vc2, &vcelem, 1); + vc_combine(vc, &vc2); + vc_cleanup(&vc2); + } +} + +/** + * @return True if all thread id's that are present in vc1 also exist in + * vc2, and if additionally all corresponding counters in v2 are higher or + * equal. + */ +Bool vc_lte(const VectorClock* const vc1, + const VectorClock* const vc2) +{ + unsigned i; + unsigned j = 0; + for (i = 0; i < vc1->size; i++) + { + while (j < vc2->size && vc2->vc[j].threadid < vc1->vc[i].threadid) + { + j++; + } + if (j >= vc2->size || vc2->vc[j].threadid > vc1->vc[i].threadid) + return False; + tl_assert(j < vc2->size && vc2->vc[j].threadid == vc1->vc[i].threadid); + if (vc1->vc[i].count > vc2->vc[j].count) + return False; + } + return True; +} + +/** + * @return True if vector clocks vc1 and vc2 are ordered, and false otherwise. + * Order is as imposed by thread synchronization actions ("happens before"). + */ +Bool vc_ordered(const VectorClock* const vc1, + const VectorClock* const vc2) +{ + return vc_lte(vc1, vc2) || vc_lte(vc2, vc1); +} + +/** + * Compute elementwise minimum. + */ +void vc_min(VectorClock* const result, + const VectorClock* const rhs) +{ + unsigned i; + unsigned j; + unsigned shared; + unsigned new_size; + + tl_assert(result); + tl_assert(rhs); + + // First count the number of shared thread id's. + j = 0; + shared = 0; + for (i = 0; i < result->size; i++) + { + while (j < rhs->size && rhs->vc[j].threadid < result->vc[i].threadid) + j++; + if (j >= rhs->size) + break; + if (result->vc[i].threadid == rhs->vc[j].threadid) + shared++; + } + + vc_check(result); + + new_size = result->size + rhs->size - shared; + if (new_size > result->capacity) + vc_reserve(result, new_size); + + vc_check(result); + + // Next, combine both vector clocks into one. + i = 0; + for (j = 0; j < rhs->size; j++) + { + vc_check(result); + + while (i < result->size && result->vc[i].threadid < rhs->vc[j].threadid) + i++; + if (i >= result->size) + { + result->size++; + result->vc[i] = rhs->vc[j]; + vc_check(result); + } + else if (result->vc[i].threadid > rhs->vc[j].threadid) + { + unsigned k; + for (k = result->size; k > i; k--) + { + result->vc[k] = result->vc[k - 1]; + } + result->size++; + result->vc[i] = rhs->vc[j]; + vc_check(result); + } + else + { + tl_assert(result->vc[i].threadid == rhs->vc[j].threadid); + if (rhs->vc[j].count < result->vc[i].count) + { + result->vc[i].count = rhs->vc[j].count; + } + vc_check(result); + } + } + vc_check(result); + tl_assert(result->size == new_size); +} + +/** + * Compute elementwise maximum. + */ +void vc_combine(VectorClock* const result, + const VectorClock* const rhs) +{ + unsigned i; + unsigned j; + unsigned shared; + unsigned new_size; + + tl_assert(result); + tl_assert(rhs); + + // First count the number of shared thread id's. + j = 0; + shared = 0; + for (i = 0; i < result->size; i++) + { + while (j < rhs->size && rhs->vc[j].threadid < result->vc[i].threadid) + j++; + if (j >= rhs->size) + break; + if (result->vc[i].threadid == rhs->vc[j].threadid) + shared++; + } + + vc_check(result); + + new_size = result->size + rhs->size - shared; + if (new_size > result->capacity) + vc_reserve(result, new_size); + + vc_check(result); + + // Next, combine both vector clocks into one. + i = 0; + for (j = 0; j < rhs->size; j++) + { + vc_check(result); + + while (i < result->size && result->vc[i].threadid < rhs->vc[j].threadid) + i++; + if (i >= result->size) + { + result->size++; + result->vc[i] = rhs->vc[j]; + vc_check(result); + } + else if (result->vc[i].threadid > rhs->vc[j].threadid) + { + unsigned k; + for (k = result->size; k > i; k--) + { + result->vc[k] = result->vc[k - 1]; + } + result->size++; + result->vc[i] = rhs->vc[j]; + vc_check(result); + } + else + { + tl_assert(result->vc[i].threadid == rhs->vc[j].threadid); + if (rhs->vc[j].count > result->vc[i].count) + { + result->vc[i].count = rhs->vc[j].count; + } + vc_check(result); + } + } + vc_check(result); + tl_assert(result->size == new_size); +} + +void vc_print(const VectorClock* const vc) +{ + unsigned i; + + tl_assert(vc); + VG_(printf)("["); + for (i = 0; i < vc->size; i++) + { + tl_assert(vc->vc); + VG_(printf)("%s %d: %d", i > 0 ? "," : "", + vc->vc[i].threadid, vc->vc[i].count); + } + VG_(printf)(" ]"); +} + +void vc_snprint(Char* const str, Int const size, + const VectorClock* const vc) +{ + unsigned i; + + tl_assert(vc); + VG_(snprintf)(str, size, "["); + for (i = 0; i < vc->size; i++) + { + tl_assert(vc->vc); + VG_(snprintf)(str + VG_(strlen)(str), size - VG_(strlen)(str), + "%s %d: %d", i > 0 ? "," : "", + vc->vc[i].threadid, vc->vc[i].count); + } + VG_(snprintf)(str + VG_(strlen)(str), size - VG_(strlen)(str), " ]"); +} + +/** + * Invariant test. + */ +void vc_check(const VectorClock* const vc) +{ + unsigned i; + tl_assert(vc->size <= vc->capacity); + for (i = 1; i < vc->size; i++) + { + tl_assert(vc->vc[i-1].threadid < vc->vc[i].threadid); + } +} + +/** + * Change the size of the memory block pointed at by vc->vc. + * Changes capacity, but does not change size. If the size of the memory + * block is increased, the newly allocated memory is not initialized. + */ +static +void vc_reserve(VectorClock* const vc, const unsigned new_capacity) +{ + tl_assert(vc); + if (new_capacity > vc->capacity) + { + if (vc->vc) + { + vc->vc = VG_(realloc)(vc->vc, new_capacity * sizeof(vc->vc[0])); + } + else if (new_capacity > 0) + { + vc->vc = VG_(malloc)(new_capacity * sizeof(vc->vc[0])); + } + else + { + tl_assert(vc->vc == 0 && new_capacity == 0); + } + vc->capacity = new_capacity; + } + tl_assert(new_capacity == 0 || vc->vc != 0); +} + +/** + * Unit test. + */ +void vc_test(void) +{ + VectorClock vc1; + VCElem vc1elem[] = { { 3, 7 }, { 5, 8 }, }; + VectorClock vc2; + VCElem vc2elem[] = { { 1, 4 }, { 3, 9 }, }; + VectorClock vc3; + VCElem vc4elem[] = { { 1, 3 }, { 2, 1 }, }; + VectorClock vc4; + VCElem vc5elem[] = { { 1, 4 }, }; + VectorClock vc5; + + vc_init(&vc1, vc1elem, sizeof(vc1elem)/sizeof(vc1elem[0])); + vc_init(&vc2, vc2elem, sizeof(vc2elem)/sizeof(vc2elem[0])); + vc_init(&vc3, 0, 0); + vc_init(&vc4, vc4elem, sizeof(vc4elem)/sizeof(vc4elem[0])); + vc_init(&vc5, vc5elem, sizeof(vc5elem)/sizeof(vc5elem[0])); + + vc_combine(&vc3, &vc1); + vc_combine(&vc3, &vc2); + + VG_(printf)("vc1: "); + vc_print(&vc1); + VG_(printf)("\nvc2: "); + vc_print(&vc2); + VG_(printf)("\nvc3: "); + vc_print(&vc3); + VG_(printf)("\n"); + VG_(printf)("vc_lte(vc1, vc2) = %d, vc_lte(vc1, vc3) = %d, vc_lte(vc2, vc3) = %d, vc_lte(", vc_lte(&vc1, &vc2), vc_lte(&vc1, &vc3), vc_lte(&vc2, &vc3)); + vc_print(&vc4); + VG_(printf)(", "); + vc_print(&vc5); + VG_(printf)(") = %d sw %d\n", vc_lte(&vc4, &vc5), vc_lte(&vc5, &vc4)); + + vc_cleanup(&vc1); + vc_cleanup(&vc2); + vc_cleanup(&vc3); +} diff --git a/exp-drd/drd_vc.h b/exp-drd/drd_vc.h new file mode 100644 index 0000000000..dd2b6db665 --- /dev/null +++ b/exp-drd/drd_vc.h @@ -0,0 +1,86 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +#ifndef __DRD_VC_H +#define __DRD_VC_H + + +// DRD vector clock implementation: +// - One counter per thread. +// - A vector clock is implemented as multiple pairs of (thread id, counter). +// - Pairs are stored in an array sorted by thread id. +// Semantics: +// - Each time a thread performs an action that implies an ordering between +// intra-thread events, the counter of that thread is incremented. +// - Vector clocks are compared by comparing all counters of all threads. +// - When a thread synchronization action is performed that guarantees that +// new actions of the current thread are executed after the actions of the +// other thread, the vector clock of the synchronization object and the +// current thread are combined (by taking the component-wise maximum). +// - A vector clock is incremented during actions such as +// pthread_create(), pthread_mutex_unlock(), sem_post(). (Actions where +// an inter-thread ordering "arrow" starts). + + +#include "pub_tool_basics.h" // Addr, SizeT + + +typedef struct +{ + ThreadId threadid; + UInt count; +} VCElem; + +typedef struct +{ + unsigned capacity; + unsigned size; + VCElem* vc; +} VectorClock; + + +void vc_init(VectorClock* const vc, + const VCElem* const vcelem, + const unsigned size); +void vc_cleanup(VectorClock* const vc); +void vc_copy(VectorClock* const new, + const VectorClock* const rhs); +void vc_increment(VectorClock* const vc, ThreadId const threadid); +Bool vc_lte(const VectorClock* const vc1, + const VectorClock* const vc2); +Bool vc_ordered(const VectorClock* const vc1, + const VectorClock* const vc2); +void vc_min(VectorClock* const result, + const VectorClock* const rhs); +void vc_combine(VectorClock* const result, + const VectorClock* const rhs); +void vc_print(const VectorClock* const vc); +void vc_snprint(Char* const str, Int const size, + const VectorClock* const vc); +void vc_check(const VectorClock* const vc); +void vc_test(void); + + +#endif /* __DRD_VC_H */ diff --git a/exp-drd/pthread_object_size.h b/exp-drd/pthread_object_size.h new file mode 100644 index 0000000000..845eadf15a --- /dev/null +++ b/exp-drd/pthread_object_size.h @@ -0,0 +1,13 @@ +// TO DO: replace the constants below by macro's #define'd during the configure +// phase. + +#if defined(VGA_x86) +#define PTHREAD_MUTEX_SIZE 24 +#define PTHREAD_COND_SIZE 48 +#elif defined(VGA_amd64) +#define PTHREAD_MUTEX_SIZE 40 +#define PTHREAD_COND_SIZE 48 +#else +#error Unknown platform +#endif +#define PTHREAD_SPINLOCK_SIZE 4 diff --git a/exp-drd/pub_drd_bitmap.h b/exp-drd/pub_drd_bitmap.h new file mode 100644 index 0000000000..6f6b867fa6 --- /dev/null +++ b/exp-drd/pub_drd_bitmap.h @@ -0,0 +1,103 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + + +// A bitmap is a data structure that contains information about which +// addresses have been accessed for reading or writing within a given +// segment. + + +#ifndef __DRD_BITMAP_H +#define __DRD_BITMAP_H + + +#include "pub_tool_basics.h" /* Addr, SizeT */ + + +// Constant definitions. + +#define LHS_R (1<<0) +#define LHS_W (1<<1) +#define RHS_R (1<<2) +#define RHS_W (1<<3) +#define HAS_RACE(a) ((((a) & RHS_W) && ((a) & (LHS_R | LHS_W))) \ + || (((a) & LHS_W) && ((a) & (RHS_R | RHS_W)))) + + +// Forward declarations. +struct bitmap; + + +// Datatype definitions. +typedef enum { eLoad, eStore } BmAccessTypeT; + + +// Function declarations. +struct bitmap* bm_new(void); +void bm_delete(struct bitmap* const bm); +void bm_access_range(struct bitmap* const bm, + const Addr address, + const SizeT size, + const BmAccessTypeT access_type); +void bm_access_4(struct bitmap* const bm, + const Addr address, + const BmAccessTypeT access_type); +Bool bm_has(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type); +Bool bm_has_any(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type); +UWord bm_has_any_access(const struct bitmap* const bm, + const Addr a1, + const Addr a2); +UWord bm_has_1(const struct bitmap* const bm, + const Addr address, + const BmAccessTypeT access_type); +void bm_clear_all(const struct bitmap* const bm); +void bm_clear(const struct bitmap* const bm, + const Addr a1, + const Addr a2); +Bool bm_has_conflict_with(const struct bitmap* const bm, + const Addr a1, + const Addr a2, + const BmAccessTypeT access_type); +void bm_swap(struct bitmap* const bm1, struct bitmap* const bm2); +void bm_merge2(struct bitmap* const lhs, + const struct bitmap* const rhs); +int bm_has_races(const struct bitmap* const bm1, + const struct bitmap* const bm2); +void bm_report_races(ThreadId const tid1, ThreadId const tid2, + const struct bitmap* const bm1, + const struct bitmap* const bm2); +void bm_print(const struct bitmap* bm); +ULong bm_get_bitmap_creation_count(void); +ULong bm_get_bitmap2_creation_count(void); +void bm_test(void); + + + +#endif /* __DRD_BITMAP_H */ diff --git a/exp-drd/tests/Makefile.am b/exp-drd/tests/Makefile.am new file mode 100644 index 0000000000..014424efaa --- /dev/null +++ b/exp-drd/tests/Makefile.am @@ -0,0 +1,78 @@ +# For AM_FLAG_M3264_PRI +include $(top_srcdir)/Makefile.flags.am + +SUBDIRS = . + +DIST_SUBDIRS = . + +noinst_SCRIPTS = \ + filter_cmdline0 filter_linenos \ + filter_fdleak filter_none_discards filter_stderr + +EXTRA_DIST = $(noinst_SCRIPTS) \ + abort.vgtest \ + abort.stdout.exp abort.stderr.exp \ + fp_race.vgtest \ + fp_race.stdout.exp fp_race.stderr.exp \ + fp_race2.vgtest \ + fp_race2.stdout.exp fp_race2.stderr.exp \ + new_delete.vgtest \ + new_delete.stdout.exp new_delete.stderr.exp \ + pth_broadcast.vgtest \ + pth_broadcast.stdout.exp pth_broadcast.stderr.exp \ + pth_cond_race.vgtest \ + pth_cond_race.stdout.exp pth_cond_race.stderr.exp \ + pth_cond_race2.vgtest \ + pth_cond_race2.stdout.exp pth_cond_race2.stderr.exp \ + pth_create_chain.vgtest \ + pth_create_chain.stdout.exp pth_create_chain.stderr.exp \ + pth_detached.vgtest \ + pth_detached.stdout.exp pth_detached.stderr.exp \ + pth_detached2.vgtest \ + pth_detached2.stdout.exp pth_detached2.stderr.exp \ + sigalrm.vgtest \ + sigalrm.stdout.exp sigalrm.stderr.exp \ + std-string.vgtest \ + std-string.stdout.exp std-string.stderr.exp + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g $(AM_FLAG_M3264_PRI) +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_builddir)/include +AM_CXXFLAGS = $(AM_CFLAGS) + +check_PROGRAMS = \ + abort \ + fp_race \ + new_delete \ + pth_broadcast \ + pth_cond_race \ + pth_create_chain \ + pth_detached \ + sigalrm \ + std-string + +abort_SOURCES = abort.cpp +abort_LDADD = -lpthread + +fp_race_SOURCES = fp_race.cpp +fp_race_LDADD = -lpthread + +new_delete_SOURCES = new_delete.cpp +new_delete_LDADD = -lpthread + +pth_broadcast_SOURCES = pth_broadcast.cpp +pth_broadcast_LDADD = -lpthread + +pth_cond_race_SOURCES = pth_cond_race.cpp +pth_cond_race_LDADD = -lpthread + +pth_create_chain_SOURCES = pth_create_chain.cpp +pth_create_chain_LDADD = -lpthread + +pth_detached_SOURCES = pth_detached.c +pth_detached_LDADD = -lpthread + +sigalrm_SOURCES = sigalrm.cpp +sigalrm_LDADD = -lpthread -lrt + +std_string_SOURCES = std-string.cpp +std_string_LDADD = -lpthread diff --git a/exp-drd/tests/abort.cpp b/exp-drd/tests/abort.cpp new file mode 100644 index 0000000000..9e62533682 --- /dev/null +++ b/exp-drd/tests/abort.cpp @@ -0,0 +1,37 @@ +// assert(false) calls __assert_fail(), which in turn calls abort() and +// _IO_flush_all_lockp(). This last function triggers a race. Check that this +// race is suppressed. Note: the test program below is not sufficient for +// reproducing this race. + + +#include +#include +#include +#include + +static pthread_mutex_t s_mutex; + +void* thread_func(void*) +{ + pthread_mutex_lock(&s_mutex); + pthread_mutex_unlock(&s_mutex); + std::cout << "thread\n"; + assert(false); + return 0; +} + +int main(int argc, char** argv) +{ + pthread_mutex_init(&s_mutex, 0); + pthread_t tid; + pthread_mutex_lock(&s_mutex); + pthread_create(&tid, 0, thread_func, 0); + FILE* fp = fopen("/tmp/valgrind-drd-tests-abort", "w"); + fprintf(fp, "x"); + pthread_mutex_unlock(&s_mutex); + pthread_join(tid, 0); + pthread_mutex_destroy(&s_mutex); + fclose(fp); + + return 0; +} diff --git a/exp-drd/tests/filter_cmdline0 b/exp-drd/tests/filter_cmdline0 new file mode 100644 index 0000000000..3b56968baf --- /dev/null +++ b/exp-drd/tests/filter_cmdline0 @@ -0,0 +1,3 @@ +#! /bin/sh + +sed "s/^valgrind-.*/valgrind-XXX/g" diff --git a/exp-drd/tests/filter_fdleak b/exp-drd/tests/filter_fdleak new file mode 100644 index 0000000000..73d7635a20 --- /dev/null +++ b/exp-drd/tests/filter_fdleak @@ -0,0 +1,14 @@ +#! /bin/sh + +dir=`dirname $0` + +./filter_stderr | + +$dir/../../tests/filter_test_paths | + +sed s/"^Open AF_UNIX socket [0-9]*: /Open AF_UNIX socket .: /" | +sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]*: \/dev\/null/Open \\1 .: \/dev\/null/" | +sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]*: \/tmp\/\(sock\|data1\|data2\|file\)\.[0-9]*/Open \\1 .: \/tmp\/\\2/" | +sed s/"^Open file descriptor [0-9]*: .*/Open file descriptor .: ./" | +sed s/"^Open file descriptor [0-9]*:$/Open file descriptor .:/" | +sed s/"127.0.0.1:[0-9]*/127.0.0.1:.../g" diff --git a/exp-drd/tests/filter_linenos b/exp-drd/tests/filter_linenos new file mode 100644 index 0000000000..7e3a2db6ac --- /dev/null +++ b/exp-drd/tests/filter_linenos @@ -0,0 +1,5 @@ +#!/bin/sh + +dir=`dirname $0` + +$dir/filter_stderr | sed "s/ line [0-9]*://" diff --git a/exp-drd/tests/filter_none_discards b/exp-drd/tests/filter_none_discards new file mode 100644 index 0000000000..dc3242bf7c --- /dev/null +++ b/exp-drd/tests/filter_none_discards @@ -0,0 +1,6 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/filter_stderr | $dir/../../tests/filter_discards + diff --git a/exp-drd/tests/filter_stderr b/exp-drd/tests/filter_stderr new file mode 100755 index 0000000000..6f87f94ceb --- /dev/null +++ b/exp-drd/tests/filter_stderr @@ -0,0 +1,22 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Remove "drd, ..." line and the following copyright line. +# Remove line numbers referring to drd's source code. +# Remove libpthread's version number. +# Remove line numbers from stack traces. +sed \ +-e "/^drd, a data race detector\.$/d" \ +-e "/^NOTE: This is an Experimental-Class Valgrind Tool.$/d" \ +-e "/^Copyright (C) 2006-200., and GNU GPL'd, by Bart Van Assche.$/d" \ +-e "s/in [^ ]*libpthread-[0-9]*\.[0-9]*\.so/in libpthread-?.?.so/" \ +-e "s/in [^ ]*libpthread-[0-9]*\.[0-9]*\.[0-9]*\.so/in libpthread-?.?.so/" \ +-e "s/ (\([a-zA-Z_]*\.c\):[0-9]*)/ (\1:?)/" \ +-e "s/ (\([a-zA-Z_]*\.cpp\):[0-9]*)/ (\1:?)/" | + +# Anonymise addresses +$dir/../../tests/filter_addresses + diff --git a/exp-drd/tests/fp_race.cpp b/exp-drd/tests/fp_race.cpp new file mode 100644 index 0000000000..ad627cd717 --- /dev/null +++ b/exp-drd/tests/fp_race.cpp @@ -0,0 +1,150 @@ +/* + This file is part of drd, a data race detector. + + Copyright (C) 2006-2007 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. +*/ + +// Test data race detection between floating point variables. + +#include +#include // printf() +#include +#include // usleep() +#include "../drd_clientreq.h" + + +// Local functions declarations. + +static void* thread_func(void*); + +// Local variables. + +// s_mutex protects s_d3. +static pthread_mutex_t s_mutex; + +static double s_d1; // accessed before thread creation and in the created + // thread (not a race). +static double s_d2; // accessed in the created thread and after the join + // (not a race). +static double s_d3; // accessed simultaneously from both threads (race). +static bool s_debug = false; +static bool s_do_printf = false; +static bool s_use_mutex = false; + + +class CScopedLock +{ +public: + CScopedLock() + { if (s_use_mutex) pthread_mutex_lock(&s_mutex); } + ~CScopedLock() + { if (s_use_mutex) pthread_mutex_unlock(&s_mutex); } + +private: + CScopedLock(CScopedLock const&); + CScopedLock& operator=(CScopedLock const&); +}; + + +// Function definitions. + +static void set_thread_name(const char* const name) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__SET_THREAD_NAME, + name, 0, 0, 0, 0); +} + +int main(int argc, char** argv) +{ + set_thread_name("main"); + + int optchar; + while ((optchar = getopt(argc, argv, "dmp")) != EOF) + { + switch (optchar) + { + case 'd': + s_debug = true; + break; + case 'm': + s_use_mutex = true; + break; + case 'p': + s_do_printf = true; + break; + default: + assert(false); + } + } + + pthread_mutex_init(&s_mutex, 0); + + // Switch to line-buffered mode, such that timing information can be + // obtained for each printf() call with strace. + setlinebuf(stdout); + + if (s_debug) + { + printf("&s_d1 = %p; &s_d2 = %p; &s_d3 = %p\n", &s_d1, &s_d2, &s_d3); + } + + s_d1 = 1; + s_d3 = 3; + + pthread_t threadid; + pthread_create(&threadid, 0, thread_func, 0); + // Wait until the printf() in the created thread finished. + + { + CScopedLock ScopedLock; + s_d3++; + } + + // Wait until the thread finished. + //printf("Before call to pthread_join()\n"); + //fflush(stdout); + pthread_join(threadid, 0); + //printf("After call to pthread_join()\n"); + //fflush(stdout); + if (s_do_printf) printf("s_d2 = %g (should be 2)\n", s_d2); + if (s_do_printf) printf("s_d3 = %g (should be 5)\n", s_d3); + + pthread_mutex_destroy(&s_mutex); + + return 0; +} + +static void* thread_func(void*) +{ + set_thread_name("thread_func"); + + if (s_do_printf) + { + printf("s_d1 = %g (should be 1)\n", s_d1); + } + s_d2 = 2; + { + CScopedLock ScopedLock; + s_d3++; + } + return 0; +} diff --git a/exp-drd/tests/fp_race.stderr.diff b/exp-drd/tests/fp_race.stderr.diff new file mode 100644 index 0000000000..fdd6beb899 --- /dev/null +++ b/exp-drd/tests/fp_race.stderr.diff @@ -0,0 +1,10 @@ +0a1 +> exp-drd, a data race detector. +4c5 +< Allocation context: s_d3 (offset 0, size 8) in fp_race, NONE:BSS +--- +> Allocation context: unknown +12c13 +< Allocation context: s_d3 (offset 0, size 8) in fp_race, NONE:BSS +--- +> Allocation context: unknown diff --git a/exp-drd/tests/fp_race.stderr.diff2 b/exp-drd/tests/fp_race.stderr.diff2 new file mode 100644 index 0000000000..d017f41f38 --- /dev/null +++ b/exp-drd/tests/fp_race.stderr.diff2 @@ -0,0 +1,2 @@ +0a1 +> exp-drd, a data race detector. diff --git a/exp-drd/tests/fp_race.stderr.exp b/exp-drd/tests/fp_race.stderr.exp new file mode 100644 index 0000000000..d758083c8b --- /dev/null +++ b/exp-drd/tests/fp_race.stderr.exp @@ -0,0 +1,18 @@ + +Conflicting load by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: s_d3 (offset 0, size 8) in fp_race, NONE:BSS +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +Conflicting store by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: s_d3 (offset 0, size 8) in fp_race, NONE:BSS +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race.stderr.exp2 b/exp-drd/tests/fp_race.stderr.exp2 new file mode 100644 index 0000000000..a05d8c9128 --- /dev/null +++ b/exp-drd/tests/fp_race.stderr.exp2 @@ -0,0 +1,18 @@ + +Conflicting load by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: unknown +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +Conflicting store by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: unknown +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race.stderr.out b/exp-drd/tests/fp_race.stderr.out new file mode 100644 index 0000000000..47b3fb624b --- /dev/null +++ b/exp-drd/tests/fp_race.stderr.out @@ -0,0 +1,19 @@ +exp-drd, a data race detector. + +Conflicting load by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: unknown +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +Conflicting store by main at 0x........ size 8 + at 0x........: main (fp_race.cpp:?) +Allocation context: unknown +Other segment start (thread_func) + (thread finished, call stack no longer available) +Other segment end (thread_func) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race.vgtest b/exp-drd/tests/fp_race.vgtest new file mode 100644 index 0000000000..3801bc476d --- /dev/null +++ b/exp-drd/tests/fp_race.vgtest @@ -0,0 +1 @@ +prog: fp_race diff --git a/exp-drd/tests/fp_race2.stderr.diff b/exp-drd/tests/fp_race2.stderr.diff new file mode 100644 index 0000000000..90646f75fc --- /dev/null +++ b/exp-drd/tests/fp_race2.stderr.diff @@ -0,0 +1,22 @@ +0a1 +> exp-drd, a data race detector. +1a3,17 +> Conflicting load by main at 0x........ size 8 +> at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/tests/fp_race) +> by 0x........: main (fp_race.cpp:?) +> Allocation context: unknown +> Other segment start (thread_func) +> at 0x........: clone (in /...libc...) +> by 0x........: (within libpthread-?.?.so) +> by 0x........: ??? +> Other segment end (thread_func) +> at 0x........: (within libpthread-?.?.so) +> by 0x........: pthread_mutex_lock (drd_preloaded.c:?) +> by 0x........: thread_func(void*) (fp_race.cpp:?) +> by 0x........: vg_thread_wrapper (drd_preloaded.c:?) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +3c19 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race2.stderr.exp b/exp-drd/tests/fp_race2.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/fp_race2.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race2.stderr.out b/exp-drd/tests/fp_race2.stderr.out new file mode 100644 index 0000000000..4f3da795b3 --- /dev/null +++ b/exp-drd/tests/fp_race2.stderr.out @@ -0,0 +1,19 @@ +exp-drd, a data race detector. + +Conflicting load by main at 0x........ size 8 + at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/tests/fp_race) + by 0x........: main (fp_race.cpp:?) +Allocation context: unknown +Other segment start (thread_func) + at 0x........: clone (in /...libc...) + by 0x........: (within libpthread-?.?.so) + by 0x........: ??? +Other segment end (thread_func) + at 0x........: (within libpthread-?.?.so) + by 0x........: pthread_mutex_lock (drd_preloaded.c:?) + by 0x........: thread_func(void*) (fp_race.cpp:?) + by 0x........: vg_thread_wrapper (drd_preloaded.c:?) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/fp_race2.vgtest b/exp-drd/tests/fp_race2.vgtest new file mode 100644 index 0000000000..db57ab2508 --- /dev/null +++ b/exp-drd/tests/fp_race2.vgtest @@ -0,0 +1,2 @@ +prog: fp_race +args: -m diff --git a/exp-drd/tests/new_delete.cpp b/exp-drd/tests/new_delete.cpp new file mode 100644 index 0000000000..0f949c568c --- /dev/null +++ b/exp-drd/tests/new_delete.cpp @@ -0,0 +1,21 @@ +#include +#include + +void* thread_func(void*) +{ + delete new int; + return 0; +} + +int main(int argc, char** argv) +{ + pthread_t tid; + std::cout << "main, before pthread_create()\n" << std::flush; + pthread_create(&tid, 0, thread_func, 0); + std::cout << "main, after pthread_create()\n" << std::flush; + delete new int; + std::cout << "main, before pthread_join()\n" << std::flush; + pthread_join(tid, 0); + std::cout << "main, after pthread_join()\n" << std::flush; + return 0; +} diff --git a/exp-drd/tests/pth_broadcast.cpp b/exp-drd/tests/pth_broadcast.cpp new file mode 100644 index 0000000000..95b9976842 --- /dev/null +++ b/exp-drd/tests/pth_broadcast.cpp @@ -0,0 +1,163 @@ +// Broadcast a (POSIX threads) signal to all running threads, where the +// number of threads can be specified on the command line. This test program +// is intended not only to test the correctness of drd but also to test +// whether performance does not degrade too much when the number of threads +// increases. + + +#include +#include +#include +#include + + +// Class definitions. + +// Counting semaphore. + +class CSema +{ +public: + CSema() + : m_mutex(), m_cond(), m_count(0) + { + pthread_mutex_init(&m_mutex, 0); + pthread_cond_init(&m_cond, 0); + } + ~CSema() + { + pthread_cond_destroy(&m_cond); + pthread_mutex_destroy(&m_mutex); + } + void p(const int n) + { + pthread_mutex_lock(&m_mutex); + while (m_count < n) + pthread_cond_wait(&m_cond, &m_mutex); + m_count -= n; + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + } + void v() + { + pthread_mutex_lock(&m_mutex); + m_count++; + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); + } + +private: + CSema(CSema const&); + CSema& operator=(CSema const&); + + pthread_mutex_t m_mutex; + pthread_cond_t m_cond; + int m_count; +}; + +struct CThread +{ + CThread() + : m_thread(), m_sema() + { } + ~CThread() + { } + + pthread_t m_thread; + int m_threadnum; + CSema* m_sema; +}; + + +// Local variables. + +static bool s_debug = false; +static bool s_trace = false; +static int s_signal_count; +static pthread_mutex_t s_mutex; +static pthread_cond_t s_cond; + + +// Function definitions. + +static void thread_func(CThread* thread_info) +{ + pthread_mutex_lock(&s_mutex); + + for (int i = 0; i < s_signal_count; i++) + { + if (s_trace) + { + std::cout << "thread " << thread_info->m_threadnum + << " [" << i << "] (1)" << std::endl; + } + thread_info->m_sema->v(); + + // Wait until the main thread signals us via pthread_cond_broadcast(). + pthread_cond_wait(&s_cond, &s_mutex); + if (s_trace) + { + std::cout << "thread " << thread_info->m_threadnum + << " [" << i << "] (2)" << std::endl; + } + } + + pthread_mutex_unlock(&s_mutex); +} + +int main(int argc, char** argv) +{ + int optchar; + while ((optchar = getopt(argc, argv, "d")) != EOF) + { + switch (optchar) + { + case 'd': + s_debug = true; + break; + default: + assert(false); + break; + } + } + s_signal_count = argc > optind ? atoi(argv[optind]) : 10; + const int thread_count = argc > optind + 1 ? atoi(argv[optind + 1]) : 10; + + if (s_debug) + std::cout << "&s_cond = " << &s_cond << std::endl; + + pthread_mutex_init(&s_mutex, 0); + pthread_cond_init(&s_cond, 0); + { + CSema sema; + std::vector thread_vec(thread_count); + for (std::vector::iterator p = thread_vec.begin(); + p != thread_vec.end(); p++) + { + p->m_threadnum = std::distance(thread_vec.begin(), p); + p->m_sema = &sema; + pthread_create(&p->m_thread, 0, + (void*(*)(void*))thread_func, &*p); + } + for (int i = 0; i < s_signal_count; i++) + { + if (s_trace) + std::cout << "main [" << i << "] (1)\n"; + sema.p(thread_count); + if (s_trace) + std::cout << "main [" << i << "] (2)\n"; + pthread_mutex_lock(&s_mutex); + pthread_cond_broadcast(&s_cond); + pthread_mutex_unlock(&s_mutex); + if (s_trace) + std::cout << "main [" << i << "] (3)\n"; + } + for (int i = 0; i < thread_count; i++) + { + pthread_join(thread_vec[i].m_thread, 0); + } + } + pthread_cond_destroy(&s_cond); + pthread_mutex_destroy(&s_mutex); + return 0; +} diff --git a/exp-drd/tests/pth_broadcast.stderr.diff b/exp-drd/tests/pth_broadcast.stderr.diff new file mode 100644 index 0000000000..cbddd0cca2 --- /dev/null +++ b/exp-drd/tests/pth_broadcast.stderr.diff @@ -0,0 +1,105 @@ +0a1 +> exp-drd, a data race detector. +1a3,12 +> Thread 9: +> Conflicting load by thread 9 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +3c14,102 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> Thread 10: +> Conflicting load by thread 10 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 11: +> Conflicting load by thread 11 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 2: +> Conflicting load by thread 2 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 3: +> Conflicting load by thread 3 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 5: +> Conflicting load by thread 5 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 4: +> Conflicting load by thread 4 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 6: +> Conflicting load by thread 6 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> Thread 7: +> Conflicting load by thread 7 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 8) +> (thread finished, call stack no longer available) +> Other segment end (thread 8) +> (thread finished, call stack no longer available) +> +> ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_broadcast.stderr.exp b/exp-drd/tests/pth_broadcast.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/pth_broadcast.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_broadcast.stderr.out b/exp-drd/tests/pth_broadcast.stderr.out new file mode 100644 index 0000000000..e0f76b2228 --- /dev/null +++ b/exp-drd/tests/pth_broadcast.stderr.out @@ -0,0 +1,102 @@ +exp-drd, a data race detector. + +Thread 9: +Conflicting load by thread 9 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 10: +Conflicting load by thread 10 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 11: +Conflicting load by thread 11 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 2: +Conflicting load by thread 2 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 3: +Conflicting load by thread 3 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 5: +Conflicting load by thread 5 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 4: +Conflicting load by thread 4 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 6: +Conflicting load by thread 6 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +Thread 7: +Conflicting load by thread 7 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 8) + (thread finished, call stack no longer available) +Other segment end (thread 8) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_broadcast.vgtest b/exp-drd/tests/pth_broadcast.vgtest new file mode 100644 index 0000000000..9e26962894 --- /dev/null +++ b/exp-drd/tests/pth_broadcast.vgtest @@ -0,0 +1 @@ +prog: pth_broadcast diff --git a/exp-drd/tests/pth_cond_race.cpp b/exp-drd/tests/pth_cond_race.cpp new file mode 100644 index 0000000000..5eed2096c7 --- /dev/null +++ b/exp-drd/tests/pth_cond_race.cpp @@ -0,0 +1,96 @@ +/* Unit test for drd that triggers a race on the use of a POSIX condition + variable. By Bart Van Assche. +*/ + +#include +#include // printf() +#include +#include // usleep() +#include "../drd_clientreq.h" + + +// Local functions declarations. + +static void* thread_func(void*); + + +// Local variables. + +static pthread_mutex_t s_mutex; +static pthread_cond_t s_cond; +static bool s_use_mutex = false; + + +class CScopedLock +{ +public: + CScopedLock() + { if (s_use_mutex) pthread_mutex_lock(&s_mutex); } + ~CScopedLock() + { if (s_use_mutex) pthread_mutex_unlock(&s_mutex); } + +private: + CScopedLock(CScopedLock const&); + CScopedLock& operator=(CScopedLock const&); +}; + + +// Function definitions. + +static void set_thread_name(const char* const name) +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__SET_THREAD_NAME, + "%s", name, 0, 0, 0); +} + +int main(int argc, char** argv) +{ + set_thread_name("main"); + + int optchar; + while ((optchar = getopt(argc, argv, "m")) != EOF) + { + switch (optchar) + { + case 'm': + s_use_mutex = true; + break; + default: + assert(false); + } + } + + pthread_cond_init(&s_cond, 0); + pthread_mutex_init(&s_mutex, 0); + pthread_mutex_lock(&s_mutex); + + pthread_t threadid; + pthread_create(&threadid, 0, thread_func, 0); + + pthread_cond_wait(&s_cond, &s_mutex); + pthread_mutex_unlock(&s_mutex); + + pthread_join(threadid, 0); + + pthread_mutex_destroy(&s_mutex); + pthread_cond_destroy(&s_cond); + + return 0; +} + +static void* thread_func(void*) +{ + set_thread_name("thread_func"); + + // Wait until the main thread has entered pthread_cond_wait(). + pthread_mutex_lock(&s_mutex); + pthread_mutex_unlock(&s_mutex); + + // Signal the condition variable. + if (s_use_mutex) pthread_mutex_lock(&s_mutex); + pthread_cond_signal(&s_cond); + if (s_use_mutex) pthread_mutex_unlock(&s_mutex); + + return 0; +} diff --git a/exp-drd/tests/pth_cond_race.stderr.diff b/exp-drd/tests/pth_cond_race.stderr.diff new file mode 100644 index 0000000000..d017f41f38 --- /dev/null +++ b/exp-drd/tests/pth_cond_race.stderr.diff @@ -0,0 +1,2 @@ +0a1 +> exp-drd, a data race detector. diff --git a/exp-drd/tests/pth_cond_race.stderr.exp b/exp-drd/tests/pth_cond_race.stderr.exp new file mode 100644 index 0000000000..489ab983c4 --- /dev/null +++ b/exp-drd/tests/pth_cond_race.stderr.exp @@ -0,0 +1,10 @@ + +Thread 2: +Race condition: condition variable 0x........ has been signalled but the associated mutex 0x........ is not locked by the signalling thread + at 0x........: pthread_cond_signal@* (drd_preloaded.c:?) + by 0x........: thread_func(void*) (pth_cond_race.cpp:?) + by 0x........: vg_thread_wrapper (drd_preloaded.c:?) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_cond_race.stderr.out b/exp-drd/tests/pth_cond_race.stderr.out new file mode 100644 index 0000000000..ff2547ca76 --- /dev/null +++ b/exp-drd/tests/pth_cond_race.stderr.out @@ -0,0 +1,11 @@ +exp-drd, a data race detector. + +Thread 2: +Race condition: condition variable 0x........ has been signalled but the associated mutex 0x........ is not locked by the signalling thread + at 0x........: pthread_cond_signal@* (drd_preloaded.c:?) + by 0x........: thread_func(void*) (pth_cond_race.cpp:?) + by 0x........: vg_thread_wrapper (drd_preloaded.c:?) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_cond_race.vgtest b/exp-drd/tests/pth_cond_race.vgtest new file mode 100644 index 0000000000..87c66826e1 --- /dev/null +++ b/exp-drd/tests/pth_cond_race.vgtest @@ -0,0 +1 @@ +prog: pth_cond_race diff --git a/exp-drd/tests/pth_cond_race2.stderr.diff b/exp-drd/tests/pth_cond_race2.stderr.diff new file mode 100644 index 0000000000..d017f41f38 --- /dev/null +++ b/exp-drd/tests/pth_cond_race2.stderr.diff @@ -0,0 +1,2 @@ +0a1 +> exp-drd, a data race detector. diff --git a/exp-drd/tests/pth_cond_race2.stderr.exp b/exp-drd/tests/pth_cond_race2.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/pth_cond_race2.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_cond_race2.stderr.out b/exp-drd/tests/pth_cond_race2.stderr.out new file mode 100644 index 0000000000..e321eb3117 --- /dev/null +++ b/exp-drd/tests/pth_cond_race2.stderr.out @@ -0,0 +1,4 @@ +exp-drd, a data race detector. + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_cond_race2.vgtest b/exp-drd/tests/pth_cond_race2.vgtest new file mode 100644 index 0000000000..a3c557bcde --- /dev/null +++ b/exp-drd/tests/pth_cond_race2.vgtest @@ -0,0 +1,2 @@ +prog: pth_cond_race +args: -m diff --git a/exp-drd/tests/pth_create_chain.cpp b/exp-drd/tests/pth_create_chain.cpp new file mode 100644 index 0000000000..ab3dc84959 --- /dev/null +++ b/exp-drd/tests/pth_create_chain.cpp @@ -0,0 +1,53 @@ +// Create threads in such a way that there is a realistic chance that the +// parent thread finishes before the created thread finishes. + +#include +#include +#include +#include + +static pthread_t s_thread[1000]; +static int s_arg[1000]; + +static void* thread_func(void* p) +{ + int thread_count = *reinterpret_cast(p); + if (thread_count > 0) + { + thread_count--; + // std::cout << "create " << thread_count << std::endl; + s_arg[thread_count] = thread_count; + pthread_create(&s_thread[thread_count], 0, thread_func, + &s_arg[thread_count]); +#if 0 + std::cout << "created " << thread_count << "(" << s_thread[thread_count] + << ")" << std::endl; +#endif + } + return 0; +} + +int main(int argc, char** argv) +{ + unsigned thread_count = argc > 1 ? atoi(argv[1]) : 50; + assert(thread_count <= sizeof(s_thread) / sizeof(s_thread[0])); + assert(thread_count >= 1); + thread_count--; + // std::cout << "create " << thread_count << std::endl; + pthread_create(&s_thread[thread_count], 0, thread_func, + const_cast(&thread_count)); +#if 0 + std::cout << "created " << thread_count << "(" << s_thread[thread_count] + << ")" << std::endl; +#endif + for (int i = thread_count; i >= 0; i--) + { + // std::cout << "join " << i << "(" << s_thread[i] << ")" << std::endl; + pthread_join(s_thread[i], 0); + } + return 0; +} + +// Local variables: +// compile-command: "g++ -o pthread_create-chain -g -Wall -Wextra -Werror -Wno-sign-compare -Wno-unused pthread_create-chain.cpp -lpthread" +// End: diff --git a/exp-drd/tests/pth_create_chain.stderr.diff b/exp-drd/tests/pth_create_chain.stderr.diff new file mode 100644 index 0000000000..7813bb4b6d --- /dev/null +++ b/exp-drd/tests/pth_create_chain.stderr.diff @@ -0,0 +1,62 @@ +0a1 +> exp-drd, a data race detector. +1a3,12 +> Thread 2: +> Conflicting load by thread 2 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 3) +> (thread finished, call stack no longer available) +> Other segment end (thread 3) +> (thread finished, call stack no longer available) +3c14,59 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> Thread 4: +> Conflicting load by thread 4 at 0x........ size 8 +> at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/vgpreload_exp-drd-amd64-linux.so) +> by 0x........: thread_func(void*) (pth_create_chain.cpp:?) +> by 0x........: vg_thread_wrapper (drd_preloaded.c:?) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /home/sewardj/VgTRUNK/trunk/exp-drd/vgpreload_exp-drd-amd64-linux.so, NONE:Text +> Other segment start (thread 2) +> (thread finished, call stack no longer available) +> Other segment end (thread 2) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread 4 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 3) +> (thread finished, call stack no longer available) +> Other segment end (thread 3) +> (thread finished, call stack no longer available) +> +> Thread 5: +> Conflicting load by thread 5 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 3) +> (thread finished, call stack no longer available) +> Other segment end (thread 3) +> (thread finished, call stack no longer available) +> +> Thread 2: +> Conflicting load by thread 2 at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread 3) +> (thread finished, call stack no longer available) +> Other segment end (thread 3) +> (thread finished, call stack no longer available) +> +> ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_create_chain.stderr.exp b/exp-drd/tests/pth_create_chain.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/pth_create_chain.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_create_chain.stderr.out b/exp-drd/tests/pth_create_chain.stderr.out new file mode 100644 index 0000000000..d517b718ba --- /dev/null +++ b/exp-drd/tests/pth_create_chain.stderr.out @@ -0,0 +1,59 @@ +exp-drd, a data race detector. + +Thread 2: +Conflicting load by thread 2 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 3) + (thread finished, call stack no longer available) +Other segment end (thread 3) + (thread finished, call stack no longer available) + +Thread 4: +Conflicting load by thread 4 at 0x........ size 8 + at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/vgpreload_exp-drd-amd64-linux.so) + by 0x........: thread_func(void*) (pth_create_chain.cpp:?) + by 0x........: vg_thread_wrapper (drd_preloaded.c:?) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /home/sewardj/VgTRUNK/trunk/exp-drd/vgpreload_exp-drd-amd64-linux.so, NONE:Text +Other segment start (thread 2) + (thread finished, call stack no longer available) +Other segment end (thread 2) + (thread finished, call stack no longer available) + +Conflicting load by thread 4 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 3) + (thread finished, call stack no longer available) +Other segment end (thread 3) + (thread finished, call stack no longer available) + +Thread 5: +Conflicting load by thread 5 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 3) + (thread finished, call stack no longer available) +Other segment end (thread 3) + (thread finished, call stack no longer available) + +Thread 2: +Conflicting load by thread 2 at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread 3) + (thread finished, call stack no longer available) +Other segment end (thread 3) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_create_chain.vgtest b/exp-drd/tests/pth_create_chain.vgtest new file mode 100644 index 0000000000..6cb6a4657a --- /dev/null +++ b/exp-drd/tests/pth_create_chain.vgtest @@ -0,0 +1 @@ +prog: pth_create_chain 10 diff --git a/exp-drd/tests/pth_detached.c b/exp-drd/tests/pth_detached.c new file mode 100644 index 0000000000..1d61aa11a3 --- /dev/null +++ b/exp-drd/tests/pth_detached.c @@ -0,0 +1,109 @@ +/* Test whether detached threads are handled properly. + Contributed by Bart Van Assche (bart.vanassche@gmail.com). +*/ + +#include +#include +#include +#include +#include +#include "../drd_clientreq.h" + +static int s_finished_count; +static pthread_mutex_t s_mutex; + +static void set_thread_name(const char* const fmt, const int arg) +{ + int res; + char name[32]; + snprintf(name, sizeof(name), fmt, arg); + name[sizeof(name) - 1] = 0; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__SET_THREAD_NAME, + name, 0, 0, 0, 0); +} + +void increment_finished_count() +{ + pthread_mutex_lock(&s_mutex); + s_finished_count++; + pthread_mutex_unlock(&s_mutex); +} + +int get_finished_count() +{ + int result; + pthread_mutex_lock(&s_mutex); + result = s_finished_count; + pthread_mutex_unlock(&s_mutex); + return result; +} + +static void* thread_func1(void* arg) +{ + set_thread_name("thread_func1[%d]", *(int*)arg); + write(STDOUT_FILENO, ".", 1); + increment_finished_count(); + return 0; +} + +static void* thread_func2(void* arg) +{ + set_thread_name("thread_func2[%d]", *(int*)arg); + pthread_detach(pthread_self()); + write(STDOUT_FILENO, ".", 1); + increment_finished_count(); + return 0; +} + +int main(int argc, char** argv) +{ + const int count1 = argc > 1 ? atoi(argv[1]) : 100; + const int count2 = argc > 2 ? atoi(argv[2]) : 100; + int thread_arg[count1 > count2 ? count1 : count2]; + int i; + int detachstate; + pthread_attr_t attr; + + set_thread_name("main", 0); + + for (i = 0; i < count1 || i < count2; i++) + thread_arg[i] = i; + + pthread_mutex_init(&s_mutex, 0); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + assert(pthread_attr_getdetachstate(&attr, &detachstate) == 0); + assert(detachstate == PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, 16384); + // Create count1 detached threads by setting the "detached" property via + // thread attributes. + for (i = 0; i < count1; i++) + { + pthread_t thread; + pthread_create(&thread, &attr, thread_func1, &thread_arg[i]); + } + // Create count2 detached threads by letting the threads detach themselves. + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + assert(pthread_attr_getdetachstate(&attr, &detachstate) == 0); + assert(detachstate == PTHREAD_CREATE_JOINABLE); + for (i = 0; i < count2; i++) + { + pthread_t thread; + pthread_create(&thread, &attr, thread_func2, &thread_arg[i]); + } + pthread_attr_destroy(&attr); + + // Wait until all detached threads have written their output to stdout. + while (get_finished_count() < count1 + count2) + { + struct timespec delay = { 0, 1 * 1000 * 1000 }; + nanosleep(&delay, 0); + } + + printf("\n"); + + pthread_mutex_destroy(&s_mutex); + + return 0; +} diff --git a/exp-drd/tests/pth_detached.stderr.diff b/exp-drd/tests/pth_detached.stderr.diff new file mode 100644 index 0000000000..091d652181 --- /dev/null +++ b/exp-drd/tests/pth_detached.stderr.diff @@ -0,0 +1,17 @@ +0a1 +> exp-drd, a data race detector. +1a3,12 +> Thread 3: +> Conflicting load by thread_func2[0] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +3c14 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached.stderr.exp b/exp-drd/tests/pth_detached.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/pth_detached.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached.stderr.out b/exp-drd/tests/pth_detached.stderr.out new file mode 100644 index 0000000000..2a08c78002 --- /dev/null +++ b/exp-drd/tests/pth_detached.stderr.out @@ -0,0 +1,14 @@ +exp-drd, a data race detector. + +Thread 3: +Conflicting load by thread_func2[0] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached.stdout.exp b/exp-drd/tests/pth_detached.stdout.exp new file mode 100644 index 0000000000..f3229c5b98 --- /dev/null +++ b/exp-drd/tests/pth_detached.stdout.exp @@ -0,0 +1 @@ +.. diff --git a/exp-drd/tests/pth_detached.vgtest b/exp-drd/tests/pth_detached.vgtest new file mode 100644 index 0000000000..6a500b5a0f --- /dev/null +++ b/exp-drd/tests/pth_detached.vgtest @@ -0,0 +1,2 @@ +prog: pth_detached +args: 1 1 diff --git a/exp-drd/tests/pth_detached2.stderr.diff b/exp-drd/tests/pth_detached2.stderr.diff new file mode 100644 index 0000000000..08d4226d1f --- /dev/null +++ b/exp-drd/tests/pth_detached2.stderr.diff @@ -0,0 +1,202 @@ +0a1 +> exp-drd, a data race detector. +1a3,12 +> Thread 3: +> Conflicting load by thread_func1[1] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +3c14,199 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> Thread 2: +> Conflicting load by thread_func1[2] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[3] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[4] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[5] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[6] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[7] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func1[8] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Thread 3: +> Conflicting load by thread_func1[9] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Thread 2: +> Conflicting load by thread_func2[0] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Thread 3: +> Conflicting load by thread_func2[1] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Thread 2: +> Conflicting load by thread_func2[2] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[3] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[4] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[5] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[6] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[7] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[8] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> Conflicting load by thread_func2[9] at 0x........ size 8 +> at 0x........: (within libpthread-?.?.so) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +> Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +> Other segment start (thread_func1[0]) +> (thread finished, call stack no longer available) +> Other segment end (thread_func1[0]) +> (thread finished, call stack no longer available) +> +> ERROR SUMMARY: 19 errors from 19 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached2.stderr.exp b/exp-drd/tests/pth_detached2.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/pth_detached2.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached2.stderr.out b/exp-drd/tests/pth_detached2.stderr.out new file mode 100644 index 0000000000..acc9590d8e --- /dev/null +++ b/exp-drd/tests/pth_detached2.stderr.out @@ -0,0 +1,199 @@ +exp-drd, a data race detector. + +Thread 3: +Conflicting load by thread_func1[1] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Thread 2: +Conflicting load by thread_func1[2] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[3] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[4] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[5] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[6] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[7] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func1[8] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Thread 3: +Conflicting load by thread_func1[9] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Thread 2: +Conflicting load by thread_func2[0] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Thread 3: +Conflicting load by thread_func2[1] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Thread 2: +Conflicting load by thread_func2[2] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[3] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[4] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[5] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[6] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[7] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[8] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +Conflicting load by thread_func2[9] at 0x........ size 8 + at 0x........: (within libpthread-?.?.so) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) +Allocation context: /lib64/libpthread-2.5.so, libpthread.so.0:Text +Other segment start (thread_func1[0]) + (thread finished, call stack no longer available) +Other segment end (thread_func1[0]) + (thread finished, call stack no longer available) + +ERROR SUMMARY: 19 errors from 19 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/pth_detached2.stdout.exp b/exp-drd/tests/pth_detached2.stdout.exp new file mode 100644 index 0000000000..01bfb0256c --- /dev/null +++ b/exp-drd/tests/pth_detached2.stdout.exp @@ -0,0 +1 @@ +.................... diff --git a/exp-drd/tests/pth_detached2.vgtest b/exp-drd/tests/pth_detached2.vgtest new file mode 100644 index 0000000000..f6c4da2302 --- /dev/null +++ b/exp-drd/tests/pth_detached2.vgtest @@ -0,0 +1,2 @@ +prog: pth_detached +args: 10 10 diff --git a/exp-drd/tests/sigalrm.cpp b/exp-drd/tests/sigalrm.cpp new file mode 100644 index 0000000000..eb119ecbd9 --- /dev/null +++ b/exp-drd/tests/sigalrm.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../drd_clientreq.h" +#include + + +#define VALGRIND_START_NEW_SEGMENT \ +{ \ + int res; \ + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__DRD_START_NEW_SEGMENT, \ + pthread_self(), 0, 0,0,0); \ +} + + +static bool s_debug = false; + + +static int getktid() +{ + return syscall(__NR_gettid); +} + +static int getvgtid() +{ + int res; + VALGRIND_DO_CLIENT_REQUEST(res, 0, VG_USERREQ__GET_THREAD_SELF, 0, 0, 0,0,0); + return res; +} + +static void SignalHandler(const int iSignal) +{ + if (s_debug) + { + char msg[256]; + snprintf(msg, sizeof(msg), "Signal %d was delivered to kernel thread ID %d" + " / Valgrind thread ID %d\n", + iSignal, getktid(), getvgtid()); + write(STDOUT_FILENO, msg, strlen(msg)); + } +} + +void* thread_func(void*) +{ + if (s_debug) + { + std::cout << "thread: kernel thread ID " << getktid() + << " / Valgrind thread ID " << getvgtid() << "\n"; + } + + const timespec tsDelay = { 10, 0 }; + timespec tsRemain; + clock_nanosleep(CLOCK_MONOTONIC, 0, &tsDelay, &tsRemain); + //assert(result < 0 && errno == EINTR); + + return 0; +} + +int main(int argc, char** ) +{ + // Primitive argument parsing. + if (argc > 1) + s_debug = true; + + const int vgthreadid = getvgtid(); + + if (s_debug) + { + std::cout << "main: kernel thread ID " << getktid() + << " / Valgrind thread ID " << vgthreadid << std::endl; + } + + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &SignalHandler; + sigemptyset(&sa.sa_mask); + sigaction(SIGALRM, &sa, 0); + } + + pthread_t threadid; + pthread_create(&threadid, 0, thread_func, 0); + // Wait until the thread is inside clock_nanosleep(). + const timespec tsDelay = { 0, 20 * 1000 * 1000 }; + clock_nanosleep(CLOCK_MONOTONIC, 0, &tsDelay, 0); + // And send SIGALRM to the thread. + pthread_kill(threadid, SIGALRM); + pthread_join(threadid, 0); + + return 0; +} diff --git a/exp-drd/tests/sigalrm.stderr.diff b/exp-drd/tests/sigalrm.stderr.diff new file mode 100644 index 0000000000..62803df64e --- /dev/null +++ b/exp-drd/tests/sigalrm.stderr.diff @@ -0,0 +1,36 @@ +0a1 +> exp-drd, a data race detector. +1a3,16 +> Conflicting load by thread 1 at 0x........ size 8 +> at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/tests/sigalrm) +> by 0x........: main (sigalrm.cpp:?) +> Allocation context: unknown +> Other segment start (thread 2) +> at 0x........: clone (in /...libc...) +> by 0x........: (within libpthread-?.?.so) +> by 0x........: ??? +> Other segment end (thread 2) +> at 0x........: clock_nanosleep (in /lib64/librt-2.5.so) +> by 0x........: thread_func(void*) (sigalrm.cpp:?) +> by 0x........: vg_thread_wrapper (drd_preloaded.c:?) +> by 0x........: start_thread (in libpthread-?.?.so) +> by 0x........: clone (in /...libc...) +3c18,33 +< ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +--- +> Thread 2: +> Conflicting load by thread 2 at 0x........ size 8 +> at 0x........: SignalHandler(int) (sigalrm.cpp:?) +> Allocation context: stack of thread 2, offset -6320 +> Other segment start (thread 1) +> at 0x........: clone (in /...libc...) +> by 0x........: do_clone (in libpthread-?.?.so) +> by 0x........: pthread_create@@GLIBC_2.2.5 (in libpthread-?.?.so) +> by 0x........: pthread_create@* (drd_preloaded.c:?) +> by 0x........: main (sigalrm.cpp:?) +> Other segment end (thread 1) +> at 0x........: pthread_join (in libpthread-?.?.so) +> by 0x........: pthread_join (drd_preloaded.c:?) +> by 0x........: main (sigalrm.cpp:?) +> +> ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/sigalrm.stderr.exp b/exp-drd/tests/sigalrm.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/exp-drd/tests/sigalrm.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/sigalrm.stderr.out b/exp-drd/tests/sigalrm.stderr.out new file mode 100644 index 0000000000..3697b78e39 --- /dev/null +++ b/exp-drd/tests/sigalrm.stderr.out @@ -0,0 +1,33 @@ +exp-drd, a data race detector. + +Conflicting load by thread 1 at 0x........ size 8 + at 0x........: (within /home/sewardj/VgTRUNK/trunk/exp-drd/tests/sigalrm) + by 0x........: main (sigalrm.cpp:?) +Allocation context: unknown +Other segment start (thread 2) + at 0x........: clone (in /...libc...) + by 0x........: (within libpthread-?.?.so) + by 0x........: ??? +Other segment end (thread 2) + at 0x........: clock_nanosleep (in /lib64/librt-2.5.so) + by 0x........: thread_func(void*) (sigalrm.cpp:?) + by 0x........: vg_thread_wrapper (drd_preloaded.c:?) + by 0x........: start_thread (in libpthread-?.?.so) + by 0x........: clone (in /...libc...) + +Thread 2: +Conflicting load by thread 2 at 0x........ size 8 + at 0x........: SignalHandler(int) (sigalrm.cpp:?) +Allocation context: stack of thread 2, offset -6320 +Other segment start (thread 1) + at 0x........: clone (in /...libc...) + by 0x........: do_clone (in libpthread-?.?.so) + by 0x........: pthread_create@@GLIBC_2.2.5 (in libpthread-?.?.so) + by 0x........: pthread_create@* (drd_preloaded.c:?) + by 0x........: main (sigalrm.cpp:?) +Other segment end (thread 1) + at 0x........: pthread_join (in libpthread-?.?.so) + by 0x........: pthread_join (drd_preloaded.c:?) + by 0x........: main (sigalrm.cpp:?) + +ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) diff --git a/exp-drd/tests/sigalrm.vgtest b/exp-drd/tests/sigalrm.vgtest new file mode 100644 index 0000000000..88d359d019 --- /dev/null +++ b/exp-drd/tests/sigalrm.vgtest @@ -0,0 +1 @@ +prog: sigalrm diff --git a/exp-drd/tests/std-string.cpp b/exp-drd/tests/std-string.cpp new file mode 100644 index 0000000000..1d4d0d6656 --- /dev/null +++ b/exp-drd/tests/std-string.cpp @@ -0,0 +1,45 @@ +// Note: the code below is not yet sufficient for reproducing the race on +// basic_string<>::_Rep_base::_M_refcount + + +#include +#include +#include +#include + + +static std::string s_string; + +static void* thread_func(void*) +{ + std::cout << "thread: string = " << s_string << std::endl; + return 0; +} + +int main(int argc, char** argv) +{ + const bool detached = argc <= 1; + + s_string = "(allocated by main thread)"; + + pthread_t tid; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, + detached + ? PTHREAD_CREATE_DETACHED + : PTHREAD_CREATE_JOINABLE); + pthread_create(&tid, &attr, thread_func, 0); + pthread_attr_destroy(&attr); + + std::cout << std::flush; + + if (detached) + sleep(1); + else + pthread_join(tid, 0); + + std::cout << std::flush; + + return 0; +} diff --git a/glibc-2.X-drd.supp b/glibc-2.X-drd.supp new file mode 100644 index 0000000000..f594b6bd48 --- /dev/null +++ b/glibc-2.X-drd.supp @@ -0,0 +1,231 @@ +{ + dl + exp-drd:ConflictingAccess + fun:_dl_lookup_symbol_x + fun:_dl_fixup + fun:_dl_runtime_resolve +} +{ + dl + exp-drd:ConflictingAccess + fun:do_lookup_x + fun:_dl_lookup_symbol_x + fun:_dl_fixup + fun:_dl_runtime_resolve +} +{ + dl + exp-drd:ConflictingAccess + fun:_dl_fini + fun:exit +} +{ + dl + exp-drd:ConflictingAccess + obj:/lib64/ld-2.6.1.so + fun:exit +} +{ + dl-2.6.1 + exp-drd:ConflictingAccess + obj:/lib64/ld-2.6.1.so + obj:/lib64/ld-2.6.1.so + obj:/lib64/ld-2.6.1.so +} +{ + libc + exp-drd:ConflictingAccess + fun:__libc_enable_asynccancel + obj:/lib/libc-* +} +{ + libc + exp-drd:ConflictingAccess + fun:__libc_disable_asynccancel + obj:/lib/libc-* +} +{ + librt + exp-drd:ConflictingAccess + fun:__librt_enable_asynccancel +} +{ + librt + exp-drd:ConflictingAccess + fun:__librt_disable_asynccancel +} +{ + libstdc++ + exp-drd:ConflictingAccess + fun:_ZN9__gnu_cxx12__atomic_addEPVii +} +{ + libstdc++ std::string::string() + exp-drd:ConflictingAccess + fun:_ZNSsC1ERKSs +} +{ + libstdc++ + exp-drd:ConflictingAccess + fun:_ZN9__gnu_cxx18__exchange_and_addEPVii +} +{ + pthread + exp-drd:ConflictingAccess + fun:start_thread + fun:clone +} +{ + pthread + exp-drd:ConflictingAccess + fun:__nptl_deallocate_tsd + fun:start_thread + fun:clone +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:clone + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:do_clone + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:memset + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:memset + fun:_dl_allocate_tls_init + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:mempcpy + fun:pthread_create@@GLIBC_* + fun:pthread_create@* +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_join + fun:pthread_join +} +{ + pthread + exp-drd:ConflictingAccess + fun:__free_tcb +} +{ + pthread + exp-drd:ConflictingAccess + fun:__deallocate_stack + fun:__free_tcb + fun:start_thread + fun:clone +} +{ + pthread stack_cache_lock + exp-drd:ConflictingAccess + fun:__deallocate_stack + fun:start_thread + fun:clone +} +{ + pthread + exp-drd:ConflictingAccess + fun:__pthread_enable_asynccancel +} +{ + pthread + exp-drd:ConflictingAccess + fun:__pthread_disable_asynccancel +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_cancel +} +{ + pthread + exp-drd:ConflictingAccess + fun:sigcancel_handler + obj:/lib/libpthread-* +} +{ + pthread-unwind + exp-drd:ConflictingAccess + fun:_Unwind_ForcedUnwind + fun:__pthread_unwind + fun:sigcancel_handler + obj:/lib/libpthread-* +} +{ + pthread-unwind + exp-drd:ConflictingAccess + fun:_Unwind_ForcedUnwind + fun:__pthread_unwind +} +{ + pthread-unwind + exp-drd:ConflictingAccess + fun:_Unwind_GetCFA + fun:unwind_stop +} +{ + pthread-unwind + exp-drd:ConflictingAccess + fun:uw_update_context + fun:_Unwind_ForcedUnwind_Phase2 +} +{ + pthread-unwind + exp-drd:ConflictingAccess + fun:uw_frame_state_for + fun:_Unwind_ForcedUnwind_Phase2 +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_detach + fun:pthread_detach +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_getspecific +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_setspecific +} +{ + pthread + exp-drd:ConflictingAccess + fun:pthread_key_delete +} +{ + pthread + exp-drd:ConflictingAccess + fun:_pthread_cleanup_push_defer +}