+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>
* ia64-tdep.c (ia64_memory_remove_breakpoint): Use
#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
/* 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)
{
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. */
+
+static unsigned long
+amd64_linux_dr_get_status (void)
+{
+ return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+}
+
+/* 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);
- amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
- ALL_LWPS (lp)
- amd64_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr);
+ return 0;
}
-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */
+/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior. */
static void
-amd64_linux_dr_reset_addr (int regnum)
+amd64_linux_dr_set_control (unsigned long control)
{
- amd64_linux_dr_set_addr (regnum, 0);
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
}
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+ inferior. */
-static unsigned long
-amd64_linux_dr_get_status (void)
+static void
+amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+ 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);
}
-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
static void
-amd64_linux_dr_unset_status (unsigned long mask)
+amd64_linux_prepare_to_resume (struct lwp_info *lwp)
{
- struct lwp_info *lp;
+ int clear_status = 0;
- ALL_LWPS (lp)
+ 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);
+ 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 (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
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. */
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);
}
/* Handle thread creation. We need to copy the breakpoints and watchpoints
in the parent thread to the child thread. */
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 ();
if (info != NULL)
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
mode, return 1 if it was in raw mode, zero otherwise. */
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;
/* 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)
{
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
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)
{
#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"
#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
/* 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
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 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
-i386_linux_dr_reset_addr (int regnum)
+i386_linux_dr_set_control (unsigned long control)
{
- i386_linux_dr_set_addr (regnum, 0);
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+ iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
}
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+ inferior. */
-static unsigned long
-i386_linux_dr_get_status (void)
+static void
+i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- return i386_linux_dr_get (inferior_ptid, DR_STATUS);
+ 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);
}
-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */
+/* Called when resuming a thread.
+ If the debug regs have changed, update the thread's copies. */
static void
-i386_linux_dr_unset_status (unsigned long mask)
+i386_linux_prepare_to_resume (struct lwp_info *lwp)
{
- struct lwp_info *lp;
+ int clear_status = 0;
- ALL_LWPS (lp)
+ if (lwp->arch_private->debug_registers_changed)
{
- unsigned long value;
+ 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)
+ {
+ 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;
+ }
- 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 (clear_status || 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
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. */
/* 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);
}
/* 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. */
/* 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. */
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;
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]);
}
CORE_ADDR addr = 0;
int i;
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 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;
+ /* We need DR_CONTROL as well, but only iff DR_STATUS indicates a
+ 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;
+
+ /* In non-stop/async, threads can be running while we change the
+ global dr_mirror (and friends). Say, we set a watchpoint, and
+ 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)
{
- if (I386_DR_WATCH_HIT (status, i)
- /* 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. */
- && 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))
+ if (!I386_DR_WATCH_HIT (status, i))
+ continue;
+
+ if (!control_p)
+ {
+ 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 = 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);
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'
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 */
#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)
{
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 */
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 */
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 */
}
static void
-ia64_linux_new_thread (ptid_t ptid)
+ia64_linux_new_thread (struct lwp_info *lp)
{
int i, any;
{
if (debug_registers[i] != 0)
any = 1;
- store_debug_register (ptid, i, debug_registers[i]);
+ store_debug_register (lp->ptid, i, debug_registers[i]);
}
if (any)
- enable_watchpoints_in_psr (ptid);
+ enable_watchpoints_in_psr (lp->ptid);
}
static int
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
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
else
lpprev->next = lp->next;
- xfree (lp);
+ lwp_free (lp);
}
else
lpprev = lp;
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;
}
else
lwp_list = lp->next;
- xfree (lp);
+ lwp_free (lp);
}
/* Return a pointer to the structure describing the LWP corresponding
/* Pass on any pending signal for this LWP. */
get_pending_status (lp, &status);
+ if (linux_nat_prepare_to_resume != NULL)
+ linux_nat_prepare_to_resume (lp);
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (status)) < 0)
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);
if (forks_exist_p ())
"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);
/* 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;
/* 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;
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;
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);
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
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)
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)
/* 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)
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)
lp->step);
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);
lp->stopped = 0;
/* 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
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)
#include <signal.h>
+struct arch_lwp_info;
+
/* Ways to "resume" a thread. */
enum resume_kind
/* 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;
};
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 *,
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
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);
register values for the new thread. */
static void
-mips_linux_new_thread (ptid_t ptid)
+mips_linux_new_thread (struct lwp_info *lp)
{
int tid;
if (!mips_linux_read_watch_registers (0))
return;
- tid = ptid_get_lwp (ptid);
+ tid = ptid_get_lwp (lp->ptid);
if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
perror_with_name (_("Couldn't write debug register"));
}
}
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 ())
{
}
static void
-s390_fix_watch_points (ptid_t ptid)
+s390_fix_watch_points (struct lwp_info *lp)
{
int tid;
CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
struct watch_area *area;
- tid = TIDGET (ptid);
+ tid = TIDGET (lp->ptid);
if (tid == 0)
- tid = PIDGET (ptid);
+ tid = PIDGET (lp->ptid);
for (area = watch_base; area; area = area->next)
{
+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>
Doug Evans <dje@google.com>
--- /dev/null
+# 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"
--- /dev/null
+/* 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;
+}
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
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. */
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
by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0
it means that the thread has died. Otherwise it is assumed to be alive. */