aboutsummaryrefslogtreecommitdiff
path: root/gdb/i386-nat.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2011-12-14 17:20:32 +0000
committerPedro Alves <palves@redhat.com>2011-12-14 17:20:32 +0000
commit7b50312ad68f7c3acac2b318f6ff96956ad381ea (patch)
tree8334359a21c6cfe3384baa3bb625a28db9ace8c7 /gdb/i386-nat.c
parent0d13c96b214428affc33281d6d119a873c45acee (diff)
downloadgdb-7b50312ad68f7c3acac2b318f6ff96956ad381ea.zip
gdb-7b50312ad68f7c3acac2b318f6ff96956ad381ea.tar.gz
gdb-7b50312ad68f7c3acac2b318f6ff96956ad381ea.tar.bz2
gdb/
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.
Diffstat (limited to 'gdb/i386-nat.c')
-rw-r--r--gdb/i386-nat.c115
1 files changed, 60 insertions, 55 deletions
diff --git a/gdb/i386-nat.c b/gdb/i386-nat.c
index 568b79b..593401b 100644
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -43,11 +43,6 @@ struct i386_dr_low_type i386_dr_low;
/* Support for 8-byte wide hw watchpoints. */
#define TARGET_HAS_DR_LEN_8 (i386_dr_low.debug_register_length == 8)
-/* Debug registers' indices. */
-#define DR_NADDR 4 /* The number of debug address registers. */
-#define DR_STATUS 6 /* Index of debug status register (DR6). */
-#define DR_CONTROL 7 /* Index of debug control register (DR7). */
-
/* DR7 Debug Control register fields. */
/* How many bits to skip in DR7 to get to R/W and LEN fields. */
@@ -158,23 +153,6 @@ struct i386_dr_low_type i386_dr_low;
/* A macro to loop over all debug registers. */
#define ALL_DEBUG_REGISTERS(i) for (i = 0; i < DR_NADDR; i++)
-
-/* Global state needed to track h/w watchpoints. */
-
-struct i386_debug_reg_state
-{
- /* Mirror the inferior's DRi registers. We keep the status and
- control registers separated because they don't hold addresses.
- Note that since we can change these mirrors while threads are
- running, we never trust them to explain a cause of a trap.
- For that, we need to peek directly in the inferior registers. */
- CORE_ADDR dr_mirror[DR_NADDR];
- unsigned dr_status_mirror, dr_control_mirror;
-
- /* Reference counts for each debug register. */
- int dr_ref_count[DR_NADDR];
-};
-
/* Clear the reference counts and forget everything we knew about the
debug registers. */
@@ -192,8 +170,16 @@ i386_init_dregs (struct i386_debug_reg_state *state)
state->dr_status_mirror = 0;
}
+/* The local mirror of the inferior's debug registers. Currently this
+ is a global, but it should really be per-inferior. */
static struct i386_debug_reg_state dr_mirror;
+struct i386_debug_reg_state *
+i386_debug_reg_state (void)
+{
+ return &dr_mirror;
+}
+
/* Whether or not to print the mirrored debug registers. */
static int maint_show_dr;
@@ -513,22 +499,7 @@ i386_update_inferior_debug_regs (struct i386_debug_reg_state *new_state)
ALL_DEBUG_REGISTERS (i)
{
if (I386_DR_VACANT (new_state, i) != I386_DR_VACANT (&dr_mirror, i))
- {
- if (!I386_DR_VACANT (new_state, i))
- {
- i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
-
- /* Only a sanity check for leftover bits (set possibly only
- by inferior). */
- if (i386_dr_low.unset_status)
- i386_dr_low.unset_status (I386_DR_WATCH_MASK (i));
- }
- else
- {
- if (i386_dr_low.reset_addr)
- i386_dr_low.reset_addr (i);
- }
- }
+ i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
else
gdb_assert (new_state->dr_mirror[i] == dr_mirror.dr_mirror[i]);
}
@@ -634,28 +605,62 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
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);