From: Julian Seward Date: Fri, 14 Aug 2009 11:11:12 +0000 (+0000) Subject: Initial tests for "Skeletal support for TSan-compatible annotations" X-Git-Tag: svn/VALGRIND_3_5_0~39 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ef38e4a3de107c9f9991e9b0ebe451c25689d38a;p=thirdparty%2Fvalgrind.git Initial tests for "Skeletal support for TSan-compatible annotations" (r10810). The rwlock test is kludged and needs de-kludging. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10811 --- diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am index 602ea3213a..61b2ef3ff0 100644 --- a/helgrind/tests/Makefile.am +++ b/helgrind/tests/Makefile.am @@ -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 index 0000000000..2df2982315 --- /dev/null +++ b/helgrind/tests/annotate_hbefore.c @@ -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 +#include +#include + +#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 index 0000000000..e69de29bb2 diff --git a/helgrind/tests/annotate_hbefore.stdout.exp b/helgrind/tests/annotate_hbefore.stdout.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/helgrind/tests/annotate_hbefore.vgtest b/helgrind/tests/annotate_hbefore.vgtest new file mode 100644 index 0000000000..1e37939527 --- /dev/null +++ b/helgrind/tests/annotate_hbefore.vgtest @@ -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 index 0000000000..8842b3c88d --- /dev/null +++ b/helgrind/tests/annotate_rwlock.c @@ -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 +#include +#include +#include /* 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 index 0000000000..e8c6856960 --- /dev/null +++ b/helgrind/tests/annotate_rwlock.stderr.exp @@ -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 index 0000000000..e69de29bb2 diff --git a/helgrind/tests/annotate_rwlock.vgtest b/helgrind/tests/annotate_rwlock.vgtest new file mode 100644 index 0000000000..16f066ee8b --- /dev/null +++ b/helgrind/tests/annotate_rwlock.vgtest @@ -0,0 +1,3 @@ +prereq: test -e annotate_rwlock +vgopts: -q +prog: annotate_rwlock