Author: sewardj
Date: 2007-10-22 00:08:26 +0100 (Mon, 22 Oct 2007)
New Revision: 7026

Log:
First cut at supporting POSIX "unnamed semaphores".

Modified:
   branches/THRCHECK/thrcheck/tc_intercepts.c
   branches/THRCHECK/thrcheck/tc_main.c
   branches/THRCHECK/thrcheck/thrcheck.h


Modified: branches/THRCHECK/thrcheck/tc_intercepts.c
===================================================================
--- branches/THRCHECK/thrcheck/tc_intercepts.c  2007-10-18 23:14:48 UTC (rev 
7025)
+++ branches/THRCHECK/thrcheck/tc_intercepts.c  2007-10-21 23:08:26 UTC (rev 
7026)
@@ -825,9 +825,149 @@
 
 
 /*----------------------------------------------------------------*/
-/*---                                                          ---*/
+/*--- POSIX semaphores                                         ---*/
 /*----------------------------------------------------------------*/
 
+#include <semaphore.h>
+
+#define TRACE_SEM_FNS 0
+
+/* Handled: 
+     int sem_init(sem_t *sem, int pshared, unsigned value);
+     int sem_destroy(sem_t *sem);
+     int sem_wait(sem_t *sem);
+     int sem_post(sem_t *sem);
+
+   Unhandled:
+     int sem_trywait(sem_t *sem);
+     int sem_timedwait(sem_t *restrict sem,
+                       const struct timespec *restrict abs_timeout);
+*/
+
+/* glibc-2.5 has sem_init@@GLIBC_2.2.5; match [EMAIL PROTECTED] */
+PTH_FUNC(int, semZuinitZAZa, sem_t* sem, int pshared, unsigned long value)
+{
+   OrigFn fn;
+   int    ret;
+   VALGRIND_GET_ORIG_FN(fn);
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, "<< sem_init(%p,%d,%lu) ", sem,pshared,value);
+      fflush(stderr);
+   }
+
+   CALL_FN_W_WWW(ret, fn, sem,pshared,value);
+
+   if (ret == 0) {
+      /* Probably overly paranoid, but still ... */
+      DO_CREQ_v_W(_VG_USERREQ__TC_POSIX_SEM_ZAPSTACK, sem_t*,sem);
+   } else {
+      DO_PthAPIerror( "sem_init", errno );
+   }
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, " sem_init -> %d >>\n", ret);
+      fflush(stderr);
+   }
+
+   return ret;
+}
+
+
+/* glibc-2.5 has sem_destroy@@GLIBC_2.2.5; match [EMAIL PROTECTED] */
+PTH_FUNC(int, semZudestroyZAZa, sem_t* sem)
+{
+   OrigFn fn;
+   int    ret;
+   VALGRIND_GET_ORIG_FN(fn);
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, "<< sem_destroy(%p) ", sem);
+      fflush(stderr);
+   }
+
+   DO_CREQ_v_W(_VG_USERREQ__TC_POSIX_SEM_ZAPSTACK, sem_t*,sem);
+
+   CALL_FN_W_W(ret, fn, sem);
+
+   if (ret != 0) {
+      DO_PthAPIerror( "sem_destroy", errno );
+   }
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, " sem_destroy -> %d >>\n", ret);
+      fflush(stderr);
+   }
+
+   return ret;
+}
+
+
+/* glibc-2.5 has sem_wait; match sem_wait */
+/* wait: decrement semaphore - acquire lockage */
+PTH_FUNC(int, semZuwait, sem_t* sem)
+{
+   OrigFn fn;
+   int    ret;
+   VALGRIND_GET_ORIG_FN(fn);
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, "<< sem_wait(%p) ", sem);
+      fflush(stderr);
+   }
+
+   CALL_FN_W_W(ret, fn, sem);
+
+   if (ret == 0) {
+      DO_CREQ_v_W(_VG_USERREQ__TC_POSIX_SEMWAIT_POST, sem_t*,sem);
+   } else {
+      DO_PthAPIerror( "sem_wait", errno );
+   }
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, " sem_wait -> %d >>\n", ret);
+      fflush(stderr);
+   }
+
+   return ret;
+}
+
+
+/* glibc-2.5 has sem_post; match sem_post */
+/* post: increment semaphore - release lockage */
+PTH_FUNC(int, semZupost, sem_t* sem)
+{
+   OrigFn fn;
+   int    ret;
+
+   VALGRIND_GET_ORIG_FN(fn);
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, "<< sem_post(%p) ", sem);
+      fflush(stderr);
+   }
+
+   DO_CREQ_v_W(_VG_USERREQ__TC_POSIX_SEMPOST_PRE, sem_t*,sem);
+
+   CALL_FN_W_W(ret, fn, sem);
+
+   if (ret != 0) {
+      DO_PthAPIerror( "sem_post", errno );
+   }
+
+   if (TRACE_SEM_FNS) {
+      fprintf(stderr, " sem_post -> %d >>\n", ret);
+      fflush(stderr);
+   }
+
+   return ret;
+}
+
+
+/*----------------------------------------------------------------*/
+/*--- Qt 4 threading functions (w/ GNU name mangling)          ---*/
+/*----------------------------------------------------------------*/
+
 /* Handled:   QMutex::lock()
               QMutex::unlock()
               QMutex::tryLock

Modified: branches/THRCHECK/thrcheck/tc_main.c
===================================================================
--- branches/THRCHECK/thrcheck/tc_main.c        2007-10-18 23:14:48 UTC (rev 
7025)
+++ branches/THRCHECK/thrcheck/tc_main.c        2007-10-21 23:08:26 UTC (rev 
7026)
@@ -113,6 +113,8 @@
 // 2 = as 1 + segments at condition variable signal/broadcast/wait too
 static Int clo_happens_before = 2;  /* default setting */
 
+/* Generate .vcg output of the happens-before graph? */
+static Bool clo_gen_vcg = False;
 
 // FIXME: when a SecMap is completely set via and address range
 // setting operation to a non-ShR/M state, clear its .mbHasShared 
@@ -293,7 +295,7 @@
       struct _Segment* other;  /* Possibly a segment from some other 
                                   thread, which happened-before me */
       /* DEBUGGING ONLY: what does 'other' arise from?  
-         c=thread creation, j=join, s=cvsignal */
+         c=thread creation, j=join, s=cvsignal, S=semaphore */
       Char other_hint;
    }
    Segment;
@@ -1698,6 +1700,7 @@
          Green  -- thread creation, link to parent
          Red    -- thread exit, link to exiting thread
          Yellow -- signal edge
+         Ping   -- semaphore-up edge
    */
    Segment* seg;
    VG_(printf)(PFX "graph: { title: \"Segments\"\n");
@@ -1708,9 +1711,16 @@
    VG_(printf)(PFX "y: 20\n");
    VG_(printf)(PFX "color: lightgrey\n");
    for (seg = admin_segments; seg; seg=seg->admin) {
+
       VG_(printf)(PFX "node: { title: \"%p\" color: lightcyan "
-                  "textcolor: darkgreen label: \"Seg %x\\nThr %x\" }\n", 
-                  seg, seg, seg->thr);
+                  "textcolor: darkgreen label: \"Seg %x\\n", 
+                  seg, seg);
+      if (seg->thr->errmsg_index == 1) {
+         VG_(printf)("ROOT_THREAD\" }\n");
+      } else {
+         VG_(printf)("Thr# %d\" }\n", seg->thr->errmsg_index);
+      }
+
       if (seg->prev)
          VG_(printf)(PFX "edge: { sourcename: \"%p\" targetname: \"%p\""
                      "color: black }\n", seg->prev, seg );
@@ -1720,6 +1730,7 @@
             case 'c': colour = "darkgreen";  break; /* creation */
             case 'j': colour = "red";        break; /* join (exit) */
             case 's': colour = "orange";     break; /* signal */
+            case 'S': colour = "pink";       break; /* signal */
             case 'u': colour = "cyan";       break; /* unlock */
             default: tl_assert(0);
          }
@@ -5552,6 +5563,183 @@
 }
 
 
+/* --------------- events to do with semaphores --------------- */
+
+/* This is similar but not identical the handling for condition
+   variables. */
+
+/* For each semaphore, we maintain a stack of Segments.  When a 'post'
+   operation is done on a semaphore (unlocking, essentially), a new
+   segment is created for the posting thread, and the old segment is
+   pushed on the semaphore's stack.
+
+   Later, when a (probably different) thread completes 'wait' on the
+   semaphore, we pop a Segment off the semaphore's stack (which should
+   be nonempty).  We start a new segment for the thread and make it
+   also depend on the just-popped segment.  This mechanism creates
+   dependencies between posters and waiters of the semaphore.
+
+   It may not be necessary to use a stack - perhaps a bag of Segments
+   would do.  But we do need to keep track of how many unused-up posts
+   have happened for the semaphore.
+
+   Imagine T1 and T2 both post once on a semphore S, and T3 waits
+   twice on S.  T3 cannot complete its waits without both T1 and T2
+   posting.  The above mechanism will ensure that T3 acquires
+   dependencies on both T1 and T2.
+*/
+
+/* sem_t* -> XArray* Segment* */
+static WordFM* map_sem_to_Segment_stack = NULL;
+
+static void map_sem_to_Segment_stack_INIT ( void ) {
+   if (map_sem_to_Segment_stack == NULL) {
+      map_sem_to_Segment_stack = TC_(newFM)( tc_zalloc, tc_free, NULL );
+      tl_assert(map_sem_to_Segment_stack != NULL);
+   }
+}
+
+static void push_Segment_for_sem ( void* sem, Segment* seg ) {
+   XArray* xa;
+   tl_assert(seg);
+   map_sem_to_Segment_stack_INIT();
+   if (TC_(lookupFM)( map_sem_to_Segment_stack, 
+                      NULL, (Word*)&xa, (Word)sem )) {
+      tl_assert(xa);
+      VG_(addToXA)( xa, &seg );
+   } else {
+      xa = VG_(newXA)( tc_zalloc, tc_free, sizeof(Segment*) );
+      VG_(addToXA)( xa, &seg );
+      TC_(addToFM)( map_sem_to_Segment_stack, (Word)sem, (Word)xa );
+   }
+}
+
+static Segment* mb_pop_Segment_for_sem ( void* sem ) {
+   XArray*  xa;
+   Segment* seg;
+   map_sem_to_Segment_stack_INIT();
+   if (TC_(lookupFM)( map_sem_to_Segment_stack, 
+                      NULL, (Word*)&xa, (Word)sem )) {
+      /* xa is the stack for this semaphore. */
+      Word sz = VG_(sizeXA)( xa );
+      tl_assert(sz >= 0);
+      if (sz == 0)
+         return NULL; /* odd, the stack is empty */
+      seg = *(Segment**)VG_(indexXA)( xa, sz-1 );
+      tl_assert(seg);
+      VG_(dropTailXA)( xa, 1 );
+      return seg;
+   } else {
+      /* hmm, that's odd.  No stack for this semaphore. */
+      return NULL;
+   }
+}
+
+static void evh__TC_POSIX_SEM_ZAPSTACK ( ThreadId tid, void* sem )
+{
+   Segment* seg;
+
+   /* Empty out the semaphore's segment stack.  Occurs at
+      sem_init and sem_destroy time. */
+   if (SHOW_EVENTS >= 1)
+      VG_(printf)("evh__TC_POSIX_SEM_ZAPSTACK(ctid=%d, sem=%p)\n", 
+                  (Int)tid, (void*)sem );
+
+   /* This is stupid, but at least it's easy. */
+   do {
+     seg = mb_pop_Segment_for_sem( sem );
+   } while (seg);
+
+   tl_assert(!seg);
+}
+
+static void evh__TC_POSIX_SEMPOST_PRE ( ThreadId tid, void* sem )
+{
+   /* 'tid' has posted on 'sem'.  Start a new segment for this thread,
+      and push the old segment on a stack of segments associated with
+      'sem'.  This is later used by other thread(s) which successfully
+      exit from a sem_wait on the same sem; then they know what the
+      posting segment was, so a dependency edge back to it can be
+      constructed. */
+
+   Thread*   thr;
+   SegmentID new_segid;
+   Segment*  new_seg;
+
+   if (SHOW_EVENTS >= 1)
+      VG_(printf)("evh__TC_POSIX_SEMPOST_PRE(ctid=%d, sem=%p)\n", 
+                  (Int)tid, (void*)sem );
+
+   thr = map_threads_maybe_lookup( tid );
+   tl_assert(thr); /* cannot fail - Thread* must already exist */
+
+   // error-if: sem is bogus
+
+   if (clo_happens_before >= 2) {
+      /* create a new segment ... */
+      new_segid = 0; /* bogus */
+      new_seg   = NULL;
+      evhH__start_new_segment_for_thread( &new_segid, &new_seg, thr );
+      tl_assert( is_sane_SegmentID(new_segid) );
+      tl_assert( is_sane_Segment(new_seg) );
+      tl_assert( new_seg->thr == thr );
+      tl_assert( is_sane_Segment(new_seg->prev) );
+
+      /* ... and add the binding. */
+      push_Segment_for_sem( sem, new_seg->prev );
+   }
+}
+
+static void evh__TC_POSIX_SEMWAIT_POST ( ThreadId tid, void* sem )
+{
+   /* A sem_wait(sem) completed successfully.  Start a new segment for
+      this thread.  Pop the posting-segment for the 'sem' in the
+      mapping, and add a dependency edge from the new segment back to
+      it. */
+
+   Thread*   thr;
+   SegmentID new_segid;
+   Segment*  new_seg;
+   Segment*  posting_seg;
+
+   if (SHOW_EVENTS >= 1)
+      VG_(printf)("evh__TC_POSIX_SEMWAIT_POST(ctid=%d, sem=%p)\n", 
+                  (Int)tid, (void*)sem );
+
+   thr = map_threads_maybe_lookup( tid );
+   tl_assert(thr); /* cannot fail - Thread* must already exist */
+
+   // error-if: sem is bogus
+
+   if (clo_happens_before >= 2) {
+      /* create a new segment ... */
+      new_segid = 0; /* bogus */
+      new_seg   = NULL;
+      evhH__start_new_segment_for_thread( &new_segid, &new_seg, thr );
+      tl_assert( is_sane_SegmentID(new_segid) );
+      tl_assert( is_sane_Segment(new_seg) );
+      tl_assert( new_seg->thr == thr );
+      tl_assert( is_sane_Segment(new_seg->prev) );
+      tl_assert( new_seg->other == NULL);
+
+      /* and find out which thread posted last on sem; then add a
+         dependency edge back to it. */
+      posting_seg = mb_pop_Segment_for_sem( sem );
+      if (posting_seg) {
+         tl_assert(is_sane_Segment(posting_seg));
+         new_seg->other      = posting_seg;
+         new_seg->other_hint = 'S';
+      } else {
+         /* Hmm.  How can a wait on 'sem' succeed if nobody posted to
+            it?  If this happened it would surely be a bug in the
+            threads library. */
+         record_error_Misc( thr, "Bug in libpthread: sem_wait succeeded on"
+                                 " semaphore without prior sem_post");
+      }
+   }
+}
+
+
 /*--------------------------------------------------------------*/
 /*--- Lock acquisition order monitoring                      ---*/
 /*--------------------------------------------------------------*/
@@ -6525,6 +6713,18 @@
          evh__TC_PTHREAD_RWLOCK_UNLOCK_POST( tid, (void*)args[1] );
          break;
 
+      case _VG_USERREQ__TC_POSIX_SEMPOST_PRE: /* sem_t* */
+         evh__TC_POSIX_SEMPOST_PRE( tid, (void*)args[1] );
+         break;
+
+      case _VG_USERREQ__TC_POSIX_SEMWAIT_POST: /* sem_t* */
+         evh__TC_POSIX_SEMWAIT_POST( tid, (void*)args[1] );
+         break;
+
+      case _VG_USERREQ__TC_POSIX_SEM_ZAPSTACK: /* sem_t* */
+         evh__TC_POSIX_SEM_ZAPSTACK( tid, (void*)args[1] );
+         break;
+
       default:
          /* Unhandled Thrcheck client request! */
         tl_assert2(0, "unhandled Thrcheck client request!");
@@ -7306,6 +7506,11 @@
    else if (VG_CLO_STREQ(arg, "--happens-before=condvars"))
       clo_happens_before = 2;
 
+   else if (VG_CLO_STREQ(arg, "--gen-vcg=no"))
+      clo_gen_vcg = False;
+   else if (VG_CLO_STREQ(arg, "--gen-vcg=yes"))
+      clo_gen_vcg = True;
+
    else 
       return VG_(replacement_malloc_process_cmd_line_option)(arg);
 
@@ -7324,6 +7529,8 @@
 static void tc_print_debug_usage ( void )
 {
    VG_(replacement_malloc_print_debug_usage)();
+   VG_(printf)("    --gen-vcg=no|yes          show happens-before graph "
+               "in .vcg format [no]\n");
 }
 
 static void tc_post_clo_init ( void )
@@ -7337,7 +7544,7 @@
    if (sanity_flags)
       all__sanity_check("SK_(fini)");
 
-   if (0)
+   if (clo_gen_vcg)
       segments__generate_vcg();
 
    if (VG_(clo_verbosity) >= 2) {

Modified: branches/THRCHECK/thrcheck/thrcheck.h
===================================================================
--- branches/THRCHECK/thrcheck/thrcheck.h       2007-10-18 23:14:48 UTC (rev 
7025)
+++ branches/THRCHECK/thrcheck/thrcheck.h       2007-10-21 23:08:26 UTC (rev 
7026)
@@ -87,7 +87,10 @@
       _VG_USERREQ__TC_PTHREAD_RWLOCK_LOCK_PRE,    // pth_rwlk_t*, long isW
       _VG_USERREQ__TC_PTHREAD_RWLOCK_LOCK_POST,   // pth_rwlk_t*, long isW
       _VG_USERREQ__TC_PTHREAD_RWLOCK_UNLOCK_PRE,  // pth_rwlk_t*
-      _VG_USERREQ__TC_PTHREAD_RWLOCK_UNLOCK_POST  // pth_rwlk_t*
+      _VG_USERREQ__TC_PTHREAD_RWLOCK_UNLOCK_POST, // pth_rwlk_t*
+      _VG_USERREQ__TC_POSIX_SEMPOST_PRE,          // sem_t*
+      _VG_USERREQ__TC_POSIX_SEMWAIT_POST,         // sem_t*
+      _VG_USERREQ__TC_POSIX_SEM_ZAPSTACK          // sem_t*
    } Vg_TCheckClientRequest;
 
 /* Clean memory state.  This makes Thrcheck forget everything it knew


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Valgrind-developers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/valgrind-developers

Reply via email to