]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add pthread spinlock support.
authorJulian Seward <jseward@acm.org>
Tue, 11 Aug 2009 10:35:58 +0000 (10:35 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 11 Aug 2009 10:35:58 +0000 (10:35 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10775

helgrind/helgrind.h
helgrind/hg_intercepts.c
helgrind/hg_main.c
helgrind/tests/Makefile.am
helgrind/tests/pth_spinlock.stderr.exp [new file with mode: 0644]
helgrind/tests/pth_spinlock.stdout.exp [new file with mode: 0644]
helgrind/tests/pth_spinlock.vgtest [new file with mode: 0644]

index 7696e7d77d97c65139f3d7501c0a3f7cf893bdeb..f42d5c45fada4325d1b6aaa152df8ca2b6a93a34 100644 (file)
@@ -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
index 8cb9452cacee29f5ee9a0f7d7af3ddc97a2ed088..c95597c9ba9046e2588e9f26e31b0bff58bfbeb6 100644 (file)
@@ -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                               ---*/
 /*----------------------------------------------------------------*/
index e6202623f4d013f0c27562651843fc7e7df78680..e5ba43884ec63f9d1f7a79778e244c5361d63976 100644 (file)
@@ -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",
index 3b3ba66ce3bd9acb4030bbc870c0e569f55a44d8..d17685d321129966f71c481d07e59f2ed123978b 100644 (file)
@@ -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 (file)
index 0000000..5d4da86
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/helgrind/tests/pth_spinlock.vgtest b/helgrind/tests/pth_spinlock.vgtest
new file mode 100644 (file)
index 0000000..512b287
--- /dev/null
@@ -0,0 +1,3 @@
+prereq: test -e ../../drd/tests/pth_spinlock
+vgopts: -q
+prog: ../../drd/tests/pth_spinlock