diff options
author | Pedro Alves <palves@redhat.com> | 2011-12-14 17:20:32 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2011-12-14 17:20:32 +0000 |
commit | 7b50312ad68f7c3acac2b318f6ff96956ad381ea (patch) | |
tree | 8334359a21c6cfe3384baa3bb625a28db9ace8c7 /gdb/i386-nat.c | |
parent | 0d13c96b214428affc33281d6d119a873c45acee (diff) | |
download | gdb-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.c | 115 |
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); |