* gdb.threads/watchthreads2.exp: New testcase.
authorDoug Evans <dje@google.com>
Thu, 30 Apr 2009 18:25:51 +0000 (18:25 +0000)
committerDoug Evans <dje@google.com>
Thu, 30 Apr 2009 18:25:51 +0000 (18:25 +0000)
* gdb.threads/watchthreads2.c: New testcase.

gdb/testsuite/ChangeLog
gdb/testsuite/gdb.threads/watchthreads2.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/watchthreads2.exp [new file with mode: 0644]

index 8e8744b99f886fc15febd8d03b9f0c6c7ae47ebc..83409c729bff6c5126d03deeb39ba83b8679c02d 100644 (file)
@@ -1,3 +1,8 @@
+2009-04-30  Doug Evans  <dje@google.com>
+
+       * gdb.threads/watchthreads2.exp: New testcase.
+       * gdb.threads/watchthreads2.c: New testcase.
+
 2009-04-29  Doug Evans  <dje@google.com>
 
        * gdb.cp/mb-ctor.exp: Add multi-line source statement test.
diff --git a/gdb/testsuite/gdb.threads/watchthreads2.c b/gdb/testsuite/gdb.threads/watchthreads2.c
new file mode 100644 (file)
index 0000000..8df97fd
--- /dev/null
@@ -0,0 +1,115 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Check that watchpoints get propagated to all existing threads when the
+   watchpoint is created.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4
+#endif
+
+#ifndef X_INCR_COUNT
+#define X_INCR_COUNT 10
+#endif
+
+void *thread_function (void *arg); /* Function executed by each thread.  */
+
+pthread_mutex_t x_mutex;
+int x;
+
+/* Used to hold threads back until watchthreads2.exp is ready.  */
+int test_ready = 0;
+
+int
+main ()
+{
+  int res;
+  pthread_t threads[NR_THREADS];
+  int i;
+
+  pthread_mutex_init (&x_mutex, NULL);
+
+  for (i = 0; i < NR_THREADS; ++i)
+    {
+      res = pthread_create (&threads[i],
+                           NULL,
+                           thread_function,
+                           (void *) (intptr_t) i);
+      if (res != 0)
+       {
+         fprintf (stderr, "error in thread %d create\n", i);
+         abort ();
+       }
+    }
+
+  for (i = 0; i < NR_THREADS; ++i)
+    {
+      res = pthread_join (threads[i], NULL);
+      if (res != 0)
+       {
+         fprintf (stderr, "error in thread %d join\n", i);
+         abort ();
+       }
+    }
+
+  exit (EXIT_SUCCESS);
+}
+
+/* Easy place for a breakpoint.
+   watchthreads2.exp uses this to track when all threads are running
+   instead of, for example, the program keeping track
+   because we don't need the program to know when all threads are running,
+   instead we need gdb to know when all threads are running.
+   There is a delay between when a thread has started and when the thread
+   has been registered with gdb.  */
+
+void
+thread_started ()
+{
+}
+
+void *
+thread_function (void *arg)
+{
+  int i;
+
+  thread_started ();
+
+  /* Don't start incrementing X until watchthreads2.exp is ready.  */
+  while (! test_ready)
+    usleep (1);
+
+  for (i = 0; i < X_INCR_COUNT; ++i)
+    {
+      pthread_mutex_lock (&x_mutex);
+      /* For debugging.  */
+      printf ("Thread %ld changing x %d -> %d\n", (long) arg, x, x + 1);
+      /* The call to usleep is so that when the watchpoint triggers,
+        the pc is still on the same line.  */
+      ++x; usleep (1);  /* X increment.  */
+      pthread_mutex_unlock (&x_mutex);
+    }
+
+  pthread_exit (NULL);
+}
diff --git a/gdb/testsuite/gdb.threads/watchthreads2.exp b/gdb/testsuite/gdb.threads/watchthreads2.exp
new file mode 100644 (file)
index 0000000..06d980e
--- /dev/null
@@ -0,0 +1,139 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Check that watchpoints get propagated to all existing threads when the
+# watchpoint is created.
+
+set NR_THREADS 4
+set X_INCR_COUNT 10
+
+if $tracelevel {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test verifies that a watchpoint is detected in the proper thread
+# so the test is only meaningful on a system with hardware watchpoints.
+if [target_info exists gdb,no_hardware_watchpoints] {
+    return 0;
+}
+
+set testfile "watchthreads2"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}" "additional_flags=-DNR_THREADS=$NR_THREADS -DX_INCR_COUNT=$X_INCR_COUNT"]] != "" } {
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test "set can-use-hw-watchpoints 1" "" ""
+
+#
+# Run to `main' where we begin our tests.
+#
+
+if ![runto_main] then {
+    gdb_suppress_tests
+}
+
+gdb_test "break thread_started" \
+         "Breakpoint 2 at .*: file .*${srcfile}, line .*" \
+         "breakpoint on thread_started"
+
+# Run the program until all threads have hit thread_started.
+# We use this as the vehicle to determine when gdb is aware
+# of all threads (i.e. "info threads" would show all threads).
+
+set nr_started 0
+set message "run to thread_started"
+for { set i 0 } { $i < $NR_THREADS } { incr i } {
+    gdb_test_multiple "continue" $message {
+       -re ".*Breakpoint 2, thread_started ().*$gdb_prompt $" {
+           incr nr_started
+       }
+       timeout {
+           set i $NR_THREADS
+       }
+    }
+}
+if { $nr_started == $NR_THREADS } {
+    pass "all threads started"
+} else {
+    fail "all threads started"
+    # No point in continuing.
+    return -1
+}
+
+# Watch X, it will be modified by all threads.
+# We want this watchpoint to be set *after* all threads are running.
+gdb_test "watch x" "Hardware watchpoint 3: x"
+
+# Now that the watchpoint is set, we can let the threads increment X.
+gdb_test "set var test_ready = 1" ""
+
+# While debugging.
+#gdb_test "set debug infrun 1" ""
+
+set x_inc_line [gdb_get_line_number "X increment"]
+set x_thread_loc "thread_function \\\(arg=.*\\\) at .*watchthreads.c:$x_inc_line"
+
+# X is incremented under a mutex, so we should get NR_THREADS * X_INCR_COUNT
+# hits.
+set limit [expr $NR_THREADS*$X_INCR_COUNT]
+set x_count 0
+set done 0
+
+set message "x watch loop"
+
+for {set i 0} {!$done && $i < $limit} {incr i} {
+    set test_flag 0
+
+    gdb_test_multiple "continue" $message {
+       -re "(.*Hardware watchpoint.*)$gdb_prompt $" {
+           set string $expect_out(1,string)
+
+           if [regexp "Hardware watchpoint 3: x\[^\r\]*\r\[^\r\]*\r\[^\r\]*Old value = $x_count\[^\r\]*\r\[^\r\]*New value = [expr $x_count+1]\r" $string] {
+               incr x_count
+               set test_flag 1
+           } else {
+               # We test for new value = old value + 1 each iteration.
+               # This can fail due to gdb/10116.
+               # This is caught after the loop exits.
+           }
+       }
+       -re "The program is not being run.*$gdb_prompt $" {
+           fail "$message (program terminated)"
+       }
+    }
+
+    # If we fail above, don't bother continuing loop.
+    if { $test_flag == 0 } {
+       set done 1
+    }
+}
+
+if { $i == $limit } {
+    pass "all threads incremented x"
+} else {
+    kfail "gdb/10116" "gdb can drop watchpoints in multithreaded app"
+}