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
+}