From: Julian Seward Date: Tue, 11 Aug 2009 10:35:58 +0000 (+0000) Subject: Add pthread spinlock support. X-Git-Tag: svn/VALGRIND_3_5_0~75 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e0cf1a903c62e286c763acd17ebf315ee715db77;p=thirdparty%2Fvalgrind.git Add pthread spinlock support. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10775 --- diff --git a/helgrind/helgrind.h b/helgrind/helgrind.h index 7696e7d77d..f42d5c45fa 100644 --- a/helgrind/helgrind.h +++ b/helgrind/helgrind.h @@ -95,7 +95,13 @@ typedef _VG_USERREQ__HG_POSIX_SEM_WAIT_POST, /* sem_t* */ _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong */ _VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, /* pth_bar_t* */ - _VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE /* pth_bar_t* */ + _VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, /* pth_bar_t* */ + _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, /* pth_slk_t* */ + _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST, /* pth_slk_t* */ + _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, /* pth_slk_t* */ + _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, /* pth_slk_t* */ + _VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE /* pth_slk_t* */ + } Vg_TCheckClientRequest; /* Clean memory state. This makes Helgrind forget everything it knew diff --git a/helgrind/hg_intercepts.c b/helgrind/hg_intercepts.c index 8cb9452cac..c95597c9ba 100644 --- a/helgrind/hg_intercepts.c +++ b/helgrind/hg_intercepts.c @@ -367,11 +367,6 @@ done. No way the joiner can return before the thread is gone. pthread_mutex_lock pthread_mutex_trylock pthread_mutex_timedlock pthread_mutex_unlock - - Unhandled: pthread_spin_init pthread_spin_destroy - pthread_spin_lock - pthread_spin_trylock - pthread_spin_unlock */ //----------------------------------------------------------- @@ -1049,6 +1044,201 @@ PTH_FUNC(int, pthreadZubarrierZudestroy, // pthread_barrier_destroy #endif // defined(HAVE_PTHREAD_BARRIER_INIT) + +/*----------------------------------------------------------------*/ +/*--- pthread_spinlock_t functions ---*/ +/*----------------------------------------------------------------*/ + +#if defined(HAVE_PTHREAD_SPIN_LOCK) + +/* Handled: pthread_spin_init pthread_spin_destroy + pthread_spin_lock pthread_spin_trylock + pthread_spin_unlock + + Unhandled: +*/ + +/* This is a nasty kludge, in that glibc "knows" that initialising a + spin lock unlocks it, and pthread_spin_{init,unlock} are names for + the same function. Hence we have to have a wrapper which does both + things, without knowing which the user intended to happen. */ + +//----------------------------------------------------------- +// glibc: pthread_spin_init +// glibc: pthread_spin_unlock +// darwin: (doesn't appear to exist) +static int pthread_spin_init_or_unlock_WRK(pthread_spinlock_t* lock, + int pshared) { + int ret; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + if (TRACE_PTH_FNS) { + fprintf(stderr, "<< pthread_spin_iORu %p", lock); fflush(stderr); + } + + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, + pthread_spinlock_t*, lock); + + CALL_FN_W_WW(ret, fn, lock,pshared); + + if (ret == 0 /*success*/) { + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST, + pthread_spinlock_t*,lock); + } else { + DO_PthAPIerror( "pthread_spinlock_{init,unlock}", ret ); + } + + if (TRACE_PTH_FNS) { + fprintf(stderr, " :: spiniORu -> %d >>\n", ret); + } + return ret; +} +#if defined(VGO_linux) + PTH_FUNC(int, pthreadZuspinZuinit, // pthread_spin_init + pthread_spinlock_t* lock, int pshared) { + return pthread_spin_init_or_unlock_WRK(lock, pshared); + } + PTH_FUNC(int, pthreadZuspinZuunlock, // pthread_spin_unlock + pthread_spinlock_t* lock) { + /* this is never actually called */ + return pthread_spin_init_or_unlock_WRK(lock, 0/*pshared*/); + } +#elif defined(VGO_darwin) +#else +# error "Unsupported OS" +#endif + + +//----------------------------------------------------------- +// glibc: pthread_spin_destroy +// darwin: (doesn't appear to exist) +#if defined(VGO_linux) + +PTH_FUNC(int, pthreadZuspinZudestroy, // pthread_spin_destroy + pthread_spinlock_t* lock) +{ + int ret; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + if (TRACE_PTH_FNS) { + fprintf(stderr, "<< pthread_spin_destroy %p", lock); + fflush(stderr); + } + + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE, + pthread_spinlock_t*,lock); + + CALL_FN_W_W(ret, fn, lock); + + if (ret != 0) { + DO_PthAPIerror( "pthread_spin_destroy", ret ); + } + + if (TRACE_PTH_FNS) { + fprintf(stderr, " :: spindestroy -> %d >>\n", ret); + } + return ret; +} + +#elif defined(VGO_darwin) +#else +# error "Unsupported OS" +#endif + + +//----------------------------------------------------------- +// glibc: pthread_spin_lock +// darwin: (doesn't appear to exist) +#if defined(VGO_linux) + +PTH_FUNC(int, pthreadZuspinZulock, // pthread_spin_lock + pthread_spinlock_t* lock) +{ + int ret; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + if (TRACE_PTH_FNS) { + fprintf(stderr, "<< pthread_spinlock %p", lock); + fflush(stderr); + } + + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, + pthread_spinlock_t*,lock, long,0/*!isTryLock*/); + + CALL_FN_W_W(ret, fn, lock); + + /* There's a hole here: libpthread now knows the lock is locked, + but the tool doesn't, so some other thread could run and detect + that the lock has been acquired by someone (this thread). Does + this matter? Not sure, but I don't think so. */ + + if (ret == 0 /*success*/) { + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, + pthread_spinlock_t*,lock); + } else { + DO_PthAPIerror( "pthread_spin_lock", ret ); + } + + if (TRACE_PTH_FNS) { + fprintf(stderr, " :: spinlock -> %d >>\n", ret); + } + return ret; +} + +#elif defined(VGO_darwin) +#else +# error "Unsupported OS" +#endif + + +//----------------------------------------------------------- +// glibc: pthread_spin_trylock +// darwin: (doesn't appear to exist) +#if defined(VGO_linux) + +PTH_FUNC(int, pthreadZuspinZutrylock, // pthread_spin_trylock + pthread_spinlock_t* lock) +{ + int ret; + OrigFn fn; + VALGRIND_GET_ORIG_FN(fn); + if (TRACE_PTH_FNS) { + fprintf(stderr, "<< pthread_spin_trylock %p", lock); + fflush(stderr); + } + + DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, + pthread_spinlock_t*,lock, long,1/*isTryLock*/); + + CALL_FN_W_W(ret, fn, lock); + + /* There's a hole here: libpthread now knows the lock is locked, + but the tool doesn't, so some other thread could run and detect + that the lock has been acquired by someone (this thread). Does + this matter? Not sure, but I don't think so. */ + + if (ret == 0 /*success*/) { + DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, + pthread_spinlock_t*,lock); + } else { + if (ret != EBUSY) + DO_PthAPIerror( "pthread_spin_trylock", ret ); + } + + if (TRACE_PTH_FNS) { + fprintf(stderr, " :: spin_trylock -> %d >>\n", ret); + } + return ret; +} + +#elif defined(VGO_darwin) +#else +# error "Unsupported OS" +#endif + +#endif // defined(HAVE_PTHREAD_SPIN_LOCK) + + /*----------------------------------------------------------------*/ /*--- pthread_rwlock_t functions ---*/ /*----------------------------------------------------------------*/ diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index e6202623f4..e5ba43884e 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -2003,6 +2003,79 @@ static void evh__HG_PTHREAD_MUTEX_UNLOCK_POST ( ThreadId tid, void* mutex ) } +/* ------------------------------------------------------- */ +/* -------------- events to do with mutexes -------------- */ +/* ------------------------------------------------------- */ + +/* All a bit of a kludge. Pretend we're really dealing with ordinary + pthread_mutex_t's instead, for the most part. */ + +static void evh__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE( ThreadId tid, + void* slock ) +{ + Thread* thr; + Lock* lk; + /* In glibc's kludgey world, we're either initialising or unlocking + it. Since this is the pre-routine, if it is locked, unlock it + and take a dependence edge. Otherwise, do nothing. */ + + if (SHOW_EVENTS >= 1) + VG_(printf)("evh__hg_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE" + "(ctid=%d, slock=%p)\n", + (Int)tid, (void*)slock ); + + thr = map_threads_maybe_lookup( tid ); + /* cannot fail - Thread* must already exist */; + tl_assert( HG_(is_sane_Thread)(thr) ); + + lk = map_locks_maybe_lookup( (Addr)slock ); + if (lk && lk->heldBy) { + /* it's held. So do the normal pre-unlock actions, as copied + from evh__HG_PTHREAD_MUTEX_UNLOCK_PRE. This stupidly + duplicates the map_locks_maybe_lookup. */ + evhH__pre_thread_releases_lock( thr, (Addr)slock, + False/*!isRDWR*/ ); + } +} + +static void evh__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST( ThreadId tid, + void* slock ) +{ + Lock* lk; + /* More kludgery. If the lock has never been seen before, do + actions as per evh__HG_PTHREAD_MUTEX_INIT_POST. Else do + nothing. */ + + if (SHOW_EVENTS >= 1) + VG_(printf)("evh__hg_PTHREAD_SPIN_INIT_OR_UNLOCK_POST" + "(ctid=%d, slock=%p)\n", + (Int)tid, (void*)slock ); + + lk = map_locks_maybe_lookup( (Addr)slock ); + if (!lk) { + map_locks_lookup_or_create( LK_nonRec, (Addr)slock, tid ); + } +} + +static void evh__HG_PTHREAD_SPIN_LOCK_PRE( ThreadId tid, + void* slock, Word isTryLock ) +{ + evh__HG_PTHREAD_MUTEX_LOCK_PRE( tid, slock, isTryLock ); +} + +static void evh__HG_PTHREAD_SPIN_LOCK_POST( ThreadId tid, + void* slock ) +{ + evh__HG_PTHREAD_MUTEX_LOCK_POST( tid, slock ); +} + +static void evh__HG_PTHREAD_SPIN_DESTROY_PRE( ThreadId tid, + void* slock ) +{ + evh__HG_PTHREAD_MUTEX_DESTROY_PRE( tid, slock ); +} + + /* ----------------------------------------------------- */ /* --------------- events to do with CVs --------------- */ /* ----------------------------------------------------- */ @@ -4144,6 +4217,31 @@ Bool hg_handle_client_request ( ThreadId tid, UWord* args, UWord* ret) evh__HG_PTHREAD_BARRIER_DESTROY_PRE( tid, (void*)args[1] ); break; + case _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE: + /* pth_spinlock_t* */ + evh__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE( tid, (void*)args[1] ); + break; + + case _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST: + /* pth_spinlock_t* */ + evh__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST( tid, (void*)args[1] ); + break; + + case _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE: + /* pth_spinlock_t*, Word */ + evh__HG_PTHREAD_SPIN_LOCK_PRE( tid, (void*)args[1], args[2] ); + break; + + case _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST: + /* pth_spinlock_t* */ + evh__HG_PTHREAD_SPIN_LOCK_POST( tid, (void*)args[1] ); + break; + + case _VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE: + /* pth_spinlock_t* */ + evh__HG_PTHREAD_SPIN_DESTROY_PRE( tid, (void*)args[1] ); + break; + default: /* Unhandled Helgrind client request! */ tl_assert2(0, "unhandled Helgrind client request 0x%lx", diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am index 3b3ba66ce3..d17685d321 100644 --- a/helgrind/tests/Makefile.am +++ b/helgrind/tests/Makefile.am @@ -16,6 +16,7 @@ EXTRA_DIST = \ 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 \ + pth_spinlock.vgtest pth_spinlock.stdout.exp pth_spinlock.stderr.exp \ rwlock_race.vgtest rwlock_race.stdout.exp rwlock_race.stderr.exp \ rwlock_test.vgtest rwlock_test.stdout.exp rwlock_test.stderr.exp \ tc01_simple_race.vgtest tc01_simple_race.stdout.exp \ diff --git a/helgrind/tests/pth_spinlock.stderr.exp b/helgrind/tests/pth_spinlock.stderr.exp new file mode 100644 index 0000000000..5d4da86201 --- /dev/null +++ b/helgrind/tests/pth_spinlock.stderr.exp @@ -0,0 +1,2 @@ +Start of test. +Test successful. diff --git a/helgrind/tests/pth_spinlock.stdout.exp b/helgrind/tests/pth_spinlock.stdout.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/helgrind/tests/pth_spinlock.vgtest b/helgrind/tests/pth_spinlock.vgtest new file mode 100644 index 0000000000..512b28767d --- /dev/null +++ b/helgrind/tests/pth_spinlock.vgtest @@ -0,0 +1,3 @@ +prereq: test -e ../../drd/tests/pth_spinlock +vgopts: -q +prog: ../../drd/tests/pth_spinlock