Add DRD as an experimental tool. Bart Van Assche is the maintainer.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7211 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/exp-drd/Makefile.am b/exp-drd/Makefile.am
new file mode 100644
index 0000000..228ee54
--- /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 0000000..02a135c
--- /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 0000000..9d6f5f4
--- /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 0000000..21d9bb3
--- /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 0000000..248a5d2
--- /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 0000000..194d31f
--- /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 0000000..57baee0
--- /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 0000000..0323fa0
--- /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 0000000..2a3b945
--- /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 0000000..6ec58e6
--- /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 0000000..dc9ed1c
--- /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 0000000..117b308
--- /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 0000000..205f473
--- /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 0000000..8c9978e
--- /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 0000000..25b4ef1
--- /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 0000000..03d2994
--- /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 0000000..8c90288
--- /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 <assert.h>
+#include <inttypes.h> // uintptr_t
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+#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 0000000..cb4b979
--- /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 0000000..209ed16
--- /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 0000000..5fe25f7
--- /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 0000000..ab018b9
--- /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 0000000..72a92dc
--- /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 0000000..174648a
--- /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 0000000..8d56222
--- /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 0000000..92f0474
--- /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 0000000..dd2b6db
--- /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 0000000..845eadf
--- /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 0000000..6f6b867
--- /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 0000000..014424e
--- /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 0000000..9e62533
--- /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 <iostream>
+#include <fstream>
+#include <cassert>
+#include <pthread.h>
+
+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 0000000..3b56968
--- /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 0000000..73d7635
--- /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]*: <unknown>/Open AF_UNIX socket .: <unknown>/" |
+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 0000000..7e3a2db
--- /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 0000000..dc3242b
--- /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 0000000..6f87f94
--- /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 0000000..ad627cd
--- /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 <cassert>
+#include <cstdio> // printf()
+#include <pthread.h>
+#include <unistd.h> // 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 0000000..fdd6beb
--- /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 0000000..d017f41
--- /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 0000000..d758083
--- /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 0000000..a05d8c9
--- /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 0000000..47b3fb6
--- /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 0000000..3801bc4
--- /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 0000000..90646f7
--- /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 0000000..d18786f
--- /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 0000000..4f3da79
--- /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 0000000..db57ab2
--- /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 0000000..0f949c5
--- /dev/null
+++ b/exp-drd/tests/new_delete.cpp
@@ -0,0 +1,21 @@
+#include <iostream>
+#include <pthread.h>
+
+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 0000000..95b9976
--- /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 <cassert>
+#include <iostream>
+#include <vector>
+#include <pthread.h>
+
+
+// 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<CThread> thread_vec(thread_count);
+ for (std::vector<CThread>::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 0000000..cbddd0c
--- /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 0000000..d18786f
--- /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 0000000..e0f76b2
--- /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 0000000..9e26962
--- /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 0000000..5eed209
--- /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 <cassert>
+#include <cstdio> // printf()
+#include <pthread.h>
+#include <unistd.h> // 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 0000000..d017f41
--- /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 0000000..489ab98
--- /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 0000000..ff2547c
--- /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 0000000..87c6682
--- /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 0000000..d017f41
--- /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 0000000..d18786f
--- /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 0000000..e321eb3
--- /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 0000000..a3c557b
--- /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 0000000..ab3dc84
--- /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 <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <pthread.h>
+
+static pthread_t s_thread[1000];
+static int s_arg[1000];
+
+static void* thread_func(void* p)
+{
+ int thread_count = *reinterpret_cast<int*>(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<unsigned*>(&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 0000000..7813bb4
--- /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 0000000..d18786f
--- /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 0000000..d517b71
--- /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 0000000..6cb6a46
--- /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 0000000..1d61aa1
--- /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 <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#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 0000000..091d652
--- /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 0000000..d18786f
--- /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 0000000..2a08c78
--- /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 0000000..f3229c5
--- /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 0000000..6a500b5
--- /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 0000000..08d4226
--- /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 0000000..d18786f
--- /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 0000000..acc9590
--- /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 0000000..01bfb02
--- /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 0000000..f6c4da2
--- /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 0000000..eb119ec
--- /dev/null
+++ b/exp-drd/tests/sigalrm.cpp
@@ -0,0 +1,97 @@
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+#include <iostream>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+#include "../drd_clientreq.h"
+#include <asm/unistd.h>
+
+
+#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 0000000..62803df
--- /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 0000000..d18786f
--- /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 0000000..3697b78
--- /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 0000000..88d359d
--- /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 0000000..1d4d0d6
--- /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 <iostream>
+#include <pthread.h>
+#include <string>
+#include <unistd.h>
+
+
+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 0000000..f594b6b
--- /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
+}