]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Initial tests for "Skeletal support for TSan-compatible annotations"
authorJulian Seward <jseward@acm.org>
Fri, 14 Aug 2009 11:11:12 +0000 (11:11 +0000)
committerJulian Seward <jseward@acm.org>
Fri, 14 Aug 2009 11:11:12 +0000 (11:11 +0000)
(r10810).  The rwlock test is kludged and needs de-kludging.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10811

helgrind/tests/Makefile.am
helgrind/tests/annotate_hbefore.c [new file with mode: 0644]
helgrind/tests/annotate_hbefore.stderr.exp [new file with mode: 0644]
helgrind/tests/annotate_hbefore.stdout.exp [new file with mode: 0644]
helgrind/tests/annotate_hbefore.vgtest [new file with mode: 0644]
helgrind/tests/annotate_rwlock.c [new file with mode: 0644]
helgrind/tests/annotate_rwlock.stderr.exp [new file with mode: 0644]
helgrind/tests/annotate_rwlock.stdout.exp [new file with mode: 0644]
helgrind/tests/annotate_rwlock.vgtest [new file with mode: 0644]

index 602ea3213ab2a0a5a3d451c156449eecbd6f72f9..61b2ef3ff0126e413d405d56aa60400d813e87d9 100644 (file)
@@ -4,6 +4,10 @@ include $(top_srcdir)/Makefile.tool-tests.am
 dist_noinst_SCRIPTS = filter_stderr
 
 EXTRA_DIST = \
+       annotate_hbefore.vgtest annotate_hbefore.stdout.exp \
+               annotate_hbefore.stderr.exp \
+       annotate_rwlock.vgtest annotate_rwlock.stdout.exp \
+               annotate_rwlock.stderr.exp \
        bar_bad.vgtest bar_bad.stdout.exp bar_bad.stderr.exp \
        bar_trivial.vgtest bar_trivial.stdout.exp bar_trivial.stderr.exp \
        hg01_all_ok.vgtest hg01_all_ok.stdout.exp hg01_all_ok.stderr.exp \
@@ -12,7 +16,7 @@ EXTRA_DIST = \
        hg04_race.vgtest hg04_race.stdout.exp hg04_race.stderr.exp \
        hg05_race2.vgtest hg05_race2.stdout.exp hg05_race2.stderr.exp \
        hg06_readshared.vgtest hg06_readshared.stdout.exp \
-           hg06_readshared.stderr.exp \
+               hg06_readshared.stderr.exp \
        pth_barrier1.vgtest pth_barrier1.stdout.exp pth_barrier1.stderr.exp \
        pth_barrier2.vgtest pth_barrier2.stdout.exp pth_barrier2.stderr.exp \
        pth_barrier3.vgtest pth_barrier3.stdout.exp pth_barrier3.stderr.exp \
@@ -72,6 +76,8 @@ EXTRA_DIST = \
 # XXX: tc18_semabuse uses operations that are unsupported on Darwin.  It
 # should be conditionally compiled like tc20_verifywrap is.
 check_PROGRAMS = \
+       annotate_hbefore \
+       annotate_rwlock \
        hg01_all_ok \
        hg02_deadlock \
        hg03_inherit \
@@ -132,6 +138,10 @@ if HAVE_PTHREAD_MUTEX_TIMEDLOCK
 check_PROGRAMS += tc20_verifywrap
 endif
 
+if HAVE_BUILTIN_ATOMIC
+check_PROGRAMS += annotate_rwlock
+endif
+
 AM_CFLAGS   += $(AM_FLAG_M3264_PRI)
 AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
 
diff --git a/helgrind/tests/annotate_hbefore.c b/helgrind/tests/annotate_hbefore.c
new file mode 100644 (file)
index 0000000..2df2982
--- /dev/null
@@ -0,0 +1,245 @@
+
+/* Program which uses a happens-before edge to coordinate an access to
+   variable 'shared_var' between two threads.  The h-b edge is created
+   by a custom (kludgesome!) mechanism and hence we need to use
+   ANNOTATES_HAPPEN_{BEFORE,AFTER} to explain to Helgrind what's going
+   on (else it reports a race). */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "../../helgrind/helgrind.h"
+
+/* Todo: move all this do_acasW guff into a support library.  It's
+   useful for multiple tests, not just this one. 
+
+   XXX: all the do_acasW routines assume the supplied address
+   is UWord (naturally) aligned. */
+
+
+typedef  unsigned long int  UWord;
+
+#if defined(VGA_ppc64)
+
+// ppc64
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+  UWord old, success;
+
+  /* Fetch the old value, and set the reservation */
+  __asm__ __volatile__ (
+     "ldarx  %0, 0,%1"     "\n"  // rD,rA,rB
+      : /*out*/   "=b"(old)
+      : /*in*/    "b"(addr)
+      : /*trash*/ "memory","cc"
+   );
+
+   /* If the old value isn't as expected, we've had it */
+   if (old != expected) return 0;
+
+   /* otherwise try to stuff the new value in */
+   __asm__ __volatile__(
+      "stdcx. %2, 0,%1"   "\n"      // rS,rA,rB
+      "mfcr   %0"         "\n\t"
+      "srdi   %0,%0,29"   "\n\t"
+      "andi.  %0,%0,1"    "\n"
+      : /*out*/ "=b"(success)
+      : /*in*/ "b"(addr), "b"(nyu)
+   );
+
+   assert(success == 0 || success == 1);
+   return success;
+}
+
+#elif defined(VGA_ppc32)
+
+// ppc32
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+  UWord old, success;
+
+  /* Fetch the old value, and set the reservation */
+  __asm__ __volatile__ (
+     "lwarx  %0, 0,%1"     "\n"  // rD,rA,rB
+      : /*out*/   "=b"(old)
+      : /*in*/    "b"(addr)
+      : /*trash*/ "memory","cc"
+   );
+
+   /* If the old value isn't as expected, we've had it */
+   if (old != expected) return 0;
+
+   /* otherwise try to stuff the new value in */
+   __asm__ __volatile__(
+      "stwcx. %2, 0,%1"   "\n"      // rS,rA,rB
+      "mfcr   %0"         "\n\t"
+      "srwi   %0,%0,29"   "\n\t"
+      "andi.  %0,%0,1"    "\n"
+      : /*out*/ "=b"(success)
+      : /*in*/ "b"(addr), "b"(nyu)
+   );
+
+   assert(success == 0 || success == 1);
+   return success;
+}
+
+#elif defined(VGA_amd64)
+
+// amd64
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+   UWord block[4] = { (UWord)addr, expected, nyu, 2 };
+   __asm__ __volatile__(
+      "movq 0(%%rsi),  %%rdi"         "\n\t" // addr
+      "movq 8(%%rsi),  %%rax"         "\n\t" // expected
+      "movq 16(%%rsi), %%rbx"         "\n\t" // nyu
+      "xorq %%rcx,%%rcx"              "\n\t"
+      "lock; cmpxchgq %%rbx,(%%rdi)"  "\n\t"
+      "setz %%cl"                     "\n\t"
+      "movq %%rcx, 24(%%rsi)"         "\n"
+      : /*out*/ 
+      : /*in*/ "S"(&block[0])
+      : /*trash*/"memory","cc","rdi","rax","rbx","rcx"
+   );
+   assert(block[3] == 0 || block[3] == 1);
+   return block[3] & 1;
+}
+
+#elif defined(VGA_x86)
+
+// x86
+/* return 1 if success, 0 if failure */
+UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
+{
+   UWord block[4] = { (UWord)addr, expected, nyu, 2 };
+   __asm__ __volatile__(
+      "movl 0(%%esi),  %%edi"         "\n\t" // addr
+      "movl 4(%%esi),  %%eax"         "\n\t" // expected
+      "movl 8(%%esi),  %%ebx"         "\n\t" // nyu
+      "xorl %%ecx,%%ecx"              "\n\t"
+      "lock; cmpxchgl %%ebx,(%%edi)"  "\n\t"
+      "setz %%cl"                     "\n\t"
+      "movl %%ecx, 12(%%esi)"         "\n"
+      : /*out*/ 
+      : /*in*/ "S"(&block[0])
+      : /*trash*/"memory","cc","edi","eax","ebx","ecx"
+   );
+   assert(block[3] == 0 || block[3] == 1);
+   return block[3] & 1;
+}
+
+#endif
+
+void atomic_incW ( UWord* w )
+{
+   while (1) {
+      UWord old = *w;
+      UWord nyu = old + 1;
+      UWord ok = do_acasW( w, old, nyu );
+      if (ok) break;
+   };
+}
+
+#if 0
+
+#define NNN 1000000
+
+void* thread_fn ( void* arg )
+{
+  UWord* w = (UWord*)arg;
+  int i;
+  for (i = 0; i < NNN; i++)
+     atomic_incW( w );
+  return NULL;
+}
+
+
+int main ( void )
+{
+   int r;
+  //ANNOTATE_HAPPENS_BEFORE(0);
+  //return 0;
+   UWord w = 0;
+  pthread_t t1, t2;
+
+  r= pthread_create( &t1, NULL, &thread_fn, (void*)&w );   assert(!r);
+  r= pthread_create( &t2, NULL, &thread_fn, (void*)&w );   assert(!r);
+
+  r= pthread_join( t1, NULL );   assert(!r);
+  r= pthread_join( t2, NULL );   assert(!r);
+
+  printf("result = %lu\n", w );
+  return 0;
+}
+
+#endif
+
+int shared_var = 0;  // is not raced upon
+
+
+void delay100ms ( void )
+{
+   struct timespec ts = { 0, 100 * 1000 * 1000 };
+   nanosleep(&ts, NULL);
+}
+
+void do_wait ( UWord* w )
+{
+  UWord w0 = *w;
+  UWord volatile * wV = w;
+  while (*wV == w0)
+    ;
+  ANNOTATE_HAPPENS_AFTER(w);
+}
+
+void do_signal ( UWord* w )
+{
+  ANNOTATE_HAPPENS_BEFORE(w);
+  atomic_incW(w);
+}
+
+
+
+void* thread_fn1 ( void* arg )
+{
+  UWord* w = (UWord*)arg;
+  delay100ms();    // ensure t2 gets to its wait first
+  shared_var = 1;  // first access
+  do_signal(w);    // cause h-b edge to second thread
+
+  delay100ms();
+  return NULL;
+}
+
+void* thread_fn2 ( void* arg )
+{
+  UWord* w = (UWord*)arg;
+  do_wait(w);      // wait for h-b edge from first thread
+  shared_var = 2;  // second access
+
+  delay100ms();
+  return NULL;
+}
+
+
+
+
+
+
+int main ( void )
+{
+   int r;
+   UWord w = 0;
+   pthread_t t1, t2;
+
+   r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w );   assert(!r);
+   r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w );   assert(!r);
+
+   r= pthread_join( t1, NULL );   assert(!r);
+   r= pthread_join( t2, NULL );   assert(!r);
+   return 0;
+}
diff --git a/helgrind/tests/annotate_hbefore.stderr.exp b/helgrind/tests/annotate_hbefore.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/helgrind/tests/annotate_hbefore.stdout.exp b/helgrind/tests/annotate_hbefore.stdout.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/helgrind/tests/annotate_hbefore.vgtest b/helgrind/tests/annotate_hbefore.vgtest
new file mode 100644 (file)
index 0000000..1e37939
--- /dev/null
@@ -0,0 +1,2 @@
+vgopts: -q
+prog: annotate_hbefore
diff --git a/helgrind/tests/annotate_rwlock.c b/helgrind/tests/annotate_rwlock.c
new file mode 100644 (file)
index 0000000..8842b3c
--- /dev/null
@@ -0,0 +1,176 @@
+
+/* This program is a marginally modified copy of
+   drd/tests/annotate_rwlock.c,
+
+   which was originally written by Bart van Assche.
+
+   Unfortunately due to the need to #include helgrind.h instead of
+   drd.h, it can't be an exact copy.
+*/
+
+/**
+ * @file  annotate_rwlock.c
+ *
+ * @brief Multithreaded test program that triggers various access patterns
+ *        without triggering any race conditions using a reader-writer lock
+ *        implemented via busy-waiting. Annotations are used to tell DRD
+ *        which higher-level rwlock operations are being performed.
+ */
+
+
+#define _GNU_SOURCE 1
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>        /* usleep() */
+#include "../../config.h"
+#include "../../helgrind/helgrind.h"
+
+
+#ifndef HAVE_BUILTIN_ATOMIC
+#error Sorry, but this test program can only be compiled by a compiler that\
+has built-in functions for atomic memory access.
+#endif
+
+
+typedef struct {
+  volatile int locked;
+  int          writer_count;
+  int          reader_count;
+} rwlock_t;
+
+
+static rwlock_t s_rwlock;
+static int s_counter;
+
+
+static void rwlock_init(rwlock_t* p)
+{
+  //  DRD_IGNORE_VAR(*p);
+  p->locked       = 0;
+  p->writer_count = 0;
+  p->reader_count = 0;
+  ANNOTATE_RWLOCK_CREATE(p);
+}
+
+static void rwlock_destroy(rwlock_t* p)
+{
+  ANNOTATE_RWLOCK_DESTROY(p);
+  assert(p->locked       == 0);
+  assert(p->writer_count == 0);
+  assert(p->reader_count == 0);
+}
+
+static void rwlock_rdlock(rwlock_t* p)
+{
+  while (1)
+  {
+    while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+      ;
+    if (p->writer_count == 0)
+      break;
+#ifdef __APPLE__
+    /* Darwin doesn't have an implementation of pthread_yield(). */
+    usleep(100 * 1000);
+#else
+    pthread_yield();
+#endif
+    (void) __sync_fetch_and_sub(&p->locked, 1);
+  }
+  p->reader_count++;
+  assert(p->reader_count >= 0);
+  assert(p->writer_count >= 0);
+  assert(p->reader_count == 0 || p->writer_count == 0);
+  (void) __sync_fetch_and_sub(&p->locked, 1);
+  //ANNOTATE_READERLOCK_ACQUIRED(p);
+  ANNOTATE_RWLOCK_ACQUIRED(p, 0);
+}
+
+static void rwlock_wrlock(rwlock_t* p)
+{
+  while (1)
+  {
+    while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+      ;
+    if (p->reader_count == 0)
+      break;
+#ifdef __APPLE__
+    /* Darwin doesn't have an implementation of pthread_yield(). */
+    usleep(100 * 1000);
+#else
+    pthread_yield();
+#endif
+    (void) __sync_fetch_and_sub(&p->locked, 1);
+  }
+  p->writer_count++;
+  assert(p->reader_count >= 0);
+  assert(p->writer_count >= 0);
+  assert(p->reader_count == 0 || p->writer_count == 0);
+  (void) __sync_fetch_and_sub(&p->locked, 1);
+  //  ANNOTATE_WRITERLOCK_ACQUIRED(p);
+  ANNOTATE_RWLOCK_ACQUIRED(p, 1);
+}
+
+static void rwlock_unlock(rwlock_t* p)
+{
+  while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
+    ;
+  if (p->reader_count > 0)
+  {
+    p->reader_count--;
+    //ANNOTATE_READERLOCK_RELEASED(p);
+    ANNOTATE_RWLOCK_RELEASED(p, 0);
+  }
+  else
+  {
+    p->writer_count--;
+    //ANNOTATE_WRITERLOCK_RELEASED(p);
+    ANNOTATE_RWLOCK_RELEASED(p, 1);
+  }
+  assert(p->reader_count >= 0);
+  assert(p->writer_count >= 0);
+  assert(p->reader_count == 0 || p->writer_count == 0);
+  (void) __sync_fetch_and_sub(&p->locked, 1);
+}
+
+static void* thread_func(void* arg)
+{
+  int i;
+  int sum = 0;
+
+  for (i = 0; i < 1000; i++)
+  {
+    rwlock_rdlock(&s_rwlock);
+    sum += s_counter;
+    rwlock_unlock(&s_rwlock);
+    rwlock_wrlock(&s_rwlock);
+    s_counter++;
+    rwlock_unlock(&s_rwlock);
+  }
+
+  return 0;
+}
+
+int main(int argc, char** argv)
+{
+  const int thread_count = 10;
+  pthread_t tid[thread_count];
+  int i;
+
+  rwlock_init(&s_rwlock);
+  for (i = 0; i < thread_count; i++)
+  {
+    pthread_create(&tid[i], 0, thread_func, 0);
+  }
+
+  for (i = 0; i < thread_count; i++)
+  {
+    pthread_join(tid[i], 0);
+  }
+  rwlock_destroy(&s_rwlock);
+
+  fprintf(stderr, "Finished.\n");
+
+  return 0;
+}
diff --git a/helgrind/tests/annotate_rwlock.stderr.exp b/helgrind/tests/annotate_rwlock.stderr.exp
new file mode 100644 (file)
index 0000000..e8c6856
--- /dev/null
@@ -0,0 +1,77 @@
+Thread #x was created
+   ...
+   by 0x........: pthread_create@* (hg_intercepts.c:...)
+   by 0x........: main (annotate_rwlock.c:164)
+
+Thread #x was created
+   ...
+   by 0x........: pthread_create@* (hg_intercepts.c:...)
+   by 0x........: main (annotate_rwlock.c:164)
+
+Possible data race during read of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_rdlock (annotate_rwlock.c:71)
+   by 0x........: thread_func (annotate_rwlock.c:144)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous write of size 4 by thread #x
+   at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
+   by 0x........: thread_func (annotate_rwlock.c:147)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during read of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+   by 0x........: thread_func (annotate_rwlock.c:144)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous write of size 4 by thread #x
+   at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+   by 0x........: thread_func (annotate_rwlock.c:144)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_rdlock (annotate_rwlock.c:81)
+   by 0x........: thread_func (annotate_rwlock.c:144)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous read of size 4 by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:131)
+   by 0x........: thread_func (annotate_rwlock.c:149)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:121)
+   by 0x........: thread_func (annotate_rwlock.c:146)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous read of size 4 by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:131)
+   by 0x........: thread_func (annotate_rwlock.c:149)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_wrlock (annotate_rwlock.c:106)
+   by 0x........: thread_func (annotate_rwlock.c:147)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous read of size 4 by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:132)
+   by 0x........: thread_func (annotate_rwlock.c:149)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:127)
+   by 0x........: thread_func (annotate_rwlock.c:149)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+ This conflicts with a previous read of size 4 by thread #x
+   at 0x........: rwlock_unlock (annotate_rwlock.c:132)
+   by 0x........: thread_func (annotate_rwlock.c:149)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Finished.
diff --git a/helgrind/tests/annotate_rwlock.stdout.exp b/helgrind/tests/annotate_rwlock.stdout.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/helgrind/tests/annotate_rwlock.vgtest b/helgrind/tests/annotate_rwlock.vgtest
new file mode 100644 (file)
index 0000000..16f066e
--- /dev/null
@@ -0,0 +1,3 @@
+prereq: test -e annotate_rwlock
+vgopts: -q
+prog: annotate_rwlock