diff options
-rw-r--r-- | gdb/ChangeLog | 28 | ||||
-rw-r--r-- | gdb/breakpoint.c | 91 | ||||
-rw-r--r-- | gdb/breakpoint.h | 5 | ||||
-rw-r--r-- | gdb/infrun.c | 70 | ||||
-rw-r--r-- | gdb/target-delegates.c | 124 | ||||
-rw-r--r-- | gdb/target.h | 44 |
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) (¤t_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) (¤t_target)) + +#define target_supports_stopped_by_sw_breakpoint() \ + ((*current_target.to_supports_stopped_by_sw_breakpoint) (¤t_target)) + +#define target_stopped_by_hw_breakpoint() \ + ((*current_target.to_stopped_by_hw_breakpoint) (¤t_target)) + +#define target_supports_stopped_by_hw_breakpoint() \ + ((*current_target.to_supports_stopped_by_hw_breakpoint) (¤t_target)) + /* Non-zero if we have steppable watchpoints */ #define target_have_steppable_watchpoint \ |