/* Debug register code for the i386.
- Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2009-2012 Free Software Foundation, Inc.
This file is part of GDB.
return (DR_LEN_2 | rw);
case 4:
return (DR_LEN_4 | rw);
+ /* ELSE FALL THROUGH */
case 8:
if (TARGET_HAS_DR_LEN_8)
return (DR_LEN_8 | rw);
state->dr_control_mirror |= DR_LOCAL_SLOWDOWN;
state->dr_control_mirror &= I386_DR_CONTROL_MASK;
- /* Finally, actually pass the info to the inferior. */
- i386_dr_low_set_addr (state, i);
- i386_dr_low_set_control (state);
-
return 0;
}
/* Reset our mirror. */
state->dr_mirror[i] = 0;
I386_DR_DISABLE (state, i);
- /* Reset it in the inferior. */
- i386_dr_low_set_control (state);
- i386_dr_low_set_addr (state, i);
}
retval = 0;
}
i386_wp_op_t what, CORE_ADDR addr, int len,
enum target_hw_bp_type type)
{
- int retval = 0, status = 0;
+ int retval = 0;
int max_wp_len = TARGET_HAS_DR_LEN_8 ? 8 : 4;
static const int size_try_array[8][8] =
unsigned len_rw = i386_length_and_rw_bits (size, type);
if (what == WP_INSERT)
- status = i386_insert_aligned_watchpoint (state, addr, len_rw);
+ retval = i386_insert_aligned_watchpoint (state, addr, len_rw);
else if (what == WP_REMOVE)
- status = i386_remove_aligned_watchpoint (state, addr, len_rw);
+ retval = i386_remove_aligned_watchpoint (state, addr, len_rw);
else
fatal ("\
Invalid value %d of operation in i386_handle_nonaligned_watchpoint.\n",
(int) what);
- /* We keep the loop going even after a failure, because some
- of the other aligned watchpoints might still succeed
- (e.g. if they watch addresses that are already watched,
- in which case we just increment the reference counts of
- occupied debug registers). If we break out of the loop
- too early, we could cause those addresses watched by
- other watchpoints to be disabled when breakpoint.c reacts
- to our failure to insert this watchpoint and tries to
- remove it. */
- if (status)
- retval = status;
+ if (retval)
+ break;
}
addr += size;
}
}
+/* Update the inferior debug registers state, in INF_STATE, with the
+ new debug registers state, in NEW_STATE. */
+
+static void
+i386_update_inferior_debug_regs (struct i386_debug_reg_state *inf_state,
+ struct i386_debug_reg_state *new_state)
+{
+ int i;
+
+ ALL_DEBUG_REGISTERS (i)
+ {
+ if (I386_DR_VACANT (new_state, i) != I386_DR_VACANT (inf_state, i))
+ i386_dr_low_set_addr (new_state, i);
+ else
+ gdb_assert (new_state->dr_mirror[i] == inf_state->dr_mirror[i]);
+ }
+
+ if (new_state->dr_control_mirror != inf_state->dr_control_mirror)
+ i386_dr_low_set_control (new_state);
+
+ *inf_state = *new_state;
+}
+
/* Insert a watchpoint to watch a memory region which starts at
address ADDR and whose length is LEN bytes. Watch memory accesses
of the type TYPE_FROM_PACKET. Return 0 on success, -1 on failure. */
{
int retval;
enum target_hw_bp_type type = Z_packet_to_hw_type (type_from_packet);
+ /* 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 = *state;
if (type == hw_read)
return 1; /* unsupported */
&& !(TARGET_HAS_DR_LEN_8 && len == 8))
|| addr % len != 0)
{
- retval = i386_handle_nonaligned_watchpoint (state, WP_INSERT,
+ retval = i386_handle_nonaligned_watchpoint (&local_state, WP_INSERT,
addr, len, type);
}
else
{
unsigned len_rw = i386_length_and_rw_bits (len, type);
- retval = i386_insert_aligned_watchpoint (state, addr, len_rw);
+ retval = i386_insert_aligned_watchpoint (&local_state, addr, len_rw);
}
+ if (retval == 0)
+ i386_update_inferior_debug_regs (state, &local_state);
+
if (debug_hw_points)
i386_show_dr (state, "insert_watchpoint", addr, len, type);
{
int retval;
enum target_hw_bp_type type = Z_packet_to_hw_type (type_from_packet);
+ /* 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 = *state;
if (((len != 1 && len != 2 && len != 4)
&& !(TARGET_HAS_DR_LEN_8 && len == 8))
|| addr % len != 0)
{
- retval = i386_handle_nonaligned_watchpoint (state, WP_REMOVE,
+ retval = i386_handle_nonaligned_watchpoint (&local_state, WP_REMOVE,
addr, len, type);
}
else
{
unsigned len_rw = i386_length_and_rw_bits (len, type);
- retval = i386_remove_aligned_watchpoint (state, addr, len_rw);
+ retval = i386_remove_aligned_watchpoint (&local_state, addr, len_rw);
}
+ if (retval == 0)
+ i386_update_inferior_debug_regs (state, &local_state);
+
if (debug_hw_points)
i386_show_dr (state, "remove_watchpoint", addr, len, type);
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;
-
- /* Get the current values the inferior has. If the thread was
- running when we last changed watchpoints, the mirror no longer
- represents what was set in this LWP's debug registers. */
+ /* 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 ();
- control = i386_dr_low_get_control ();
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)
+ 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 = i386_dr_low_get_addr (i);
rc = 1;