Index: gdb/i386-nat.c |
diff --git a/gdb/i386-nat.c b/gdb/i386-nat.c |
index 29514d7b8cb2877422080569bd5d00056f34187d..753de67d04d9ec213f1c8951e7d0b81a3790c3e8 100644 |
--- a/gdb/i386-nat.c |
+++ b/gdb/i386-nat.c |
@@ -25,6 +25,7 @@ |
#include "gdbcmd.h" |
#include "target.h" |
#include "gdb_assert.h" |
+#include "inferior.h" |
/* Support for hardware watchpoints and breakpoints using the i386 |
debug registers. |
@@ -43,11 +44,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 +154,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,7 +171,88 @@ i386_init_dregs (struct i386_debug_reg_state *state) |
state->dr_status_mirror = 0; |
} |
-static struct i386_debug_reg_state dr_mirror; |
+/* Per-inferior data key. */ |
+static const struct inferior_data *i386_inferior_data; |
+ |
+/* Per-inferior data. */ |
+struct i386_inferior_data |
+{ |
+ /* Copy of i386 hardware debug registers for performance reasons. */ |
+ struct i386_debug_reg_state state; |
+}; |
+ |
+/* Per-inferior hook for register_inferior_data_with_cleanup. */ |
+ |
+static void |
+i386_inferior_data_cleanup (struct inferior *inf, void *arg) |
+{ |
+ struct i386_inferior_data *inf_data = arg; |
+ |
+ xfree (inf_data); |
+} |
+ |
+/* Get data specific for INFERIOR_PTID LWP. Return special data area |
+ for processes being detached. */ |
+ |
+static struct i386_inferior_data * |
+i386_inferior_data_get (void) |
+{ |
+ struct inferior *inf = current_inferior (); |
+ struct i386_inferior_data *inf_data; |
+ |
+ inf_data = inferior_data (inf, i386_inferior_data); |
+ if (inf_data == NULL) |
+ { |
+ inf_data = xzalloc (sizeof (*inf_data)); |
+ set_inferior_data (current_inferior (), i386_inferior_data, inf_data); |
+ } |
+ |
+ if (inf->pid != ptid_get_pid (inferior_ptid)) |
+ { |
+ /* INFERIOR_PTID is being detached from the inferior INF. |
+ Provide local cache specific for the detached LWP. */ |
+ |
+ static struct i386_inferior_data detached_inf_data_local; |
+ static int detached_inf_pid = -1; |
+ |
+ if (detached_inf_pid != ptid_get_pid (inferior_ptid)) |
+ { |
+ /* Reinitialize the local cache if INFERIOR_PTID is |
+ different from the LWP last detached. |
+ |
+ Linux kernel before 2.6.33 commit |
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d |
+ will inherit hardware debug registers from parent |
+ on fork/vfork/clone. Newer Linux kernels create such tasks with |
+ zeroed debug registers. |
+ |
+ GDB will remove all breakpoints (and watchpoints) from the forked |
+ off process. We also need to reset the debug registers in that |
+ process to be compatible with the older Linux kernels. |
+ |
+ Copy the debug registers mirrors into the new process so that all |
+ breakpoints and watchpoints can be removed together. The debug |
+ registers mirror will become zeroed in the end before detaching |
+ the forked off process. */ |
+ |
+ detached_inf_pid = ptid_get_pid (inferior_ptid); |
+ detached_inf_data_local = *inf_data; |
+ } |
+ |
+ return &detached_inf_data_local; |
+ } |
+ |
+ return inf_data; |
+} |
+ |
+/* Get debug registers state for INFERIOR_PTID, see |
+ i386_inferior_data_get. */ |
+ |
+struct i386_debug_reg_state * |
+i386_debug_reg_state (void) |
+{ |
+ return &i386_inferior_data_get ()->state; |
+} |
/* Whether or not to print the mirrored debug registers. */ |
static int maint_show_dr; |
@@ -244,7 +304,9 @@ static int i386_handle_nonaligned_watchpoint (struct i386_debug_reg_state *state |
void |
i386_cleanup_dregs (void) |
{ |
- i386_init_dregs (&dr_mirror); |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
+ |
+ i386_init_dregs (state); |
} |
/* Print the values of the mirrored debug registers. This is called |
@@ -508,35 +570,21 @@ Invalid value %d of operation in i386_handle_nonaligned_watchpoint.\n"), |
static void |
i386_update_inferior_debug_regs (struct i386_debug_reg_state *new_state) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
int 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_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); |
- } |
- } |
+ if (I386_DR_VACANT (new_state, i) != I386_DR_VACANT (state, 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]); |
+ gdb_assert (new_state->dr_mirror[i] == state->dr_mirror[i]); |
} |
- if (new_state->dr_control_mirror != dr_mirror.dr_control_mirror) |
+ if (new_state->dr_control_mirror != state->dr_control_mirror) |
i386_dr_low.set_control (new_state->dr_control_mirror); |
- dr_mirror = *new_state; |
+ *state = *new_state; |
} |
/* Insert a watchpoint to watch a memory region which starts at |
@@ -547,10 +595,11 @@ static int |
i386_insert_watchpoint (CORE_ADDR addr, int len, int type, |
struct expression *cond) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
int retval; |
/* Work on a local copy of the debug registers, and on success, |
commit the change back to the inferior. */ |
- struct i386_debug_reg_state local_state = dr_mirror; |
+ struct i386_debug_reg_state local_state = *state; |
if (type == hw_read) |
return 1; /* unsupported */ |
@@ -571,7 +620,7 @@ i386_insert_watchpoint (CORE_ADDR addr, int len, int type, |
i386_update_inferior_debug_regs (&local_state); |
if (maint_show_dr) |
- i386_show_dr (&dr_mirror, "insert_watchpoint", addr, len, type); |
+ i386_show_dr (state, "insert_watchpoint", addr, len, type); |
return retval; |
} |
@@ -583,10 +632,11 @@ static int |
i386_remove_watchpoint (CORE_ADDR addr, int len, int type, |
struct expression *cond) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
int retval; |
/* Work on a local copy of the debug registers, and on success, |
commit the change back to the inferior. */ |
- struct i386_debug_reg_state local_state = dr_mirror; |
+ struct i386_debug_reg_state local_state = *state; |
if (((len != 1 && len !=2 && len !=4) && !(TARGET_HAS_DR_LEN_8 && len == 8)) |
|| addr % len != 0) |
@@ -604,7 +654,7 @@ i386_remove_watchpoint (CORE_ADDR addr, int len, int type, |
i386_update_inferior_debug_regs (&local_state); |
if (maint_show_dr) |
- i386_show_dr (&dr_mirror, "remove_watchpoint", addr, len, type); |
+ i386_show_dr (state, "remove_watchpoint", addr, len, type); |
return retval; |
} |
@@ -615,11 +665,12 @@ i386_remove_watchpoint (CORE_ADDR addr, int len, int type, |
static int |
i386_region_ok_for_watchpoint (CORE_ADDR addr, int len) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
int nregs; |
/* Compute how many aligned watchpoints we would need to cover this |
region. */ |
- nregs = i386_handle_nonaligned_watchpoint (&dr_mirror, |
+ nregs = i386_handle_nonaligned_watchpoint (state, |
WP_COUNT, addr, len, hw_write); |
return nregs <= DR_NADDR ? 1 : 0; |
} |
@@ -631,38 +682,72 @@ i386_region_ok_for_watchpoint (CORE_ADDR addr, int len) |
static int |
i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
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 |
+ STATE (and friends). Say, we set a watchpoint, and let threads |
+ resume. Now, say you delete the watchpoint, or add/remove |
+ watchpoints such that STATE changes while threads are running. |
+ On targets that support non-stop, inserting/deleting watchpoints |
+ updates the STATE 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 |
+ STATE contents, and we haven't yet handled the corresponding |
+ SIGTRAP. If we trusted STATE 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 STATE. |
+ So to fix this, STATE 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); |
+ i386_show_dr (state, "watchpoint_hit", addr, -1, hw_write); |
} |
} |
if (maint_show_dr && addr == 0) |
- i386_show_dr (&dr_mirror, "stopped_data_addr", 0, 0, hw_write); |
+ i386_show_dr (state, "stopped_data_addr", 0, 0, hw_write); |
if (rc) |
*addr_p = addr; |
@@ -682,11 +767,12 @@ static int |
i386_insert_hw_breakpoint (struct gdbarch *gdbarch, |
struct bp_target_info *bp_tgt) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
unsigned len_rw = i386_length_and_rw_bits (1, hw_execute); |
CORE_ADDR addr = bp_tgt->placed_address; |
/* Work on a local copy of the debug registers, and on success, |
commit the change back to the inferior. */ |
- struct i386_debug_reg_state local_state = dr_mirror; |
+ struct i386_debug_reg_state local_state = *state; |
int retval = i386_insert_aligned_watchpoint (&local_state, |
addr, len_rw) ? EBUSY : 0; |
@@ -694,7 +780,7 @@ i386_insert_hw_breakpoint (struct gdbarch *gdbarch, |
i386_update_inferior_debug_regs (&local_state); |
if (maint_show_dr) |
- i386_show_dr (&dr_mirror, "insert_hwbp", addr, 1, hw_execute); |
+ i386_show_dr (state, "insert_hwbp", addr, 1, hw_execute); |
return retval; |
} |
@@ -706,11 +792,12 @@ static int |
i386_remove_hw_breakpoint (struct gdbarch *gdbarch, |
struct bp_target_info *bp_tgt) |
{ |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
unsigned len_rw = i386_length_and_rw_bits (1, hw_execute); |
CORE_ADDR addr = bp_tgt->placed_address; |
/* Work on a local copy of the debug registers, and on success, |
commit the change back to the inferior. */ |
- struct i386_debug_reg_state local_state = dr_mirror; |
+ struct i386_debug_reg_state local_state = *state; |
int retval = i386_remove_aligned_watchpoint (&local_state, |
addr, len_rw); |
@@ -718,7 +805,7 @@ i386_remove_hw_breakpoint (struct gdbarch *gdbarch, |
i386_update_inferior_debug_regs (&local_state); |
if (maint_show_dr) |
- i386_show_dr (&dr_mirror, "remove_hwbp", addr, 1, hw_execute); |
+ i386_show_dr (state, "remove_hwbp", addr, 1, hw_execute); |
return retval; |
} |
@@ -783,6 +870,10 @@ i386_use_watchpoints (struct target_ops *t) |
t->to_remove_watchpoint = i386_remove_watchpoint; |
t->to_insert_hw_breakpoint = i386_insert_hw_breakpoint; |
t->to_remove_hw_breakpoint = i386_remove_hw_breakpoint; |
+ |
+ if (i386_inferior_data == NULL) |
+ i386_inferior_data |
+ = register_inferior_data_with_cleanup (i386_inferior_data_cleanup); |
} |
void |