[PATCH] PR threads/10729: x86 hw watchpoints and non-stop mode

Pedro Alves
2011-12-05T16:02:27Z

On native x86 targets, the desired state of hardware watchpoints is
kept on a local mirror of the inferior's debug registers, and copied
to each thread whenever the mirror changes. If any of the threads
being copied to are currently running, the copying fails with an ugly
error.

GDBserver solves this by instead only updating the threads' debug
registers prior to resume, and, when the local mirror changes, forcing
a temporary and transparent stop on running threads, so they can
update the debug registers when re-resumed. When deciding whether an
LWP stopped due to a hardware watchpoint or breakpoint, the current
state of the debug registers in the stopping thread is read, rather
than trusting the mirror still mirrors what was last copied to the
thread.

This patch matches teaches linux-nat.c to do the same as
GDBserver.

I suspect this change gets rid of a couple of workarounds we had in
place (e.g., i386_stopped_data_address), so I removed them --
GDBserver didn't have them. No regressions popped up.

I have tested this on x86_64-linux (-m64|-m32), which showed no
regressions. The patch also adjusts all other users of i396-nat.c:
djgpp/go32; i386 BSDs (FreeBSD only it seems); Windows (cygwin/mingw)
and macosx (darwin), but I haven't tested those. I'd appreciate
testing or an extra pair of eyes.

Actually darwin has the i386 watchpoint hooks in place, but it doesn't
install them:

$ grep i386_darwin_dr_get_status *
i386-darwin-nat.c:i386_darwin_dr_get_status (void)
$

??

This is necessary to fix a bunch of watchpoints regressions with my
all-stop on top of non-stop patches, as with those, we're not stopping
all threads at every internal event, and end up trying to install new
watchpoint locations with some threads still running.

P.S.:
GDBserver also supports multi-process watchpoints, but I left that
out of this patch. It's now mostly just a matter of fixing:

/* The local mirror of the inferior's debug registers. Currently this
is a global, but it should really be per-inferior. */
static struct i386_debug_reg_state dr_mirror;

2011-12-05 Pedro Alves <pedro@codesourcery.com>

PR threads/10729

* linux-nat.c (linux_nat_new_thread): Change parameter to an lwp
pointer.
(linux_nat_prepare_to_resume): New global.
(lwp_free): New.
(purge_lwp_list): Use it.
(add_lwp): Call linux_nat_new_thread even on the first LWP.
Adjust to interface change.
(delete_lwp): Call lwp_free instead of xfree.
(resume_lwp, linux_nat_resume, linux_handle_syscall_trap)
(linux_handle_extended_wait, linux_nat_filter_event)
(linux_nat_filter_event): Call linux_nat_prepare_to_resume before
resuming.
(linux_stop_lwp): New.
(linux_nat_set_new_thread): Adjust.
(linux_nat_set_prepare_to_resume): New.
* linux-nat.h (struct arch_lwp_info): Forward declare.
(struct lwp_info) <arch_private>: New field.
(linux_stop_lwp): Declare.
(linux_nat_set_new_thread): Adjust.
(linux_nat_set_prepare_to_resume): New.

* i386-nat.c (DR_NADDR, DR_STATUS, DR_CONTROL)
(struct i386_debug_reg_state): Move to i386-nat.h.
(dr_mirror): Comment.
(i386_debug_reg_state): New.
(i386_update_inferior_debug_regs): Simplify.
(i386_stopped_data_address): Use the debug register state from the
inferior, not from the local cache.
* i386-nat.h (struct i386_dr_low_type): Delete reset_addr and
unset_status fields. New get_addr and get_control fields.
(DR_FIRSTADDR, DR_LASTADDR, DR_CONTROL): Moved from i386-nat.c.
(DR_NADDR, DR_STATUS): New.
(struct i386_debug_reg_state): Moved from i386-nat.c.

* amd64-linux-nat.c (struct arch_lwp_info): New.
(amd64_linux_dr): Delete global.
(amd64_linux_dr_get_addr): New.
(amd64_linux_dr_get_control): New.
(amd64_linux_dr_unset_status): Delete.
(amd64_linux_dr_set_addr): Reimplement.
(amd64_linux_dr_reset_addr): Delete.
(update_debug_registers_callback): New.
(amd64_linux_dr_set_control): Reimplement.
(amd64_linux_dr_set_addr): Reimplement.
(amd64_linux_prepare_to_resume): New.
(amd64_linux_new_thread): Change parameter to an lwp pointer.
Reimplement.
(_initialize_amd64_linux_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status. Install
amd64_linux_dr_get_control as i386_dr_low.get_control. Install
amd64_linux_dr_get_addr as i386_dr_low.get_addr. Install
amd64_linux_prepare_to_resume.
* i386-darwin-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
(DR_CONTROL): Delete.
(i386_darwin_dr_reset_addr): Delete.
(i386_darwin_dr_get_addr): New.
(i386_darwin_dr_get_control): New.
* i386-linux-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
(DR_CONTROL): Delete.
(struct arch_lwp_info): New.
(i386_linux_dr): Delete global.
(i386_linux_dr_set_control): Reimplement.
(i386_linux_dr_get_addr): New.
(i386_linux_dr_set_addr): Reimplement.
(i386_linux_dr_get_control): New.
(update_debug_registers_callback): New.
(i386_linux_dr_unset_status): Delete.
(i386_linux_dr_set_addr): Reimplement.
(i386_linux_prepare_to_resume): New.
(i386_linux_new_thread): Change parameter to an lwp pointer.
Reimplement.
(_initialize_i386_linux_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status. Install
i386_linux_dr_get_control as i386_dr_low.get_control. Install
i386_linux_dr_get_addr as i386_dr_low.get_addr. Install
i386_linux_prepare_to_resume.

* go32-nat.c
(go32_get_dr7, go32_get_dr): New.
(init_go32_ops): No longer install i386_dr_low.reset_addr.
Install go32_get_dr7 as i386_dr_low.get_control. Install
go32_get_dr as i386_dr_low.get_addr.
* i386bsd-nat.c (i386bsd_dr_get): New.
(i386bsd_dr_reset_addr): Delete.
(i386bsd_dr_get_addr): New.
(i386bsd_dr_get_status): Use i386bsd_dr_get.
(i386bsd_dr_get_control): New.
* i386bsd-nat.h (i386bsd_dr_reset_addr): Delete.
(i386bsd_dr_get_addr): New.
(i386bsd_dr_get_control): New.
* i386fbsd-nat.c (_initialize_i386fbsd_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status. Install
i386bsd_dr_get_control as i386_dr_low.get_control. Install
i386bsd_dr_get_addr as i386_dr_low.get_addr.
* windows-nat.c (init_windows_ops): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status. Install
cygwin_get_dr7 as i386_dr_low.get_control. Install cygwin_get_dr
as i386_dr_low.get_addr.
(cygwin_get_dr): New.
(cygwin_get_dr7): New.
---
gdb/amd64-linux-nat.c | 121 +++++++++++++++++++++++++++++---------------
gdb/go32-nat.c | 25 +++++++++
gdb/i386-darwin-nat.c | 31 +++--------
gdb/i386-linux-nat.c | 134 +++++++++++++++++++++++++++++--------------------
gdb/i386-nat.c | 63 ++++++-----------------
gdb/i386-nat.h | 42 +++++++++++++--
gdb/i386bsd-nat.c | 32 ++++++++----
gdb/i386bsd-nat.h | 4 +
gdb/i386fbsd-nat.c | 3 +
gdb/linux-nat.c | 63 +++++++++++++++++++++--
gdb/linux-nat.h | 14 +++++
gdb/windows-nat.c | 20 +++++++
12 files changed, 356 insertions(+), 196 deletions(-)

diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index c673965..9699f84 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -64,6 +64,14 @@
#define PTRACE_SETREGSET 0x4205
#endif

+/* Per-thread arch-specific data we want to keep. */
+
+struct arch_lwp_info
+{
+ /* Non-zero if our copy differs from what's recorded in the thread. */
+ int debug_registers_changed;
+};
+
/* Does the current host support PTRACE_GETREGSET? */
static int have_ptrace_getregset = -1;

@@ -265,8 +273,6 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
\f
/* Support for debug registers. */

-static unsigned long amd64_linux_dr[DR_CONTROL + 1];
-
static unsigned long
amd64_linux_dr_get (ptid_t ptid, int regnum)
{
@@ -304,75 +310,105 @@ 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. */
+/* Return the inferior's debug register REGNUM. */

-static void
-amd64_linux_dr_set_control (unsigned long control)
+static CORE_ADDR
+amd64_linux_dr_get_addr (int regnum)
{
- struct lwp_info *lp;
+ /* DR6 and DR7 are retrieved with some other way. */
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);

- amd64_linux_dr[DR_CONTROL] = control;
- ALL_LWPS (lp)
- amd64_linux_dr_set (lp->ptid, DR_CONTROL, control);
+ return amd64_linux_dr_get (inferior_ptid, regnum);
}

-/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */
+/* Return the inferior's DR7 debug control register. */

-static void
-amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+static unsigned long
+amd64_linux_dr_get_control (void)
{
- struct lwp_info *lp;
+ return amd64_linux_dr_get (inferior_ptid, DR_CONTROL);
+}

- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */

- amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
- ALL_LWPS (lp)
- amd64_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr);
+static unsigned long
+amd64_linux_dr_get_status (void)
+{
+ return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
}

-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */
+/* Callback for iterate_over_lwps. Update the debug registers of
+ LWP. */

-static void
-amd64_linux_dr_reset_addr (int regnum)
+static int
+update_debug_registers_callback (struct lwp_info *lwp, void *arg)
{
- amd64_linux_dr_set_addr (regnum, 0);
+ /* The actual update is done later just before resuming the lwp, we
+ just mark that the registers need updating. */
+ lwp->arch_private->debug_registers_changed = 1;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so we
+ can update its debug registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
+
+ return 0;
}

-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
+/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior. */

-static unsigned long
-amd64_linux_dr_get_status (void)
+static void
+amd64_linux_dr_set_control (unsigned long control)
{
- return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
}

-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+ inferior. */

static void
-amd64_linux_dr_unset_status (unsigned long mask)
+amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- struct lwp_info *lp;
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));

- ALL_LWPS (lp)
+ gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
+}
+
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
+
+static void
+amd64_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+ if (lwp->arch_private->debug_registers_changed)
{
- unsigned long value;
-
- value = amd64_linux_dr_get (lp->ptid, DR_STATUS);
- value &= ~mask;
- amd64_linux_dr_set (lp->ptid, DR_STATUS, value);
+ int i;
+ struct i386_debug_reg_state *state = i386_debug_reg_state ();
+
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+ amd64_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
+
+ amd64_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
+
+ lwp->arch_private->debug_registers_changed = 0;
}
-}

+ if (lwp->stopped_by_watchpoint)
+ amd64_linux_dr_set (lwp->ptid, DR_STATUS, 0);
+}

static void
-amd64_linux_new_thread (ptid_t ptid)
+amd64_linux_new_thread (struct lwp_info *lp)
{
- int i;
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);

- for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
- amd64_linux_dr_set (ptid, i, amd64_linux_dr[i]);
+ info->debug_registers_changed = 1;

- amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]);
+ lp->arch_private = info;
}
\f

@@ -785,9 +821,9 @@ _initialize_amd64_linux_nat (void)

i386_dr_low.set_control = amd64_linux_dr_set_control;
i386_dr_low.set_addr = amd64_linux_dr_set_addr;
- i386_dr_low.reset_addr = amd64_linux_dr_reset_addr;
+ i386_dr_low.get_addr = amd64_linux_dr_get_addr;
i386_dr_low.get_status = amd64_linux_dr_get_status;
- i386_dr_low.unset_status = amd64_linux_dr_unset_status;
+ i386_dr_low.get_control = amd64_linux_dr_get_control;
i386_set_debug_register_length (8);

/* Override the GNU/Linux inferior startup hook. */
@@ -804,4 +840,5 @@ _initialize_amd64_linux_nat (void)
linux_nat_add_target (t);
linux_nat_set_new_thread (t, amd64_linux_new_thread);
linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
+ linux_nat_set_prepare_to_resume (t, amd64_linux_prepare_to_resume);
}
diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
index 8295adf..b650a94 100644
--- a/gdb/go32-nat.c
+++ b/gdb/go32-nat.c
@@ -801,6 +801,28 @@ go32_get_dr6 (void)
return STATUS;
}

+/* Get the value of the DR6 debug status register from the inferior.
+ 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_dr7 (void)
+{
+ return CONTROL;
+}
+
+/* Get the value of the DR debug register I from the inferior. Here
+ we just return the value stored in D_REGS, as we've got it from the
+ last go32_wait call. */
+
+static CORE_ADDR
+go32_get_dr (int i)
+{
+ if (i < 0 || i > 3)
+ internal_error (__FILE__, __LINE__,
+ _("Invalid register %d in go32_get_dr.\n"), i);
+ return D_REGS[i];
+}
+
/* Put the device open on handle FD into either raw or cooked
mode, return 1 if it was in raw mode, zero otherwise. */

@@ -984,8 +1006,9 @@ init_go32_ops (void)

i386_dr_low.set_control = go32_set_dr7;
i386_dr_low.set_addr = go32_set_dr;
- i386_dr_low.reset_addr = NULL;
i386_dr_low.get_status = go32_get_dr6;
+ i386_dr_low.get_control = go32_get_dr7;
+ i386_dr_low.get_addr = go32_get_dr;
i386_set_debug_register_length (4);

go32_ops.to_magic = OPS_MAGIC;
diff --git a/gdb/i386-darwin-nat.c b/gdb/i386-darwin-nat.c
index 61e2e15..23f6a6d 100644
--- a/gdb/i386-darwin-nat.c
+++ b/gdb/i386-darwin-nat.c
@@ -263,23 +263,6 @@ i386_darwin_store_inferior_registers (struct target_ops *ops,

/* Support for debug registers, boosted mostly from i386-linux-nat.c. */

-#ifndef DR_FIRSTADDR
-#define DR_FIRSTADDR 0
-#endif
-
-#ifndef DR_LASTADDR
-#define DR_LASTADDR 3
-#endif
-
-#ifndef DR_STATUS
-#define DR_STATUS 6
-#endif
-
-#ifndef DR_CONTROL
-#define DR_CONTROL 7
-#endif
-
-
static void
i386_darwin_dr_set (int regnum, uint32_t value)
{
@@ -410,12 +393,10 @@ i386_darwin_dr_set_addr (int regnum, CORE_ADDR addr)
i386_darwin_dr_set (DR_FIRSTADDR + regnum, addr);
}

-void
-i386_darwin_dr_reset_addr (int regnum)
+CORE_ADDR
+i386_darwin_dr_get_addr (int regnum)
{
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
- i386_darwin_dr_set (DR_FIRSTADDR + regnum, 0L);
+ return i386_darwin_dr_get (regnum);
}

unsigned long
@@ -424,6 +405,12 @@ i386_darwin_dr_get_status (void)
return i386_darwin_dr_get (DR_STATUS);
}

+unsigned long
+i386_darwin_dr_get_control (void)
+{
+ return i386_darwin_dr_get (DR_CONTROL);
+}
+
void
darwin_check_osabi (darwin_inferior *inf, thread_t thread)
{
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 7eb49ae..70a5919 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -47,22 +47,6 @@
#include <sys/debugreg.h>
#endif

-#ifndef DR_FIRSTADDR
-#define DR_FIRSTADDR 0
-#endif
-
-#ifndef DR_LASTADDR
-#define DR_LASTADDR 3
-#endif
-
-#ifndef DR_STATUS
-#define DR_STATUS 6
-#endif
-
-#ifndef DR_CONTROL
-#define DR_CONTROL 7
-#endif
-
/* Prototypes for supply_gregset etc. */
#include "gregset.h"

@@ -83,6 +67,14 @@
#define PTRACE_SETREGSET 0x4205
#endif

+/* Per-thread arch-specific data we want to keep. */
+
+struct arch_lwp_info
+{
+ /* Non-zero if our copy differs from what's recorded in the thread. */
+ int debug_registers_changed;
+};
+
/* Does the current host support PTRACE_GETREGSET? */
static int have_ptrace_getregset = -1;
\f
@@ -651,8 +643,6 @@ i386_linux_store_inferior_registers (struct target_ops *ops,

/* Support for debug registers. */

-static unsigned long i386_linux_dr[DR_CONTROL + 1];
-
/* Get debug register REGNUM value from only the one LWP of PTID. */

static unsigned long
@@ -692,74 +682,105 @@ 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. */
+/* Return the inferior's debug register REGNUM. */

-static void
-i386_linux_dr_set_control (unsigned long control)
+static CORE_ADDR
+i386_linux_dr_get_addr (int regnum)
{
- struct lwp_info *lp;
+ /* DR6 and DR7 are retrieved with some other way. */
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);

- i386_linux_dr[DR_CONTROL] = control;
- ALL_LWPS (lp)
- i386_linux_dr_set (lp->ptid, DR_CONTROL, control);
+ return i386_linux_dr_get (inferior_ptid, regnum);
}

-/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */
+/* Return the inferior's DR7 debug control register. */

-static void
-i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+static unsigned long
+i386_linux_dr_get_control (void)
{
- struct lwp_info *lp;
+ return i386_linux_dr_get (inferior_ptid, DR_CONTROL);
+}

- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */

- i386_linux_dr[DR_FIRSTADDR + regnum] = addr;
- ALL_LWPS (lp)
- i386_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr);
+static unsigned long
+i386_linux_dr_get_status (void)
+{
+ return i386_linux_dr_get (inferior_ptid, DR_STATUS);
}

-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */
+/* Callback for iterate_over_lwps. Update the debug registers of
+ LWP. */

-static void
-i386_linux_dr_reset_addr (int regnum)
+static int
+update_debug_registers_callback (struct lwp_info *lwp, void *arg)
{
- i386_linux_dr_set_addr (regnum, 0);
+ /* The actual update is done later just before resuming the lwp, we
+ just mark that the registers need updating. */
+ lwp->arch_private->debug_registers_changed = 1;
+
+ /* If the lwp isn't stopped, force it to momentarily pause, so we
+ can update its debug registers. */
+ if (!lwp->stopped)
+ linux_stop_lwp (lwp);
+
+ return 0;
}

-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
+/* Set DR_CONTROL to ADDR in all LWPs of the current inferior. */

-static unsigned long
-i386_linux_dr_get_status (void)
+static void
+i386_linux_dr_set_control (unsigned long control)
{
- return i386_linux_dr_get (inferior_ptid, DR_STATUS);
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
}

-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+ inferior. */

static void
-i386_linux_dr_unset_status (unsigned long mask)
+i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- struct lwp_info *lp;
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));

- ALL_LWPS (lp)
+ gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
+}
+
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
+
+static void
+i386_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+ if (lwp->arch_private->debug_registers_changed)
{
- unsigned long value;
+ int i;
+ struct i386_debug_reg_state *state = i386_debug_reg_state ();
+
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+ i386_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);

- value = i386_linux_dr_get (lp->ptid, DR_STATUS);
- value &= ~mask;
- i386_linux_dr_set (lp->ptid, DR_STATUS, value);
+ i386_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
+
+ lwp->arch_private->debug_registers_changed = 0;
}
+
+ if (lwp->stopped_by_watchpoint)
+ i386_linux_dr_set (lwp->ptid, DR_STATUS, 0);
}

static void
-i386_linux_new_thread (ptid_t ptid)
+i386_linux_new_thread (struct lwp_info *lp)
{
- int i;
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);

- for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
- i386_linux_dr_set (ptid, i, i386_linux_dr[i]);
+ info->debug_registers_changed = 1;

- i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]);
+ lp->arch_private = info;
}
\f

@@ -978,9 +999,9 @@ _initialize_i386_linux_nat (void)

i386_dr_low.set_control = i386_linux_dr_set_control;
i386_dr_low.set_addr = i386_linux_dr_set_addr;
- i386_dr_low.reset_addr = i386_linux_dr_reset_addr;
+ i386_dr_low.get_addr = i386_linux_dr_get_addr;
i386_dr_low.get_status = i386_linux_dr_get_status;
- i386_dr_low.unset_status = i386_linux_dr_unset_status;
+ i386_dr_low.get_control = i386_linux_dr_get_control;
i386_set_debug_register_length (4);

/* Override the default ptrace resume method. */
@@ -999,4 +1020,5 @@ _initialize_i386_linux_nat (void)
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, i386_linux_new_thread);
+ linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume);
}
diff --git a/gdb/i386-nat.c b/gdb/i386-nat.c
index 568b79b..94306a1 100644
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -43,11 +43,6 @@ struct i386_dr_low_type i386_dr_low;
/* Support for 8-byte wide hw watchpoints. */
#define TARGET_HAS_DR_LEN_8 (i386_dr_low.debug_register_length == 8)

-/* Debug registers' indices. */
-#define DR_NADDR 4 /* The number of debug address registers. */
-#define DR_STATUS 6 /* Index of debug status register (DR6). */
-#define DR_CONTROL 7 /* Index of debug control register (DR7). */
-
/* DR7 Debug Control register fields. */

/* How many bits to skip in DR7 to get to R/W and LEN fields. */
@@ -158,23 +153,6 @@ struct i386_dr_low_type i386_dr_low;
/* A macro to loop over all debug registers. */
#define ALL_DEBUG_REGISTERS(i) for (i = 0; i < DR_NADDR; i++)

-
-/* Global state needed to track h/w watchpoints. */
-
-struct i386_debug_reg_state
-{
- /* Mirror the inferior's DRi registers. We keep the status and
- control registers separated because they don't hold addresses.
- Note that since we can change these mirrors while threads are
- running, we never trust them to explain a cause of a trap.
- For that, we need to peek directly in the inferior registers. */
- CORE_ADDR dr_mirror[DR_NADDR];
- unsigned dr_status_mirror, dr_control_mirror;
-
- /* Reference counts for each debug register. */
- int dr_ref_count[DR_NADDR];
-};
-
/* Clear the reference counts and forget everything we knew about the
debug registers. */

@@ -192,8 +170,16 @@ i386_init_dregs (struct i386_debug_reg_state *state)
state->dr_status_mirror = 0;
}

+/* The local mirror of the inferior's debug registers. Currently this
+ is a global, but it should really be per-inferior. */
static struct i386_debug_reg_state dr_mirror;

+struct i386_debug_reg_state *
+i386_debug_reg_state (void)
+{
+ return &dr_mirror;
+}
+
/* Whether or not to print the mirrored debug registers. */
static int maint_show_dr;

@@ -513,22 +499,7 @@ i386_update_inferior_debug_regs (struct i386_debug_reg_state *new_state)
ALL_DEBUG_REGISTERS (i)
{
if (I386_DR_VACANT (new_state, i) != I386_DR_VACANT (&dr_mirror, i))
- {
- if (!I386_DR_VACANT (new_state, i))
- {
- i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
-
- /* Only a sanity check for leftover bits (set possibly only
- by inferior). */
- if (i386_dr_low.unset_status)
- i386_dr_low.unset_status (I386_DR_WATCH_MASK (i));
- }
- else
- {
- if (i386_dr_low.reset_addr)
- i386_dr_low.reset_addr (i);
- }
- }
+ i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
else
gdb_assert (new_state->dr_mirror[i] == dr_mirror.dr_mirror[i]);
}
@@ -636,11 +607,12 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
int rc = 0;
unsigned status;
unsigned control;
- struct i386_debug_reg_state *state = &dr_mirror;

- dr_mirror.dr_status_mirror = i386_dr_low.get_status ();
- status = dr_mirror.dr_status_mirror;
- control = dr_mirror.dr_control_mirror;
+ /* Get the current values the inferior has. If the thread was
+ running when we last changed watchpoints, the mirror no longer
+ represents what was set in this thread's debug registers. */
+ status = i386_dr_low.get_status ();
+ control = i386_dr_low.get_control ();

ALL_DEBUG_REGISTERS(i)
{
@@ -650,12 +622,9 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
that GDB doesn't call the target_stopped_data_address
method except for data watchpoints. In other words, I'm
being paranoiac. */
- && I386_DR_GET_RW_LEN (control, i) != 0
- /* This third condition makes sure DRi is not vacant, this
- avoids false positives in windows-nat.c. */
- && !I386_DR_VACANT (state, i))
+ && I386_DR_GET_RW_LEN (control, i) != 0)
{
- addr = state->dr_mirror[i];
+ addr = i386_dr_low.get_addr (i);
rc = 1;
if (maint_show_dr)
i386_show_dr (&dr_mirror, "watchpoint_hit", addr, -1, hw_write);
diff --git a/gdb/i386-nat.h b/gdb/i386-nat.h
index 819c6b8..1a75daa 100644
--- a/gdb/i386-nat.h
+++ b/gdb/i386-nat.h
@@ -53,31 +53,54 @@ extern void i386_use_watchpoints (struct target_ops *);
set_addr -- put an address into one debug
register for all LWPs

- reset_addr -- reset the address stored in
- one debug register for all LWPs
+ get_addr -- return the address in a given debug
+ register of the current LWP

get_status -- return the value of the debug
status (DR6) register for current LWP

- unset_status -- unset the specified bits of the debug
- status (DR6) register for all LWPs
+ get_control -- return the value of the debug
+ control (DR7) register for current LWP

Additionally, the native file should set the debug_register_length
field to 4 or 8 depending on the number of bytes used for
deubg registers. */

-struct i386_dr_low_type
+struct i386_dr_low_type
{
void (*set_control) (unsigned long);
void (*set_addr) (int, CORE_ADDR);
- void (*reset_addr) (int);
+ CORE_ADDR (*get_addr) (int);
unsigned long (*get_status) (void);
- void (*unset_status) (unsigned long);
+ unsigned long (*get_control) (void);
int debug_register_length;
};

extern struct i386_dr_low_type i386_dr_low;

+/* Debug registers' indices. */
+#define DR_FIRSTADDR 0
+#define DR_LASTADDR 3
+#define DR_NADDR 4 /* The number of debug address registers. */
+#define DR_STATUS 6 /* Index of debug status register (DR6). */
+#define DR_CONTROL 7 /* Index of debug control register (DR7). */
+
+/* Global state needed to track h/w watchpoints. */
+
+struct i386_debug_reg_state
+{
+ /* Mirror the inferior's DRi registers. We keep the status and
+ control registers separated because they don't hold addresses.
+ Note that since we can change these mirrors while threads are
+ running, we never trust them to explain a cause of a trap.
+ For that, we need to peek directly in the inferior registers. */
+ CORE_ADDR dr_mirror[DR_NADDR];
+ unsigned dr_status_mirror, dr_control_mirror;
+
+ /* Reference counts for each debug register. */
+ int dr_ref_count[DR_NADDR];
+};
+
/* Use this function to set i386_dr_low debug_register_length field
rather than setting it directly to check that the length is only
set once. It also enables the 'maint set/show show-debug-regs'
@@ -89,4 +112,9 @@ extern void i386_set_debug_register_length (int len);

extern void i386_cleanup_dregs (void);

+/* Return a pointer to the the local mirror of the inferior's debug
+ registers. */
+
+extern struct i386_debug_reg_state *i386_debug_reg_state (void);
+
#endif /* I386_NAT_H */
diff --git a/gdb/i386bsd-nat.c b/gdb/i386bsd-nat.c
index fcd772f..22c79e2 100644
--- a/gdb/i386bsd-nat.c
+++ b/gdb/i386bsd-nat.c
@@ -264,6 +264,18 @@ i386bsd_target (void)
#define DBREG_DRX(d, x) ((&d->dr0)[x])
#endif

+static unsigned long
+i386bsd_dr_get (ptid_t ptid, int regnum)
+{
+ struct dbreg dbregs;
+
+ if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
+ perror_with_name (_("Couldn't read debug registers"));
+
+ return DBREG_DRX ((&dbregs), regnum);
+}
+
static void
i386bsd_dr_set (int regnum, unsigned int value)
{
@@ -299,24 +311,22 @@ i386bsd_dr_set_addr (int regnum, CORE_ADDR addr)
i386bsd_dr_set (regnum, addr);
}

-void
-i386bsd_dr_reset_addr (int regnum)
+CORE_ADDR
+i386bsd_dr_get_addr (int regnum)
{
- gdb_assert (regnum >= 0 && regnum <= 4);
-
- i386bsd_dr_set (regnum, 0);
+ return i386bsd_dr_get (inferior_ptid, regnum);
}

unsigned long
i386bsd_dr_get_status (void)
{
- struct dbreg dbregs;
-
- if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid),
- (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
- perror_with_name (_("Couldn't read debug registers"));
+ return i386bsd_dr_get (inferior_ptid, 6);
+}

- return DBREG_DRX ((&dbregs), 6);
+unsigned long
+i386bsd_dr_get_control (void)
+{
+ return i386bsd_dr_get (inferior_ptid, 7);
}

#endif /* PT_GETDBREGS */
diff --git a/gdb/i386bsd-nat.h b/gdb/i386bsd-nat.h
index 1c27ed5..df0b0f3 100644
--- a/gdb/i386bsd-nat.h
+++ b/gdb/i386bsd-nat.h
@@ -32,8 +32,10 @@ extern void i386bsd_dr_set_control (unsigned long control);

extern void i386bsd_dr_set_addr (int regnum, CORE_ADDR addr);

-extern void i386bsd_dr_reset_addr (int regnum);
+extern CORE_ADDR i386bsd_dr_get_addr (int regnum);

extern unsigned long i386bsd_dr_get_status (void);

+extern unsigned long i386bsd_dr_get_control (void);
+
#endif /* i386bsd-nat.h */
diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
index ecc797e..52ae031 100644
--- a/gdb/i386fbsd-nat.c
+++ b/gdb/i386fbsd-nat.c
@@ -134,8 +134,9 @@ _initialize_i386fbsd_nat (void)

i386_dr_low.set_control = i386bsd_dr_set_control;
i386_dr_low.set_addr = i386bsd_dr_set_addr;
- i386_dr_low.reset_addr = i386bsd_dr_reset_addr;
+ i386_dr_low.get_addr = i386bsd_dr_get_addr;
i386_dr_low.get_status = i386bsd_dr_get_status;
+ i386_dr_low.get_control = i386bsd_dr_get_control;
i386_set_debug_register_length (4);

#endif /* HAVE_PT_GETDBREGS */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index d54f303..f62c4de 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -175,7 +175,10 @@ static struct target_ops *linux_ops;
static struct target_ops linux_ops_saved;

/* The method to call, if any, when a new thread is attached. */
-static void (*linux_nat_new_thread) (ptid_t);
+static void (*linux_nat_new_thread) (struct lwp_info *);
+
+/* Hook to call prior to resuming a thread. */
+static void (*linux_nat_prepare_to_resume) (struct lwp_info *);

/* The method to call, if any, when the siginfo object needs to be
converted between the layout returned by ptrace, and the layout in
@@ -1073,6 +1076,15 @@ status_to_str (int status)
return buf;
}

+/* Destroy and free LP. */
+
+static void
+lwp_free (struct lwp_info *lp)
+{
+ xfree (lp->arch_private);
+ xfree (lp);
+}
+
/* Remove all LWPs belong to PID from the lwp list. */

static void
@@ -1093,7 +1105,7 @@ purge_lwp_list (int pid)
else
lpprev->next = lp->next;

- xfree (lp);
+ lwp_free (lp);
}
else
lpprev = lp;
@@ -1139,8 +1151,8 @@ add_lwp (ptid_t ptid)
lp->next = lwp_list;
lwp_list = lp;

- if (num_lwps (GET_PID (ptid)) > 1 && linux_nat_new_thread != NULL)
- linux_nat_new_thread (ptid);
+ if (linux_nat_new_thread != NULL)
+ linux_nat_new_thread (lp);

return lp;
}
@@ -1166,7 +1178,7 @@ delete_lwp (ptid_t ptid)
else
lwp_list = lp->next;

- xfree (lp);
+ lwp_free (lp);
}

/* Return a pointer to the structure describing the LWP corresponding
@@ -1825,6 +1837,8 @@ resume_lwp (struct lwp_info *lp, int step)
"RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
target_pid_to_str (lp->ptid));

+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops,
pid_to_ptid (GET_LWP (lp->ptid)),
step, TARGET_SIGNAL_0);
@@ -1969,6 +1983,8 @@ linux_nat_resume (struct target_ops *ops,
/* Convert to something the lower layer understands. */
ptid = pid_to_ptid (GET_LWP (lp->ptid));

+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, ptid, step, signo);
memset (&lp->siginfo, 0, sizeof (lp->siginfo));
lp->stopped_by_watchpoint = 0;
@@ -2138,6 +2154,8 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
/* Note that gdbarch_get_syscall_number may access registers, hence
fill a regcache. */
registers_changed ();
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
return 1;
@@ -2325,6 +2343,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
fprintf_unfiltered (gdb_stdlog,
"LHEW: resuming new LWP %ld\n",
GET_LWP (new_lp->ptid));
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (new_lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
0, TARGET_SIGNAL_0);
new_lp->stopped = 0;
@@ -2334,6 +2354,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LHEW: resuming parent LWP %d\n", pid);
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
0, TARGET_SIGNAL_0);

@@ -2597,6 +2619,14 @@ stop_callback (struct lwp_info *lp, void *data)
return 0;
}

+/* Request a stop on LWP. */
+
+void
+linux_stop_lwp (struct lwp_info *lwp)
+{
+ stop_callback (lwp, NULL);
+}
+
/* Return non-zero if LWP PID has a pending SIGINT. */

static int
@@ -3333,6 +3363,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)

registers_changed ();

+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
@@ -3364,6 +3396,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
lp->ignore_sigint = 0;

registers_changed ();
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
@@ -3538,6 +3572,8 @@ retry:
/* Resume the thread. It should halt immediately returning the
pending SIGSTOP. */
registers_changed ();
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
@@ -3787,6 +3823,8 @@ retry:
newly attached threads may cause an unwanted delay in
getting them running. */
registers_changed ();
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, signo);
if (debug_linux_nat)
@@ -3939,6 +3977,8 @@ resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
"RSRL: resuming stopped-resumed LWP %s\n",
target_pid_to_str (lp->ptid));

+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
lp->stopped = 0;
@@ -5836,7 +5876,8 @@ linux_nat_add_target (struct target_ops *t)

/* Register a method to call whenever a new thread is attached. */
void
-linux_nat_set_new_thread (struct target_ops *t, void (*new_thread) (ptid_t))
+linux_nat_set_new_thread (struct target_ops *t,
+ void (*new_thread) (struct lwp_info *))
{
/* Save the pointer. We only support a single registered instance
of the GNU/Linux native target, so we do not need to map this to
@@ -5857,6 +5898,16 @@ linux_nat_set_siginfo_fixup (struct target_ops *t,
linux_nat_siginfo_fixup = siginfo_fixup;
}

+/* Register a method to call prior to resuming a thread. */
+
+void
+linux_nat_set_prepare_to_resume (struct target_ops *t,
+ void (*prepare_to_resume) (struct lwp_info *))
+{
+ /* Save the pointer. */
+ linux_nat_prepare_to_resume = prepare_to_resume;
+}
+
/* Return the saved siginfo associated with PTID. */
struct siginfo *
linux_nat_get_siginfo (ptid_t ptid)
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 1fa94ce..33727d6 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -22,6 +22,8 @@

#include <signal.h>

+struct arch_lwp_info;
+
/* Ways to "resume" a thread. */

enum resume_kind
@@ -109,6 +111,9 @@ struct lwp_info
/* The processor core this LWP was last seen on. */
int core;

+ /* Arch-specific additions. */
+ struct arch_lwp_info *arch_private;
+
/* Next LWP in list. */
struct lwp_info *next;
};
@@ -146,6 +151,8 @@ extern void linux_enable_event_reporting (ptid_t ptid);

extern int lin_lwp_attach_lwp (ptid_t ptid);

+extern void linux_stop_lwp (struct lwp_info *lwp);
+
/* Iterator function for lin-lwp's lwp list. */
struct lwp_info *iterate_over_lwps (ptid_t filter,
int (*callback) (struct lwp_info *,
@@ -166,7 +173,7 @@ linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int));
void linux_nat_add_target (struct target_ops *);

/* Register a method to call whenever a new thread is attached. */
-void linux_nat_set_new_thread (struct target_ops *, void (*) (ptid_t));
+void linux_nat_set_new_thread (struct target_ops *, void (*) (struct lwp_info *));

/* Register a method that converts a siginfo object between the layout
that ptrace returns, and the layout in the architecture of the
@@ -176,6 +183,11 @@ void linux_nat_set_siginfo_fixup (struct target_ops *,
gdb_byte *,
int));

+/* Register a method to call prior to resuming a thread. */
+
+void linux_nat_set_prepare_to_resume (struct target_ops *,
+ void (*) (struct lwp_info *));
+
/* Update linux-nat internal state when changing from one fork
to another. */
void linux_nat_switch_fork (ptid_t new_ptid);
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 20e3c67..7781276 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2494,8 +2494,9 @@ init_windows_ops (void)

i386_dr_low.set_control = cygwin_set_dr7;
i386_dr_low.set_addr = cygwin_set_dr;
- i386_dr_low.reset_addr = NULL;
+ i386_dr_low.get_addr = cygwin_get_dr;
i386_dr_low.get_status = cygwin_get_dr6;
+ i386_dr_low.get_control = cygwin_get_dr7;

/* i386_dr_low.debug_register_length field is set by
calling i386_set_debug_register_length function
@@ -2627,6 +2628,14 @@ cygwin_set_dr7 (unsigned long val)
debug_registers_used = 1;
}

+/* Get the value of debug register I from the inferior. */
+
+static CORE_ADDR
+cygwin_get_dr (int i)
+{
+ return dr[i];
+}
+
/* Get the value of the DR6 debug status register from the inferior.
Here we just return the value stored in dr[6]
by the last call to thread_rec for current_event.dwThreadId id. */
@@ -2636,6 +2645,15 @@ cygwin_get_dr6 (void)
return (unsigned long) dr[6];
}

+/* Get the value of the DR6 debug status register from the inferior.
+ 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_dr7 (void)
+{
+ return (unsigned long) dr[7];
+}
+
/* Determine if the thread referenced by "ptid" is alive
by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0
it means that the thread has died. Otherwise it is assumed to be alive. */