mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
1 Commits
users/ARM/
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
749a928d88 |
198
gdb/infrun.c
198
gdb/infrun.c
@@ -2081,6 +2081,8 @@ start_step_over (void)
|
|||||||
step_over_what step_what;
|
step_over_what step_what;
|
||||||
int must_be_in_line;
|
int must_be_in_line;
|
||||||
|
|
||||||
|
gdb_assert (!tp->stop_requested);
|
||||||
|
|
||||||
next = thread_step_over_chain_next (tp);
|
next = thread_step_over_chain_next (tp);
|
||||||
|
|
||||||
/* If this inferior already has a displaced step in process,
|
/* If this inferior already has a displaced step in process,
|
||||||
@@ -2329,6 +2331,8 @@ do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig)
|
|||||||
{
|
{
|
||||||
struct thread_info *tp = inferior_thread ();
|
struct thread_info *tp = inferior_thread ();
|
||||||
|
|
||||||
|
gdb_assert (!tp->stop_requested);
|
||||||
|
|
||||||
/* Install inferior's terminal modes. */
|
/* Install inferior's terminal modes. */
|
||||||
target_terminal_inferior ();
|
target_terminal_inferior ();
|
||||||
|
|
||||||
@@ -2393,6 +2397,7 @@ resume (enum gdb_signal sig)
|
|||||||
single-step). */
|
single-step). */
|
||||||
int step;
|
int step;
|
||||||
|
|
||||||
|
gdb_assert (!tp->stop_requested);
|
||||||
gdb_assert (!thread_is_in_step_over_chain (tp));
|
gdb_assert (!thread_is_in_step_over_chain (tp));
|
||||||
|
|
||||||
QUIT;
|
QUIT;
|
||||||
@@ -3274,65 +3279,6 @@ static void keep_going (struct execution_control_state *ecs);
|
|||||||
static void process_event_stop_test (struct execution_control_state *ecs);
|
static void process_event_stop_test (struct execution_control_state *ecs);
|
||||||
static int switch_back_to_stepped_thread (struct execution_control_state *ecs);
|
static int switch_back_to_stepped_thread (struct execution_control_state *ecs);
|
||||||
|
|
||||||
/* Callback for iterate over threads. If the thread is stopped, but
|
|
||||||
the user/frontend doesn't know about that yet, go through
|
|
||||||
normal_stop, as if the thread had just stopped now. ARG points at
|
|
||||||
a ptid. If PTID is MINUS_ONE_PTID, applies to all threads. If
|
|
||||||
ptid_is_pid(PTID) is true, applies to all threads of the process
|
|
||||||
pointed at by PTID. Otherwise, apply only to the thread pointed by
|
|
||||||
PTID. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
|
|
||||||
{
|
|
||||||
ptid_t ptid = * (ptid_t *) arg;
|
|
||||||
|
|
||||||
if ((ptid_equal (info->ptid, ptid)
|
|
||||||
|| ptid_equal (minus_one_ptid, ptid)
|
|
||||||
|| (ptid_is_pid (ptid)
|
|
||||||
&& ptid_get_pid (ptid) == ptid_get_pid (info->ptid)))
|
|
||||||
&& is_running (info->ptid)
|
|
||||||
&& !is_executing (info->ptid))
|
|
||||||
{
|
|
||||||
struct cleanup *old_chain;
|
|
||||||
struct execution_control_state ecss;
|
|
||||||
struct execution_control_state *ecs = &ecss;
|
|
||||||
|
|
||||||
memset (ecs, 0, sizeof (*ecs));
|
|
||||||
|
|
||||||
old_chain = make_cleanup_restore_current_thread ();
|
|
||||||
|
|
||||||
overlay_cache_invalid = 1;
|
|
||||||
/* Flush target cache before starting to handle each event.
|
|
||||||
Target was running and cache could be stale. This is just a
|
|
||||||
heuristic. Running threads may modify target memory, but we
|
|
||||||
don't get any event. */
|
|
||||||
target_dcache_invalidate ();
|
|
||||||
|
|
||||||
/* Go through handle_inferior_event/normal_stop, so we always
|
|
||||||
have consistent output as if the stop event had been
|
|
||||||
reported. */
|
|
||||||
ecs->ptid = info->ptid;
|
|
||||||
ecs->event_thread = info;
|
|
||||||
ecs->ws.kind = TARGET_WAITKIND_STOPPED;
|
|
||||||
ecs->ws.value.sig = GDB_SIGNAL_0;
|
|
||||||
|
|
||||||
handle_inferior_event (ecs);
|
|
||||||
|
|
||||||
if (!ecs->wait_some_more)
|
|
||||||
{
|
|
||||||
/* Cancel any running execution command. */
|
|
||||||
thread_cancel_execution_command (info);
|
|
||||||
|
|
||||||
normal_stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_cleanups (old_chain);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function is attached as a "thread_stop_requested" observer.
|
/* This function is attached as a "thread_stop_requested" observer.
|
||||||
Cleanup local state that assumed the PTID was to be resumed, and
|
Cleanup local state that assumed the PTID was to be resumed, and
|
||||||
report the stop to the frontend. */
|
report the stop to the frontend. */
|
||||||
@@ -3342,17 +3288,51 @@ infrun_thread_stop_requested (ptid_t ptid)
|
|||||||
{
|
{
|
||||||
struct thread_info *tp;
|
struct thread_info *tp;
|
||||||
|
|
||||||
/* PTID was requested to stop. Remove matching threads from the
|
/* PTID was requested to stop. If the thread was already stopped,
|
||||||
step-over queue, so we don't try to resume them
|
but the user/frontend doesn't know about that yet (e.g., the
|
||||||
automatically. */
|
thread had been temporarily paused for some step-over), set up
|
||||||
|
for reporting the stop now. */
|
||||||
ALL_NON_EXITED_THREADS (tp)
|
ALL_NON_EXITED_THREADS (tp)
|
||||||
if (ptid_match (tp->ptid, ptid))
|
if (ptid_match (tp->ptid, ptid))
|
||||||
{
|
{
|
||||||
|
if (tp->state != THREAD_RUNNING)
|
||||||
|
continue;
|
||||||
|
if (tp->executing)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Remove matching threads from the step-over queue, so
|
||||||
|
start_step_over doesn't try to resume them
|
||||||
|
automatically. */
|
||||||
if (thread_is_in_step_over_chain (tp))
|
if (thread_is_in_step_over_chain (tp))
|
||||||
thread_step_over_chain_remove (tp);
|
thread_step_over_chain_remove (tp);
|
||||||
}
|
|
||||||
|
|
||||||
iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
|
/* If the thread is stopped, but the user/frontend doesn't
|
||||||
|
know about that yet, queue a pending event, as if the
|
||||||
|
thread had just stopped now. Unless the thread already had
|
||||||
|
a pending event. */
|
||||||
|
if (!tp->suspend.waitstatus_pending_p)
|
||||||
|
{
|
||||||
|
tp->suspend.waitstatus_pending_p = 1;
|
||||||
|
tp->suspend.waitstatus.kind = TARGET_WAITKIND_STOPPED;
|
||||||
|
tp->suspend.waitstatus.value.sig = GDB_SIGNAL_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the inline-frame state, since we're re-processing the
|
||||||
|
stop. */
|
||||||
|
clear_inline_frame_state (tp->ptid);
|
||||||
|
|
||||||
|
/* If this thread was paused because some other thread was
|
||||||
|
doing an inline-step over, let that finish first. Once
|
||||||
|
that happens, we'll restart all threads and consume pending
|
||||||
|
stop events then. */
|
||||||
|
if (step_over_info_valid_p ())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Otherwise we can process the (new) pending event now. Set
|
||||||
|
it so this pending event is considered by
|
||||||
|
do_target_wait. */
|
||||||
|
tp->resumed = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -4254,6 +4234,8 @@ stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_stop_requested (struct execution_control_state *ecs);
|
||||||
|
|
||||||
/* Auxiliary function that handles syscall entry/return events.
|
/* Auxiliary function that handles syscall entry/return events.
|
||||||
It returns 1 if the inferior should keep going (and GDB
|
It returns 1 if the inferior should keep going (and GDB
|
||||||
should ignore the event), or 0 if the event deserves to be
|
should ignore the event), or 0 if the event deserves to be
|
||||||
@@ -4283,6 +4265,9 @@ handle_syscall_event (struct execution_control_state *ecs)
|
|||||||
= bpstat_stop_status (get_regcache_aspace (regcache),
|
= bpstat_stop_status (get_regcache_aspace (regcache),
|
||||||
stop_pc, ecs->ptid, &ecs->ws);
|
stop_pc, ecs->ptid, &ecs->ws);
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
||||||
{
|
{
|
||||||
/* Catchpoint hit. */
|
/* Catchpoint hit. */
|
||||||
@@ -4290,6 +4275,9 @@ handle_syscall_event (struct execution_control_state *ecs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* If no catchpoint triggered for this, then keep going. */
|
/* If no catchpoint triggered for this, then keep going. */
|
||||||
keep_going (ecs);
|
keep_going (ecs);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -4980,6 +4968,9 @@ handle_inferior_event_1 (struct execution_control_state *ecs)
|
|||||||
= bpstat_stop_status (get_regcache_aspace (regcache),
|
= bpstat_stop_status (get_regcache_aspace (regcache),
|
||||||
stop_pc, ecs->ptid, &ecs->ws);
|
stop_pc, ecs->ptid, &ecs->ws);
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
|
|
||||||
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
||||||
{
|
{
|
||||||
/* A catchpoint triggered. */
|
/* A catchpoint triggered. */
|
||||||
@@ -5034,6 +5025,8 @@ handle_inferior_event_1 (struct execution_control_state *ecs)
|
|||||||
case TARGET_WAITKIND_SPURIOUS:
|
case TARGET_WAITKIND_SPURIOUS:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
||||||
context_switch (ecs->ptid);
|
context_switch (ecs->ptid);
|
||||||
resume (GDB_SIGNAL_0);
|
resume (GDB_SIGNAL_0);
|
||||||
@@ -5043,6 +5036,8 @@ handle_inferior_event_1 (struct execution_control_state *ecs)
|
|||||||
case TARGET_WAITKIND_THREAD_CREATED:
|
case TARGET_WAITKIND_THREAD_CREATED:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_THREAD_CREATED\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_THREAD_CREATED\n");
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
||||||
context_switch (ecs->ptid);
|
context_switch (ecs->ptid);
|
||||||
if (!switch_back_to_stepped_thread (ecs))
|
if (!switch_back_to_stepped_thread (ecs))
|
||||||
@@ -5227,6 +5222,9 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
|
|||||||
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
|
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
|
||||||
stop_pc, ecs->ptid, &ecs->ws);
|
stop_pc, ecs->ptid, &ecs->ws);
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
|
|
||||||
/* If no catchpoint triggered for this, then keep going. Note
|
/* If no catchpoint triggered for this, then keep going. Note
|
||||||
that we're interested in knowing the bpstat actually causes a
|
that we're interested in knowing the bpstat actually causes a
|
||||||
stop, not just if it may explain the signal. Software
|
stop, not just if it may explain the signal. Software
|
||||||
@@ -5301,6 +5299,10 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
|
|||||||
|
|
||||||
current_inferior ()->waiting_for_vfork_done = 0;
|
current_inferior ()->waiting_for_vfork_done = 0;
|
||||||
current_inferior ()->pspace->breakpoints_not_allowed = 0;
|
current_inferior ()->pspace->breakpoints_not_allowed = 0;
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
|
|
||||||
/* This also takes care of reinserting breakpoints in the
|
/* This also takes care of reinserting breakpoints in the
|
||||||
previously locked inferior. */
|
previously locked inferior. */
|
||||||
keep_going (ecs);
|
keep_going (ecs);
|
||||||
@@ -5337,6 +5339,9 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
|
|||||||
xfree (ecs->ws.value.execd_pathname);
|
xfree (ecs->ws.value.execd_pathname);
|
||||||
ecs->ws.value.execd_pathname = NULL;
|
ecs->ws.value.execd_pathname = NULL;
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
|
|
||||||
/* If no catchpoint triggered for this, then keep going. */
|
/* If no catchpoint triggered for this, then keep going. */
|
||||||
if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
|
||||||
{
|
{
|
||||||
@@ -5374,7 +5379,6 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
|
|||||||
case TARGET_WAITKIND_STOPPED:
|
case TARGET_WAITKIND_STOPPED:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_STOPPED\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_STOPPED\n");
|
||||||
ecs->event_thread->suspend.stop_signal = ecs->ws.value.sig;
|
|
||||||
handle_signal_stop (ecs);
|
handle_signal_stop (ecs);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -5391,6 +5395,10 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
|
|||||||
|
|
||||||
delete_just_stopped_threads_single_step_breakpoints ();
|
delete_just_stopped_threads_single_step_breakpoints ();
|
||||||
stop_pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
|
stop_pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
|
||||||
|
|
||||||
|
if (handle_stop_requested (ecs))
|
||||||
|
return;
|
||||||
|
|
||||||
observer_notify_no_history ();
|
observer_notify_no_history ();
|
||||||
stop_waiting (ecs);
|
stop_waiting (ecs);
|
||||||
return;
|
return;
|
||||||
@@ -5480,6 +5488,8 @@ restart_threads (struct thread_info *event_thread)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gdb_assert (!tp->stop_requested);
|
||||||
|
|
||||||
/* If some thread needs to start a step-over at this point, it
|
/* If some thread needs to start a step-over at this point, it
|
||||||
should still be in the step-over queue, and thus skipped
|
should still be in the step-over queue, and thus skipped
|
||||||
above. */
|
above. */
|
||||||
@@ -5549,8 +5559,7 @@ finish_step_over (struct execution_control_state *ecs)
|
|||||||
back an event. */
|
back an event. */
|
||||||
gdb_assert (ecs->event_thread->control.trap_expected);
|
gdb_assert (ecs->event_thread->control.trap_expected);
|
||||||
|
|
||||||
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
|
clear_step_over_info ();
|
||||||
clear_step_over_info ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target_is_non_stop_p ())
|
if (!target_is_non_stop_p ())
|
||||||
@@ -5649,6 +5658,23 @@ finish_step_over (struct execution_control_state *ecs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the event thread has the stop requested flag set, pretend it
|
||||||
|
stopped for a GDB_SIGNAL_0 (i.e., as if it stopped due to
|
||||||
|
target_stop). */
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_stop_requested (struct execution_control_state *ecs)
|
||||||
|
{
|
||||||
|
if (ecs->event_thread->stop_requested)
|
||||||
|
{
|
||||||
|
ecs->ws.kind = TARGET_WAITKIND_STOPPED;
|
||||||
|
ecs->ws.value.sig = GDB_SIGNAL_0;
|
||||||
|
handle_signal_stop (ecs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Come here when the program has stopped with a signal. */
|
/* Come here when the program has stopped with a signal. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -5662,6 +5688,8 @@ handle_signal_stop (struct execution_control_state *ecs)
|
|||||||
|
|
||||||
gdb_assert (ecs->ws.kind == TARGET_WAITKIND_STOPPED);
|
gdb_assert (ecs->ws.kind == TARGET_WAITKIND_STOPPED);
|
||||||
|
|
||||||
|
ecs->event_thread->suspend.stop_signal = ecs->ws.value.sig;
|
||||||
|
|
||||||
/* Do we need to clean up the state of a thread that has
|
/* Do we need to clean up the state of a thread that has
|
||||||
completed a displaced single-step? (Doing so usually affects
|
completed a displaced single-step? (Doing so usually affects
|
||||||
the PC, so do it here, before we set stop_pc.) */
|
the PC, so do it here, before we set stop_pc.) */
|
||||||
@@ -6051,6 +6079,15 @@ handle_signal_stop (struct execution_control_state *ecs)
|
|||||||
if (random_signal)
|
if (random_signal)
|
||||||
random_signal = !stopped_by_watchpoint;
|
random_signal = !stopped_by_watchpoint;
|
||||||
|
|
||||||
|
/* Always stop if the user explicitly requested this thread to
|
||||||
|
remain stopped. */
|
||||||
|
if (ecs->event_thread->stop_requested)
|
||||||
|
{
|
||||||
|
random_signal = 1;
|
||||||
|
if (debug_infrun)
|
||||||
|
fprintf_unfiltered (gdb_stdlog, "infrun: user-requested stop\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* For the program's own signals, act according to
|
/* For the program's own signals, act according to
|
||||||
the signal handling tables. */
|
the signal handling tables. */
|
||||||
|
|
||||||
@@ -6097,8 +6134,6 @@ handle_signal_stop (struct execution_control_state *ecs)
|
|||||||
&& ecs->event_thread->control.trap_expected
|
&& ecs->event_thread->control.trap_expected
|
||||||
&& ecs->event_thread->control.step_resume_breakpoint == NULL)
|
&& ecs->event_thread->control.step_resume_breakpoint == NULL)
|
||||||
{
|
{
|
||||||
int was_in_line;
|
|
||||||
|
|
||||||
/* We were just starting a new sequence, attempting to
|
/* We were just starting a new sequence, attempting to
|
||||||
single-step off of a breakpoint and expecting a SIGTRAP.
|
single-step off of a breakpoint and expecting a SIGTRAP.
|
||||||
Instead this signal arrives. This signal will take us out
|
Instead this signal arrives. This signal will take us out
|
||||||
@@ -6114,34 +6149,11 @@ handle_signal_stop (struct execution_control_state *ecs)
|
|||||||
"infrun: signal arrived while stepping over "
|
"infrun: signal arrived while stepping over "
|
||||||
"breakpoint\n");
|
"breakpoint\n");
|
||||||
|
|
||||||
was_in_line = step_over_info_valid_p ();
|
|
||||||
clear_step_over_info ();
|
|
||||||
insert_hp_step_resume_breakpoint_at_frame (frame);
|
insert_hp_step_resume_breakpoint_at_frame (frame);
|
||||||
ecs->event_thread->step_after_step_resume_breakpoint = 1;
|
ecs->event_thread->step_after_step_resume_breakpoint = 1;
|
||||||
/* Reset trap_expected to ensure breakpoints are re-inserted. */
|
/* Reset trap_expected to ensure breakpoints are re-inserted. */
|
||||||
ecs->event_thread->control.trap_expected = 0;
|
ecs->event_thread->control.trap_expected = 0;
|
||||||
|
|
||||||
if (target_is_non_stop_p ())
|
|
||||||
{
|
|
||||||
/* Either "set non-stop" is "on", or the target is
|
|
||||||
always in non-stop mode. In this case, we have a bit
|
|
||||||
more work to do. Resume the current thread, and if
|
|
||||||
we had paused all threads, restart them while the
|
|
||||||
signal handler runs. */
|
|
||||||
keep_going (ecs);
|
|
||||||
|
|
||||||
if (was_in_line)
|
|
||||||
{
|
|
||||||
restart_threads (ecs->event_thread);
|
|
||||||
}
|
|
||||||
else if (debug_infrun)
|
|
||||||
{
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
|
||||||
"infrun: no need to restart threads\n");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we were nexting/stepping some other thread, switch to
|
/* If we were nexting/stepping some other thread, switch to
|
||||||
it, so that we don't continue it, losing control. */
|
it, so that we don't continue it, losing control. */
|
||||||
if (!switch_back_to_stepped_thread (ecs))
|
if (!switch_back_to_stepped_thread (ecs))
|
||||||
@@ -7682,8 +7694,6 @@ stop_waiting (struct execution_control_state *ecs)
|
|||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: stop_waiting\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: stop_waiting\n");
|
||||||
|
|
||||||
clear_step_over_info ();
|
|
||||||
|
|
||||||
/* Let callers know we don't want to wait for the inferior anymore. */
|
/* Let callers know we don't want to wait for the inferior anymore. */
|
||||||
ecs->wait_some_more = 0;
|
ecs->wait_some_more = 0;
|
||||||
|
|
||||||
|
|||||||
75
gdb/testsuite/gdb.threads/interrupt-while-step-over.c
Normal file
75
gdb/testsuite/gdb.threads/interrupt-while-step-over.c
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2016 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define NUM_THREADS 20
|
||||||
|
const int num_threads = NUM_THREADS;
|
||||||
|
|
||||||
|
static pthread_t child_thread[NUM_THREADS];
|
||||||
|
|
||||||
|
static pthread_barrier_t threads_started_barrier;
|
||||||
|
|
||||||
|
volatile int always_zero;
|
||||||
|
volatile unsigned int dummy;
|
||||||
|
|
||||||
|
static void
|
||||||
|
infinite_loop (void)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
dummy++; /* set breakpoint here */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
child_function (void *arg)
|
||||||
|
{
|
||||||
|
pthread_barrier_wait (&threads_started_barrier);
|
||||||
|
|
||||||
|
infinite_loop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
all_started (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
alarm (300);
|
||||||
|
|
||||||
|
pthread_barrier_init (&threads_started_barrier, NULL, NUM_THREADS + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_THREADS; i++)
|
||||||
|
{
|
||||||
|
res = pthread_create (&child_thread[i], NULL, child_function, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait until all threads have been scheduled. */
|
||||||
|
pthread_barrier_wait (&threads_started_barrier);
|
||||||
|
|
||||||
|
all_started ();
|
||||||
|
|
||||||
|
infinite_loop ();
|
||||||
|
}
|
||||||
202
gdb/testsuite/gdb.threads/interrupt-while-step-over.exp
Normal file
202
gdb/testsuite/gdb.threads/interrupt-while-step-over.exp
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Regression test for PR gdb/18360. Test that "interrupt -a" while
|
||||||
|
# some thread is stepping over a breakpoint behaves as expected.
|
||||||
|
|
||||||
|
standard_testfile
|
||||||
|
|
||||||
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
|
||||||
|
{ debug pthreads }] == -1} {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read the number of threads out of the inferior.
|
||||||
|
if ![runto_main] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
set NUM_THREADS [get_integer_valueof "num_threads" -1]
|
||||||
|
|
||||||
|
# Account for the main thread.
|
||||||
|
incr NUM_THREADS
|
||||||
|
|
||||||
|
# Run command and wait for the prompt, without end anchor.
|
||||||
|
|
||||||
|
proc gdb_test_no_anchor {cmd} {
|
||||||
|
global gdb_prompt
|
||||||
|
|
||||||
|
gdb_test_multiple $cmd $cmd {
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
pass $cmd
|
||||||
|
}
|
||||||
|
-re "infrun:" {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable/disable debugging.
|
||||||
|
|
||||||
|
proc enable_debug {enable} {
|
||||||
|
|
||||||
|
# Comment out to debug problems with the test.
|
||||||
|
return
|
||||||
|
|
||||||
|
gdb_test_no_anchor "set debug infrun $enable"
|
||||||
|
gdb_test_no_anchor "set debug displaced $enable"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If RESULT is not zero, make the caller return RESULT.
|
||||||
|
|
||||||
|
proc return_if_nonzero { result } {
|
||||||
|
if {$result != 0} {
|
||||||
|
return -code return $result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Do one iteration of "c -a& -> interrupt -a". Return zero on sucess,
|
||||||
|
# and non-zero if some test fails.
|
||||||
|
|
||||||
|
proc test_one_iteration {} {
|
||||||
|
global gdb_prompt
|
||||||
|
global NUM_THREADS
|
||||||
|
global decimal
|
||||||
|
|
||||||
|
set saw_continuing 0
|
||||||
|
set test "continue -a &"
|
||||||
|
return_if_nonzero [gdb_test_multiple $test $test {
|
||||||
|
-re "Continuing.\r\n" {
|
||||||
|
set saw_continuing 1
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
if ![gdb_assert $saw_continuing $test] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-re "infrun:" {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
set running_count 0
|
||||||
|
set test "all threads are running"
|
||||||
|
return_if_nonzero [gdb_test_multiple "info threads" $test {
|
||||||
|
-re "Thread \[^\r\n\]* \\(running\\)" {
|
||||||
|
incr running_count
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
if ![gdb_assert {$running_count == $NUM_THREADS} $test] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-re "infrun:" {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
set test "interrupt -a"
|
||||||
|
return_if_nonzero [gdb_test_multiple $test $test {
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
pass $test
|
||||||
|
}
|
||||||
|
-re "infrun:" {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
set stopped_count 0
|
||||||
|
set test "wait for stops"
|
||||||
|
# Don't return on failure here, in order to let "info threads" put
|
||||||
|
# useful info in gdb.log.
|
||||||
|
gdb_test_multiple "" $test {
|
||||||
|
-re "Thread $decimal \[^\r\n\]*stopped" {
|
||||||
|
incr stopped_count
|
||||||
|
if {$stopped_count != $NUM_THREADS} {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
gdb_assert {$stopped_count == $NUM_THREADS} $test
|
||||||
|
}
|
||||||
|
-re "infrun:" {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if all threads are seen as stopped with "info
|
||||||
|
# threads". It's a bit redundant with the test above, but
|
||||||
|
# it's useful to have this in the gdb.log if the above ever
|
||||||
|
# happens to fail.
|
||||||
|
set running_count 0
|
||||||
|
set test "all threads are stopped"
|
||||||
|
return_if_nonzero [gdb_test_multiple "info threads" $test {
|
||||||
|
-re "Thread \[^\r\n\]* \\(running\\)" {
|
||||||
|
incr running_count
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt " {
|
||||||
|
if ![gdb_assert {$running_count == 0} $test] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# The test driver proper. If DISPLACED is "on", turn on displaced
|
||||||
|
# stepping. If "off", turn it off.
|
||||||
|
|
||||||
|
proc testdriver {displaced} {
|
||||||
|
global binfile
|
||||||
|
global GDBFLAGS
|
||||||
|
|
||||||
|
save_vars { GDBFLAGS } {
|
||||||
|
append GDBFLAGS " -ex \"set non-stop on\""
|
||||||
|
clean_restart $binfile
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test_no_output "set displaced-stepping $displaced"
|
||||||
|
|
||||||
|
if ![runto all_started] {
|
||||||
|
fail "couldn't run to all_started"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set break_line [gdb_get_line_number "set breakpoint here"]
|
||||||
|
|
||||||
|
gdb_test "break $break_line if always_zero" "Breakpoint .*" "set breakpoint"
|
||||||
|
|
||||||
|
enable_debug 1
|
||||||
|
|
||||||
|
for {set iter 0} {$iter < 20} {incr iter} {
|
||||||
|
with_test_prefix "iter=$iter" {
|
||||||
|
# Return early if some test fails, to avoid cascading
|
||||||
|
# timeouts if something goes wrong.
|
||||||
|
if {[test_one_iteration] != 0} {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_with_prefix displaced-stepping {"on" "off"} {
|
||||||
|
if { ${displaced-stepping} != "off" && ![support_displaced_stepping] } {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
testdriver ${displaced-stepping}
|
||||||
|
}
|
||||||
@@ -100,7 +100,6 @@ gdb_test_sequence $test $test {
|
|||||||
"need to step-over"
|
"need to step-over"
|
||||||
"resume \\(step=1"
|
"resume \\(step=1"
|
||||||
"signal arrived while stepping over breakpoint"
|
"signal arrived while stepping over breakpoint"
|
||||||
"(restart threads|switching back to stepped thread)"
|
|
||||||
"stepped to a different line"
|
"stepped to a different line"
|
||||||
"callme"
|
"callme"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user