Add tests for displaying of locks held by threads in races.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11827 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am
index f06fd26..76691b3 100644
--- a/helgrind/tests/Makefile.am
+++ b/helgrind/tests/Makefile.am
@@ -21,6 +21,18 @@
 	hg05_race2.vgtest hg05_race2.stdout.exp hg05_race2.stderr.exp \
 	hg06_readshared.vgtest hg06_readshared.stdout.exp \
 		hg06_readshared.stderr.exp \
+	locked_vs_unlocked1_fwd.vgtest \
+		locked_vs_unlocked1_fwd.stderr.exp \
+		locked_vs_unlocked1_fwd.stdout.exp \
+	locked_vs_unlocked1_rev.vgtest \
+		locked_vs_unlocked1_rev.stderr.exp \
+		locked_vs_unlocked1_rev.stdout.exp \
+	locked_vs_unlocked2.vgtest \
+		locked_vs_unlocked2.stderr.exp \
+		locked_vs_unlocked2.stdout.exp \
+	locked_vs_unlocked3.vgtest \
+		locked_vs_unlocked3.stderr.exp \
+		locked_vs_unlocked3.stdout.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 \
@@ -84,6 +96,9 @@
 	hg04_race \
 	hg05_race2 \
 	hg06_readshared \
+	locked_vs_unlocked1 \
+	locked_vs_unlocked2 \
+	locked_vs_unlocked3 \
 	tc01_simple_race \
 	tc02_simple_tls \
 	tc03_re_excl \
diff --git a/helgrind/tests/locked_vs_unlocked1.c b/helgrind/tests/locked_vs_unlocked1.c
new file mode 100644
index 0000000..96dc425
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1.c
@@ -0,0 +1,50 @@
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Test of the mechanism for showing all locks held by a thread -- one
+   thread has a lock, the other doesn't.  Running w/ command line args
+   switches the has/has-not thread around, so as to test lockset
+   retention in both the history mechanism and the primary errors. */
+
+pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;
+
+int x = 0;
+
+void* child_fn ( void* arg )
+{
+   if (arg) pthread_mutex_lock(&mx);
+   x = 1;
+   if (arg) pthread_mutex_unlock(&mx);
+   return NULL;
+}
+
+int main ( int argc, char** argv )
+{
+   int sw = argc > 1;
+   pthread_t child1, child2;
+
+   if (pthread_create(&child1, NULL, child_fn, (void*)(long)(sw ? 0 : 1))) {
+      perror("pthread_create1");
+      exit(1);
+   }
+   sleep(1); /* ensure repeatable results */
+   if (pthread_create(&child2, NULL, child_fn, (void*)(long)(sw ? 1 : 0))) {
+      perror("pthread_create1");
+      exit(1);
+   }
+
+   if (pthread_join(child1, NULL)) {
+      perror("pthread join1");
+      exit(1);
+   }
+
+   if (pthread_join(child2, NULL)) {
+      perror("pthread join2");
+      exit(1);
+   }
+
+   return 0;
+}
diff --git a/helgrind/tests/locked_vs_unlocked1_fwd.stderr.exp b/helgrind/tests/locked_vs_unlocked1_fwd.stderr.exp
new file mode 100644
index 0000000..a3677ca
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_fwd.stderr.exp
@@ -0,0 +1,34 @@
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+----------------------------------------------------------------
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_lock (hg_intercepts.c:...)
+   by 0x........: child_fn (locked_vs_unlocked1.c:18)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+Locks held: none
+   at 0x........: child_fn (locked_vs_unlocked1.c:19)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+This conflicts with a previous write of size 4 by thread #x
+Locks held: 1, at address 0x........
+   at 0x........: child_fn (locked_vs_unlocked1.c:19)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
diff --git a/helgrind/tests/locked_vs_unlocked1_fwd.stdout.exp b/helgrind/tests/locked_vs_unlocked1_fwd.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_fwd.stdout.exp
diff --git a/helgrind/tests/locked_vs_unlocked1_fwd.vgtest b/helgrind/tests/locked_vs_unlocked1_fwd.vgtest
new file mode 100644
index 0000000..eced0bf
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_fwd.vgtest
@@ -0,0 +1,2 @@
+prog: locked_vs_unlocked1
+vgopts: -q --num-callers=3
diff --git a/helgrind/tests/locked_vs_unlocked1_rev.stderr.exp b/helgrind/tests/locked_vs_unlocked1_rev.stderr.exp
new file mode 100644
index 0000000..caf7189
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_rev.stderr.exp
@@ -0,0 +1,34 @@
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+----------------------------------------------------------------
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_lock (hg_intercepts.c:...)
+   by 0x........: child_fn (locked_vs_unlocked1.c:18)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+Possible data race during write of size 4 at 0x........ by thread #x
+Locks held: 1, at address 0x........
+   at 0x........: child_fn (locked_vs_unlocked1.c:19)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+This conflicts with a previous write of size 4 by thread #x
+Locks held: none
+   at 0x........: child_fn (locked_vs_unlocked1.c:19)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
diff --git a/helgrind/tests/locked_vs_unlocked1_rev.stdout.exp b/helgrind/tests/locked_vs_unlocked1_rev.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_rev.stdout.exp
diff --git a/helgrind/tests/locked_vs_unlocked1_rev.vgtest b/helgrind/tests/locked_vs_unlocked1_rev.vgtest
new file mode 100644
index 0000000..1e32c8a
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked1_rev.vgtest
@@ -0,0 +1,3 @@
+prog: locked_vs_unlocked1
+args: x
+vgopts: -q --num-callers=3
diff --git a/helgrind/tests/locked_vs_unlocked2.c b/helgrind/tests/locked_vs_unlocked2.c
new file mode 100644
index 0000000..f95e555
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked2.c
@@ -0,0 +1,73 @@
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+
+/* Test of the mechanism for showing all locks held by a thread.  Test
+   the case where the earlier thread held, at the time of the access,
+   some locks, at least one of which is deleted by the time the second
+   access (the race) happens.  This causes problems for Helgrind's
+   error reporting mechanism in that it can no longer show the deleted
+   lock in the error message.x */
+
+pthread_mutex_t mx1a;
+pthread_mutex_t mx1b;
+pthread_mutex_t mx2a;
+pthread_mutex_t mx2b;
+
+int x = 0;
+
+void* child_fn1 ( void* arg )
+{
+   int r;
+   // We are the first-accessing thread.  Take and release two locks
+   // and then destroy one of them.
+   r= pthread_mutex_lock(&mx1a);  assert(!r);
+   r= pthread_mutex_lock(&mx1b);  assert(!r);
+   x = 1;
+   r= pthread_mutex_unlock(&mx1b);  assert(!r);
+   r= pthread_mutex_unlock(&mx1a);  assert(!r);
+   r= pthread_mutex_destroy(&mx1a);  assert(!r);
+   sleep(1);
+   return NULL;
+}
+
+void* child_fn2 ( void* arg )
+{
+   int r;
+   // We are the second-accessing thread.  Take and release
+   // our two locks, but don't otherwise mess with them. 
+   sleep(1);
+   r= pthread_mutex_lock(&mx2a);  assert(!r);
+   r= pthread_mutex_lock(&mx2b);  assert(!r);
+   x = 1;
+   r= pthread_mutex_unlock(&mx2b);  assert(!r);
+   r= pthread_mutex_unlock(&mx2a);  assert(!r);
+   return NULL;
+}
+
+int main ( int argc, char** argv )
+{
+   pthread_t child1, child2;
+   int r;
+
+   r= pthread_mutex_init(&mx1a, NULL);  assert(!r);
+   r= pthread_mutex_init(&mx1b, NULL);  assert(!r);
+   r= pthread_mutex_init(&mx2a, NULL);  assert(!r);
+   r= pthread_mutex_init(&mx2b, NULL);  assert(!r);
+
+   r= pthread_create(&child2, NULL, child_fn2, NULL);  assert(!r);
+   r= pthread_create(&child1, NULL, child_fn1, NULL);  assert(!r);
+
+   r= pthread_join(child1, NULL);  assert(!r);
+   r= pthread_join(child2, NULL);  assert(!r);
+
+   // don't destroy mx1a; it's already destroyed.
+   r= pthread_mutex_destroy(&mx1b);  assert(!r);
+   r= pthread_mutex_destroy(&mx2a);  assert(!r);
+   r= pthread_mutex_destroy(&mx2b);  assert(!r);
+
+   return 0;
+}
diff --git a/helgrind/tests/locked_vs_unlocked2.stderr.exp b/helgrind/tests/locked_vs_unlocked2.stderr.exp
new file mode 100644
index 0000000..591f90e
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked2.stderr.exp
@@ -0,0 +1,40 @@
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+----------------------------------------------------------------
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_init (hg_intercepts.c:...)
+   by 0x........: main (locked_vs_unlocked2.c:58)
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_init (hg_intercepts.c:...)
+   by 0x........: main (locked_vs_unlocked2.c:59)
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_init (hg_intercepts.c:...)
+   by 0x........: main (locked_vs_unlocked2.c:57)
+
+Possible data race during write of size 4 at 0x........ by thread #x
+Locks held: 2, at addresses 0x........ 0x........
+   at 0x........: child_fn2 (locked_vs_unlocked2.c:45)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+This conflicts with a previous write of size 4 by thread #x
+Locks held: 2, at address 0x........ (and 1 that can't be shown)
+   at 0x........: child_fn1 (locked_vs_unlocked2.c:29)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
diff --git a/helgrind/tests/locked_vs_unlocked2.stdout.exp b/helgrind/tests/locked_vs_unlocked2.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked2.stdout.exp
diff --git a/helgrind/tests/locked_vs_unlocked2.vgtest b/helgrind/tests/locked_vs_unlocked2.vgtest
new file mode 100644
index 0000000..3244156
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked2.vgtest
@@ -0,0 +1,2 @@
+prog: locked_vs_unlocked2
+vgopts: -q --num-callers=3
diff --git a/helgrind/tests/locked_vs_unlocked3.c b/helgrind/tests/locked_vs_unlocked3.c
new file mode 100644
index 0000000..77a0fe8
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked3.c
@@ -0,0 +1,62 @@
+
+/* Needed for older glibcs (2.3 and older, at least) who don't
+   otherwise "know" about pthread_rwlock_anything or about
+   PTHREAD_MUTEX_RECURSIVE (amongst things). */
+#define _GNU_SOURCE 1
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+
+/* Test of the mechanism for showing all locks held by a thread.  This
+   is like locked_vs_unlocked.c, except that it uses a recursively
+   lockable lock, and one thread holds the lock more than once.  Point
+   is to check that the lock showing mechanism shows the
+   lock-number-of-times-held count. */
+
+pthread_mutex_t mx;
+
+int x = 0;
+
+void* child_fn1 ( void* arg )
+{
+   int r;
+   r= pthread_mutex_lock(&mx);  assert(!r);
+   r= pthread_mutex_lock(&mx);  assert(!r);
+   x = 1;
+   r= pthread_mutex_unlock(&mx);  assert(!r);
+   r= pthread_mutex_unlock(&mx);  assert(!r);
+   sleep(1);
+   return NULL;
+}
+
+void* child_fn2 ( void* arg )
+{
+   sleep(1);
+   x = 1;
+   return NULL;
+}
+
+int main ( int argc, char** argv )
+{
+   int r;
+   pthread_t child1, child2;
+   pthread_mutexattr_t attr;
+   r = pthread_mutexattr_init( &attr );
+   assert(!r);
+   r = pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
+   assert(!r);
+   r= pthread_mutex_init(&mx, &attr);  assert(!r);
+
+   r= pthread_create(&child2, NULL, child_fn2, NULL);  assert(!r);
+   r= pthread_create(&child1, NULL, child_fn1, NULL);  assert(!r);
+
+   r= pthread_join(child1, NULL);  assert(!r);
+   r= pthread_join(child2, NULL);  assert(!r);
+
+   r= pthread_mutex_destroy(&mx);  assert(!r);
+
+   return 0;
+}
diff --git a/helgrind/tests/locked_vs_unlocked3.stderr.exp b/helgrind/tests/locked_vs_unlocked3.stderr.exp
new file mode 100644
index 0000000..d3fce52
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked3.stderr.exp
@@ -0,0 +1,32 @@
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+---Thread-Announcement------------------------------------------
+
+Thread #x was created
+   at 0x........: clone (in /...libc...)
+   by 0x........: pthread_create@@GLIBC_2.2.5 (in /...libpthread...)
+   by 0x........: pthread_create_WRK (hg_intercepts.c:...)
+
+----------------------------------------------------------------
+
+Lock at 0x........ was first observed
+   at 0x........: pthread_mutex_init (hg_intercepts.c:...)
+   by 0x........: main (locked_vs_unlocked3.c:51)
+
+Possible data race during write of size 4 at 0x........ by thread #x
+Locks held: none
+   at 0x........: child_fn2 (locked_vs_unlocked3.c:38)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
+This conflicts with a previous write of size 4 by thread #x
+Locks held: 1, at address 0x........
+   at 0x........: child_fn1 (locked_vs_unlocked3.c:28)
+   by 0x........: mythread_wrapper (hg_intercepts.c:...)
+   ...
+
diff --git a/helgrind/tests/locked_vs_unlocked3.stdout.exp b/helgrind/tests/locked_vs_unlocked3.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked3.stdout.exp
diff --git a/helgrind/tests/locked_vs_unlocked3.vgtest b/helgrind/tests/locked_vs_unlocked3.vgtest
new file mode 100644
index 0000000..d97a197
--- /dev/null
+++ b/helgrind/tests/locked_vs_unlocked3.vgtest
@@ -0,0 +1,2 @@
+prog: locked_vs_unlocked3
+vgopts: -q --num-callers=3