2011-12-14  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.
	(detach_callback, linux_nat_detach, resume_lwp, linux_nat_resume)
	(linux_handle_syscall_trap, linux_handle_extended_wait)
	(linux_nat_filter_event, resume_stopped_resumed_lwps): 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-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.

	* arm-linux-nat.c (arm_linux_new_thread): Change parameter to an
	lwp pointer.  Adjust.
	* ia64-linux-nat.c (ia64_linux_new_thread): Likewise.
	* mips-linux-nat.c (mips_linux_new_thread): Likewise.
	* ppc-linux-nat.c (ppc_linux_new_thread): Likewise.
	* s390-nat.c (s390_fix_watch_points): Likewise.

	* 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.
	* 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/testsuite/
2011-12-14  Pedro Alves  <pedro@codesourcery.com>

	PR threads/10729

	* gdb.mi/watch-nonstop.c: New file.
 	* gdb.mi/mi-watch-nonstop.exp: New file.
This commit is contained in:
Pedro Alves 2011-12-14 17:20:32 +00:00
parent 0d13c96b21
commit 7b50312ad6
21 changed files with 665 additions and 221 deletions

View file

@ -1,3 +1,115 @@
2011-12-14 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.
(detach_callback, linux_nat_detach, resume_lwp, linux_nat_resume)
(linux_handle_syscall_trap, linux_handle_extended_wait)
(linux_nat_filter_event, resume_stopped_resumed_lwps): 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-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.
* arm-linux-nat.c (arm_linux_new_thread): Change parameter to an
lwp pointer. Adjust.
* ia64-linux-nat.c (ia64_linux_new_thread): Likewise.
* mips-linux-nat.c (mips_linux_new_thread): Likewise.
* ppc-linux-nat.c (ppc_linux_new_thread): Likewise.
* s390-nat.c (s390_fix_watch_points): Likewise.
* 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.
* 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.
2011-12-14 Pedro Alves <pedro@codesourcery.com> 2011-12-14 Pedro Alves <pedro@codesourcery.com>
* ia64-tdep.c (ia64_memory_remove_breakpoint): Use * ia64-tdep.c (ia64_memory_remove_breakpoint): Use

View file

@ -64,6 +64,14 @@
#define PTRACE_SETREGSET 0x4205 #define PTRACE_SETREGSET 0x4205
#endif #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? */ /* Does the current host support PTRACE_GETREGSET? */
static int have_ptrace_getregset = -1; static int have_ptrace_getregset = -1;
@ -265,8 +273,6 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
/* Support for debug registers. */ /* Support for debug registers. */
static unsigned long amd64_linux_dr[DR_CONTROL + 1];
static unsigned long static unsigned long
amd64_linux_dr_get (ptid_t ptid, int regnum) amd64_linux_dr_get (ptid_t ptid, int regnum)
{ {
@ -304,38 +310,23 @@ amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
perror_with_name (_("Couldn't write debug register")); 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 static CORE_ADDR
amd64_linux_dr_set_control (unsigned long control) 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; return amd64_linux_dr_get (inferior_ptid, regnum);
ALL_LWPS (lp)
amd64_linux_dr_set (lp->ptid, DR_CONTROL, control);
} }
/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ /* Return the inferior's DR7 debug control register. */
static void static unsigned long
amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) 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);
amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
ALL_LWPS (lp)
amd64_linux_dr_set (lp->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 of INFERIOR_PTID. */ /* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
@ -346,33 +337,89 @@ amd64_linux_dr_get_status (void)
return amd64_linux_dr_get (inferior_ptid, DR_STATUS); return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
} }
/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */ /* Callback for iterate_over_lwps. Update the debug registers of
LWP. */
static void static int
amd64_linux_dr_unset_status (unsigned long mask) update_debug_registers_callback (struct lwp_info *lwp, void *arg)
{ {
struct lwp_info *lp; /* 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;
ALL_LWPS (lp) /* If the lwp isn't stopped, force it to momentarily pause, so we
{ can update its debug registers. */
unsigned long value; if (!lwp->stopped)
linux_stop_lwp (lwp);
value = amd64_linux_dr_get (lp->ptid, DR_STATUS); return 0;
value &= ~mask;
amd64_linux_dr_set (lp->ptid, DR_STATUS, value);
}
} }
/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior. */
static void static void
amd64_linux_new_thread (ptid_t ptid) amd64_linux_dr_set_control (unsigned long control)
{ {
int i; ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
amd64_linux_dr_set (ptid, i, amd64_linux_dr[i]); }
amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]); /* Set address REGNUM (zero based) to ADDR in all LWPs of the current
inferior. */
static void
amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
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)
{
int clear_status = 0;
if (lwp->arch_private->debug_registers_changed)
{
struct i386_debug_reg_state *state = i386_debug_reg_state ();
int i;
for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
if (state->dr_ref_count[i] > 0)
{
amd64_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
/* If we're setting a watchpoint, any change the inferior
had done itself to the debug registers needs to be
discarded, otherwise, i386_stopped_data_address can get
confused. */
clear_status = 1;
}
amd64_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
lwp->arch_private->debug_registers_changed = 0;
}
if (clear_status || lwp->stopped_by_watchpoint)
amd64_linux_dr_set (lwp->ptid, DR_STATUS, 0);
}
static void
amd64_linux_new_thread (struct lwp_info *lp)
{
struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
info->debug_registers_changed = 1;
lp->arch_private = info;
} }
@ -785,9 +832,9 @@ _initialize_amd64_linux_nat (void)
i386_dr_low.set_control = amd64_linux_dr_set_control; i386_dr_low.set_control = amd64_linux_dr_set_control;
i386_dr_low.set_addr = amd64_linux_dr_set_addr; 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.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); i386_set_debug_register_length (8);
/* Override the GNU/Linux inferior startup hook. */ /* Override the GNU/Linux inferior startup hook. */
@ -804,4 +851,5 @@ _initialize_amd64_linux_nat (void)
linux_nat_add_target (t); linux_nat_add_target (t);
linux_nat_set_new_thread (t, amd64_linux_new_thread); linux_nat_set_new_thread (t, amd64_linux_new_thread);
linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup); linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
linux_nat_set_prepare_to_resume (t, amd64_linux_prepare_to_resume);
} }

View file

@ -1178,9 +1178,9 @@ arm_linux_watchpoint_addr_within_range (struct target_ops *target,
/* Handle thread creation. We need to copy the breakpoints and watchpoints /* Handle thread creation. We need to copy the breakpoints and watchpoints
in the parent thread to the child thread. */ in the parent thread to the child thread. */
static void static void
arm_linux_new_thread (ptid_t ptid) arm_linux_new_thread (struct lwp_info *lp)
{ {
int tid = TIDGET (ptid); int tid = TIDGET (lp->ptid);
const struct arm_linux_hwbp_cap *info = arm_linux_get_hwbp_cap (); const struct arm_linux_hwbp_cap *info = arm_linux_get_hwbp_cap ();
if (info != NULL) if (info != NULL)

View file

@ -801,6 +801,29 @@ go32_get_dr6 (void)
return STATUS; return STATUS;
} }
/* Get the value of the DR7 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 /* Put the device open on handle FD into either raw or cooked
mode, return 1 if it was in raw mode, zero otherwise. */ mode, return 1 if it was in raw mode, zero otherwise. */
@ -984,8 +1007,9 @@ init_go32_ops (void)
i386_dr_low.set_control = go32_set_dr7; i386_dr_low.set_control = go32_set_dr7;
i386_dr_low.set_addr = go32_set_dr; 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_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); i386_set_debug_register_length (4);
go32_ops.to_magic = OPS_MAGIC; go32_ops.to_magic = OPS_MAGIC;

View file

@ -263,23 +263,6 @@ i386_darwin_store_inferior_registers (struct target_ops *ops,
/* Support for debug registers, boosted mostly from i386-linux-nat.c. */ /* 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 static void
i386_darwin_dr_set (int regnum, uint32_t value) 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); i386_darwin_dr_set (DR_FIRSTADDR + regnum, addr);
} }
void CORE_ADDR
i386_darwin_dr_reset_addr (int regnum) i386_darwin_dr_get_addr (int regnum)
{ {
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); return i386_darwin_dr_get (regnum);
i386_darwin_dr_set (DR_FIRSTADDR + regnum, 0L);
} }
unsigned long unsigned long
@ -424,6 +405,12 @@ i386_darwin_dr_get_status (void)
return i386_darwin_dr_get (DR_STATUS); return i386_darwin_dr_get (DR_STATUS);
} }
unsigned long
i386_darwin_dr_get_control (void)
{
return i386_darwin_dr_get (DR_CONTROL);
}
void void
darwin_check_osabi (darwin_inferior *inf, thread_t thread) darwin_check_osabi (darwin_inferior *inf, thread_t thread)
{ {

View file

@ -47,22 +47,6 @@
#include <sys/debugreg.h> #include <sys/debugreg.h>
#endif #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. */ /* Prototypes for supply_gregset etc. */
#include "gregset.h" #include "gregset.h"
@ -83,6 +67,14 @@
#define PTRACE_SETREGSET 0x4205 #define PTRACE_SETREGSET 0x4205
#endif #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? */ /* Does the current host support PTRACE_GETREGSET? */
static int have_ptrace_getregset = -1; static int have_ptrace_getregset = -1;
@ -651,8 +643,6 @@ i386_linux_store_inferior_registers (struct target_ops *ops,
/* Support for debug registers. */ /* 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. */ /* Get debug register REGNUM value from only the one LWP of PTID. */
static unsigned long static unsigned long
@ -692,38 +682,23 @@ i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
perror_with_name (_("Couldn't write debug register")); 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 static CORE_ADDR
i386_linux_dr_set_control (unsigned long control) 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; return i386_linux_dr_get (inferior_ptid, regnum);
ALL_LWPS (lp)
i386_linux_dr_set (lp->ptid, DR_CONTROL, control);
} }
/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ /* Return the inferior's DR7 debug control register. */
static void static unsigned long
i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) 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);
i386_linux_dr[DR_FIRSTADDR + regnum] = addr;
ALL_LWPS (lp)
i386_linux_dr_set (lp->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 of INFERIOR_PTID. */ /* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
@ -734,32 +709,89 @@ i386_linux_dr_get_status (void)
return i386_linux_dr_get (inferior_ptid, DR_STATUS); return i386_linux_dr_get (inferior_ptid, DR_STATUS);
} }
/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */ /* Callback for iterate_over_lwps. Update the debug registers of
LWP. */
static int
update_debug_registers_callback (struct lwp_info *lwp, void *arg)
{
/* 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;
}
/* Set DR_CONTROL to ADDR in all LWPs of the current inferior. */
static void static void
i386_linux_dr_unset_status (unsigned long mask) i386_linux_dr_set_control (unsigned long control)
{ {
struct lwp_info *lp; ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
ALL_LWPS (lp) iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
}
/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
inferior. */
static void
i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
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)
{
int clear_status = 0;
if (lwp->arch_private->debug_registers_changed)
{ {
unsigned long value; struct i386_debug_reg_state *state = i386_debug_reg_state ();
int i;
value = i386_linux_dr_get (lp->ptid, DR_STATUS); for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
value &= ~mask; if (state->dr_ref_count[i] > 0)
i386_linux_dr_set (lp->ptid, DR_STATUS, value); {
i386_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
/* If we're setting a watchpoint, any change the inferior
had done itself to the debug registers needs to be
discarded, otherwise, i386_stopped_data_address can get
confused. */
clear_status = 1;
}
i386_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
lwp->arch_private->debug_registers_changed = 0;
} }
if (clear_status || lwp->stopped_by_watchpoint)
i386_linux_dr_set (lwp->ptid, DR_STATUS, 0);
} }
static void 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++) info->debug_registers_changed = 1;
i386_linux_dr_set (ptid, i, i386_linux_dr[i]);
i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]); lp->arch_private = info;
} }
@ -978,9 +1010,9 @@ _initialize_i386_linux_nat (void)
i386_dr_low.set_control = i386_linux_dr_set_control; i386_dr_low.set_control = i386_linux_dr_set_control;
i386_dr_low.set_addr = i386_linux_dr_set_addr; 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.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); i386_set_debug_register_length (4);
/* Override the default ptrace resume method. */ /* Override the default ptrace resume method. */
@ -999,4 +1031,5 @@ _initialize_i386_linux_nat (void)
/* Register the target. */ /* Register the target. */
linux_nat_add_target (t); linux_nat_add_target (t);
linux_nat_set_new_thread (t, i386_linux_new_thread); linux_nat_set_new_thread (t, i386_linux_new_thread);
linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume);
} }

View file

@ -43,11 +43,6 @@ struct i386_dr_low_type i386_dr_low;
/* Support for 8-byte wide hw watchpoints. */ /* Support for 8-byte wide hw watchpoints. */
#define TARGET_HAS_DR_LEN_8 (i386_dr_low.debug_register_length == 8) #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. */ /* DR7 Debug Control register fields. */
/* How many bits to skip in DR7 to get to R/W and LEN 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. */ /* A macro to loop over all debug registers. */
#define ALL_DEBUG_REGISTERS(i) for (i = 0; i < DR_NADDR; i++) #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 /* Clear the reference counts and forget everything we knew about the
debug registers. */ debug registers. */
@ -192,8 +170,16 @@ i386_init_dregs (struct i386_debug_reg_state *state)
state->dr_status_mirror = 0; 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; 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. */ /* Whether or not to print the mirrored debug registers. */
static int maint_show_dr; 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) 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_VACANT (&dr_mirror, i))
{ i386_dr_low.set_addr (i, new_state->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);
}
}
else else
gdb_assert (new_state->dr_mirror[i] == dr_mirror.dr_mirror[i]); gdb_assert (new_state->dr_mirror[i] == dr_mirror.dr_mirror[i]);
} }
@ -634,28 +605,62 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
CORE_ADDR addr = 0; CORE_ADDR addr = 0;
int i; int i;
int rc = 0; int rc = 0;
/* The current thread's DR_STATUS. We always need to read this to
check whether some watchpoint caused the trap. */
unsigned status; unsigned status;
unsigned control; /* We need DR_CONTROL as well, but only iff DR_STATUS indicates a
struct i386_debug_reg_state *state = &dr_mirror; data breakpoint trap. Only fetch it when necessary, to avoid an
unnecessary extra syscall when no watchpoint triggered. */
int control_p = 0;
unsigned control = 0;
dr_mirror.dr_status_mirror = i386_dr_low.get_status (); /* In non-stop/async, threads can be running while we change the
status = dr_mirror.dr_status_mirror; global dr_mirror (and friends). Say, we set a watchpoint, and
control = dr_mirror.dr_control_mirror; let threads resume. Now, say you delete the watchpoint, or
add/remove watchpoints such that dr_mirror changes while threads
are running. On targets that support non-stop,
inserting/deleting watchpoints updates the global dr_mirror only.
It does not update the real thread's debug registers; that's only
done prior to resume. Instead, if threads are running when the
mirror changes, a temporary and transparent stop on all threads
is forced so they can get their copy of the debug registers
updated on re-resume. Now, say, a thread hit a watchpoint before
having been updated with the new dr_mirror contents, and we
haven't yet handled the corresponding SIGTRAP. If we trusted
dr_mirror below, we'd mistake the real trapped address (from the
last time we had updated debug registers in the thread) with
whatever was currently in dr_mirror. So to fix this, dr_mirror
always represents intention, what we _want_ threads to have in
debug registers. To get at the address and cause of the trap, we
need to read the state the thread still has in its debug
registers.
In sum, always get the current debug register values the current
thread has, instead of trusting the global mirror. 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 ();
ALL_DEBUG_REGISTERS(i) ALL_DEBUG_REGISTERS(i)
{ {
if (I386_DR_WATCH_HIT (status, i) if (!I386_DR_WATCH_HIT (status, i))
/* This second condition makes sure DRi is set up for a data continue;
watchpoint, not a hardware breakpoint. The reason is
that GDB doesn't call the target_stopped_data_address if (!control_p)
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))
{ {
addr = state->dr_mirror[i]; control = i386_dr_low.get_control ();
control_p = 1;
}
/* This second condition makes sure DRi is set up for a data
watchpoint, not a hardware breakpoint. The reason is that
GDB doesn't call the target_stopped_data_address method
except for data watchpoints. In other words, I'm being
paranoiac. */
if (I386_DR_GET_RW_LEN (control, i) != 0)
{
addr = i386_dr_low.get_addr (i);
rc = 1; rc = 1;
if (maint_show_dr) if (maint_show_dr)
i386_show_dr (&dr_mirror, "watchpoint_hit", addr, -1, hw_write); i386_show_dr (&dr_mirror, "watchpoint_hit", addr, -1, hw_write);

View file

@ -53,14 +53,14 @@ extern void i386_use_watchpoints (struct target_ops *);
set_addr -- put an address into one debug set_addr -- put an address into one debug
register for all LWPs register for all LWPs
reset_addr -- reset the address stored in get_addr -- return the address in a given debug
one debug register for all LWPs register of the current LWP
get_status -- return the value of the debug get_status -- return the value of the debug
status (DR6) register for current LWP status (DR6) register for current LWP
unset_status -- unset the specified bits of the debug get_control -- return the value of the debug
status (DR6) register for all LWPs control (DR7) register for current LWP
Additionally, the native file should set the debug_register_length Additionally, the native file should set the debug_register_length
field to 4 or 8 depending on the number of bytes used for field to 4 or 8 depending on the number of bytes used for
@ -70,14 +70,37 @@ struct i386_dr_low_type
{ {
void (*set_control) (unsigned long); void (*set_control) (unsigned long);
void (*set_addr) (int, CORE_ADDR); void (*set_addr) (int, CORE_ADDR);
void (*reset_addr) (int); CORE_ADDR (*get_addr) (int);
unsigned long (*get_status) (void); unsigned long (*get_status) (void);
void (*unset_status) (unsigned long); unsigned long (*get_control) (void);
int debug_register_length; int debug_register_length;
}; };
extern struct i386_dr_low_type i386_dr_low; 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 /* Use this function to set i386_dr_low debug_register_length field
rather than setting it directly to check that the length is only rather than setting it directly to check that the length is only
set once. It also enables the 'maint set/show show-debug-regs' 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); 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 */ #endif /* I386_NAT_H */

View file

@ -264,6 +264,18 @@ i386bsd_target (void)
#define DBREG_DRX(d, x) ((&d->dr0)[x]) #define DBREG_DRX(d, x) ((&d->dr0)[x])
#endif #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 static void
i386bsd_dr_set (int regnum, unsigned int value) 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); i386bsd_dr_set (regnum, addr);
} }
void CORE_ADDR
i386bsd_dr_reset_addr (int regnum) i386bsd_dr_get_addr (int regnum)
{ {
gdb_assert (regnum >= 0 && regnum <= 4); return i386bsd_dr_get (inferior_ptid, regnum);
i386bsd_dr_set (regnum, 0);
} }
unsigned long unsigned long
i386bsd_dr_get_status (void) i386bsd_dr_get_status (void)
{ {
struct dbreg dbregs; return i386bsd_dr_get (inferior_ptid, 6);
}
if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid), unsigned long
(PTRACE_TYPE_ARG3) &dbregs, 0) == -1) i386bsd_dr_get_control (void)
perror_with_name (_("Couldn't read debug registers")); {
return i386bsd_dr_get (inferior_ptid, 7);
return DBREG_DRX ((&dbregs), 6);
} }
#endif /* PT_GETDBREGS */ #endif /* PT_GETDBREGS */

View file

@ -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_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_status (void);
extern unsigned long i386bsd_dr_get_control (void);
#endif /* i386bsd-nat.h */ #endif /* i386bsd-nat.h */

View file

@ -134,8 +134,9 @@ _initialize_i386fbsd_nat (void)
i386_dr_low.set_control = i386bsd_dr_set_control; i386_dr_low.set_control = i386bsd_dr_set_control;
i386_dr_low.set_addr = i386bsd_dr_set_addr; 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_status = i386bsd_dr_get_status;
i386_dr_low.get_control = i386bsd_dr_get_control;
i386_set_debug_register_length (4); i386_set_debug_register_length (4);
#endif /* HAVE_PT_GETDBREGS */ #endif /* HAVE_PT_GETDBREGS */

View file

@ -618,7 +618,7 @@ ia64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type,
} }
static void static void
ia64_linux_new_thread (ptid_t ptid) ia64_linux_new_thread (struct lwp_info *lp)
{ {
int i, any; int i, any;
@ -627,11 +627,11 @@ ia64_linux_new_thread (ptid_t ptid)
{ {
if (debug_registers[i] != 0) if (debug_registers[i] != 0)
any = 1; any = 1;
store_debug_register (ptid, i, debug_registers[i]); store_debug_register (lp->ptid, i, debug_registers[i]);
} }
if (any) if (any)
enable_watchpoints_in_psr (ptid); enable_watchpoints_in_psr (lp->ptid);
} }
static int static int

View file

@ -175,7 +175,10 @@ static struct target_ops *linux_ops;
static struct target_ops linux_ops_saved; static struct target_ops linux_ops_saved;
/* The method to call, if any, when a new thread is attached. */ /* 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 /* The method to call, if any, when the siginfo object needs to be
converted between the layout returned by ptrace, and the layout in converted between the layout returned by ptrace, and the layout in
@ -1073,6 +1076,15 @@ status_to_str (int status)
return buf; 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. */ /* Remove all LWPs belong to PID from the lwp list. */
static void static void
@ -1093,7 +1105,7 @@ purge_lwp_list (int pid)
else else
lpprev->next = lp->next; lpprev->next = lp->next;
xfree (lp); lwp_free (lp);
} }
else else
lpprev = lp; lpprev = lp;
@ -1139,8 +1151,8 @@ add_lwp (ptid_t ptid)
lp->next = lwp_list; lp->next = lwp_list;
lwp_list = lp; lwp_list = lp;
if (num_lwps (GET_PID (ptid)) > 1 && linux_nat_new_thread != NULL) if (linux_nat_new_thread != NULL)
linux_nat_new_thread (ptid); linux_nat_new_thread (lp);
return lp; return lp;
} }
@ -1166,7 +1178,7 @@ delete_lwp (ptid_t ptid)
else else
lwp_list = lp->next; lwp_list = lp->next;
xfree (lp); lwp_free (lp);
} }
/* Return a pointer to the structure describing the LWP corresponding /* Return a pointer to the structure describing the LWP corresponding
@ -1724,6 +1736,8 @@ detach_callback (struct lwp_info *lp, void *data)
/* Pass on any pending signal for this LWP. */ /* Pass on any pending signal for this LWP. */
get_pending_status (lp, &status); get_pending_status (lp, &status);
if (linux_nat_prepare_to_resume != NULL)
linux_nat_prepare_to_resume (lp);
errno = 0; errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0, if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (status)) < 0) WSTOPSIG (status)) < 0)
@ -1784,6 +1798,8 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
target_pid_to_str (main_lwp->ptid)); target_pid_to_str (main_lwp->ptid));
} }
if (linux_nat_prepare_to_resume != NULL)
linux_nat_prepare_to_resume (main_lwp);
delete_lwp (main_lwp->ptid); delete_lwp (main_lwp->ptid);
if (forks_exist_p ()) if (forks_exist_p ())
@ -1825,6 +1841,8 @@ resume_lwp (struct lwp_info *lp, int step)
"RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n", "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
target_pid_to_str (lp->ptid)); 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, linux_ops->to_resume (linux_ops,
pid_to_ptid (GET_LWP (lp->ptid)), pid_to_ptid (GET_LWP (lp->ptid)),
step, TARGET_SIGNAL_0); step, TARGET_SIGNAL_0);
@ -1969,6 +1987,8 @@ linux_nat_resume (struct target_ops *ops,
/* Convert to something the lower layer understands. */ /* Convert to something the lower layer understands. */
ptid = pid_to_ptid (GET_LWP (lp->ptid)); 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); linux_ops->to_resume (linux_ops, ptid, step, signo);
memset (&lp->siginfo, 0, sizeof (lp->siginfo)); memset (&lp->siginfo, 0, sizeof (lp->siginfo));
lp->stopped_by_watchpoint = 0; lp->stopped_by_watchpoint = 0;
@ -2138,6 +2158,8 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
/* Note that gdbarch_get_syscall_number may access registers, hence /* Note that gdbarch_get_syscall_number may access registers, hence
fill a regcache. */ fill a regcache. */
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0); lp->step, TARGET_SIGNAL_0);
return 1; return 1;
@ -2325,6 +2347,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
fprintf_unfiltered (gdb_stdlog, fprintf_unfiltered (gdb_stdlog,
"LHEW: resuming new LWP %ld\n", "LHEW: resuming new LWP %ld\n",
GET_LWP (new_lp->ptid)); 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), linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
0, TARGET_SIGNAL_0); 0, TARGET_SIGNAL_0);
new_lp->stopped = 0; new_lp->stopped = 0;
@ -2334,6 +2358,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
if (debug_linux_nat) if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, fprintf_unfiltered (gdb_stdlog,
"LHEW: resuming parent LWP %d\n", pid); "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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
0, TARGET_SIGNAL_0); 0, TARGET_SIGNAL_0);
@ -2597,6 +2623,14 @@ stop_callback (struct lwp_info *lp, void *data)
return 0; 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. */ /* Return non-zero if LWP PID has a pending SIGINT. */
static int static int
@ -3333,6 +3367,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0); lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat) if (debug_linux_nat)
@ -3364,6 +3400,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
lp->ignore_sigint = 0; lp->ignore_sigint = 0;
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0); lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat) if (debug_linux_nat)
@ -3538,6 +3576,8 @@ retry:
/* Resume the thread. It should halt immediately returning the /* Resume the thread. It should halt immediately returning the
pending SIGSTOP. */ pending SIGSTOP. */
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0); lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat) if (debug_linux_nat)
@ -3787,6 +3827,8 @@ retry:
newly attached threads may cause an unwanted delay in newly attached threads may cause an unwanted delay in
getting them running. */ getting them running. */
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, signo); lp->step, signo);
if (debug_linux_nat) if (debug_linux_nat)
@ -3943,6 +3985,8 @@ resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
lp->step); lp->step);
registers_changed (); 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)), linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0); lp->step, TARGET_SIGNAL_0);
lp->stopped = 0; lp->stopped = 0;
@ -5840,7 +5884,8 @@ linux_nat_add_target (struct target_ops *t)
/* Register a method to call whenever a new thread is attached. */ /* Register a method to call whenever a new thread is attached. */
void 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 /* 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 of the GNU/Linux native target, so we do not need to map this to
@ -5861,6 +5906,16 @@ linux_nat_set_siginfo_fixup (struct target_ops *t,
linux_nat_siginfo_fixup = siginfo_fixup; 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. */ /* Return the saved siginfo associated with PTID. */
struct siginfo * struct siginfo *
linux_nat_get_siginfo (ptid_t ptid) linux_nat_get_siginfo (ptid_t ptid)

View file

@ -22,6 +22,8 @@
#include <signal.h> #include <signal.h>
struct arch_lwp_info;
/* Ways to "resume" a thread. */ /* Ways to "resume" a thread. */
enum resume_kind enum resume_kind
@ -109,6 +111,9 @@ struct lwp_info
/* The processor core this LWP was last seen on. */ /* The processor core this LWP was last seen on. */
int core; int core;
/* Arch-specific additions. */
struct arch_lwp_info *arch_private;
/* Next LWP in list. */ /* Next LWP in list. */
struct lwp_info *next; 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 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. */ /* Iterator function for lin-lwp's lwp list. */
struct lwp_info *iterate_over_lwps (ptid_t filter, struct lwp_info *iterate_over_lwps (ptid_t filter,
int (*callback) (struct lwp_info *, 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 *); void linux_nat_add_target (struct target_ops *);
/* Register a method to call whenever a new thread is attached. */ /* 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 /* Register a method that converts a siginfo object between the layout
that ptrace returns, and the layout in the architecture of the 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 *, gdb_byte *,
int)); 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 /* Update linux-nat internal state when changing from one fork
to another. */ to another. */
void linux_nat_switch_fork (ptid_t new_ptid); void linux_nat_switch_fork (ptid_t new_ptid);

View file

@ -886,14 +886,14 @@ write_watchpoint_regs (void)
register values for the new thread. */ register values for the new thread. */
static void static void
mips_linux_new_thread (ptid_t ptid) mips_linux_new_thread (struct lwp_info *lp)
{ {
int tid; int tid;
if (!mips_linux_read_watch_registers (0)) if (!mips_linux_read_watch_registers (0))
return; return;
tid = ptid_get_lwp (ptid); tid = ptid_get_lwp (lp->ptid);
if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1) if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
perror_with_name (_("Couldn't write debug register")); perror_with_name (_("Couldn't write debug register"));
} }

View file

@ -2151,9 +2151,9 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw,
} }
static void static void
ppc_linux_new_thread (ptid_t ptid) ppc_linux_new_thread (struct lwp_info *lp)
{ {
int tid = TIDGET (ptid); int tid = TIDGET (lp->ptid);
if (have_ptrace_booke_interface ()) if (have_ptrace_booke_interface ())
{ {

View file

@ -472,7 +472,7 @@ s390_stopped_by_watchpoint (void)
} }
static void static void
s390_fix_watch_points (ptid_t ptid) s390_fix_watch_points (struct lwp_info *lp)
{ {
int tid; int tid;
@ -482,9 +482,9 @@ s390_fix_watch_points (ptid_t ptid)
CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0; CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
struct watch_area *area; struct watch_area *area;
tid = TIDGET (ptid); tid = TIDGET (lp->ptid);
if (tid == 0) if (tid == 0)
tid = PIDGET (ptid); tid = PIDGET (lp->ptid);
for (area = watch_base; area; area = area->next) for (area = watch_base; area; area = area->next)
{ {

View file

@ -1,3 +1,10 @@
2011-12-14 Pedro Alves <pedro@codesourcery.com>
PR threads/10729
* gdb.mi/watch-nonstop.c: New file.
* gdb.mi/mi-watch-nonstop.exp: New file.
2011-12-13 Pedro Alves <pedro@codesourcery.com> 2011-12-13 Pedro Alves <pedro@codesourcery.com>
Doug Evans <dje@google.com> Doug Evans <dje@google.com>

View file

@ -0,0 +1,77 @@
# Copyright 2011 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/>.
if [target_info exists gdb,no_hardware_watchpoints] {
return -1
}
if { ![support_displaced_stepping] } {
unsupported "displaced stepping"
return -1
}
load_lib mi-support.exp
set MIFLAGS "-i=mi"
gdb_exit
if {[mi_gdb_start]} {
continue
}
proc mi_nonstop_resume { command test } {
if { [mi_send_resuming_command $command $test] != 0 } {
# If a resume fails, assume non-stop is broken or unsupported
# for this target. We have logged a FAIL or UNSUPPORTED; skip
# the remaining tests to limit timeouts.
return -code continue
}
}
#
# Start here
#
set testfile "watch-nonstop"
set srcfile "$testfile.c"
set binfile "$objdir/$subdir/mi-$testfile"
if {[gdb_compile "$srcdir/$subdir/$srcfile" $binfile executable {debug}] != "" } {
return -1
}
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load $binfile
mi_gdb_test "-gdb-set non-stop 1" ".*"
mi_gdb_test "-gdb-set target-async 1" ".*"
mi_detect_async
if { [mi_run_to_main] < 0 } {
continue
}
# Set a watchpoint.
mi_gdb_test "111-break-watch global" \
"111\\^done,wpt=\{number=\"2\",exp=\"global\"\}" \
"break-watch operation"
# Set the target running.
mi_nonstop_resume "exec-continue" "resume 1"
# Now try deleting the watchpoint. This would fail with "Couldn't
# write debug register: No such process." on GNU/Linux, because we'd
# try to poke at the debug registers of a running thread.
mi_gdb_test "777-break-delete 2" \
"777\\^done" \
"delete watchpoint"

View file

@ -0,0 +1,24 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2011 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/>. */
int global;
int main ()
{
sleep (60);
return 0;
}

View file

@ -2494,8 +2494,9 @@ init_windows_ops (void)
i386_dr_low.set_control = cygwin_set_dr7; i386_dr_low.set_control = cygwin_set_dr7;
i386_dr_low.set_addr = cygwin_set_dr; 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_status = cygwin_get_dr6;
i386_dr_low.get_control = cygwin_get_dr7;
/* i386_dr_low.debug_register_length field is set by /* i386_dr_low.debug_register_length field is set by
calling i386_set_debug_register_length function calling i386_set_debug_register_length function
@ -2627,6 +2628,14 @@ cygwin_set_dr7 (unsigned long val)
debug_registers_used = 1; 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. /* Get the value of the DR6 debug status register from the inferior.
Here we just return the value stored in dr[6] Here we just return the value stored in dr[6]
by the last call to thread_rec for current_event.dwThreadId id. */ by the last call to thread_rec for current_event.dwThreadId id. */
@ -2636,6 +2645,16 @@ cygwin_get_dr6 (void)
return (unsigned long) dr[6]; return (unsigned long) dr[6];
} }
/* Get the value of the DR7 debug status register from the inferior.
Here we just return the value stored in dr[7] 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 /* Determine if the thread referenced by "ptid" is alive
by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0 by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0
it means that the thread has died. Otherwise it is assumed to be alive. */ it means that the thread has died. Otherwise it is assumed to be alive. */