* [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit
@ 2009-08-17 19:46 Jan Kratochvil
2009-10-02 22:13 ` [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] Jan Kratochvil
0 siblings, 1 reply; 9+ messages in thread
From: Jan Kratochvil @ 2009-08-17 19:46 UTC (permalink / raw)
To: gdb-patches
Hi,
if you hit two watchpoints at the same time in the default all-stop mode GDB
does not cope well with the pending watchpoint hit in non-current LWP.
Moreover when you change the watchpoints setup during such stop.
The is a bit problem the dependency on debug register number is specific to
x86* (on other arches hardware may directly report the address of triggered
watchpoint). But it is also dependent on Linux as it depends on LWPs (so
i386-nat.c is too general). And it would need to be duplicate in both
{i386,amd64}-linux-nat.c. Therefore the code is put in linux-nat.c where it
does not hurt non-x86* arches (it may not even get installed on those).
The testcase reproducer is difficult to make reliable (as is now): GDB must
find two threads both with a watchpoint hit at the single stop. The used
SIGSTOP of GDB is a bit fragile as the inferior being traced must not trigger
a ptrace event otherwise it deadlocks with its own debugger SIGSTOPped.
Thanks,
Jan
gdb/
2009-08-17 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix reordered watchpoints triggered in other threads during all-stop.
* i386-nat.c (i386_stopped_by_watchpoint): Call
i386_stopped_data_address through the target vector.
* linux-nat.c (save_sigtrap, linux_nat_stopped_data_address): New.
(stop_wait_callback, linux_nat_filter_event): Call save_sigtrap.
(linux_nat_add_target): Install linux_nat_stopped_data_address.
* linux-nat.h (struct lwp_info): New fields watchpoint_hit_set and
watchpoint_hit.
gdb/testsuite/
2009-08-17 Jan Kratochvil <jan.kratochvil@redhat.com>
* watchthreads-reorder.exp, watchthreads-reorder.c: New.
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -587,7 +587,7 @@ static int
i386_stopped_by_watchpoint (void)
{
CORE_ADDR addr = 0;
- return i386_stopped_data_address (¤t_target, &addr);
+ return target_stopped_data_address (¤t_target, &addr);
}
/* Return non-zero if the inferior has some break/watchpoint that
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2164,6 +2164,45 @@ maybe_clear_ignore_sigint (struct lwp_info *lp)
}
}
+/* Fetch the possible triggered data watchpoint info and store it to LP.
+ The hardware data watchpoint trigger gets cleared during this fetch. */
+
+static void
+save_sigtrap (struct lwp_info *lp)
+{
+ struct cleanup *old_chain;
+
+ /* linux_nat_stopped_data_address is not even installed in this case. */
+ if (linux_ops->to_stopped_data_address == NULL)
+ return;
+
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = lp->ptid;
+
+ lp->watchpoint_hit_set =
+ linux_ops->to_stopped_data_address (¤t_target, &lp->watchpoint_hit);
+
+ do_cleanups (old_chain);
+}
+
+/* Wrap target_stopped_data_address where the GNU/Linux native target may be
+ directed by the watchpoint/debug register number. Base the reported value
+ on the triggered data address instead. During inferior stop the assignment
+ of watchpoint/debug registers may change making the register number specific
+ trigger info stale. */
+
+static int
+linux_nat_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
+{
+ struct lwp_info *lp = find_lwp_pid (inferior_ptid);
+
+ gdb_assert (lp != NULL);
+
+ *addr_p = lp->watchpoint_hit;
+
+ return lp->watchpoint_hit_set;
+}
+
/* Wait until LP is stopped. */
static int
@@ -2215,6 +2254,8 @@ stop_wait_callback (struct lwp_info *lp, void *data)
/* Save the trap's siginfo in case we need it later. */
save_siginfo (lp);
+ save_sigtrap (lp);
+
/* Now resume this LWP and get the SIGSTOP event. */
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
@@ -2579,9 +2620,13 @@ linux_nat_filter_event (int lwpid, int status, int options)
add_thread (lp->ptid);
}
- /* Save the trap's siginfo in case we need it later. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
- save_siginfo (lp);
+ {
+ /* Save the trap's siginfo in case we need it later. */
+ save_siginfo (lp);
+
+ save_sigtrap (lp);
+ }
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
@@ -4871,6 +4916,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_thread_alive = linux_nat_thread_alive;
t->to_pid_to_str = linux_nat_pid_to_str;
t->to_has_thread_control = tc_schedlock;
+ if (linux_ops->to_stopped_data_address)
+ t->to_stopped_data_address = linux_nat_stopped_data_address;
t->to_can_async_p = linux_nat_can_async_p;
t->to_is_async_p = linux_nat_is_async_p;
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -62,6 +62,12 @@ struct lwp_info
be the address of a hardware watchpoint. */
struct siginfo siginfo;
+ /* WATCHPOINT_HIT_SET is non-zero if this LWP stopped with a trap and a data
+ watchpoint has been found as triggered. In such case WATCHPOINT_HIT
+ contains data address of the triggered data watchpoint. */
+ unsigned watchpoint_hit_set : 1;
+ CORE_ADDR watchpoint_hit;
+
/* Non-zero if we expect a duplicated SIGINT. */
int ignore_sigint;
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.c
@@ -0,0 +1,366 @@
+/* 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/>. */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+
+#define gettid() syscall (__NR_gettid)
+
+/* Terminate always in the main task, it can lock up with SIGSTOPped GDB
+ otherwise. */
+#define TIMEOUT (gettid () == getpid() ? 10 : 15)
+
+static pthread_mutex_t gdbstop_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+static pid_t thread1_tid;
+static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+static pid_t thread2_tid;
+static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+static volatile int thread1_rwatch;
+static volatile int thread2_rwatch;
+
+static int unused1_rwatch;
+static int unused2_rwatch;
+
+/* Do not use alarm as it would create a ptrace event which would hang up us if
+ * we are being traced by GDB which we stopped ourselves. */
+
+static void timed_mutex_lock (pthread_mutex_t *mutex)
+{
+ int i;
+ struct timespec start, now;
+
+ i = clock_gettime (CLOCK_MONOTONIC, &start);
+ assert (i == 0);
+
+ do
+ {
+ i = pthread_mutex_trylock (mutex);
+ if (i == 0)
+ return;
+ assert (i == EBUSY);
+
+ i = clock_gettime (CLOCK_MONOTONIC, &now);
+ assert (i == 0);
+ assert (now.tv_sec >= start.tv_sec);
+ }
+ while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+ fprintf (stderr, "Timed out waiting for internal lock!\n");
+ exit (EXIT_FAILURE);
+}
+
+static void *
+thread1_func (void *unused)
+{
+ int i;
+ volatile int rwatch_store;
+
+ thread1_tid = gettid ();
+ i = pthread_cond_signal (&thread1_tid_cond);
+ assert (i == 0);
+
+ /* Be sure GDB is already stopped before continuing. */
+ timed_mutex_lock (&gdbstop_mutex);
+ i = pthread_mutex_unlock (&gdbstop_mutex);
+ assert (i == 0);
+
+ rwatch_store = thread1_rwatch;
+
+ /* Be sure the "T (tracing stop)" test can proceed for both threads. */
+ timed_mutex_lock (&terminate_mutex);
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ return NULL;
+}
+
+static void *
+thread2_func (void *unused)
+{
+ int i;
+ volatile int rwatch_store;
+
+ thread2_tid = gettid ();
+ i = pthread_cond_signal (&thread2_tid_cond);
+ assert (i == 0);
+
+ /* Be sure GDB is already stopped before continuing. */
+ timed_mutex_lock (&gdbstop_mutex);
+ i = pthread_mutex_unlock (&gdbstop_mutex);
+ assert (i == 0);
+
+ rwatch_store = thread2_rwatch;
+
+ /* Be sure the "T (tracing stop)" test can proceed for both threads. */
+ timed_mutex_lock (&terminate_mutex);
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ return NULL;
+}
+
+static const char *
+proc_string (const char *filename, const char *line)
+{
+ FILE *f;
+ static char buf[LINE_MAX];
+ size_t line_len = strlen (line);
+
+ f = fopen (filename, "r");
+ if (f == NULL)
+ {
+ fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ while (errno = 0, fgets (buf, sizeof (buf), f))
+ {
+ char *s;
+
+ s = strchr (buf, '\n');
+ assert (s != NULL);
+ *s = 0;
+
+ if (strncmp (buf, line, line_len) != 0)
+ continue;
+
+ if (fclose (f))
+ {
+ fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ return &buf[line_len];
+ }
+ if (errno != 0)
+ {
+ fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
+ exit (EXIT_FAILURE);
+}
+
+static unsigned long
+proc_ulong (const char *filename, const char *line)
+{
+ const char *s = proc_string (filename, line);
+ long retval;
+ char *end;
+
+ errno = 0;
+ retval = strtol (s, &end, 10);
+ if (retval < 0 || retval >= LONG_MAX || (end && *end))
+ {
+ fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ return retval;
+}
+
+static void
+state_wait (pid_t process, const char *wanted)
+{
+ char *filename;
+ int i;
+ struct timespec start, now;
+ const char *state;
+
+ i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
+ assert (i > 0);
+
+ i = clock_gettime (CLOCK_MONOTONIC, &start);
+ assert (i == 0);
+
+ do
+ {
+ state = proc_string (filename, "State:\t");
+ if (strcmp (state, wanted) == 0)
+ {
+ free (filename);
+ return;
+ }
+
+ if (sched_yield ())
+ {
+ perror ("sched_yield()");
+ exit (EXIT_FAILURE);
+ }
+
+ i = clock_gettime (CLOCK_MONOTONIC, &now);
+ assert (i == 0);
+ assert (now.tv_sec >= start.tv_sec);
+ }
+ while (now.tv_sec - start.tv_sec < TIMEOUT);
+
+ fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
+ (unsigned long) process, wanted, state);
+ exit (EXIT_FAILURE);
+}
+
+static volatile pid_t tracer = 0;
+static pthread_t thread1, thread2;
+
+static void
+cleanup (void)
+{
+ printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
+
+ if (tracer)
+ {
+ int i;
+ int tracer_save = tracer;
+
+ tracer = 0;
+
+ i = kill (tracer_save, SIGCONT);
+ assert (i == 0);
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ int i;
+ int standalone = 0;
+
+ if (argc == 2 && strcmp (argv[1], "-s") == 0)
+ standalone = 1;
+ else
+ assert (argc == 1);
+
+ setbuf (stdout, NULL);
+
+ timed_mutex_lock (&gdbstop_mutex);
+
+ timed_mutex_lock (&terminate_mutex);
+
+ i = pthread_create (&thread1, NULL, thread1_func, NULL);
+ assert (i == 0);
+
+ i = pthread_create (&thread2, NULL, thread2_func, NULL);
+ assert (i == 0);
+
+ if (!standalone)
+ {
+ tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
+ if (tracer == 0)
+ {
+ fprintf (stderr, "The testcase must be run by GDB!\n");
+ exit (EXIT_FAILURE);
+ }
+ if (tracer != getppid ())
+ {
+ fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* SIGCONT our debugger in the case of our crash as we would deadlock
+ otherwise. */
+
+ atexit (cleanup);
+
+ printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
+
+ if (tracer)
+ {
+ i = kill (tracer, SIGSTOP);
+ assert (i == 0);
+ state_wait (tracer, "T (stopped)");
+ }
+
+ timed_mutex_lock (&thread1_tid_mutex);
+ timed_mutex_lock (&thread2_tid_mutex);
+
+ /* Let the threads start. */
+ i = pthread_mutex_unlock (&gdbstop_mutex);
+ assert (i == 0);
+
+ printf ("Waiting till the threads initialize their TIDs.\n");
+
+ if (thread1_tid == 0)
+ {
+ i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
+ assert (i == 0);
+
+ assert (thread1_tid > 0);
+ }
+
+ if (thread2_tid == 0)
+ {
+ i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
+ assert (i == 0);
+
+ assert (thread2_tid > 0);
+ }
+
+ printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
+ (unsigned long) thread1_tid, (unsigned long) thread2_tid,
+ (unsigned long) getpid ());
+
+ printf ("Waiting till the threads get trapped by the watchpoints.\n");
+
+ if (tracer)
+ {
+ /* s390x-unknown-linux-gnu will fail with "R (running)". */
+
+ state_wait (thread1_tid, "T (tracing stop)");
+
+ state_wait (thread2_tid, "T (tracing stop)");
+ }
+
+ cleanup ();
+
+ printf ("Joining the threads.\n");
+
+ i = pthread_mutex_unlock (&terminate_mutex);
+ assert (i == 0);
+
+ i = pthread_join (thread1, NULL);
+ assert (i == 0);
+
+ i = pthread_join (thread2, NULL);
+ assert (i == 0);
+
+ printf ("Exiting.\n"); /* break-at-exit */
+
+ /* Just prevent compiler `warning: ‘unusedX_rwatch’ defined but not used'. */
+ unused1_rwatch = 1;
+ unused2_rwatch = 2;
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
@@ -0,0 +1,101 @@
+# 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/>.
+
+# Test GDB can cope with two watchpoints being hit by different threads at the
+# same time, GDB reports one of them and after "continue" to report the other
+# one GDB should not be confused by differently set watchpoints that time.
+# This is the goal of "reorder1". "reorder0" tests the basic functionality of
+# two watchpoint being hit at the same time, without reordering them during the
+# stop. The formerly broken functionality is due to the all-stop mode default
+# "show breakpoint always-inserted" being "off". Formerly the remembered hit
+# could be assigned during continuation of a thread with pending SIGTRAP to the
+# different/new watchpoint, just based on the watchpoint/debug register number.
+
+if {[target_info exists gdb,no_hardware_watchpoints]
+ || ![istarget *-*-linux*]} {
+ return 0;
+}
+
+set testfile "watchthreads-reorder"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" ${binfile} executable [list debug additional_flags=-lrt]] != "" } {
+ return -1
+}
+
+foreach reorder {0 1} {
+
+ global pf_prefix
+ set prefix_test $pf_prefix
+ lappend pf_prefix "reorder$reorder:"
+
+ clean_restart $testfile
+
+ gdb_test "set can-use-hw-watchpoints 1"
+
+ if ![runto_main] {
+ gdb_suppress_tests
+ }
+
+ # Use "rwatch" as "watch" would report the watchpoint changed just based on its
+ # read memory value during a stop by unrelated event. We are interested to not
+ # to lose the hardware watchpoint trigger.
+
+ gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: thread1_rwatch"
+ gdb_test {set $rwatch1=$bpnum}
+ set test "rwatch thread2_rwatch"
+ gdb_test_multiple $test $test {
+ -re "Target does not support this type of hardware watchpoint\\.\r\n$gdb_prompt $" {
+ # ppc64 supports at most 1 hw watchpoints.
+ unsupported $test
+ return
+ }
+ -re "Hardware read watchpoint \[0-9\]+: thread2_rwatch\r\n$gdb_prompt $" {
+ pass $test
+ }
+ }
+ gdb_test {set $rwatch2=$bpnum}
+ gdb_breakpoint [gdb_get_line_number "break-at-exit"]
+
+ # The watchpoints can happen in arbitrary order depending on random:
+ # SEL: Found 2 SIGTRAP events, selecting #[01]
+ # As GDB contains no srand() on the specific host/OS it will behave always the
+ # same. Such order cannot be guaranteed for GDB in general.
+
+ gdb_test "continue" \
+ "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue = 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \
+ "continue a"
+
+ if $reorder {
+ gdb_test {delete $rwatch1}
+ gdb_test {delete $rwatch2}
+
+ gdb_test "rwatch unused1_rwatch" "Hardware read watchpoint \[0-9\]+: unused1_rwatch"
+ gdb_test "rwatch unused2_rwatch" "Hardware read watchpoint \[0-9\]+: unused2_rwatch"
+
+ gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: thread1_rwatch"
+ gdb_test "rwatch thread2_rwatch" "Hardware read watchpoint \[0-9\]+: thread2_rwatch"
+ }
+
+ gdb_test "continue" \
+ "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue = 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \
+ "continue b"
+
+ gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*"
+
+ set pf_prefix $prefix_test
+}
^ permalink raw reply [flat|nested] 9+ messages in thread* [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-08-17 19:46 [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit Jan Kratochvil @ 2009-10-02 22:13 ` Jan Kratochvil 2009-10-02 23:01 ` Joel Brobecker 0 siblings, 1 reply; 9+ messages in thread From: Jan Kratochvil @ 2009-10-02 22:13 UTC (permalink / raw) To: gdb-patches Hi, the patch got extended by this incremental change (fixing a regression): gdb/ Fix assertion failure with `set debug infrun 1'. * infrun.c (handle_inferior_event <debug_infrun>): New variable old_chain. Temporarily switch INFERIOR_PTID. * target.h (target_stopped_by_watchpoint): Extend the comment. (target_stopped_data_address): New comment. Rename X as ADDR_P. gdb/testsuite/ * gdb.threads/watchthreads-reorder.exp: Call `set debug infrun 1'. Thanks, Jan ------------------------------------------------------------------------------ If you hit two watchpoints at the same time in the default all-stop mode GDB does not cope well with the pending watchpoint hit in non-current LWP. Moreover when you change the watchpoints setup during such stop. The is a bit problem the dependency on debug register number is specific to x86* (on other arches hardware may directly report the address of triggered watchpoint). But it is also dependent on Linux as it depends on LWPs (so i386-nat.c is too general). And it would need to be duplicate in both {i386,amd64}-linux-nat.c. Therefore the code is put in linux-nat.c where it does not hurt non-x86* arches (it may not even get installed on those). The testcase reproducer is difficult to make reliable (as is now): GDB must find two threads both with a watchpoint hit at the single stop. The used SIGSTOP of GDB is a bit fragile as the inferior being traced must not trigger a ptrace event otherwise it deadlocks with its own debugger SIGSTOPped. gdb/ 2009-10-03 Jan Kratochvil <jan.kratochvil@redhat.com> Fix reordered watchpoints triggered in other threads during all-stop. * i386-nat.c (i386_stopped_by_watchpoint): Call i386_stopped_data_address through the target vector. * linux-nat.c (save_sigtrap, linux_nat_stopped_data_address): New. (stop_wait_callback, linux_nat_filter_event): Call save_sigtrap. (linux_nat_add_target): Install linux_nat_stopped_data_address. * linux-nat.h (struct lwp_info): New fields watchpoint_hit_set and watchpoint_hit. * infrun.c (handle_inferior_event <debug_infrun>): New variable old_chain. Temporarily switch INFERIOR_PTID. * target.h (target_stopped_by_watchpoint): Extend the comment. (target_stopped_data_address): New comment. Rename X as ADDR_P. gdb/testsuite/ 2009-10-03 Jan Kratochvil <jan.kratochvil@redhat.com> * watchthreads-reorder.exp, watchthreads-reorder.c: New. --- a/gdb/i386-nat.c +++ b/gdb/i386-nat.c @@ -590,7 +590,7 @@ static int i386_stopped_by_watchpoint (void) { CORE_ADDR addr = 0; - return i386_stopped_data_address (¤t_target, &addr); + return target_stopped_data_address (¤t_target, &addr); } /* Return non-zero if the inferior has some break/watchpoint that --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -2839,6 +2839,9 @@ targets should add new threads to the thread list themselves in non-stop mode.") { struct regcache *regcache = get_thread_regcache (ecs->ptid); struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct cleanup *old_chain = save_inferior_ptid (); + + inferior_ptid = ecs->ptid; fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = %s\n", paddress (gdbarch, stop_pc)); @@ -2855,6 +2858,8 @@ targets should add new threads to the thread list themselves in non-stop mode.") fprintf_unfiltered (gdb_stdlog, "infrun: (no data address available)\n"); } + + do_cleanups (old_chain); } if (stepping_past_singlestep_breakpoint) --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2406,6 +2406,45 @@ maybe_clear_ignore_sigint (struct lwp_info *lp) } } +/* Fetch the possible triggered data watchpoint info and store it to LP. + The hardware data watchpoint trigger gets cleared during this fetch. */ + +static void +save_sigtrap (struct lwp_info *lp) +{ + struct cleanup *old_chain; + + /* linux_nat_stopped_data_address is not even installed in this case. */ + if (linux_ops->to_stopped_data_address == NULL) + return; + + old_chain = save_inferior_ptid (); + inferior_ptid = lp->ptid; + + lp->watchpoint_hit_set = + linux_ops->to_stopped_data_address (¤t_target, &lp->watchpoint_hit); + + do_cleanups (old_chain); +} + +/* Wrap target_stopped_data_address where the GNU/Linux native target may be + directed by the watchpoint/debug register number. Base the reported value + on the triggered data address instead. During inferior stop the assignment + of watchpoint/debug registers may change making the register number specific + trigger info stale. */ + +static int +linux_nat_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) +{ + struct lwp_info *lp = find_lwp_pid (inferior_ptid); + + gdb_assert (lp != NULL); + + *addr_p = lp->watchpoint_hit; + + return lp->watchpoint_hit_set; +} + /* Wait until LP is stopped. */ static int @@ -2457,6 +2496,8 @@ stop_wait_callback (struct lwp_info *lp, void *data) /* Save the trap's siginfo in case we need it later. */ save_siginfo (lp); + save_sigtrap (lp); + /* Now resume this LWP and get the SIGSTOP event. */ errno = 0; ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); @@ -2856,9 +2897,13 @@ linux_nat_filter_event (int lwpid, int status, int options) return NULL; } - /* Save the trap's siginfo in case we need it later. */ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP) - save_siginfo (lp); + { + /* Save the trap's siginfo in case we need it later. */ + save_siginfo (lp); + + save_sigtrap (lp); + } /* Check if the thread has exited. */ if ((WIFEXITED (status) || WIFSIGNALED (status)) @@ -5152,6 +5197,8 @@ linux_nat_add_target (struct target_ops *t) t->to_thread_alive = linux_nat_thread_alive; t->to_pid_to_str = linux_nat_pid_to_str; t->to_has_thread_control = tc_schedlock; + if (linux_ops->to_stopped_data_address) + t->to_stopped_data_address = linux_nat_stopped_data_address; t->to_can_async_p = linux_nat_can_async_p; t->to_is_async_p = linux_nat_is_async_p; --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -62,6 +62,12 @@ struct lwp_info be the address of a hardware watchpoint. */ struct siginfo siginfo; + /* WATCHPOINT_HIT_SET is non-zero if this LWP stopped with a trap and a data + watchpoint has been found as triggered. In such case WATCHPOINT_HIT + contains data address of the triggered data watchpoint. */ + unsigned watchpoint_hit_set : 1; + CORE_ADDR watchpoint_hit; + /* Non-zero if we expect a duplicated SIGINT. */ int ignore_sigint; --- a/gdb/target.h +++ b/gdb/target.h @@ -1124,7 +1124,7 @@ extern char *normal_pid_to_str (ptid_t ptid); /* Hardware watchpoint interfaces. */ /* Returns non-zero if we were stopped by a hardware watchpoint (memory read or - write). */ + write). Only the INFERIOR_PTID task is being queried. */ #define target_stopped_by_watchpoint \ (*current_target.to_stopped_by_watchpoint) @@ -1172,8 +1172,11 @@ extern char *normal_pid_to_str (ptid_t ptid); #define target_remove_hw_breakpoint(gdbarch, bp_tgt) \ (*current_target.to_remove_hw_breakpoint) (gdbarch, bp_tgt) -#define target_stopped_data_address(target, x) \ - (*target.to_stopped_data_address) (target, x) +/* Return non-zero if target knows the data address which triggered this + target_stopped_by_watchpoint, in such case place it to *ADDR_P. Only the + INFERIOR_PTID task is being queried. */ +#define target_stopped_data_address(target, addr_p) \ + (*target.to_stopped_data_address) (target, addr_p) #define target_watchpoint_addr_within_range(target, addr, start, length) \ (*target.to_watchpoint_addr_within_range) (target, addr, start, length) --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.c @@ -0,0 +1,366 @@ +/* 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/>. */ + +#define _GNU_SOURCE +#include <pthread.h> +#include <stdio.h> +#include <limits.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <asm/unistd.h> + +#define gettid() syscall (__NR_gettid) + +/* Terminate always in the main task, it can lock up with SIGSTOPped GDB + otherwise. */ +#define TIMEOUT (gettid () == getpid() ? 10 : 15) + +static pthread_mutex_t gdbstop_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +static pid_t thread1_tid; +static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +static pid_t thread2_tid; +static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +static volatile int thread1_rwatch; +static volatile int thread2_rwatch; + +static int unused1_rwatch; +static int unused2_rwatch; + +/* Do not use alarm as it would create a ptrace event which would hang up us if + * we are being traced by GDB which we stopped ourselves. */ + +static void timed_mutex_lock (pthread_mutex_t *mutex) +{ + int i; + struct timespec start, now; + + i = clock_gettime (CLOCK_MONOTONIC, &start); + assert (i == 0); + + do + { + i = pthread_mutex_trylock (mutex); + if (i == 0) + return; + assert (i == EBUSY); + + i = clock_gettime (CLOCK_MONOTONIC, &now); + assert (i == 0); + assert (now.tv_sec >= start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for internal lock!\n"); + exit (EXIT_FAILURE); +} + +static void * +thread1_func (void *unused) +{ + int i; + volatile int rwatch_store; + + thread1_tid = gettid (); + i = pthread_cond_signal (&thread1_tid_cond); + assert (i == 0); + + /* Be sure GDB is already stopped before continuing. */ + timed_mutex_lock (&gdbstop_mutex); + i = pthread_mutex_unlock (&gdbstop_mutex); + assert (i == 0); + + rwatch_store = thread1_rwatch; + + /* Be sure the "T (tracing stop)" test can proceed for both threads. */ + timed_mutex_lock (&terminate_mutex); + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + return NULL; +} + +static void * +thread2_func (void *unused) +{ + int i; + volatile int rwatch_store; + + thread2_tid = gettid (); + i = pthread_cond_signal (&thread2_tid_cond); + assert (i == 0); + + /* Be sure GDB is already stopped before continuing. */ + timed_mutex_lock (&gdbstop_mutex); + i = pthread_mutex_unlock (&gdbstop_mutex); + assert (i == 0); + + rwatch_store = thread2_rwatch; + + /* Be sure the "T (tracing stop)" test can proceed for both threads. */ + timed_mutex_lock (&terminate_mutex); + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + return NULL; +} + +static const char * +proc_string (const char *filename, const char *line) +{ + FILE *f; + static char buf[LINE_MAX]; + size_t line_len = strlen (line); + + f = fopen (filename, "r"); + if (f == NULL) + { + fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + while (errno = 0, fgets (buf, sizeof (buf), f)) + { + char *s; + + s = strchr (buf, '\n'); + assert (s != NULL); + *s = 0; + + if (strncmp (buf, line, line_len) != 0) + continue; + + if (fclose (f)) + { + fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + + return &buf[line_len]; + } + if (errno != 0) + { + fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno)); + exit (EXIT_FAILURE); + } + fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line); + exit (EXIT_FAILURE); +} + +static unsigned long +proc_ulong (const char *filename, const char *line) +{ + const char *s = proc_string (filename, line); + long retval; + char *end; + + errno = 0; + retval = strtol (s, &end, 10); + if (retval < 0 || retval >= LONG_MAX || (end && *end)) + { + fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval, + strerror (errno)); + exit (EXIT_FAILURE); + } + return retval; +} + +static void +state_wait (pid_t process, const char *wanted) +{ + char *filename; + int i; + struct timespec start, now; + const char *state; + + i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process); + assert (i > 0); + + i = clock_gettime (CLOCK_MONOTONIC, &start); + assert (i == 0); + + do + { + state = proc_string (filename, "State:\t"); + if (strcmp (state, wanted) == 0) + { + free (filename); + return; + } + + if (sched_yield ()) + { + perror ("sched_yield()"); + exit (EXIT_FAILURE); + } + + i = clock_gettime (CLOCK_MONOTONIC, &now); + assert (i == 0); + assert (now.tv_sec >= start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n", + (unsigned long) process, wanted, state); + exit (EXIT_FAILURE); +} + +static volatile pid_t tracer = 0; +static pthread_t thread1, thread2; + +static void +cleanup (void) +{ + printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + int i; + int tracer_save = tracer; + + tracer = 0; + + i = kill (tracer_save, SIGCONT); + assert (i == 0); + } +} + +int +main (int argc, char **argv) +{ + int i; + int standalone = 0; + + if (argc == 2 && strcmp (argv[1], "-s") == 0) + standalone = 1; + else + assert (argc == 1); + + setbuf (stdout, NULL); + + timed_mutex_lock (&gdbstop_mutex); + + timed_mutex_lock (&terminate_mutex); + + i = pthread_create (&thread1, NULL, thread1_func, NULL); + assert (i == 0); + + i = pthread_create (&thread2, NULL, thread2_func, NULL); + assert (i == 0); + + if (!standalone) + { + tracer = proc_ulong ("/proc/self/status", "TracerPid:\t"); + if (tracer == 0) + { + fprintf (stderr, "The testcase must be run by GDB!\n"); + exit (EXIT_FAILURE); + } + if (tracer != getppid ()) + { + fprintf (stderr, "The testcase parent must be our GDB tracer!\n"); + exit (EXIT_FAILURE); + } + } + + /* SIGCONT our debugger in the case of our crash as we would deadlock + otherwise. */ + + atexit (cleanup); + + printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + i = kill (tracer, SIGSTOP); + assert (i == 0); + state_wait (tracer, "T (stopped)"); + } + + timed_mutex_lock (&thread1_tid_mutex); + timed_mutex_lock (&thread2_tid_mutex); + + /* Let the threads start. */ + i = pthread_mutex_unlock (&gdbstop_mutex); + assert (i == 0); + + printf ("Waiting till the threads initialize their TIDs.\n"); + + if (thread1_tid == 0) + { + i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex); + assert (i == 0); + + assert (thread1_tid > 0); + } + + if (thread2_tid == 0) + { + i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex); + assert (i == 0); + + assert (thread2_tid > 0); + } + + printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n", + (unsigned long) thread1_tid, (unsigned long) thread2_tid, + (unsigned long) getpid ()); + + printf ("Waiting till the threads get trapped by the watchpoints.\n"); + + if (tracer) + { + /* s390x-unknown-linux-gnu will fail with "R (running)". */ + + state_wait (thread1_tid, "T (tracing stop)"); + + state_wait (thread2_tid, "T (tracing stop)"); + } + + cleanup (); + + printf ("Joining the threads.\n"); + + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + i = pthread_join (thread1, NULL); + assert (i == 0); + + i = pthread_join (thread2, NULL); + assert (i == 0); + + printf ("Exiting.\n"); /* break-at-exit */ + + /* Just prevent compiler `warning: ^[$B!F^[(BunusedX_rwatch^[$B!G^[(B defined but not used'. */ + unused1_rwatch = 1; + unused2_rwatch = 2; + + return EXIT_SUCCESS; +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp @@ -0,0 +1,105 @@ +# 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/>. + +# Test GDB can cope with two watchpoints being hit by different threads at the +# same time, GDB reports one of them and after "continue" to report the other +# one GDB should not be confused by differently set watchpoints that time. +# This is the goal of "reorder1". "reorder0" tests the basic functionality of +# two watchpoint being hit at the same time, without reordering them during the +# stop. The formerly broken functionality is due to the all-stop mode default +# "show breakpoint always-inserted" being "off". Formerly the remembered hit +# could be assigned during continuation of a thread with pending SIGTRAP to the +# different/new watchpoint, just based on the watchpoint/debug register number. + +if {[target_info exists gdb,no_hardware_watchpoints] + || ![istarget *-*-linux*]} { + return 0; +} + +set testfile "watchthreads-reorder" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" ${binfile} executable [list debug additional_flags=-lrt]] != "" } { + return -1 +} + +foreach reorder {0 1} { + + global pf_prefix + set prefix_test $pf_prefix + lappend pf_prefix "reorder$reorder:" + + clean_restart $testfile + + gdb_test "set can-use-hw-watchpoints 1" + + if ![runto_main] { + gdb_suppress_tests + } + + # Use "rwatch" as "watch" would report the watchpoint changed just based on its + # read memory value during a stop by unrelated event. We are interested to not + # to lose the hardware watchpoint trigger. + + gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: thread1_rwatch" + gdb_test {set $rwatch1=$bpnum} + set test "rwatch thread2_rwatch" + gdb_test_multiple $test $test { + -re "Target does not support this type of hardware watchpoint\\.\r\n$gdb_prompt $" { + # ppc64 supports at most 1 hw watchpoints. + unsupported $test + return + } + -re "Hardware read watchpoint \[0-9\]+: thread2_rwatch\r\n$gdb_prompt $" { + pass $test + } + } + gdb_test {set $rwatch2=$bpnum} + gdb_breakpoint [gdb_get_line_number "break-at-exit"] + + # The watchpoints can happen in arbitrary order depending on random: + # SEL: Found 2 SIGTRAP events, selecting #[01] + # As GDB contains no srand() on the specific host/OS it will behave always the + # same. Such order cannot be guaranteed for GDB in general. + + gdb_test "continue" \ + "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue = 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \ + "continue a" + + if $reorder { + gdb_test {delete $rwatch1} + gdb_test {delete $rwatch2} + + gdb_test "rwatch unused1_rwatch" "Hardware read watchpoint \[0-9\]+: unused1_rwatch" + gdb_test "rwatch unused2_rwatch" "Hardware read watchpoint \[0-9\]+: unused2_rwatch" + + gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: thread1_rwatch" + gdb_test "rwatch thread2_rwatch" "Hardware read watchpoint \[0-9\]+: thread2_rwatch" + } + + gdb_test "continue" \ + "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue = 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \ + "continue b" + + # While the debug output itself is not checked in this testcase one bug was + # found in the DEBUG_INFRUN code path. + gdb_test "set debug infrun 1" + + gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*" + + set pf_prefix $prefix_test +} ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-02 22:13 ` [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] Jan Kratochvil @ 2009-10-02 23:01 ` Joel Brobecker 2009-10-03 17:23 ` Jan Kratochvil 0 siblings, 1 reply; 9+ messages in thread From: Joel Brobecker @ 2009-10-02 23:01 UTC (permalink / raw) To: Jan Kratochvil; +Cc: gdb-patches Jan, > the patch got extended by this incremental change (fixing a regression): > > gdb/ > Fix assertion failure with `set debug infrun 1'. > * infrun.c (handle_inferior_event <debug_infrun>): New variable > old_chain. Temporarily switch INFERIOR_PTID. > * target.h (target_stopped_by_watchpoint): Extend the comment. > (target_stopped_data_address): New comment. Rename X as ADDR_P. Can we treat this part independently from the rest? I assume that this is a latent issue that only gets uncovered by your watchpoint patch? I think the bug is there regardless, and even if we can't test it now, it's going to simplify a bit my task if we treat this piece independently. I think that your fix is not optimal. Here is what I suggest instead: Make the ptid an explicit parameter in the call to target_stopped_by_watchpoint. There are two parts to this change, and I think we can find a way of making the change in two steps: - First step: Change the profile of target_stopped_by_watchpoint to add a ptid (say as the second argument). We're also trying to get rid of these target_... macros, so now is the time to turn that macro into a function. We update all the callers to pass the ptid (could be either inferior_ptid or ecs-ptid in your infrun case). We hold off the update of all the to_stopped_data_address callbacks in struct target_ops for now. This means that we need to keep the current interface as is, and that we need to temporarily change the inferior_ptid before calling the callback. - Second step: Change the callbacks interface, and update all the callbacks. This is trickier. For the platforms that we can test, I suggest trying to fix the code accordingly. For the others, do the same as before: Start by changing the inferior_ptid during the life of the callback. This second step might or might not be worth it, as the code on which the callbacks depend might not entirely ready. But at least we pushed the mess to the target code. Since I'm dumping some cleanup work on you, I can take care of the second part if you are willing to take care of the first one. -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-02 23:01 ` Joel Brobecker @ 2009-10-03 17:23 ` Jan Kratochvil 2009-10-07 18:39 ` Joel Brobecker 0 siblings, 1 reply; 9+ messages in thread From: Jan Kratochvil @ 2009-10-03 17:23 UTC (permalink / raw) To: Joel Brobecker; +Cc: gdb-patches Hi Joel, I fully agree with you (I started to code the new parameter there then I did reset --hard it and provided the save_inferior_ptid hack there instead). Still I would like to get accepted first the watchpoints set and the cleanups can get in later, the watchpoints were first posted for FSF GDB on 14 Oct 2007. There are currently/still 31 usages of save_inferior_ptid() in FSF GDB HEAD. Unfortunately it is unavoidable to patch it in/before the patch 3/4 because gdb_assert() in linux_nat_stopped_data_address() triggers otherwise (GDB just harmlessly reported wrong debug output before). Removing that gdb_assert() is inappropriate as it is a correct assumption at that point. Or do you suggest different patch schedule? Thanks, Jan On Sat, 03 Oct 2009 01:01:24 +0200, Joel Brobecker wrote: > Jan, > > > the patch got extended by this incremental change (fixing a regression): > > > > gdb/ > > Fix assertion failure with `set debug infrun 1'. > > * infrun.c (handle_inferior_event <debug_infrun>): New variable > > old_chain. Temporarily switch INFERIOR_PTID. > > * target.h (target_stopped_by_watchpoint): Extend the comment. > > (target_stopped_data_address): New comment. Rename X as ADDR_P. > > Can we treat this part independently from the rest? I assume that > this is a latent issue that only gets uncovered by your watchpoint > patch? I think the bug is there regardless, and even if we can't test > it now, it's going to simplify a bit my task if we treat this piece > independently. > > I think that your fix is not optimal. Here is what I suggest instead: > Make the ptid an explicit parameter in the call to > target_stopped_by_watchpoint. There are two parts to this change, > and I think we can find a way of making the change in two steps: > > - First step: Change the profile of target_stopped_by_watchpoint > to add a ptid (say as the second argument). We're also trying > to get rid of these target_... macros, so now is the time to > turn that macro into a function. We update all the callers > to pass the ptid (could be either inferior_ptid or ecs-ptid > in your infrun case). > > We hold off the update of all the to_stopped_data_address > callbacks in struct target_ops for now. This means that we need > to keep the current interface as is, and that we need to temporarily > change the inferior_ptid before calling the callback. > > - Second step: Change the callbacks interface, and update all > the callbacks. This is trickier. For the platforms that we can test, > I suggest trying to fix the code accordingly. For the others, do > the same as before: Start by changing the inferior_ptid during > the life of the callback. > > This second step might or might not be worth it, as the code > on which the callbacks depend might not entirely ready. But > at least we pushed the mess to the target code. > > Since I'm dumping some cleanup work on you, I can take care of the > second part if you are willing to take care of the first one. > > -- > Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-03 17:23 ` Jan Kratochvil @ 2009-10-07 18:39 ` Joel Brobecker 2009-10-12 16:00 ` Jan Kratochvil 0 siblings, 1 reply; 9+ messages in thread From: Joel Brobecker @ 2009-10-07 18:39 UTC (permalink / raw) To: Jan Kratochvil; +Cc: gdb-patches > Still I would like to get accepted first the watchpoints set and the > cleanups can get in later, the watchpoints were first posted for FSF > GDB on 14 Oct 2007. We can definitely work on that, although it's very slow going because I'm actually learning everything that you must have been learning when you first started looking at the issues. However, my concern about postponing this cleanup is that we might forget about it as soon as this patch set is included. And even if we don't, we might forget about this workaround that we're adding right now. My suggestion, instead of wrapping that one call to target_stopped_by_watchpoint was to turn that macro into a function which does the wrapping for you, before it eventually calls the target_ops method. I cannot find the linux_nat_stopped_data_address that you are refering to, not even in the patches you sent so I don't know if it's affecting my suggestion or not. -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-07 18:39 ` Joel Brobecker @ 2009-10-12 16:00 ` Jan Kratochvil 2009-10-12 18:58 ` Eli Zaretskii ` (2 more replies) 0 siblings, 3 replies; 9+ messages in thread From: Jan Kratochvil @ 2009-10-12 16:00 UTC (permalink / raw) To: Joel Brobecker; +Cc: gdb-patches On Wed, 07 Oct 2009 20:38:58 +0200, Joel Brobecker wrote: > My suggestion, instead of wrapping that one call to > target_stopped_by_watchpoint was to turn that macro into a function which > does the wrapping for you, before it eventually calls the target_ops method. Provided some patch, it is regression tested on {x86_64,x86_64-m32,i686}-fedora11-linux-gnu, it would be better to regression test it in with the hw-watchpoints patchset after its complete regression testing also for the upgraded (not deprecated_* symbols using) targets ia64, ppc*, s390*. If we should do the upgrade my primary reason for this change is that I find the single functionality being split into two target functions (to_stopped_by_watchpoint and to_stopped_data_address) to be confusing. Chose a new name to easily be able to keep the old deprecated implementations working until its host maintainers can get to update them as I cannot even compile some of the host files. Asking if this is approved to rebase the hw-watchpoints fixes on top of this patch. This patch also changes the former sentence in the doc which I was not aware of before as internally x86 was violating this rule: Then @value{GDBN} calls @code{target_stopped_data_address} exactly once. But maybe if this rule is still valid linux_nat_stopped_data_address can be dropped and I should rework the patchset including this patch instead. > I cannot find the linux_nat_stopped_data_address that you are refering to, [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] http://sourceware.org/ml/gdb-patches/2009-10/msg00075.html Thanks, Jan gdb/ Introduce new target_thread_stopped_by_watchpoint. * amd64-linux-nat.c (amd64_linux_dr_set, amd64_linux_dr_set_control) (amd64_linux_dr_set_addr, amd64_linux_dr_reset_addr): New comments. (amd64_linux_dr_get_status): New parameter ptid, use it, new comment. * breakpoint.c (update_watchpoint): Extend the comment. (watchpoints_triggered): Drop the parameter ws, new parameter ptid. Call the new relay target_thread_stopped_by_watchpoint. Move the state specific code to ... (watchpoints_triggered_no, watchpoints_triggered_yes_address_unknown) (watchpoints_triggered_yes_address_known): ... these new functions. * breakpoint.h (watchpoints_triggered): Update the prototype. * go32-nat.c (go32_get_dr6): New parameter ptid. * i386-darwin-nat.c (i386_darwin_dr_get_status): Likewise. * i386-linux-nat.c (i386_linux_dr_get, i386_linux_dr_set) (i386_linux_dr_set_control, i386_linux_dr_set_addr) (i386_linux_dr_reset_addr): New comments. (i386_linux_dr_get_status): New parameter ptid, use it, new comment. * i386-nat.c (i386_stopped_data_address): Rename to ... (i386_thread_stopped_by_watchpoint): ... this function, drop parameter ops, new parameter ptid, new return type. Call i386_dr_low.get_status with new ptid. Drop variables addr and rc. Permit addr_p to be NULL. Drop redundant check of addr if maint_show_dr. (i386_stopped_by_watchpoint): Remove. (i386_use_watchpoints): Drop the initialization of to_stopped_by_watchpoint and to_stopped_data_address. New initialization of to_thread_stopped_by_watchpoint. * i386-nat.h (struct i386_dr_low_type): Extend comments for set_control, set_addr, reset_addr and get_status. (struct i386_dr_low_type <get_status>): New parameter ptid. * i386bsd-nat.c (i386bsd_dr_get_status): New parameter ptid, use it. * i386bsd-nat.h (i386bsd_dr_get_status): New parameter ptid. * ia64-linux-nat.c (ia64_linux_stopped_data_address): Rename to ... (ia64_linux_thread_stopped_by_watchpoint): ... this function, drop parameter ops, new parameter ptid, use it, rename addr_p to data_address_p, new return type. Move the regcache initialization later. Permit data_address_p to be NULL. (ia64_linux_stopped_by_watchpoint): Remove. (_initialize_ia64_linux_nat): Drop the initialization of to_stopped_by_watchpoint and to_stopped_data_address. New initialization of to_thread_stopped_by_watchpoint. * inf-ttrace.c (inf_ttrace_target): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. * infrun.c (handle_inferior_event <debug_infrun>): Rename the variable addr as data_address. Convert the call of target_stopped_by_watchpoint and target_stopped_data_address to target_thread_stopped_by_watchpoint. (handle_inferior_event): Update the parameters of watchpoints_triggered. * mips-linux-nat.c: Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. Rename to_stopped_data_address to deprecated_stopped_data_address. * nto-procfs.c (init_procfs_ops): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. * ppc-linux-nat.c (ppc_linux_stopped_data_address): Rename to ... (ppc_linux_thread_stopped_by_watchpoint): ... this function, drop parameter ops, new parameter ptid, use it, rename addr_p to data_address_p, new return type. Permit data_address_p to be NULL. (ppc_linux_stopped_by_watchpoint): Remove. (_initialize_ppc_linux_nat): Drop the initialization of to_stopped_by_watchpoint and to_stopped_data_address. New initialization of to_thread_stopped_by_watchpoint. * procfs.c (procfs_use_watchpoints): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. * remote-m32r-sdi.c (init_m32r_ops): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. Rename to_stopped_data_address to deprecated_stopped_data_address. * remote-mips.c (_initialize_remote_mips): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. * remote.c (init_remote_ops): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. Rename to_stopped_data_address to deprecated_stopped_data_address. * s390-nat.c (s390_stopped_by_watchpoint): Rename to ... (s390_thread_stopped_by_watchpoint): ... this function, new parameter ptid, use it, new parameter data_address_p, new return type, new variable tid, initialize it. New FIXME comment on removing the trigger. (_initialize_s390_nat): Drop the initialization of to_stopped_by_watchpoint. New initialization of to_thread_stopped_by_watchpoint. * score-tdep.c (score_stopped_by_watch): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. * target.c (debug_to_stopped_by_watchpoint) (debug_to_stopped_data_address): Remove. (update_current_target): Stop calling INHERIT and de_fault for to_stopped_data_address and to_stopped_by_watchpoint. New comment for to_thread_stopped_by_watchpoint. (target_thread_stopped_by_watchpoint): New function. (setup_target_debug): Drop the initialization of to_stopped_by_watchpoint and to_stopped_data_address. * target.h (struct target_ops): Rename to_stopped_by_watchpoint to deprecated_stopped_by_watchpoint. Rename to_stopped_data_address to deprecated_stopped_data_address. New field to_thread_stopped_by_watchpoint. (target_stopped_by_watchpoint, target_stopped_data_address): Remove. (enum stopped_by_watchpoint, target_thread_stopped_by_watchpoint): New. * windows-nat.c (cygwin_get_dr6): New parameter ptid. gdb/doc/ * gdbint.texinfo (Watchpoints): Change STOPPED_BY_WATCHPOINT with target_stopped_data_address to target_thread_stopped_by_watchpoint, note the new return value. (target_stopped_data_address): Together with ... (STOPPED_BY_WATCHPOINT): ... merge and rename to ... (target_thread_stopped_by_watchpoint): ... a new node, describe the new parameters and return value. Reference the update_watchpoint comment. (target_watchpoint_addr_within_range): Update the former reference to target_stopped_data_address. (Watchpoints and Threads): Rename former STOPPED_BY_WATCHPOINT. (i386_stopped_data_address): Rename to ... (i386_thread_stopped_by_watchpoint): ... this function. (i386_stopped_by_watchpoint): Remove. --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -270,6 +270,8 @@ amd64_linux_dr_get (ptid_t ptid, int regnum) return value; } +/* Set debug register REGNUM to VALUE in only the one LWP of PTID. */ + static void amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { @@ -286,6 +288,8 @@ amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) perror_with_name (_("Couldn't write debug register")); } +/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST. */ + static void amd64_linux_dr_set_control (unsigned long control) { @@ -297,6 +301,8 @@ amd64_linux_dr_set_control (unsigned long control) amd64_linux_dr_set (ptid, DR_CONTROL, control); } +/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ + static void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) { @@ -310,16 +316,20 @@ amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); } +/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */ + static void amd64_linux_dr_reset_addr (int regnum) { amd64_linux_dr_set_addr (regnum, 0); } +/* Get DR_STATUS from only the one LWP PTID. */ + static unsigned long -amd64_linux_dr_get_status (void) +amd64_linux_dr_get_status (ptid_t ptid) { - return amd64_linux_dr_get (inferior_ptid, DR_STATUS); + return amd64_linux_dr_get (ptid, DR_STATUS); } static void --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -903,7 +903,46 @@ fetch_watchpoint_value (struct expression *exp, struct value **valp, - Update the list of values that must be watched in B->loc. If the watchpoint disposition is disp_del_at_next_stop, then do nothing. - If this is local watchpoint that is out of scope, delete it. */ + If this is local watchpoint that is out of scope, delete it. + + Even with `set breakpoint always-inserted on' the watchpoints are removed + + inserted on each stop here. Normal breakpoints must never be removed + because they might be missed by a running thread when debugging in non-stop + mode. On the other hand, hardware watchpoints (is_hardware_watchpoint; + processed here) are specific to each LWP since they are stored in each LWP's + hardware debug registers. Therefore, such LWP must be stopped first in + order to be able to modify its hardware watchpoints. + + Hardware watchpoints must be reset exactly once after being presented to the + user. It cannot be done sooner, because it would reset the data used to + present the watchpoint hit to the user. And it must not be done later + because it could display the same single watchpoint hit during multiple GDB + stops. Note that the latter is relevant only to the hardware watchpoint + types bp_read_watchpoint and bp_access_watchpoint. False hit by + bp_hardware_watchpoint is not user-visible - its hit is suppressed if the + memory content has not changed. + + The following constraints influence the location where we can reset hardware + watchpoints: + + * target_stopped_by_watchpoint and target_stopped_data_address are called + several times when GDB stops. + + [linux] + * Multiple hardware watchpoints can be hit at the same time, causing GDB to + stop. GDB only presents one hardware watchpoint hit at a time as the + reason for stopping, and all the other hits are presented later, one after + the other, each time the user requests the execution to be resumed. + Execution is not resumed for the threads still having pending hit event + stored in LWP_INFO->STATUS. While the watchpoint is already removed from + the inferior on the first stop the thread hit event is kept being reported + from its cached value by linux_nat_stopped_data_address until the real + thread resume happens after the watchpoint gets presented and thus its + LWP_INFO->STATUS gets reset. + + Therefore the hardware watchpoint hit can get safely reset on the watchpoint + removal from inferior. */ + static void update_watchpoint (struct breakpoint *b, int reparse) { @@ -2721,46 +2760,44 @@ bpstat_alloc (const struct bp_location *bl, bpstat cbs /* Current "bs" value */ bs->print_it = print_it_normal; return bs; } -\f -/* The target has stopped with waitstatus WS. Check if any hardware - watchpoints have triggered, according to the target. */ -int -watchpoints_triggered (struct target_waitstatus *ws) +/* We were not stopped by a watchpoint. Mark all watchpoints as not triggered. + */ + +static void +watchpoints_triggered_no (void) { - int stopped_by_watchpoint = target_stopped_by_watchpoint (); - CORE_ADDR addr; struct breakpoint *b; - if (!stopped_by_watchpoint) - { - /* We were not stopped by a watchpoint. Mark all watchpoints - as not triggered. */ - ALL_BREAKPOINTS (b) - if (b->type == bp_hardware_watchpoint - || b->type == bp_read_watchpoint - || b->type == bp_access_watchpoint) - b->watchpoint_triggered = watch_triggered_no; + ALL_BREAKPOINTS (b) + if (b->type == bp_hardware_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint) + b->watchpoint_triggered = watch_triggered_no; +} - return 0; - } +/* We were stopped by a watchpoint, but we don't know where. Mark all + watchpoints as unknown. */ - if (!target_stopped_data_address (¤t_target, &addr)) - { - /* We were stopped by a watchpoint, but we don't know where. - Mark all watchpoints as unknown. */ - ALL_BREAKPOINTS (b) - if (b->type == bp_hardware_watchpoint - || b->type == bp_read_watchpoint - || b->type == bp_access_watchpoint) - b->watchpoint_triggered = watch_triggered_unknown; +static void +watchpoints_triggered_yes_address_unknown (void) +{ + struct breakpoint *b; - return stopped_by_watchpoint; - } + ALL_BREAKPOINTS (b) + if (b->type == bp_hardware_watchpoint + || b->type == bp_read_watchpoint + || b->type == bp_access_watchpoint) + b->watchpoint_triggered = watch_triggered_unknown; +} - /* The target could report the data address. Mark watchpoints - affected by this data address as triggered, and all others as not - triggered. */ +/* The target could report the data address. Mark watchpoints affected by this + data address as triggered, and all others as not triggered. */ + +static void +watchpoints_triggered_yes_address_known (CORE_ADDR data_address) +{ + struct breakpoint *b; ALL_BREAKPOINTS (b) if (b->type == bp_hardware_watchpoint @@ -2775,15 +2812,40 @@ watchpoints_triggered (struct target_waitstatus *ws) /* Exact match not required. Within range is sufficient. */ if (target_watchpoint_addr_within_range (¤t_target, - addr, loc->address, + data_address, loc->address, loc->length)) { b->watchpoint_triggered = watch_triggered_yes; break; } } +} - return 1; +/* The target has stopped with waitstatus WS. Check if any hardware + watchpoints have triggered, according to the target. */ + +int +watchpoints_triggered (ptid_t ptid) +{ + CORE_ADDR data_address; + + switch (target_thread_stopped_by_watchpoint (ptid, &data_address)) + { + case stopped_by_watchpoint_no: + watchpoints_triggered_no (); + return 0; + + case stopped_by_watchpoint_yes_address_unknown: + watchpoints_triggered_yes_address_unknown (); + return 1; + + case stopped_by_watchpoint_yes_address_known: + watchpoints_triggered_yes_address_known (data_address); + return 1; + } + + /* NOTREACHED */ + return 0; } /* Possible return values for watchpoint_check (this can't be an enum --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -918,9 +918,10 @@ extern void remove_single_step_breakpoints (void); extern void *deprecated_insert_raw_breakpoint (struct gdbarch *, CORE_ADDR); extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *); -/* Check if any hardware watchpoints have triggered, according to the - target. */ -int watchpoints_triggered (struct target_waitstatus *); +/* Check if any hardware watchpoints have triggered in thread PTID, according + to the target. */ + +int watchpoints_triggered (ptid_t ptid); /* Update BUF, which is LEN bytes read from the target address MEMADDR, by replacing any memory breakpoints with their shadowed contents. */ --- a/gdb/doc/gdbint.texinfo +++ b/gdb/doc/gdbint.texinfo @@ -667,11 +667,11 @@ section is mostly irrelevant for software watchpoints. When the inferior stops, @value{GDBN} tries to establish, among other possible reasons, whether it stopped due to a watchpoint being hit. -It first uses @code{STOPPED_BY_WATCHPOINT} to see if any watchpoint -was hit. If not, all watchpoint checking is skipped. +It uses @code{target_thread_stopped_by_watchpoint} to see if any watchpoint was +hit. If its returns @code{stopped_by_watchpoint_no}, all watchpoint checking +is skipped. -Then @value{GDBN} calls @code{target_stopped_data_address} exactly -once. This method returns the address of the watchpoint which +This can also return the address of the watchpoint which triggered, if the target can determine it. If the triggered address is available, @value{GDBN} compares the address returned by this method with each watched memory address in each active watchpoint. @@ -733,24 +733,30 @@ defined by @file{breakpoint.h} as follows: @noindent These two macros should return 0 for success, non-zero for failure. -@findex target_stopped_data_address -@item target_stopped_data_address (@var{addr_p}) +@findex target_thread_stopped_by_watchpoint +@item target_thread_stopped_by_watchpoint (@var{ptid}, @var{data_address_p}) If the inferior has some watchpoint that triggered, place the address associated with the watchpoint at the location pointed to by -@var{addr_p} and return non-zero. Otherwise, return zero. This -is required for data-read and data-access watchpoints. It is -not required for data-write watchpoints, but @value{GDBN} uses -it to improve handling of those also. - -@value{GDBN} will only call this method once per watchpoint stop, -immediately after calling @code{STOPPED_BY_WATCHPOINT}. If the -target's watchpoint indication is sticky, i.e., stays set after -resuming, this method should clear it. For instance, the x86 debug -control register has sticky triggered flags. +@var{data_address_p} and return @code{stopped_by_watchpoint_yes_address_known}. +If some watchpoint has triggered but the target does not know any specific +location, return @code{stopped_by_watchpoint_yes_address_unknown}. Otherwise, +return @code{stopped_by_watchpoint_no}. This is required for data-read and +data-access watchpoints. It is not required for data-write watchpoints, but +@value{GDBN} uses it to improve handling of those also. + +See the comment at function @code{update_watchpoint} for more info. + +@value{GDBN} does not require the +@code{stopped_by_watchpoint_yes_address_unknown} vs. +@code{stopped_by_watchpoint_no} determination to be 100% correct, so if +a target cannot determine for sure whether the inferior stopped due to +a watchpoint, it could return @code{stopped_by_watchpoint_yes_address_unknown} +``just in case''. @findex target_watchpoint_addr_within_range @item target_watchpoint_addr_within_range (@var{target}, @var{addr}, @var{start}, @var{length}) -Check whether @var{addr} (as returned by @code{target_stopped_data_address}) +Check whether @var{addr} (as returned in @code{data_address_p} +from @code{target_thread_stopped_by_watchpoint}) lies within the hardware-defined watchpoint region described by @var{start} and @var{length}. This only needs to be provided if the granularity of a watchpoint is greater than one byte, i.e., if the @@ -785,21 +791,6 @@ read or write. @item CANNOT_STEP_HW_WATCHPOINTS If this is defined to a non-zero value, @value{GDBN} will remove all watchpoints before stepping the inferior. - -@findex STOPPED_BY_WATCHPOINT -@item STOPPED_BY_WATCHPOINT (@var{wait_status}) -Return non-zero if stopped by a watchpoint. @var{wait_status} is of -the type @code{struct target_waitstatus}, defined by @file{target.h}. -Normally, this macro is defined to invoke the function pointed to by -the @code{to_stopped_by_watchpoint} member of the structure (of the -type @code{target_ops}, defined on @file{target.h}) that describes the -target-specific operations; @code{to_stopped_by_watchpoint} ignores -the @var{wait_status} argument. - -@value{GDBN} does not require the non-zero value returned by -@code{STOPPED_BY_WATCHPOINT} to be 100% correct, so if a target cannot -determine for sure whether the inferior stopped due to a watchpoint, -it could return non-zero ``just in case''. @end table @subsection Watchpoints and Threads @@ -822,11 +813,10 @@ threads. at a time, although multiple events can trigger simultaneously for multi-threaded programs. When multiple events occur, @file{linux-nat.c} queues subsequent events and returns them the next time the program -is resumed. This means that @code{STOPPED_BY_WATCHPOINT} and -@code{target_stopped_data_address} only need to consult the current -thread's state---the thread indicated by @code{inferior_ptid}. If -two threads have hit watchpoints simultaneously, those routines -will be called a second time for the second thread. +is resumed. This means that @code{thread_stopped_by_watchpoint} only needs to +consult the specified thread's state. If two threads have hit watchpoints +simultaneously, that routine will be called a second time for the second +thread. @subsection x86 Watchpoints @cindex x86 debug registers @@ -921,26 +911,16 @@ watch a given region, and returns a non-zero value if that number is less than 4, the number of debug registers available to x86 processors. -@findex i386_stopped_data_address -@item i386_stopped_data_address (@var{addr_p}) +@findex i386_thread_stopped_by_watchpoint +@item i386_thread_stopped_by_watchpoint (@var{ptid}, @var{addr_p}) The target function -@code{target_stopped_data_address} is set to call this function. +@code{target_thread_stopped_by_watchpoint} is set to call this function. This function examines the breakpoint condition bits in the DR6 Debug Status register, as returned by the @code{I386_DR_LOW_GET_STATUS} macro, and returns the address associated with the first bit that is set in DR6. -@findex i386_stopped_by_watchpoint -@item i386_stopped_by_watchpoint (void) -The macro @code{STOPPED_BY_WATCHPOINT} -is set to call this function. The -argument passed to @code{STOPPED_BY_WATCHPOINT} is ignored. This -function examines the breakpoint condition bits in the DR6 Debug -Status register, as returned by the @code{I386_DR_LOW_GET_STATUS} -macro, and returns true if any bit is set. Otherwise, false is -returned. - @findex i386_insert_watchpoint @findex i386_remove_watchpoint @item i386_insert_watchpoint (@var{addr}, @var{len}, @var{type}) --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -791,7 +791,7 @@ go32_set_dr7 (unsigned long val) Here we just return the value stored in D_REGS, as we've got it from the last go32_wait call. */ static unsigned long -go32_get_dr6 (void) +go32_get_dr6 (ptid_t ptid) { return STATUS; } --- a/gdb/i386-darwin-nat.c +++ b/gdb/i386-darwin-nat.c @@ -404,7 +404,7 @@ i386_darwin_dr_reset_addr (int regnum) } unsigned long -i386_darwin_dr_get_status (void) +i386_darwin_dr_get_status (ptid_t ptid) { return i386_darwin_dr_get (DR_STATUS); } --- a/gdb/i386-linux-nat.c +++ b/gdb/i386-linux-nat.c @@ -586,6 +586,8 @@ i386_linux_store_inferior_registers (struct target_ops *ops, static unsigned long i386_linux_dr[DR_CONTROL + 1]; +/* Get debug register REGNUM value from only the one LWP of PTID. */ + static unsigned long i386_linux_dr_get (ptid_t ptid, int regnum) { @@ -614,6 +616,8 @@ i386_linux_dr_get (ptid_t ptid, int regnum) return value; } +/* Set debug register REGNUM to VALUE in only the one LWP of PTID. */ + static void i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { @@ -630,6 +634,8 @@ i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) perror_with_name (_("Couldn't write debug register")); } +/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST. */ + static void i386_linux_dr_set_control (unsigned long control) { @@ -641,6 +647,8 @@ i386_linux_dr_set_control (unsigned long control) i386_linux_dr_set (ptid, DR_CONTROL, control); } +/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ + static void i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) { @@ -654,16 +662,20 @@ i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); } +/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */ + static void i386_linux_dr_reset_addr (int regnum) { i386_linux_dr_set_addr (regnum, 0); } +/* Get DR_STATUS from only the one LWP PTID. */ + static unsigned long -i386_linux_dr_get_status (void) +i386_linux_dr_get_status (ptid_t ptid) { - return i386_linux_dr_get (inferior_ptid, DR_STATUS); + return i386_linux_dr_get (ptid, DR_STATUS); } static void --- a/gdb/i386-nat.c +++ b/gdb/i386-nat.c @@ -538,14 +538,12 @@ i386_region_ok_for_watchpoint (CORE_ADDR addr, int len) address associated with that watchpoint and return non-zero. Otherwise, return zero. */ -static int -i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) +static enum stopped_by_watchpoint +i386_thread_stopped_by_watchpoint (ptid_t ptid, CORE_ADDR *addr_p) { - CORE_ADDR addr = 0; int i; - int rc = 0; - dr_status_mirror = i386_dr_low.get_status (); + dr_status_mirror = i386_dr_low.get_status (ptid); ALL_DEBUG_REGISTERS(i) { @@ -560,25 +558,17 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) avoids false positives in windows-nat.c. */ && !I386_DR_VACANT (i)) { - addr = dr_mirror[i]; - rc = 1; + if (addr_p) + *addr_p = dr_mirror[i]; if (maint_show_dr) - i386_show_dr ("watchpoint_hit", addr, -1, hw_write); + i386_show_dr ("watchpoint_hit", dr_mirror[i], -1, hw_write); + return stopped_by_watchpoint_yes_address_known; } } - if (maint_show_dr && addr == 0) + if (maint_show_dr) i386_show_dr ("stopped_data_addr", 0, 0, hw_write); - if (rc) - *addr_p = addr; - return rc; -} - -static int -i386_stopped_by_watchpoint (void) -{ - CORE_ADDR addr = 0; - return i386_stopped_data_address (¤t_target, &addr); + return stopped_by_watchpoint_no; } /* Insert a hardware-assisted breakpoint at BP_TGT->placed_address. @@ -668,8 +658,7 @@ i386_use_watchpoints (struct target_ops *t) t->to_can_use_hw_breakpoint = i386_can_use_hw_breakpoint; t->to_region_ok_for_hw_watchpoint = i386_region_ok_for_watchpoint; - t->to_stopped_by_watchpoint = i386_stopped_by_watchpoint; - t->to_stopped_data_address = i386_stopped_data_address; + t->to_thread_stopped_by_watchpoint = i386_thread_stopped_by_watchpoint; t->to_insert_watchpoint = i386_insert_watchpoint; t->to_remove_watchpoint = i386_remove_watchpoint; t->to_insert_hw_breakpoint = i386_insert_hw_breakpoint; --- a/gdb/i386-nat.h +++ b/gdb/i386-nat.h @@ -49,16 +49,16 @@ extern void i386_use_watchpoints (struct target_ops *); functions are: set_control -- set the debug control (DR7) - register to a given value + register to a given value for all LWPs set_addr -- put an address into one debug - register + register for all LWPs reset_addr -- reset the address stored in - one debug register + one debug register for all LWPs get_status -- return the value of the debug - status (DR6) register. + status (DR6) register for LWP PTID Additionally, the native file should set the debug_register_length field to 4 or 8 depending on the number of bytes used for @@ -69,7 +69,7 @@ struct i386_dr_low_type void (*set_control) (unsigned long); void (*set_addr) (int, CORE_ADDR); void (*reset_addr) (int); - unsigned long (*get_status) (void); + unsigned long (*get_status) (ptid_t ptid); int debug_register_length; }; --- a/gdb/i386bsd-nat.c +++ b/gdb/i386bsd-nat.c @@ -308,7 +308,7 @@ i386bsd_dr_reset_addr (int regnum) } unsigned long -i386bsd_dr_get_status (void) +i386bsd_dr_get_status (ptid_t ptid) { struct dbreg dbregs; @@ -317,7 +317,7 @@ i386bsd_dr_get_status (void) way to fix this is to add the hardware breakpoint and watchpoint stuff to the target vector. For now, just return zero if the ptrace call fails. */ - if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid), + if (ptrace (PT_GETDBREGS, PIDGET (ptid), (PTRACE_TYPE_ARG3) &dbregs, 0) == -1) #if 0 perror_with_name (_("Couldn't read debug registers")); --- a/gdb/i386bsd-nat.h +++ b/gdb/i386bsd-nat.h @@ -33,6 +33,6 @@ extern void i386bsd_dr_set_addr (int regnum, CORE_ADDR addr); extern void i386bsd_dr_reset_addr (int regnum); -extern unsigned long i386bsd_dr_get_status (void); +extern unsigned long i386bsd_dr_get_status (ptid_t ptid); #endif /* i386bsd-nat.h */ --- a/gdb/ia64-linux-nat.c +++ b/gdb/ia64-linux-nat.c @@ -633,33 +633,29 @@ ia64_linux_new_thread (ptid_t ptid) enable_watchpoints_in_psr (ptid); } -static int -ia64_linux_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) +static enum stopped_by_watchpoint +ia64_linux_thread_stopped_by_watchpoint (ptid_t ptid, CORE_ADDR *data_address_p) { CORE_ADDR psr; struct siginfo *siginfo_p; - struct regcache *regcache = get_current_regcache (); - - siginfo_p = linux_nat_get_siginfo (inferior_ptid); + struct regcache *regcache; + + siginfo_p = linux_nat_get_siginfo (ptid); if (siginfo_p->si_signo != SIGTRAP || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) - return 0; + return stopped_by_watchpoint_no; + + regcache = get_thread_regcache (ptid); regcache_cooked_read_unsigned (regcache, IA64_PSR_REGNUM, &psr); psr |= IA64_PSR_DD; /* Set the dd bit - this will disable the watchpoint for the next instruction */ regcache_cooked_write_unsigned (regcache, IA64_PSR_REGNUM, psr); - *addr_p = (CORE_ADDR)siginfo_p->si_addr; - return 1; -} - -static int -ia64_linux_stopped_by_watchpoint (void) -{ - CORE_ADDR addr; - return ia64_linux_stopped_data_address (¤t_target, &addr); + if (data_address_p) + *data_address_p = (uintptr_t) siginfo_p->si_addr; + return stopped_by_watchpoint_yes_address_known; } static int @@ -831,15 +827,14 @@ _initialize_ia64_linux_nat (void) it again) if the "dd" (data debug fault disable) bit in the processor status word is set. - This PSR bit is set in ia64_linux_stopped_by_watchpoint when the + This PSR bit is set in ia64_linux_thread_stopped_by_watchpoint when the code there has determined that a hardware watchpoint has indeed been hit. The CPU will then be able to execute one instruction without triggering a watchpoint. */ t->to_have_steppable_watchpoint = 1; t->to_can_use_hw_breakpoint = ia64_linux_can_use_hw_breakpoint; - t->to_stopped_by_watchpoint = ia64_linux_stopped_by_watchpoint; - t->to_stopped_data_address = ia64_linux_stopped_data_address; + t->to_thread_stopped_by_watchpoint = ia64_linux_thread_stopped_by_watchpoint; t->to_insert_watchpoint = ia64_linux_insert_watchpoint; t->to_remove_watchpoint = ia64_linux_remove_watchpoint; --- a/gdb/inf-ttrace.c +++ b/gdb/inf-ttrace.c @@ -1260,7 +1260,7 @@ inf_ttrace_target (void) t->to_can_use_hw_breakpoint = inf_ttrace_can_use_hw_breakpoint; t->to_insert_watchpoint = inf_ttrace_insert_watchpoint; t->to_remove_watchpoint = inf_ttrace_remove_watchpoint; - t->to_stopped_by_watchpoint = inf_ttrace_stopped_by_watchpoint; + t->deprecated_stopped_by_watchpoint = inf_ttrace_stopped_by_watchpoint; t->to_region_ok_for_hw_watchpoint = inf_ttrace_region_ok_for_hw_watchpoint; t->to_kill = inf_ttrace_kill; --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -2837,21 +2837,28 @@ targets should add new threads to the thread list themselves in non-stop mode.") { struct regcache *regcache = get_thread_regcache (ecs->ptid); struct gdbarch *gdbarch = get_regcache_arch (regcache); + CORE_ADDR data_address; fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = %s\n", paddress (gdbarch, stop_pc)); - if (target_stopped_by_watchpoint ()) + + switch (target_thread_stopped_by_watchpoint (ecs->ptid, &data_address)) { - CORE_ADDR addr; - fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n"); + case stopped_by_watchpoint_no: + break; - if (target_stopped_data_address (¤t_target, &addr)) - fprintf_unfiltered (gdb_stdlog, - "infrun: stopped data address = %s\n", - paddress (gdbarch, addr)); - else - fprintf_unfiltered (gdb_stdlog, - "infrun: (no data address available)\n"); + case stopped_by_watchpoint_yes_address_unknown: + fprintf_unfiltered (gdb_stdlog, + "infrun: stopped by watchpoint\n" + "infrun: (no data address available)\n"); + break; + + case stopped_by_watchpoint_yes_address_known: + fprintf_unfiltered (gdb_stdlog, + "infrun: stopped by watchpoint\n" + "infrun: stopped data address = %s\n", + paddress (gdbarch, data_address)); + break; } } @@ -3099,7 +3106,7 @@ targets should add new threads to the thread list themselves in non-stop mode.") if (stepped_after_stopped_by_watchpoint) stopped_by_watchpoint = 0; else - stopped_by_watchpoint = watchpoints_triggered (&ecs->ws); + stopped_by_watchpoint = watchpoints_triggered (ecs->ptid); /* If necessary, step over this watchpoint. We'll be back to display it in a moment. */ --- a/gdb/mips-linux-nat.c +++ b/gdb/mips-linux-nat.c @@ -708,7 +708,7 @@ mips_linux_can_use_hw_breakpoint (int type, int cnt, int ot) return (cnt == 0) ? 1 : 0; } -/* Target to_stopped_by_watchpoint implementation. Return 1 if +/* Target deprecated_stopped_by_watchpoint implementation. Return 1 if stopped by watchpoint. The watchhi R and W bits indicate the watch register triggered. */ @@ -730,7 +730,7 @@ mips_linux_stopped_by_watchpoint (void) return 0; } -/* Target to_stopped_data_address implementation. Set the address +/* Target deprecated_stopped_data_address implementation. Set the address where the watch triggered (if known). Return 1 if the address was known. */ @@ -1074,8 +1074,8 @@ triggers a breakpoint or watchpoint."), t->to_can_use_hw_breakpoint = mips_linux_can_use_hw_breakpoint; t->to_remove_watchpoint = mips_linux_remove_watchpoint; t->to_insert_watchpoint = mips_linux_insert_watchpoint; - t->to_stopped_by_watchpoint = mips_linux_stopped_by_watchpoint; - t->to_stopped_data_address = mips_linux_stopped_data_address; + t->deprecated_stopped_by_watchpoint = mips_linux_stopped_by_watchpoint; + t->deprecated_stopped_data_address = mips_linux_stopped_data_address; t->to_region_ok_for_hw_watchpoint = mips_linux_region_ok_for_hw_watchpoint; t->to_read_description = mips_linux_read_description; --- a/gdb/nto-procfs.c +++ b/gdb/nto-procfs.c @@ -1413,7 +1413,7 @@ init_procfs_ops (void) procfs_ops.to_remove_hw_breakpoint = procfs_remove_breakpoint; procfs_ops.to_insert_watchpoint = procfs_insert_hw_watchpoint; procfs_ops.to_remove_watchpoint = procfs_remove_hw_watchpoint; - procfs_ops.to_stopped_by_watchpoint = procfs_stopped_by_watchpoint; + procfs_ops.deprecated_stopped_by_watchpoint = procfs_stopped_by_watchpoint; procfs_ops.to_terminal_init = terminal_init_inferior; procfs_ops.to_terminal_inferior = terminal_inferior; procfs_ops.to_terminal_ours_for_output = terminal_ours_for_output; --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -1397,26 +1397,20 @@ ppc_linux_new_thread (ptid_t ptid) ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value); } -static int -ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) +static enum stopped_by_watchpoint +ppc_linux_thread_stopped_by_watchpoint (ptid_t ptid, CORE_ADDR *data_address_p) { struct siginfo *siginfo_p; - siginfo_p = linux_nat_get_siginfo (inferior_ptid); + siginfo_p = linux_nat_get_siginfo (ptid); if (siginfo_p->si_signo != SIGTRAP || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) - return 0; + return stopped_by_watchpoint_no; - *addr_p = (CORE_ADDR) (uintptr_t) siginfo_p->si_addr; - return 1; -} - -static int -ppc_linux_stopped_by_watchpoint (void) -{ - CORE_ADDR addr; - return ppc_linux_stopped_data_address (¤t_target, &addr); + if (data_address_p) + *data_address_p = (uintptr_t) siginfo_p->si_addr; + return stopped_by_watchpoint_yes_address_known; } static int @@ -1648,8 +1642,7 @@ _initialize_ppc_linux_nat (void) t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint; t->to_insert_watchpoint = ppc_linux_insert_watchpoint; t->to_remove_watchpoint = ppc_linux_remove_watchpoint; - t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint; - t->to_stopped_data_address = ppc_linux_stopped_data_address; + t->to_thread_stopped_by_watchpoint = ppc_linux_thread_stopped_by_watchpoint; t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range; t->to_read_description = ppc_linux_read_description; --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -5390,7 +5390,7 @@ procfs_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) void procfs_use_watchpoints (struct target_ops *t) { - t->to_stopped_by_watchpoint = procfs_stopped_by_watchpoint; + t->deprecated_stopped_by_watchpoint = procfs_stopped_by_watchpoint; t->to_insert_watchpoint = procfs_insert_watchpoint; t->to_remove_watchpoint = procfs_remove_watchpoint; t->to_region_ok_for_hw_watchpoint = procfs_region_ok_for_hw_watchpoint; --- a/gdb/remote-m32r-sdi.c +++ b/gdb/remote-m32r-sdi.c @@ -1625,8 +1625,8 @@ init_m32r_ops (void) m32r_ops.to_can_use_hw_breakpoint = m32r_can_use_hw_watchpoint; m32r_ops.to_insert_watchpoint = m32r_insert_watchpoint; m32r_ops.to_remove_watchpoint = m32r_remove_watchpoint; - m32r_ops.to_stopped_by_watchpoint = m32r_stopped_by_watchpoint; - m32r_ops.to_stopped_data_address = m32r_stopped_data_address; + m32r_ops.deprecated_stopped_by_watchpoint = m32r_stopped_by_watchpoint; + m32r_ops.deprecated_stopped_data_address = m32r_stopped_data_address; m32r_ops.to_kill = m32r_kill; m32r_ops.to_load = m32r_load; m32r_ops.to_create_inferior = m32r_create_inferior; --- a/gdb/remote-mips.c +++ b/gdb/remote-mips.c @@ -3345,7 +3345,7 @@ _initialize_remote_mips (void) mips_ops.to_remove_breakpoint = mips_remove_breakpoint; mips_ops.to_insert_watchpoint = mips_insert_watchpoint; mips_ops.to_remove_watchpoint = mips_remove_watchpoint; - mips_ops.to_stopped_by_watchpoint = mips_stopped_by_watchpoint; + mips_ops.deprecated_stopped_by_watchpoint = mips_stopped_by_watchpoint; mips_ops.to_can_use_hw_breakpoint = mips_can_use_watchpoint; mips_ops.to_kill = mips_kill; mips_ops.to_load = mips_load; --- a/gdb/remote.c +++ b/gdb/remote.c @@ -8798,8 +8798,8 @@ Specify the serial device it is connected to\n\ remote_ops.to_files_info = remote_files_info; remote_ops.to_insert_breakpoint = remote_insert_breakpoint; remote_ops.to_remove_breakpoint = remote_remove_breakpoint; - remote_ops.to_stopped_by_watchpoint = remote_stopped_by_watchpoint; - remote_ops.to_stopped_data_address = remote_stopped_data_address; + remote_ops.deprecated_stopped_by_watchpoint = remote_stopped_by_watchpoint; + remote_ops.deprecated_stopped_data_address = remote_stopped_data_address; remote_ops.to_can_use_hw_breakpoint = remote_check_watch_resources; remote_ops.to_insert_hw_breakpoint = remote_insert_hw_breakpoint; remote_ops.to_remove_hw_breakpoint = remote_remove_hw_breakpoint; --- a/gdb/s390-nat.c +++ b/gdb/s390-nat.c @@ -253,35 +253,42 @@ struct watch_area static struct watch_area *watch_base = NULL; -static int -s390_stopped_by_watchpoint (void) +static enum stopped_by_watchpoint +s390_thread_stopped_by_watchpoint (ptid_t ptid, CORE_ADDR *data_address_p) { per_lowcore_bits per_lowcore; ptrace_area parea; int result; + int tid; /* Speed up common case. */ if (!watch_base) - return 0; + return stopped_by_watchpoint_no; + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); parea.len = sizeof (per_lowcore); parea.process_addr = (addr_t) & per_lowcore; parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore); - if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0) perror_with_name (_("Couldn't retrieve watchpoint status")); result = (per_lowcore.perc_storage_alteration == 1 && per_lowcore.perc_store_real_address == 0); + /* FIXME: To be done only in s390_remove_watchpoint. */ if (result) { /* Do not report this watchpoint again. */ memset (&per_lowcore, 0, sizeof (per_lowcore)); - if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0) + if (ptrace (PTRACE_POKEUSR_AREA, tid, &parea) < 0) perror_with_name (_("Couldn't clear watchpoint status")); } - return result; + return result ? stopped_by_watchpoint_yes_address_unknown + : stopped_by_watchpoint_no; } static void @@ -408,7 +415,7 @@ _initialize_s390_nat (void) t->to_can_use_hw_breakpoint = s390_can_use_hw_breakpoint; t->to_region_ok_for_hw_watchpoint = s390_region_ok_for_hw_watchpoint; t->to_have_continuable_watchpoint = 1; - t->to_stopped_by_watchpoint = s390_stopped_by_watchpoint; + t->to_thread_stopped_by_watchpoint = s390_thread_stopped_by_watchpoint; t->to_insert_watchpoint = s390_insert_watchpoint; t->to_remove_watchpoint = s390_remove_watchpoint; --- a/gdb/score-tdep.c +++ b/gdb/score-tdep.c @@ -70,7 +70,7 @@ score_stopped_by_watch (void) { if (strcmp (current_target.to_shortname, "sim") == 0) return soc_gh_stopped_by_watch (); - return (*current_target.to_stopped_by_watchpoint) (); + return (*current_target.deprecated_stopped_by_watchpoint) (); } int --- a/gdb/target.c +++ b/gdb/target.c @@ -124,10 +124,6 @@ static int debug_to_insert_watchpoint (CORE_ADDR, int, int); static int debug_to_remove_watchpoint (CORE_ADDR, int, int); -static int debug_to_stopped_by_watchpoint (void); - -static int debug_to_stopped_data_address (struct target_ops *, CORE_ADDR *); - static int debug_to_watchpoint_addr_within_range (struct target_ops *, CORE_ADDR, CORE_ADDR, int); @@ -622,10 +618,9 @@ update_current_target (void) INHERIT (to_remove_hw_breakpoint, t); INHERIT (to_insert_watchpoint, t); INHERIT (to_remove_watchpoint, t); - INHERIT (to_stopped_data_address, t); INHERIT (to_have_steppable_watchpoint, t); INHERIT (to_have_continuable_watchpoint, t); - INHERIT (to_stopped_by_watchpoint, t); + /* Do not inherit to_thread_stopped_by_watchpoint. */ INHERIT (to_watchpoint_addr_within_range, t); INHERIT (to_region_ok_for_hw_watchpoint, t); INHERIT (to_terminal_init, t); @@ -733,12 +728,6 @@ update_current_target (void) de_fault (to_remove_watchpoint, (int (*) (CORE_ADDR, int, int)) return_minus_one); - de_fault (to_stopped_by_watchpoint, - (int (*) (void)) - return_zero); - de_fault (to_stopped_data_address, - (int (*) (struct target_ops *, CORE_ADDR *)) - return_zero); de_fault (to_watchpoint_addr_within_range, default_watchpoint_addr_within_range); de_fault (to_region_ok_for_hw_watchpoint, @@ -2207,6 +2196,79 @@ target_mourn_inferior (void) "could not find a target to follow mourn inferiour"); } +enum stopped_by_watchpoint +target_thread_stopped_by_watchpoint (ptid_t ptid, CORE_ADDR *data_address_p) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + { + enum stopped_by_watchpoint retval; + + if (t->to_thread_stopped_by_watchpoint != NULL) + retval = t->to_thread_stopped_by_watchpoint (ptid, data_address_p); + /* Deprecated. */ + else if (t->deprecated_stopped_by_watchpoint != NULL) + { + CORE_ADDR data_address; + struct cleanup *old_chain = save_inferior_ptid (); + + inferior_ptid = ptid; + + if (!t->deprecated_stopped_by_watchpoint ()) + retval = stopped_by_watchpoint_no; + else if (t->deprecated_stopped_data_address == NULL + || !t->deprecated_stopped_data_address (t, &data_address)) + retval = stopped_by_watchpoint_yes_address_unknown; + else + { + retval = stopped_by_watchpoint_yes_address_known; + if (data_address_p) + *data_address_p = data_address; + } + + do_cleanups (old_chain); + } + else + continue; + + if (targetdebug) + { + fprintf_unfiltered (gdb_stdlog, + "target_thread_stopped_by_watchpoint ("); + if (TIDGET (ptid) != 0) + fprintf_unfiltered (gdb_stdlog, "TID %ld", TIDGET (ptid)); + else + fprintf_unfiltered (gdb_stdlog, "PID %d", PIDGET (ptid)); + fprintf_unfiltered (gdb_stdlog, ") = "); + switch (retval) + { + case stopped_by_watchpoint_no: + fprintf_unfiltered (gdb_stdlog, "stopped_by_watchpoint_no"); + break; + case stopped_by_watchpoint_yes_address_unknown: + fprintf_unfiltered (gdb_stdlog, + "stopped_by_watchpoint_yes_address_unknown"); + break; + case stopped_by_watchpoint_yes_address_known: + fprintf_unfiltered (gdb_stdlog, + "stopped_by_watchpoint_yes_address_known "); + if (data_address_p) + fprintf_unfiltered (gdb_stdlog, "(%s)", + paddress (target_gdbarch, *data_address_p)); + else + fprintf_unfiltered (gdb_stdlog, "<NULL>"); + break; + } + fprintf_unfiltered (gdb_stdlog, "\n"); + } + + return retval; + } + + return stopped_by_watchpoint_no; +} + /* Look for a target which can describe architectural features, starting from TARGET. If we find one, return its description. */ @@ -3074,33 +3136,6 @@ debug_to_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) } static int -debug_to_stopped_by_watchpoint (void) -{ - int retval; - - retval = debug_target.to_stopped_by_watchpoint (); - - fprintf_unfiltered (gdb_stdlog, - "target_stopped_by_watchpoint () = %ld\n", - (unsigned long) retval); - return retval; -} - -static int -debug_to_stopped_data_address (struct target_ops *target, CORE_ADDR *addr) -{ - int retval; - - retval = debug_target.to_stopped_data_address (target, addr); - - fprintf_unfiltered (gdb_stdlog, - "target_stopped_data_address ([0x%lx]) = %ld\n", - (unsigned long)*addr, - (unsigned long)retval); - return retval; -} - -static int debug_to_watchpoint_addr_within_range (struct target_ops *target, CORE_ADDR addr, CORE_ADDR start, int length) @@ -3420,8 +3455,6 @@ setup_target_debug (void) current_target.to_remove_hw_breakpoint = debug_to_remove_hw_breakpoint; current_target.to_insert_watchpoint = debug_to_insert_watchpoint; current_target.to_remove_watchpoint = debug_to_remove_watchpoint; - current_target.to_stopped_by_watchpoint = debug_to_stopped_by_watchpoint; - current_target.to_stopped_data_address = debug_to_stopped_data_address; current_target.to_watchpoint_addr_within_range = debug_to_watchpoint_addr_within_range; current_target.to_region_ok_for_hw_watchpoint = debug_to_region_ok_for_hw_watchpoint; current_target.to_terminal_init = debug_to_terminal_init; --- a/gdb/target.h +++ b/gdb/target.h @@ -395,10 +395,14 @@ struct target_ops int (*to_remove_hw_breakpoint) (struct gdbarch *, struct bp_target_info *); int (*to_remove_watchpoint) (CORE_ADDR, int, int); int (*to_insert_watchpoint) (CORE_ADDR, int, int); - int (*to_stopped_by_watchpoint) (void); + enum stopped_by_watchpoint (*to_thread_stopped_by_watchpoint) + (ptid_t ptid, CORE_ADDR *data_address_p); + /* Deprecated, to be replaced by to_thread_stopped_by_watchpoint. */ + int (*deprecated_stopped_by_watchpoint) (void); + /* Deprecated, to be replaced by to_thread_stopped_by_watchpoint. */ + int (*deprecated_stopped_data_address) (struct target_ops *, CORE_ADDR *); int to_have_steppable_watchpoint; int to_have_continuable_watchpoint; - int (*to_stopped_data_address) (struct target_ops *, CORE_ADDR *); int (*to_watchpoint_addr_within_range) (struct target_ops *, CORE_ADDR, CORE_ADDR, int); int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int); @@ -1126,8 +1130,15 @@ extern char *normal_pid_to_str (ptid_t ptid); /* Returns non-zero if we were stopped by a hardware watchpoint (memory read or write). */ -#define target_stopped_by_watchpoint \ - (*current_target.to_stopped_by_watchpoint) +enum stopped_by_watchpoint + { + stopped_by_watchpoint_no, + stopped_by_watchpoint_yes_address_unknown, + stopped_by_watchpoint_yes_address_known + }; + +extern enum stopped_by_watchpoint target_thread_stopped_by_watchpoint + (ptid_t ptid, CORE_ADDR *data_address_p); /* Non-zero if we have steppable watchpoints */ @@ -1172,9 +1183,6 @@ extern char *normal_pid_to_str (ptid_t ptid); #define target_remove_hw_breakpoint(gdbarch, bp_tgt) \ (*current_target.to_remove_hw_breakpoint) (gdbarch, bp_tgt) -#define target_stopped_data_address(target, x) \ - (*target.to_stopped_data_address) (target, x) - #define target_watchpoint_addr_within_range(target, addr, start, length) \ (*target.to_watchpoint_addr_within_range) (target, addr, start, length) --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -143,7 +143,7 @@ static void windows_kill_inferior (struct target_ops *); static void cygwin_set_dr (int i, CORE_ADDR addr); static void cygwin_set_dr7 (unsigned long val); -static unsigned long cygwin_get_dr6 (void); +static unsigned long cygwin_get_dr6 (ptid_t ptid); static enum target_signal last_sig = TARGET_SIGNAL_0; /* Set if a signal was received from the debugged process */ @@ -2313,7 +2313,7 @@ cygwin_set_dr7 (unsigned long val) Here we just return the value stored in dr[6] by the last call to thread_rec for current_event.dwThreadId id. */ static unsigned long -cygwin_get_dr6 (void) +cygwin_get_dr6 (ptid_t ptid) { return (unsigned long) dr[6]; } ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-12 16:00 ` Jan Kratochvil @ 2009-10-12 18:58 ` Eli Zaretskii 2009-10-13 16:02 ` Jan Kratochvil 2009-11-03 1:12 ` Joel Brobecker 2 siblings, 0 replies; 9+ messages in thread From: Eli Zaretskii @ 2009-10-12 18:58 UTC (permalink / raw) To: Jan Kratochvil; +Cc: brobecker, gdb-patches > Date: Mon, 12 Oct 2009 17:59:16 +0200 > From: Jan Kratochvil <jan.kratochvil@redhat.com> > Cc: gdb-patches@sourceware.org > > gdb/doc/ > * gdbint.texinfo (Watchpoints): Change STOPPED_BY_WATCHPOINT with > target_stopped_data_address to target_thread_stopped_by_watchpoint, > note the new return value. > (target_stopped_data_address): Together with ... > (STOPPED_BY_WATCHPOINT): ... merge and rename to ... > (target_thread_stopped_by_watchpoint): ... a new node, describe the new > parameters and return value. Reference the update_watchpoint comment. > (target_watchpoint_addr_within_range): Update the former reference to > target_stopped_data_address. > (Watchpoints and Threads): Rename former STOPPED_BY_WATCHPOINT. > (i386_stopped_data_address): Rename to ... > (i386_thread_stopped_by_watchpoint): ... this function. > (i386_stopped_by_watchpoint): Remove. This part is approved. And go32-nat.c also. Thanks. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-12 16:00 ` Jan Kratochvil 2009-10-12 18:58 ` Eli Zaretskii @ 2009-10-13 16:02 ` Jan Kratochvil 2009-11-03 1:12 ` Joel Brobecker 2 siblings, 0 replies; 9+ messages in thread From: Jan Kratochvil @ 2009-10-13 16:02 UTC (permalink / raw) To: Joel Brobecker; +Cc: gdb-patches On Mon, 12 Oct 2009 17:59:16 +0200, Jan Kratochvil wrote: > This patch also changes the former sentence in the doc which I was not aware > of before as internally x86 was violating this rule: > Then @value{GDBN} calls @code{target_stopped_data_address} exactly > once. > But maybe if this rule is still valid linux_nat_stopped_data_address can be > dropped and I should rework the patchset including this patch instead. While thinking about it: * GDB already was violating this rule calling it twice "if (debug_infrun)" (infrun.c line 2836) block of handle_inferior_event. * GDB already was violating this rule calling it zero times both by "if (stepped_after_stopped_by_watchpoint)" (infrun.c line 3100) and in other cases of "return;" above this line. * From a design point of view I do not find it right that the only function to query a value (target_stopped_data_address) also does + must reset it. * The comment at update_watchpoint() describes that for modifying watchpoints GDB needs the LWP to be stopped and so remove+reinsert cannot hurt the LWP. remove+reinsert is now always done and the trigger must be reset on watchpoint remove so there is no need to reset the trigger earlier. It may start to be an issue only of someone optimizes update_watchpoint() so that it no longer does remove+reinsert, it would need to explicitely reset the trigger that time. So I stand behind the patch to remove the trigger-reset from target_stopped_data_address. In current HEAD it is missing in i386_stopped_data_address which can be visible as a bug in my patch 1/4 testcase. The reset is present in s390_stopped_by_watchpoint - but for the s390 it is wrong anyway as it is now in target_stopped_by_watchpoint while gdbint.texinfo says it should be in target_stopped_data_address which is missing on s390. Thanks, Jan ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] 2009-10-12 16:00 ` Jan Kratochvil 2009-10-12 18:58 ` Eli Zaretskii 2009-10-13 16:02 ` Jan Kratochvil @ 2009-11-03 1:12 ` Joel Brobecker 2 siblings, 0 replies; 9+ messages in thread From: Joel Brobecker @ 2009-11-03 1:12 UTC (permalink / raw) To: Jan Kratochvil; +Cc: gdb-patches Jan, Sorry for the delay on reviewing this... I'll confess that I am having a hard time following the patch, now, since we're now trying to achieve three different things: there is the fact that we want one of the target operations to take a ptid, and then the fact that we use that opportunity to change the interface, and (oh yeah!) a bug that we're trying to fix on x86/x86_64. Baby steps are really easier to follow, at least for me. > If we should do the upgrade my primary reason for this change is that > I find the single functionality being split into two target functions > (to_stopped_by_watchpoint and to_stopped_data_address) to be > confusing. Chose a new name to easily be able to keep the old > deprecated implementations working until its host maintainers can get > to update them as I cannot even compile some of the host files. I'm having second thoughts on this one, and I am no longer seeing much benefit in one interface vs the other (personal opinion, of course). In fact, the function semantics are not very confusing when you look at how the functions are used by the callers. It might be confusing for the implementation, although that would be very easy to fix by improved documentation in the target.h code. That being said, I'm still 50/50 on this. If other maintainers would like to merge the two functions, then I would be fine with that. But I see that such a transition would leave the code in a transitional state where we have both the old code and the new code co-existing. The problem is that it does not help maintaining the code while transitioning, and I fear that transitions of this kind can take a long time before we complete them. I do not normally hesitate making this sort of progressive change when I can see a clear benefit, but I just don't see a clear-cut benefit right now. Anything I might have missed? (I left patch #1 out for now, as it is now dependent on this patch) -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2009-11-03 1:12 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-08-17 19:46 [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit Jan Kratochvil 2009-10-02 22:13 ` [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] Jan Kratochvil 2009-10-02 23:01 ` Joel Brobecker 2009-10-03 17:23 ` Jan Kratochvil 2009-10-07 18:39 ` Joel Brobecker 2009-10-12 16:00 ` Jan Kratochvil 2009-10-12 18:58 ` Eli Zaretskii 2009-10-13 16:02 ` Jan Kratochvil 2009-11-03 1:12 ` Joel Brobecker
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox