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  <palves@redhat.com>

	* 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)
	<single_step_breakpoints>: 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.
This commit is contained in:
Pedro Alves 2014-10-15 20:18:32 +01:00
parent 5b834a0a5d
commit 34b7e8a6ad
7 changed files with 174 additions and 123 deletions

View File

@ -1,3 +1,54 @@
2014-10-15 Pedro Alves <palves@redhat.com>
* 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)
<single_step_breakpoints>: 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 <palves@redhat.com>
* thread.c (delete_thread_breakpoint): New function.

View File

@ -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,

View File

@ -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 *);

View File

@ -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);

View File

@ -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);
}
if (single_step_breakpoints_inserted ())
remove_single_step_breakpoints ();
/* Delete the single-step breakpoints of the threads that just
stopped. */
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);

View File

@ -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;

View File

@ -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);