summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis Machado <luisgpm@br.ibm.com>2013-05-07 07:43:33 +0000
committerLuis Machado <luisgpm@br.ibm.com>2013-05-07 07:43:33 +0000
commit1abe07e6a2c57536afbe6f0ddb14f3e9b1d83509 (patch)
treef06ea7b2c620bcf6ef2765e3275139af861a8069
parent1fead7632f25000bedff5f753a32b9987117d66a (diff)
downloadgdb-1abe07e6a2c57536afbe6f0ddb14f3e9b1d83509.tar.gz
gdb/
* ppc-linux-nat.c (ppc_linux_new_thread): Clear the new thread's debug state prior to replicating existing hardware watchpoints or breakpoints. gdb/testsuite/ * gdb.threads/wp-replication.c: New file. * gdb.threads/wp-replication.exp: New file.
-rw-r--r--gdb/ChangeLog6
-rw-r--r--gdb/ppc-linux-nat.c13
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.threads/wp-replication.c163
-rw-r--r--gdb/testsuite/gdb.threads/wp-replication.exp151
5 files changed, 337 insertions, 1 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b729ec1e3b0..880ec6c8859 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,9 @@
+2013-05-07 Luis Machado <lgustavo@codesourcery.com>
+
+ * ppc-linux-nat.c (ppc_linux_new_thread): Clear the new thread's
+ debug state prior to replicating existing hardware watchpoints or
+ breakpoints.
+
2013-05-07 Jan Kratochvil <jan.kratochvil@redhat.com>
* gcore.c (gcore_create_callback): Ignore sections with
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index edb16c0b7a6..280dcbe887c 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -2178,7 +2178,18 @@ ppc_linux_new_thread (struct lwp_info *lp)
/* Copy that thread's breakpoints and watchpoints to the new thread. */
for (i = 0; i < max_slots_number; i++)
if (hw_breaks[i].hw_break)
- booke_insert_point (hw_breaks[i].hw_break, tid);
+ {
+ /* Older kernels did not make new threads inherit their parent
+ thread's debug state, so we always clear the slot and replicate
+ the debug state ourselves, ensuring compatibility with all
+ kernels. */
+
+ /* The ppc debug resource accounting is done through "slots".
+ Ask the kernel the deallocate this specific *point's slot. */
+ ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot);
+
+ booke_insert_point (hw_breaks[i].hw_break, tid);
+ }
}
else
ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value);
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 3d928bb5e09..a33d6d0cf6d 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2013-05-07 Luis Machado <lgustavo@codesourcery.com>
+
+ * gdb.threads/wp-replication.c: New file.
+ * gdb.threads/wp-replication.exp: New file.
+
2013-05-06 Sandra Loosemore <sandra@codesourcery.com>
* gdb.xml/tdesc-regs.exp: Add case for nios2.
diff --git a/gdb/testsuite/gdb.threads/wp-replication.c b/gdb/testsuite/gdb.threads/wp-replication.c
new file mode 100644
index 00000000000..a0afab2d90a
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/wp-replication.c
@@ -0,0 +1,163 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009-2013 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 hardware watchpoints get correctly replicated to all
+ existing threads when hardware watchpoints are created. This test
+ creates one hardware watchpoint per thread until a maximum is
+ reached. It originally addresses a deficiency seen on embedded
+ powerpc targets with slotted hardware *point designs.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4 /* Set by the testcase. */
+#endif
+
+#ifndef X_INCR_COUNT
+#define X_INCR_COUNT 10 /* Set by the testcase. */
+#endif
+
+void *thread_function (void *arg); /* Function executed by each thread. */
+
+/* Used to hold threads back until wp-replication.exp is ready. */
+int test_ready = 0;
+
+/* Used to hold threads back until every thread has had a chance of causing
+ a watchpoint trigger. This prevents a situation in GDB where it may miss
+ watchpoint triggers when threads exit while other threads are causing
+ watchpoint triggers. */
+int can_terminate = 0;
+
+/* Used to push the program out of the waiting loop after the
+ testcase is done counting the number of hardware watchpoints
+ available for our target. */
+int watch_count_done = 0;
+
+/* Number of watchpoints GDB is capable of using (this is provided
+ by GDB during the test run). */
+int hw_watch_count = 0;
+
+/* Array with elements we can create watchpoints for. */
+static int watched_data[NR_THREADS];
+pthread_mutex_t data_mutex;
+
+/* Wait function to keep threads busy while the testcase does
+ what it needs to do. */
+void
+empty_cycle (void)
+{
+ usleep (1);
+}
+
+int
+main ()
+{
+ int res;
+ pthread_t threads[NR_THREADS];
+ int i;
+
+ while (watch_count_done == 0)
+ {
+ /* GDB will modify the value of "i" at runtime and we will
+ get past this point. */
+ empty_cycle ();
+ }
+
+ pthread_mutex_init (&data_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.
+ wp-replication.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)
+{
+}
+
+void *
+thread_function (void *arg)
+{
+ int i, j;
+ long thread_number = (long) arg;
+
+ thread_started ();
+
+ /* Don't start incrementing X until wp-replication.exp is ready. */
+ while (!test_ready)
+ usleep (1);
+
+ pthread_mutex_lock (&data_mutex);
+
+ for (i = 0; i < NR_TRIGGERS_PER_THREAD; i++)
+ {
+ for (j = 0; j < hw_watch_count; j++)
+ {
+ /* For debugging. */
+ printf ("Thread %ld changing watch_thread[%d] data"
+ " from %d -> %d\n", thread_number, j,
+ watched_data[j], watched_data[j] + 1);
+ /* Increment the watched data field. */
+ watched_data[j]++;
+ }
+ }
+
+ pthread_mutex_unlock (&data_mutex);
+
+ /* Hold the threads here to work around a problem GDB has evaluating
+ watchpoints right when a DSO event shows up (PR breakpoints/10116).
+ Sleep a little longer (than, say, 1, 5 or 10) to avoid consuming
+ lots of cycles while the other threads are trying to execute the
+ loop. */
+ while (!can_terminate)
+ usleep (100);
+
+ pthread_exit (NULL);
+}
diff --git a/gdb/testsuite/gdb.threads/wp-replication.exp b/gdb/testsuite/gdb.threads/wp-replication.exp
new file mode 100644
index 00000000000..8927a434d24
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/wp-replication.exp
@@ -0,0 +1,151 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2009-2013 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 hardware watchpoints get correctly replicated to all
+# existing threads when hardware watchpoints are created. This test
+# creates one hardware watchpoint per thread until a maximum is
+# reached. It originally addresses a deficiency seen on embedded
+# powerpc targets with slotted hardware *point designs.
+
+set NR_THREADS 10
+set NR_TRIGGERS_PER_THREAD 2
+
+# This test verifies that a hardware watchpoint gets replicated to
+# every existing thread and is detected properly. This test is
+# only meaningful on a target with hardware watchpoint support.
+if {[skip_hw_watchpoint_tests]} {
+ return 0
+}
+
+standard_testfile
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "additional_flags=-DNR_THREADS=$NR_THREADS -DNR_TRIGGERS_PER_THREAD=$NR_TRIGGERS_PER_THREAD"]] != "" } {
+ return -1
+}
+
+clean_restart ${binfile}
+
+# Force hardware watchpoints to be used.
+gdb_test_no_output "set can-use-hw-watchpoints 1" ""
+
+# Run to `main' where we begin our tests.
+if ![runto_main] then {
+ fail "Failed to run to main"
+ return 0
+}
+
+# First, break at empty_cycle.
+gdb_test "break empty_cycle" \
+ "Breakpoint 2 at .*: file .*${srcfile}, line .*" \
+ "Breakpoint on empty_cycle"
+
+# Set some default values.
+set hwatch_count 0
+set done 0
+
+# Count the number of hardware watchpoints available on
+# this target.
+while { $done == 0 } {
+
+ gdb_test "continue" \
+ ".*Breakpoint 2, empty_cycle \\(\\) at .*${srcfile}.*" \
+ "Continue to empty_cycle to insert watchpoint $hwatch_count"
+
+ # Some targets do resource counting as we insert watchpoints.
+ # Such targets won't cause a watchpoint insertion failure, but
+ # will switch to software watchpoints silently. We check for
+ # both cases here.
+ gdb_test_multiple "watch watched_data\[$hwatch_count\]" \
+ "watch watched_data\[$hwatch_count\]" {
+ -re "Hardware watchpoint .*$gdb_prompt $" {
+ }
+ -re "Watchpoint .*$gdb_prompt $" {
+ set done 1
+ break
+ }
+ }
+
+ gdb_test_multiple "continue" "watchpoint created successfully" {
+ -re ".*Breakpoint 2, empty_cycle \\(\\).*$gdb_prompt $" {
+ incr hwatch_count
+ }
+ -re ".*Could not insert hardware watchpoint.*$gdb_prompt $" {
+ set done 1
+ break
+ }
+ }
+}
+
+# Target cannot insert hardware watchpoints. It should have reported
+# (through board settings) that it did not support them in the first place.
+# Just exit.
+if { $hwatch_count == 0} {
+ fail "No hardware watchpoints available"
+ return 0
+}
+
+# Set the testcase's internal variable indicating the number of
+# hardware watchpoints the target supports.
+gdb_test_no_output "set var hw_watch_count=${hwatch_count}" \
+ "set var hw_watch_count=${hwatch_count}"
+
+# At this point, we know how many hardware watchpoints
+# the target supports. Use that to do further testing.
+delete_breakpoints
+
+# Break out of the empty_cycle loop by changing the
+# controlling variable.
+gdb_test_no_output "set var watch_count_done=1" \
+ "set var watch_count_done=1"
+
+# Prepare to create all the threads.
+gdb_test "break thread_started" \
+ "Breakpoint \[0-9\]+ at .*: file .*${srcfile}, line .*" \
+ "Breakpoint on thread_started"
+
+# Move all threads to where they're supposed to be for testing.
+for { set i 0 } { $i < $NR_THREADS } { incr i } {
+
+ # We want to set the maximum number of hardware watchpoints
+ # and make sure the target can handle that without an error.
+ # That will show us the watchpoints got replicated to all the
+ # threads correctly, and that no new watchpoints got created
+ # in the background for a specific thread.
+ if {$i < $hwatch_count} {
+ gdb_test "watch watched_data\[$i\]" \
+ "Hardware watchpoint .*" \
+ "watch watched_data\[$i\]"
+ } else {
+ verbose -log "Not setting watchpoint for watched_data\[$i\]\n"
+ }
+
+ gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, thread_started \\(\\) at .*$srcfile.*" \
+ "Thread $i hit breakpoint at thread_started"
+}
+
+# Let the threads run and change the watched data, leading
+# to watchpoint triggers.
+gdb_test_no_output "set var test_ready=1" \
+ "set var test_ready=1"
+
+# Set the number of expected watchpoint triggers.
+set TRIGGERS [expr "$NR_THREADS * $hwatch_count * $NR_TRIGGERS_PER_THREAD"]
+
+# Move the threads and hit the watchpoints TRIGGERS times.
+for { set i 1 } { $i <= $TRIGGERS } { incr i } {
+ gdb_test continue "Continuing\\..*Hardware watchpoint \[0-9\]+: watched_data\[\[0-9\]+\].*Old value = \[0-9\]+.*New value = \[0-9\]+.*thread_function \\(arg=$hex\\) at .*$srcfile.*" \
+ "Continue to watchpoint trigger $i out of ${TRIGGERS} on watched_data"
+}