drd: Add semaphore annotations (#333072)

This is a slightly modified version of a patch from Ivo Raisr <ivosh@ivosh.net>


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13984 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/drd/drd.h b/drd/drd.h
index fa511e0..f82c2b2 100644
--- a/drd/drd.h
+++ b/drd/drd.h
@@ -271,6 +271,31 @@
  */
 #define ANNOTATE_WRITERLOCK_RELEASED(rwlock) ANNOTATE_RWLOCK_RELEASED(rwlock, 1)
 
+/** Tell DRD that a semaphore object is going to be initialized. */
+#define ANNOTATE_SEM_INIT_PRE(sem, value)                                 \
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE, \
+                                   sem, value, 0, 0, 0);
+
+/** Tell DRD that a semaphore object has been destroyed. */
+#define ANNOTATE_SEM_DESTROY_POST(sem)                                        \
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST, \
+                                   sem, 0, 0, 0, 0);
+
+/** Tell DRD that a semaphore is going to be acquired. */
+#define ANNOTATE_SEM_WAIT_PRE(sem)                                        \
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE, \
+                                   sem, 0, 0, 0, 0)
+
+/** Tell DRD that a semaphore has been acquired. */
+#define ANNOTATE_SEM_WAIT_POST(sem)                                        \
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST, \
+                                   sem, 0, 0, 0, 0)
+
+/** Tell DRD that a semaphore is going to be released. */
+#define ANNOTATE_SEM_POST_PRE(sem)                                        \
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE, \
+                                   sem, 0, 0, 0, 0)
+
 /*
  * Report that a barrier has been initialized with a given barrier count.  The
  * third argument specifies whether or not reinitialization is allowed, that
@@ -442,6 +467,27 @@
    VG_USERREQ__DRD_ANNOTATION_UNIMP,
    /* args: char*. */
 
+   /* Tell DRD that a user-defined semaphore synchronization object
+    * is about to be created. */
+   VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE,
+   /* args: Addr, UInt value. */
+   /* Tell DRD that a user-defined semaphore synchronization object
+    * has been destroyed. */
+   VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST,
+   /* args: Addr. */
+   /* Tell DRD that a user-defined semaphore synchronization
+    * object is going to be acquired (semaphore wait). */
+   VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE,
+   /* args: Addr. */
+   /* Tell DRD that a user-defined semaphore synchronization
+    * object has been acquired (semaphore wait). */
+   VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST,
+   /* args: Addr. */
+   /* Tell DRD that a user-defined semaphore synchronization
+    * object is about to be released (semaphore post). */
+   VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE,
+   /* args: Addr. */
+
    /* Tell DRD that a user-defined reader-writer synchronization object
     * has been created. */
    VG_USERREQ__DRD_ANNOTATE_RWLOCK_CREATE
diff --git a/drd/drd_clientreq.c b/drd/drd_clientreq.c
index 0c97f41..92ac92a 100644
--- a/drd/drd_clientreq.c
+++ b/drd/drd_clientreq.c
@@ -225,6 +225,26 @@
       DRD_(rwlock_pre_unlock)(arg[1], user_rwlock);
       break;
 
+   case VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE:
+      DRD_(semaphore_init)(arg[1], 0, arg[2]);
+      break;
+
+   case VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST:
+      DRD_(semaphore_destroy)(arg[1]);
+      break;
+
+   case VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE:
+      DRD_(semaphore_pre_wait)(arg[1]);
+      break;
+
+   case VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST:
+      DRD_(semaphore_post_wait)(drd_tid, arg[1], True /* waited */);
+      break;
+
+   case VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE:
+      DRD_(semaphore_pre_post)(drd_tid, arg[1]);
+      break;
+
    case VG_USERREQ__SET_PTHREAD_COND_INITIALIZER:
       DRD_(pthread_cond_initializer) = (Addr)arg[1];
       DRD_(pthread_cond_initializer_size) = arg[2];
diff --git a/drd/tests/Makefile.am b/drd/tests/Makefile.am
index 24cd089..0ebee2f 100644
--- a/drd/tests/Makefile.am
+++ b/drd/tests/Makefile.am
@@ -67,6 +67,8 @@
 	annotate_trace_memory_xml.stderr.exp-64bit  \
 	annotate_trace_memory_xml.stderr.exp-mips32 \
 	annotate_trace_memory_xml.vgtest	    \
+	annotate_sem.stderr.exp                     \
+	annotate_sem.vgtest                         \
 	annotate_static.stderr.exp	            \
 	annotate_static.vgtest		            \
 	atomic_var.stderr.exp			    \
@@ -375,6 +377,7 @@
 check_PROGRAMS +=        \
   annotate_barrier       \
   annotate_rwlock        \
+  annotate_sem           \
   atomic_var             \
   circular_buffer
 endif
diff --git a/drd/tests/annotate_sem.c b/drd/tests/annotate_sem.c
new file mode 100644
index 0000000..016e8b5
--- /dev/null
+++ b/drd/tests/annotate_sem.c
@@ -0,0 +1,96 @@
+/**
+ * @file  annotate_sem.c
+ *
+ * @brief Multithreaded test program that triggers various access patterns
+ *        without triggering any race conditions using a binary semaphore
+ *        implemented via busy-waiting. Annotations are used to tell DRD
+ *        which higher-level semaphore operations are being performed.
+ */
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include "../../config.h"
+#include "../../drd/drd.h"
+
+#define THREADS 10
+#define ITERATIONS 1000
+
+typedef struct {
+  volatile unsigned value;
+} sem_t;
+
+static sem_t s_sem;
+static unsigned int s_counter;
+
+static void sem_init(sem_t *p, unsigned value)
+{
+  DRD_IGNORE_VAR(*p);
+  p->value = value;
+  ANNOTATE_SEM_INIT_PRE(p, value);
+}
+
+static void sem_destroy(sem_t *p)
+{
+  ANNOTATE_SEM_DESTROY_POST(p);
+}
+
+static void sem_wait(sem_t *p)
+{
+  unsigned old, new;
+  struct timespec ts = { 0, 0 };
+
+  ANNOTATE_SEM_WAIT_PRE(p);
+  do {
+    old = p->value;
+    new = old - 1;
+    nanosleep(&ts, NULL);
+    ts.tv_nsec = 1;
+  } while (!old || !__sync_bool_compare_and_swap(&p->value, old, new));
+  ANNOTATE_SEM_WAIT_POST(p);
+}
+
+static void sem_post(sem_t *p)
+{
+  ANNOTATE_SEM_POST_PRE(p);
+  __sync_fetch_and_add(&p->value, 1);
+}
+
+static void *thread_func(void *arg)
+{
+  unsigned int i;
+  unsigned int sum = 0;
+
+  for (i = 0; i < ITERATIONS; i++) {
+    sem_wait(&s_sem);
+    sum += s_counter;
+    sem_post(&s_sem);
+
+    sem_wait(&s_sem);
+    s_counter++;
+    sem_post(&s_sem);
+  }
+
+  return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+  pthread_t tid[THREADS];
+  unsigned int i;
+
+  sem_init(&s_sem, 1);
+  for (i = 0; i < THREADS; i++)
+    pthread_create(&tid[i], 0, thread_func, 0);
+
+  for (i = 0; i < THREADS; i++)
+    pthread_join(tid[i], 0);
+
+  assert(s_counter == THREADS * ITERATIONS);
+  assert(s_sem.value == 1);
+  sem_destroy(&s_sem);
+
+  fprintf(stderr, "Finished.\n");
+
+  return 0;
+}
diff --git a/drd/tests/annotate_sem.stderr.exp b/drd/tests/annotate_sem.stderr.exp
new file mode 100644
index 0000000..a7089bb
--- /dev/null
+++ b/drd/tests/annotate_sem.stderr.exp
@@ -0,0 +1,4 @@
+
+Finished.
+
+ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
diff --git a/drd/tests/annotate_sem.vgtest b/drd/tests/annotate_sem.vgtest
new file mode 100644
index 0000000..3c5071c
--- /dev/null
+++ b/drd/tests/annotate_sem.vgtest
@@ -0,0 +1,4 @@
+prereq: test -e annotate_sem && ./supported_libpthread
+vgopts: --fair-sched=try --read-var-info=yes --check-stack-var=yes --show-confl-seg=no
+prog: annotate_sem
+stderr_filter: filter_stderr_and_thread_no