From: Pedro Alves Date: Wed, 15 Oct 2014 19:18:32 +0000 (+0100) Subject: Make single-step breakpoints be per-thread X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=34b7e8a6ad0a735ecc0a953c8b65846d4776c88e;p=binutils-gdb.git Make single-step breakpoints be per-thread This patch finally makes each thread have its own set of single-step breakpoints. This paves the way to have multiple threads software single-stepping, though this patch doesn't flip that switch on yet. That'll be done on a subsequent patch. gdb/ 2014-10-15 Pedro Alves * breakpoint.c (single_step_breakpoints): Delete global. (insert_single_step_breakpoint): Adjust to store the breakpoint pointer in the current thread. (single_step_breakpoints_inserted, remove_single_step_breakpoints) (cancel_single_step_breakpoints): Delete functions. (breakpoint_has_location_inserted_here): Make extern. (single_step_breakpoint_inserted_here_p): Adjust to walk the breakpoint list. * breakpoint.h (breakpoint_has_location_inserted_here): New declaration. (single_step_breakpoints_inserted, remove_single_step_breakpoints) (cancel_single_step_breakpoints): Remove declarations. * gdbthread.h (struct thread_control_state) : New field. (delete_single_step_breakpoints) (thread_has_single_step_breakpoints_set) (thread_has_single_step_breakpoint_here): New declarations. * infrun.c (follow_exec): Also clear the single-step breakpoints. (singlestep_breakpoints_inserted_p, singlestep_ptid) (singlestep_pc): Delete globals. (infrun_thread_ptid_changed): Remove references to removed globals. (resume_cleanups): Delete the current thread's single-step breakpoints. (maybe_software_singlestep): Remove references to removed globals. (resume): Adjust to use thread_has_single_step_breakpoints_set and delete_single_step_breakpoints. (init_wait_for_inferior): Remove references to removed globals. (delete_thread_infrun_breakpoints): Delete the thread's single-step breakpoints too. (delete_just_stopped_threads_infrun_breakpoints): Don't delete single-step breakpoints here. (delete_stopped_threads_single_step_breakpoints): New function. (adjust_pc_after_break): Adjust to use thread_has_single_step_breakpoints_set. (handle_inferior_event): Remove references to removed globals. Use delete_stopped_threads_single_step_breakpoints. (handle_signal_stop): Adjust to per-thread single-step breakpoints. Swap test order to do cheaper tests first. (switch_back_to_stepped_thread): Extend debug output. Remove references to removed globals. * record-full.c (record_full_wait_1): Adjust to per-thread single-step breakpoints. * thread.c (delete_single_step_breakpoints) (thread_has_single_step_breakpoints_set) (thread_has_single_step_breakpoint_here): New functions. (clear_thread_inferior_resources): Also delete the thread's single-step breakpoints. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e7108abfcc9..a2b197cb740 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,54 @@ +2014-10-15 Pedro Alves + + * breakpoint.c (single_step_breakpoints): Delete global. + (insert_single_step_breakpoint): Adjust to store the breakpoint + pointer in the current thread. + (single_step_breakpoints_inserted, remove_single_step_breakpoints) + (cancel_single_step_breakpoints): Delete functions. + (breakpoint_has_location_inserted_here): Make extern. + (single_step_breakpoint_inserted_here_p): Adjust to walk the + breakpoint list. + * breakpoint.h (breakpoint_has_location_inserted_here): New + declaration. + (single_step_breakpoints_inserted, remove_single_step_breakpoints) + (cancel_single_step_breakpoints): Remove declarations. + * gdbthread.h (struct thread_control_state) + : New field. + (delete_single_step_breakpoints) + (thread_has_single_step_breakpoints_set) + (thread_has_single_step_breakpoint_here): New declarations. + * infrun.c (follow_exec): Also clear the single-step breakpoints. + (singlestep_breakpoints_inserted_p, singlestep_ptid) + (singlestep_pc): Delete globals. + (infrun_thread_ptid_changed): Remove references to removed + globals. + (resume_cleanups): Delete the current thread's single-step + breakpoints. + (maybe_software_singlestep): Remove references to removed globals. + (resume): Adjust to use thread_has_single_step_breakpoints_set and + delete_single_step_breakpoints. + (init_wait_for_inferior): Remove references to removed globals. + (delete_thread_infrun_breakpoints): Delete the thread's + single-step breakpoints too. + (delete_just_stopped_threads_infrun_breakpoints): Don't delete + single-step breakpoints here. + (delete_stopped_threads_single_step_breakpoints): New function. + (adjust_pc_after_break): Adjust to use + thread_has_single_step_breakpoints_set. + (handle_inferior_event): Remove references to removed globals. + Use delete_stopped_threads_single_step_breakpoints. + (handle_signal_stop): Adjust to per-thread single-step + breakpoints. Swap test order to do cheaper tests first. + (switch_back_to_stepped_thread): Extend debug output. Remove + references to removed globals. + * record-full.c (record_full_wait_1): Adjust to per-thread + single-step breakpoints. + * thread.c (delete_single_step_breakpoints) + (thread_has_single_step_breakpoints_set) + (thread_has_single_step_breakpoint_here): New functions. + (clear_thread_inferior_resources): Also delete the thread's + single-step breakpoints. + 2014-10-15 Pedro Alves * thread.c (delete_thread_breakpoint): New function. diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index d85757bd041..cab6c562252 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -325,11 +325,6 @@ static struct breakpoint_ops bkpt_probe_breakpoint_ops; /* Dynamic printf class type. */ struct breakpoint_ops dprintf_breakpoint_ops; -/* One (or perhaps two) breakpoints used for software single - stepping. */ - -static struct breakpoint *single_step_breakpoints; - /* The style in which to perform a dynamic printf. This is a user option because different output options have different tradeoffs; if GDB does the printing, there is better error handling if there @@ -15319,57 +15314,24 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal; CORE_ADDR pc = next_pc; - if (single_step_breakpoints == NULL) - single_step_breakpoints = new_single_step_breakpoint (tp->num, gdbarch); + if (tp->control.single_step_breakpoints == NULL) + { + tp->control.single_step_breakpoints + = new_single_step_breakpoint (tp->num, gdbarch); + } sal = find_pc_line (pc, 0); sal.pc = pc; sal.section = find_pc_overlay (pc); sal.explicit_pc = 1; - add_location_to_breakpoint (single_step_breakpoints, &sal); + add_location_to_breakpoint (tp->control.single_step_breakpoints, &sal); update_global_location_list (UGLL_INSERT); } -/* Check if the breakpoints used for software single stepping - were inserted or not. */ +/* See breakpoint.h. */ int -single_step_breakpoints_inserted (void) -{ - return (single_step_breakpoints != NULL); -} - -/* Remove and delete any breakpoints used for software single step. */ - -void -remove_single_step_breakpoints (void) -{ - gdb_assert (single_step_breakpoints != NULL); - - delete_breakpoint (single_step_breakpoints); - - single_step_breakpoints = NULL; -} - -/* Delete software single step breakpoints without removing them from - the inferior. This is intended to be used if the inferior's address - space where they were inserted is already gone, e.g. after exit or - exec. */ - -void -cancel_single_step_breakpoints (void) -{ - /* We don't really need to (or should) delete them here. After an - exit, breakpoint_init_inferior deletes it. After an exec, - update_breakpoints_after_exec does it. Just clear our - reference. */ - single_step_breakpoints = NULL; -} - -/* Check whether any location of BP is inserted at PC. */ - -static int breakpoint_has_location_inserted_here (struct breakpoint *bp, struct address_space *aspace, CORE_ADDR pc) @@ -15391,9 +15353,15 @@ int single_step_breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { - return (single_step_breakpoints != NULL - && breakpoint_has_location_inserted_here (single_step_breakpoints, - aspace, pc)); + struct breakpoint *bpt; + + ALL_BREAKPOINTS (bpt) + { + if (bpt->type == bp_single_step + && breakpoint_has_location_inserted_here (bpt, aspace, pc)) + return 1; + } + return 0; } /* Returns 0 if 'bp' is NOT a syscall catchpoint, diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 8b833bd2e0b..cd3e7eedf6f 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1129,6 +1129,12 @@ extern int regular_breakpoint_inserted_here_p (struct address_space *, extern int software_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR); +/* Check whether any location of BP is inserted at PC. */ + +extern int breakpoint_has_location_inserted_here (struct breakpoint *bp, + struct address_space *aspace, + CORE_ADDR pc); + extern int single_step_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR); @@ -1463,10 +1469,6 @@ extern void delete_command (char *arg, int from_tty); extern void insert_single_step_breakpoint (struct gdbarch *, struct address_space *, CORE_ADDR); -extern int single_step_breakpoints_inserted (void); -extern void remove_single_step_breakpoints (void); -extern void cancel_single_step_breakpoints (void); - /* Check if any hardware watchpoints have triggered, according to the target. */ int watchpoints_triggered (struct target_waitstatus *); diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 36ad5a79f30..178fd4a38bb 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -52,6 +52,13 @@ struct thread_control_state /* Exception-resume breakpoint. */ struct breakpoint *exception_resume_breakpoint; + /* Breakpoints used for software single stepping. Plural, because + it may have multiple locations. E.g., if stepping over a + conditional branch instruction we can't decode the condition for, + we'll need to put a breakpoint at the branch destination, and + another at the instruction after the branch. */ + struct breakpoint *single_step_breakpoints; + /* Range to single step within. If this is nonzero, respond to a single-step signal by continuing @@ -285,6 +292,19 @@ extern void delete_step_resume_breakpoint (struct thread_info *); /* Delete an exception_resume_breakpoint from the thread database. */ extern void delete_exception_resume_breakpoint (struct thread_info *); +/* Delete the single-step breakpoints of thread TP, if any. */ +extern void delete_single_step_breakpoints (struct thread_info *tp); + +/* Check if the thread has software single stepping breakpoints + set. */ +extern int thread_has_single_step_breakpoints_set (struct thread_info *tp); + +/* Check whether the thread has software single stepping breakpoints + set at PC. */ +extern int thread_has_single_step_breakpoint_here (struct thread_info *tp, + struct address_space *aspace, + CORE_ADDR addr); + /* Translate the integer thread id (GDB's homegrown id, not the system's) into a "pid" (which may be overloaded with extra thread information). */ extern ptid_t thread_id_to_pid (int); diff --git a/gdb/infrun.c b/gdb/infrun.c index 29c37e1fefe..0f30a623f25 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1082,6 +1082,7 @@ follow_exec (ptid_t pid, char *execd_pathname) statement through an exec(). */ th->control.step_resume_breakpoint = NULL; th->control.exception_resume_breakpoint = NULL; + th->control.single_step_breakpoints = NULL; th->control.step_range_start = 0; th->control.step_range_end = 0; @@ -1195,17 +1196,6 @@ follow_exec (ptid_t pid, char *execd_pathname) matically get reset there in the new process.). */ } -/* Non-zero if we just simulating a single-step. This is needed - because we cannot remove the breakpoints in the inferior process - until after the `wait' in `wait_for_inferior'. */ -static int singlestep_breakpoints_inserted_p = 0; - -/* The thread we inserted single-step breakpoints for. */ -static ptid_t singlestep_ptid; - -/* PC when we started this single-step. */ -static CORE_ADDR singlestep_pc; - /* Info about an instruction that is being stepped over. */ struct step_over_info @@ -1895,9 +1885,6 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) if (ptid_equal (inferior_ptid, old_ptid)) inferior_ptid = new_ptid; - if (ptid_equal (singlestep_ptid, old_ptid)) - singlestep_ptid = new_ptid; - for (displaced = displaced_step_inferior_states; displaced; displaced = displaced->next) @@ -1918,8 +1905,8 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) static void resume_cleanups (void *ignore) { - if (single_step_breakpoints_inserted ()) - remove_single_step_breakpoints (); + if (!ptid_equal (inferior_ptid, null_ptid)) + delete_single_step_breakpoints (inferior_thread ()); normal_stop (); } @@ -1975,11 +1962,6 @@ maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc) && gdbarch_software_single_step (gdbarch, get_current_frame ())) { hw_step = 0; - /* Do not pull these breakpoints until after a `wait' in - `wait_for_inferior'. */ - singlestep_breakpoints_inserted_p = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = pc; } return hw_step; } @@ -2167,7 +2149,7 @@ a command like `return' or `jump' to continue execution.")); at the current address, deliver the signal without stepping, and once we arrive back at the step-resume breakpoint, actually step over the breakpoint we originally wanted to step over. */ - if (singlestep_breakpoints_inserted_p + if (thread_has_single_step_breakpoints_set (tp) && sig != GDB_SIGNAL_0 && step_over_info_valid_p ()) { @@ -2182,8 +2164,7 @@ a command like `return' or `jump' to continue execution.")); tp->step_after_step_resume_breakpoint = 1; } - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + delete_single_step_breakpoints (tp); clear_step_over_info (); tp->control.trap_expected = 0; @@ -2194,7 +2175,7 @@ a command like `return' or `jump' to continue execution.")); /* If STEP is set, it's a request to use hardware stepping facilities. But in that case, we should never use singlestep breakpoint. */ - gdb_assert (!(singlestep_breakpoints_inserted_p && step)); + gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step)); /* Decide the set of threads to ask the target to resume. Start by assuming everything will be resumed, than narrow the set @@ -2210,7 +2191,7 @@ a command like `return' or `jump' to continue execution.")); set_running (resume_ptid, 1); /* Maybe resume a single thread after all. */ - if ((step || singlestep_breakpoints_inserted_p) + if ((step || thread_has_single_step_breakpoints_set (tp)) && tp->control.trap_expected) { /* We're allowing a thread to run past a breakpoint it has @@ -2679,9 +2660,6 @@ init_wait_for_inferior (void) /* Discard any skipped inlined frames. */ clear_inline_frame_state (minus_one_ptid); - - singlestep_ptid = null_ptid; - singlestep_pc = 0; } @@ -2857,6 +2835,7 @@ delete_thread_infrun_breakpoints (struct thread_info *tp) { delete_step_resume_breakpoint (tp); delete_exception_resume_breakpoint (tp); + delete_single_step_breakpoints (tp); } /* If the target still has execution, call FUNC for each thread that @@ -2896,9 +2875,15 @@ static void delete_just_stopped_threads_infrun_breakpoints (void) { for_each_just_stopped_thread (delete_thread_infrun_breakpoints); +} + +/* Delete the single-step breakpoints of the threads that just + stopped. */ - if (single_step_breakpoints_inserted ()) - remove_single_step_breakpoints (); +static void +delete_just_stopped_threads_single_step_breakpoints (void) +{ + for_each_just_stopped_thread (delete_single_step_breakpoints); } /* A cleanup wrapper. */ @@ -3386,7 +3371,7 @@ adjust_pc_after_break (struct execution_control_state *ecs) software breakpoint. In this case (prev_pc == breakpoint_pc), we also need to back up to the breakpoint address. */ - if (singlestep_breakpoints_inserted_p + if (thread_has_single_step_breakpoints_set (ecs->event_thread) || !ptid_equal (ecs->ptid, inferior_ptid) || !currently_stepping (ecs->event_thread) || ecs->event_thread->prev_pc == breakpoint_pc) @@ -3772,8 +3757,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); gdb_flush (gdb_stdout); target_mourn_inferior (); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); stop_print_frame = 0; stop_waiting (ecs); return; @@ -3866,12 +3849,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); detach_breakpoints (ecs->ws.value.related_pid); } - if (singlestep_breakpoints_inserted_p) - { - /* Pull the single step breakpoints out of the target. */ - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_just_stopped_threads_single_step_breakpoints (); /* In case the event is caught by a catchpoint, remember that the event is to be followed at the next resume of the thread, @@ -3958,9 +3936,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); if (!ptid_equal (ecs->ptid, inferior_ptid)) context_switch (ecs->ptid); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); - stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); /* Do whatever is necessary to the parent branch of the vfork. */ @@ -4026,14 +4001,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n"); /* Reverse execution: target ran out of history info. */ - /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) - { - if (!ptid_equal (ecs->ptid, inferior_ptid)) - context_switch (ecs->ptid); - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_just_stopped_threads_single_step_breakpoints (); stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); observer_notify_no_history (); stop_waiting (ecs); @@ -4172,13 +4140,9 @@ handle_signal_stop (struct execution_control_state *ecs) gdbarch = get_frame_arch (frame); /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) + if (gdbarch_software_single_step_p (gdbarch)) { - /* However, before doing so, if this single-step breakpoint was - actually for another thread, set this thread up for moving - past it. */ - if (!ptid_equal (ecs->ptid, singlestep_ptid) - && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) { struct regcache *regcache; struct address_space *aspace; @@ -4187,22 +4151,38 @@ handle_signal_stop (struct execution_control_state *ecs) regcache = get_thread_regcache (ecs->ptid); aspace = get_regcache_aspace (regcache); pc = regcache_read_pc (regcache); - if (single_step_breakpoint_inserted_here_p (aspace, pc)) + + /* However, before doing so, if this single-step breakpoint was + actually for another thread, set this thread up for moving + past it. */ + if (!thread_has_single_step_breakpoint_here (ecs->event_thread, + aspace, pc)) + { + if (single_step_breakpoint_inserted_here_p (aspace, pc)) + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: [%s] hit another thread's " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); + } + ecs->hit_singlestep_breakpoint = 1; + } + } + else { if (debug_infrun) { fprintf_unfiltered (gdb_stdlog, - "infrun: [%s] hit step over single-step" - " breakpoint of [%s]\n", - target_pid_to_str (ecs->ptid), - target_pid_to_str (singlestep_ptid)); + "infrun: [%s] hit its " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); } - ecs->hit_singlestep_breakpoint = 1; } } - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + delete_just_stopped_threads_single_step_breakpoints (); } if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP @@ -5544,10 +5524,7 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) insert_single_step_breakpoint (get_frame_arch (frame), get_frame_address_space (frame), stop_pc); - singlestep_breakpoints_inserted_p = 1; ecs->event_thread->control.trap_expected = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = stop_pc; resume (0, GDB_SIGNAL_0); prepare_to_wait (ecs); diff --git a/gdb/record-full.c b/gdb/record-full.c index abe505fee15..c0418533978 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -960,7 +960,7 @@ record_full_resume (struct target_ops *ops, ptid_t ptid, int step, else { /* This arch support soft sigle step. */ - if (single_step_breakpoints_inserted ()) + if (thread_has_single_step_breakpoints_set (inferior_thread ())) { /* This is a soft single step. */ record_full_resume_step = 1; @@ -1084,6 +1084,8 @@ record_full_wait_1 (struct target_ops *ops, while (1) { + struct thread_info *tp; + ret = ops->beneath->to_wait (ops->beneath, ptid, status, options); if (status->kind == TARGET_WAITKIND_IGNORE) { @@ -1094,8 +1096,8 @@ record_full_wait_1 (struct target_ops *ops, return ret; } - if (single_step_breakpoints_inserted ()) - remove_single_step_breakpoints (); + ALL_NON_EXITED_THREADS (tp) + delete_single_step_breakpoints (tp); if (record_full_resume_step) return ret; diff --git a/gdb/thread.c b/gdb/thread.c index 82c67607bd3..5a1d2e3922d 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -117,6 +117,15 @@ delete_exception_resume_breakpoint (struct thread_info *tp) delete_thread_breakpoint (&tp->control.exception_resume_breakpoint); } +/* See gdbthread.h. */ + +void +delete_single_step_breakpoints (struct thread_info *tp) +{ + if (tp != NULL) + delete_thread_breakpoint (&tp->control.single_step_breakpoints); +} + /* Delete the breakpoint pointed at by BP_P at the next stop, if there's one. */ @@ -130,6 +139,27 @@ delete_at_next_stop (struct breakpoint **bp) } } +/* See gdbthread.h. */ + +int +thread_has_single_step_breakpoints_set (struct thread_info *tp) +{ + return tp->control.single_step_breakpoints != NULL; +} + +/* See gdbthread.h. */ + +int +thread_has_single_step_breakpoint_here (struct thread_info *tp, + struct address_space *aspace, + CORE_ADDR addr) +{ + struct breakpoint *ss_bps = tp->control.single_step_breakpoints; + + return (ss_bps != NULL + && breakpoint_has_location_inserted_here (ss_bps, aspace, addr)); +} + static void clear_thread_inferior_resources (struct thread_info *tp) { @@ -139,6 +169,7 @@ clear_thread_inferior_resources (struct thread_info *tp) be stopped at the moment. */ delete_at_next_stop (&tp->control.step_resume_breakpoint); delete_at_next_stop (&tp->control.exception_resume_breakpoint); + delete_at_next_stop (&tp->control.single_step_breakpoints); delete_longjmp_breakpoint_at_next_stop (tp->num);