--- /dev/null
+/* 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);
+}
--- /dev/null
+# 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"
+}