aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog28
-rw-r--r--gdb/breakpoint.c91
-rw-r--r--gdb/breakpoint.h5
-rw-r--r--gdb/infrun.c70
-rw-r--r--gdb/target-delegates.c124
-rw-r--r--gdb/target.h44
6 files changed, 331 insertions, 31 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6ed6f4a..59fcf9a 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,33 @@
2015-03-04 Pedro Alves <palves@redhat.com>
+ * breakpoint.c (need_moribund_for_location_type): New function.
+ (bpstat_stop_status): Don't skipping checking moribund locations
+ of breakpoint types which the target tell caused a stop.
+ (program_breakpoint_here_p): New function, factored out from ...
+ (bp_loc_is_permanent): ... this.
+ (update_global_location_list): Don't create a moribund location if
+ the target supports reporting stops of the type of the removed
+ breakpoint.
+ * breakpoint.h (program_breakpoint_here_p): New declaration.
+ * infrun.c (adjust_pc_after_break): Return early if the target has
+ already adjusted the PC. Add comments.
+ (handle_signal_stop): If nothing explains a signal, and the target
+ tells us the stop was caused by a software breakpoint, check if
+ there's a breakpoint instruction in the memory. If so, adjust the
+ PC before presenting the stop to the user. Otherwise, ignore the
+ trap. If nothing explains a signal, and the target tells us the
+ stop was caused by a hardware breakpoint, ignore the trap.
+ * target.h (struct target_ops) <to_stopped_by_sw_breakpoint,
+ to_supports_stopped_by_sw_breakpoint, to_stopped_by_hw_breakpoint,
+ to_supports_stopped_by_hw_breakpoint>: New fields.
+ (target_stopped_by_sw_breakpoint)
+ (target_supports_stopped_by_sw_breakpoint)
+ (target_stopped_by_hw_breakpoint)
+ (target_supports_stopped_by_hw_breakpoint): Define.
+ * target-delegates.c: Regenerate.
+
+2015-03-04 Pedro Alves <palves@redhat.com>
+
* infrun.c (follow_fork_inferior): Use the whole of the
inferior_ptid and pending_follow.related_pid ptids instead of
building ptids from the process components. Adjust verbose output
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index db4b872..c5d3240 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5452,6 +5452,18 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
}
}
+/* Returns true if we need to track moribund locations of LOC's type
+ on the current target. */
+
+static int
+need_moribund_for_location_type (struct bp_location *loc)
+{
+ return ((loc->loc_type == bp_loc_software_breakpoint
+ && !target_supports_stopped_by_sw_breakpoint ())
+ || (loc->loc_type == bp_loc_hardware_breakpoint
+ && !target_supports_stopped_by_hw_breakpoint ()));
+}
+
/* Get a bpstat associated with having just stopped at address
BP_ADDR in thread PTID.
@@ -5541,15 +5553,20 @@ bpstat_stop_status (struct address_space *aspace,
}
/* Check if a moribund breakpoint explains the stop. */
- for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
+ if (!target_supports_stopped_by_sw_breakpoint ()
+ || !target_supports_stopped_by_hw_breakpoint ())
{
- if (breakpoint_location_address_match (loc, aspace, bp_addr))
+ for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
{
- bs = bpstat_alloc (loc, &bs_link);
- /* For hits of moribund locations, we should just proceed. */
- bs->stop = 0;
- bs->print = 0;
- bs->print_it = print_it_noop;
+ if (breakpoint_location_address_match (loc, aspace, bp_addr)
+ && need_moribund_for_location_type (loc))
+ {
+ bs = bpstat_alloc (loc, &bs_link);
+ /* For hits of moribund locations, we should just proceed. */
+ bs->stop = 0;
+ bs->print = 0;
+ bs->print_it = print_it_noop;
+ }
}
}
@@ -9304,11 +9321,10 @@ add_location_to_breakpoint (struct breakpoint *b,
}
-/* Return 1 if LOC is pointing to a permanent breakpoint,
- return 0 otherwise. */
+/* See breakpoint.h. */
-static int
-bp_loc_is_permanent (struct bp_location *loc)
+int
+program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address)
{
int len;
CORE_ADDR addr;
@@ -9317,6 +9333,38 @@ bp_loc_is_permanent (struct bp_location *loc)
struct cleanup *cleanup;
int retval = 0;
+ addr = address;
+ bpoint = gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+ /* Software breakpoints unsupported? */
+ if (bpoint == NULL)
+ return 0;
+
+ target_mem = alloca (len);
+
+ /* Enable the automatic memory restoration from breakpoints while
+ we read the memory. Otherwise we could say about our temporary
+ breakpoints they are permanent. */
+ cleanup = make_show_memory_breakpoints_cleanup (0);
+
+ if (target_read_memory (address, target_mem, len) == 0
+ && memcmp (target_mem, bpoint, len) == 0)
+ retval = 1;
+
+ do_cleanups (cleanup);
+
+ return retval;
+}
+
+/* Return 1 if LOC is pointing to a permanent breakpoint,
+ return 0 otherwise. */
+
+static int
+bp_loc_is_permanent (struct bp_location *loc)
+{
+ struct cleanup *cleanup;
+ int retval;
+
gdb_assert (loc != NULL);
/* bp_call_dummy breakpoint locations are usually memory locations
@@ -9333,26 +9381,10 @@ bp_loc_is_permanent (struct bp_location *loc)
if (loc->owner->type == bp_call_dummy)
return 0;
- addr = loc->address;
- bpoint = gdbarch_breakpoint_from_pc (loc->gdbarch, &addr, &len);
-
- /* Software breakpoints unsupported? */
- if (bpoint == NULL)
- return 0;
-
- target_mem = alloca (len);
-
- /* Enable the automatic memory restoration from breakpoints while
- we read the memory. Otherwise we could say about our temporary
- breakpoints they are permanent. */
cleanup = save_current_space_and_thread ();
-
switch_to_program_space_and_thread (loc->pspace);
- make_show_memory_breakpoints_cleanup (0);
- if (target_read_memory (loc->address, target_mem, len) == 0
- && memcmp (target_mem, bpoint, len) == 0)
- retval = 1;
+ retval = program_breakpoint_here_p (loc->gdbarch, loc->address);
do_cleanups (cleanup);
@@ -12797,8 +12829,7 @@ update_global_location_list (enum ugll_insert_mode insert_mode)
if (!found_object)
{
if (removed && non_stop
- && breakpoint_address_is_meaningful (old_loc->owner)
- && !is_hardware_watchpoint (old_loc->owner))
+ && need_moribund_for_location_type (old_loc))
{
/* This location was removed from the target. In
non-stop mode, a race condition is possible where
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ad91102..85c2240 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1117,6 +1117,11 @@ enum breakpoint_here
/* Prototypes for breakpoint-related functions. */
+/* Return 1 if there's a program/permanent breakpoint planted in
+ memory at ADDRESS, return 0 otherwise. */
+
+extern int program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address);
+
extern enum breakpoint_here breakpoint_here_p (struct address_space *,
CORE_ADDR);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index abfeeee..8d3a9bf 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3468,6 +3468,18 @@ adjust_pc_after_break (struct execution_control_state *ecs)
if (execution_direction == EXEC_REVERSE)
return;
+ /* If the target can tell whether the thread hit a SW breakpoint,
+ trust it. Targets that can tell also adjust the PC
+ themselves. */
+ if (target_supports_stopped_by_sw_breakpoint ())
+ return;
+
+ /* Note that relying on whether a breakpoint is planted in memory to
+ determine this can fail. E.g,. the breakpoint could have been
+ removed since. Or the thread could have been told to step an
+ instruction the size of a breakpoint instruction, and only
+ _after_ was a breakpoint inserted at its address. */
+
/* If this target does not decrement the PC after breakpoints, then
we have nothing to do. */
regcache = get_thread_regcache (ecs->ptid);
@@ -3483,6 +3495,11 @@ adjust_pc_after_break (struct execution_control_state *ecs)
breakpoint would be. */
breakpoint_pc = regcache_read_pc (regcache) - decr_pc;
+ /* If the target can't tell whether a software breakpoint triggered,
+ fallback to figuring it out based on breakpoints we think were
+ inserted in the target, and on whether the thread was stepped or
+ continued. */
+
/* Check whether there actually is a software breakpoint inserted at
that location.
@@ -3490,7 +3507,10 @@ adjust_pc_after_break (struct execution_control_state *ecs)
removed a breakpoint, but stop events for that breakpoint were
already queued and arrive later. To suppress those spurious
SIGTRAPs, we keep a list of such breakpoint locations for a bit,
- and retire them after a number of stop events are reported. */
+ and retire them after a number of stop events are reported. Note
+ this is an heuristic and can thus get confused. The real fix is
+ to get the "stopped by SW BP and needs adjustment" info out of
+ the target/kernel (and thus never reach here; see above). */
if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
|| (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
{
@@ -4505,6 +4525,54 @@ handle_signal_stop (struct execution_control_state *ecs)
= !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat,
ecs->event_thread->suspend.stop_signal);
+ /* Maybe this was a trap for a software breakpoint that has since
+ been removed. */
+ if (random_signal && target_stopped_by_sw_breakpoint ())
+ {
+ if (program_breakpoint_here_p (gdbarch, stop_pc))
+ {
+ struct regcache *regcache;
+ int decr_pc;
+
+ /* Re-adjust PC to what the program would see if GDB was not
+ debugging it. */
+ regcache = get_thread_regcache (ecs->event_thread->ptid);
+ decr_pc = target_decr_pc_after_break (gdbarch);
+ if (decr_pc != 0)
+ {
+ struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL);
+
+ if (record_full_is_used ())
+ record_full_gdb_operation_disable_set ();
+
+ regcache_write_pc (regcache, stop_pc + decr_pc);
+
+ do_cleanups (old_cleanups);
+ }
+ }
+ else
+ {
+ /* A delayed software breakpoint event. Ignore the trap. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: delayed software breakpoint "
+ "trap, ignoring\n");
+ random_signal = 0;
+ }
+ }
+
+ /* Maybe this was a trap for a hardware breakpoint/watchpoint that
+ has since been removed. */
+ if (random_signal && target_stopped_by_hw_breakpoint ())
+ {
+ /* A delayed hardware breakpoint event. Ignore the trap. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: delayed hardware breakpoint/watchpoint "
+ "trap, ignoring\n");
+ random_signal = 0;
+ }
+
/* If not, perhaps stepping/nexting can. */
if (random_signal)
random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index e026179..1f827f5 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -293,6 +293,114 @@ debug_remove_breakpoint (struct target_ops *self, struct gdbarch *arg1, struct b
}
static int
+delegate_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ self = self->beneath;
+ return self->to_stopped_by_sw_breakpoint (self);
+}
+
+static int
+tdefault_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ return 0;
+}
+
+static int
+debug_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ int result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->to_stopped_by_sw_breakpoint (...)\n", debug_target.to_shortname);
+ result = debug_target.to_stopped_by_sw_breakpoint (&debug_target);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->to_stopped_by_sw_breakpoint (", debug_target.to_shortname);
+ target_debug_print_struct_target_ops_p (&debug_target);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_int (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
+static int
+delegate_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ self = self->beneath;
+ return self->to_supports_stopped_by_sw_breakpoint (self);
+}
+
+static int
+tdefault_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ return 0;
+}
+
+static int
+debug_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+ int result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->to_supports_stopped_by_sw_breakpoint (...)\n", debug_target.to_shortname);
+ result = debug_target.to_supports_stopped_by_sw_breakpoint (&debug_target);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->to_supports_stopped_by_sw_breakpoint (", debug_target.to_shortname);
+ target_debug_print_struct_target_ops_p (&debug_target);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_int (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
+static int
+delegate_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ self = self->beneath;
+ return self->to_stopped_by_hw_breakpoint (self);
+}
+
+static int
+tdefault_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ return 0;
+}
+
+static int
+debug_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ int result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->to_stopped_by_hw_breakpoint (...)\n", debug_target.to_shortname);
+ result = debug_target.to_stopped_by_hw_breakpoint (&debug_target);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->to_stopped_by_hw_breakpoint (", debug_target.to_shortname);
+ target_debug_print_struct_target_ops_p (&debug_target);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_int (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
+static int
+delegate_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ self = self->beneath;
+ return self->to_supports_stopped_by_hw_breakpoint (self);
+}
+
+static int
+tdefault_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ return 0;
+}
+
+static int
+debug_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+ int result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->to_supports_stopped_by_hw_breakpoint (...)\n", debug_target.to_shortname);
+ result = debug_target.to_supports_stopped_by_hw_breakpoint (&debug_target);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->to_supports_stopped_by_hw_breakpoint (", debug_target.to_shortname);
+ target_debug_print_struct_target_ops_p (&debug_target);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_int (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
+static int
delegate_can_use_hw_breakpoint (struct target_ops *self, int arg1, int arg2, int arg3)
{
self = self->beneath;
@@ -3789,6 +3897,14 @@ install_delegators (struct target_ops *ops)
ops->to_insert_breakpoint = delegate_insert_breakpoint;
if (ops->to_remove_breakpoint == NULL)
ops->to_remove_breakpoint = delegate_remove_breakpoint;
+ if (ops->to_stopped_by_sw_breakpoint == NULL)
+ ops->to_stopped_by_sw_breakpoint = delegate_stopped_by_sw_breakpoint;
+ if (ops->to_supports_stopped_by_sw_breakpoint == NULL)
+ ops->to_supports_stopped_by_sw_breakpoint = delegate_supports_stopped_by_sw_breakpoint;
+ if (ops->to_stopped_by_hw_breakpoint == NULL)
+ ops->to_stopped_by_hw_breakpoint = delegate_stopped_by_hw_breakpoint;
+ if (ops->to_supports_stopped_by_hw_breakpoint == NULL)
+ ops->to_supports_stopped_by_hw_breakpoint = delegate_supports_stopped_by_hw_breakpoint;
if (ops->to_can_use_hw_breakpoint == NULL)
ops->to_can_use_hw_breakpoint = delegate_can_use_hw_breakpoint;
if (ops->to_ranged_break_num_registers == NULL)
@@ -4061,6 +4177,10 @@ install_dummy_methods (struct target_ops *ops)
ops->to_files_info = tdefault_files_info;
ops->to_insert_breakpoint = memory_insert_breakpoint;
ops->to_remove_breakpoint = memory_remove_breakpoint;
+ ops->to_stopped_by_sw_breakpoint = tdefault_stopped_by_sw_breakpoint;
+ ops->to_supports_stopped_by_sw_breakpoint = tdefault_supports_stopped_by_sw_breakpoint;
+ ops->to_stopped_by_hw_breakpoint = tdefault_stopped_by_hw_breakpoint;
+ ops->to_supports_stopped_by_hw_breakpoint = tdefault_supports_stopped_by_hw_breakpoint;
ops->to_can_use_hw_breakpoint = tdefault_can_use_hw_breakpoint;
ops->to_ranged_break_num_registers = tdefault_ranged_break_num_registers;
ops->to_insert_hw_breakpoint = tdefault_insert_hw_breakpoint;
@@ -4205,6 +4325,10 @@ init_debug_target (struct target_ops *ops)
ops->to_files_info = debug_files_info;
ops->to_insert_breakpoint = debug_insert_breakpoint;
ops->to_remove_breakpoint = debug_remove_breakpoint;
+ ops->to_stopped_by_sw_breakpoint = debug_stopped_by_sw_breakpoint;
+ ops->to_supports_stopped_by_sw_breakpoint = debug_supports_stopped_by_sw_breakpoint;
+ ops->to_stopped_by_hw_breakpoint = debug_stopped_by_hw_breakpoint;
+ ops->to_supports_stopped_by_hw_breakpoint = debug_supports_stopped_by_hw_breakpoint;
ops->to_can_use_hw_breakpoint = debug_can_use_hw_breakpoint;
ops->to_ranged_break_num_registers = debug_ranged_break_num_registers;
ops->to_insert_hw_breakpoint = debug_insert_hw_breakpoint;
diff --git a/gdb/target.h b/gdb/target.h
index 5ba8425..a7c2e82 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -454,6 +454,35 @@ struct target_ops
int (*to_remove_breakpoint) (struct target_ops *, struct gdbarch *,
struct bp_target_info *)
TARGET_DEFAULT_FUNC (memory_remove_breakpoint);
+
+ /* Returns true if the target stopped because it executed a
+ software breakpoint. This is necessary for correct background
+ execution / non-stop mode operation, and for correct PC
+ adjustment on targets where the PC needs to be adjusted when a
+ software breakpoint triggers. In these modes, by the time GDB
+ processes a breakpoint event, the breakpoint may already be
+ done from the target, so GDB needs to be able to tell whether
+ it should ignore the event and whether it should adjust the PC.
+ See adjust_pc_after_break. */
+ int (*to_stopped_by_sw_breakpoint) (struct target_ops *)
+ TARGET_DEFAULT_RETURN (0);
+ /* Returns true if the above method is supported. */
+ int (*to_supports_stopped_by_sw_breakpoint) (struct target_ops *)
+ TARGET_DEFAULT_RETURN (0);
+
+ /* Returns true if the target stopped for a hardware breakpoint.
+ Likewise, if the target supports hardware breakpoints, this
+ method is necessary for correct background execution / non-stop
+ mode operation. Even though hardware breakpoints do not
+ require PC adjustment, GDB needs to be able to tell whether the
+ hardware breakpoint event is a delayed event for a breakpoint
+ that is already gone and should thus be ignored. */
+ int (*to_stopped_by_hw_breakpoint) (struct target_ops *)
+ TARGET_DEFAULT_RETURN (0);
+ /* Returns true if the above method is supported. */
+ int (*to_supports_stopped_by_hw_breakpoint) (struct target_ops *)
+ TARGET_DEFAULT_RETURN (0);
+
int (*to_can_use_hw_breakpoint) (struct target_ops *, int, int, int)
TARGET_DEFAULT_RETURN (0);
int (*to_ranged_break_num_registers) (struct target_ops *)
@@ -1743,6 +1772,21 @@ extern char *target_thread_name (struct thread_info *);
#define target_stopped_by_watchpoint() \
((*current_target.to_stopped_by_watchpoint) (&current_target))
+/* Returns non-zero if the target stopped because it executed a
+ software breakpoint instruction. */
+
+#define target_stopped_by_sw_breakpoint() \
+ ((*current_target.to_stopped_by_sw_breakpoint) (&current_target))
+
+#define target_supports_stopped_by_sw_breakpoint() \
+ ((*current_target.to_supports_stopped_by_sw_breakpoint) (&current_target))
+
+#define target_stopped_by_hw_breakpoint() \
+ ((*current_target.to_stopped_by_hw_breakpoint) (&current_target))
+
+#define target_supports_stopped_by_hw_breakpoint() \
+ ((*current_target.to_supports_stopped_by_hw_breakpoint) (&current_target))
+
/* Non-zero if we have steppable watchpoints */
#define target_have_steppable_watchpoint \