mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-17 13:10:12 +08:00
GNU/Linux: Interrupt/Ctrl-C with SIGSTOP instead of SIGINT [PR gdb/9425, PR gdb/14559]
After the "Always put inferiors in their own terminal/session [gdb/9425, gdb/14559]" change, when a user types "Ctrl-C" while the inferior is running, GDB is the one who gets the SIGINT, not the inferior process. GDB then forwards the SIGINT to the inferior with target_pass_ctrlc. That was necessary but not not sufficient to fix PRs gdb/9425, gdb/14559, because if a program blocks SIGINT with e.g. sigprocmask, then if GDB sends it a SIGINT, the signal isn't ever delivered to the process, so ptrace does not intercept it. You type Ctrl-C, but nothing happens. Similarly, if a program uses sigwait to wait for SIGINT, and the program receives a SIGINT, the SIGINT is _not_ intercepted by ptrace, it goes straight to the inferior. Now that the Ctrl-C results in a SIGINT sent to GDB instead of the inferior, we can make GDB interrupt the program any other way we like. This patch makes non-stop-capable ports interrupt the program with stop_all_threads / target_stop (i.e., SIGSTOP) instead of target_pass_ctrlc (i.e., SIGINT), which always works -- SIGSTOP can't be blocked/ignored. (In the future GDB may even switch to PTRACE_INTERRUPT on Linux, though that's a project of its own.) Another advantage here is with multi-target -- currently, because GDB relies on Ctrl-C stopping one thread, and then stopping all other threads in reaction to that stop, target_pass_ctrlc tries to find one inferior with a thread that is running, in any target. If the selected target for some reason fails to process the Ctrl-C request, then the Ctrl-C ends up lost. The mechanism implemented in this patch is different -- we never have to pick a thread, inferior or target -- we're going to stop everything, so we end up in stop_all_threads. For non-stop, the patch preserves the current behavior of only stopping one thread in reaction to Ctrl-C, so it can still happen that the thread that GDB selects to stop disappears and the Ctrl-C ends up being lost. However, because now GDB always sees the SIGINT first, we can change how Ctrl-C behaves there too. We could even make it configurable -- for example, it could be reasonable that Ctrl-C simply drops the CLI back to the prompt, without stopping anything at all. That might be interesting for "set observer-mode on", at least. This commit has a user-visible behavior change in all-stop mode -- when you interrupt the program with Ctrl-C, instead of: Thread 1 "threads" received signal SIGINT, Interrupt. You'll get: Thread 1 "threads" stopped. Which is what you already got with the "interrupt" command in non-stop mode. If you really want to pass a SIGINT to the program, you can then issue: (gdb) signal SIGINT This commit also adjusts the testsuite to cope with that output alternative. With this change, the gdb.base/sigint-sigwait.exp and gdb.base/sigint-masked-out.exp testcases now pass cleanly on GNU/Linux, on both native debugging and gdbserver + "maint set target-non-stop on", so the kfails are adjusted accordingly. gdb/ChangeLog: yyyy-mm-dd Pedro Alves <pedro@palves.net> PR gdb/9425 PR gdb/14559 * event-top.c (default_quit_handler): Mark infrun event-loop handler with mark_infrun_async_event_handler_ctrl_c instead of passing the Ctrl-C to the target directly with target_pass_ctrlc. * infcmd.c (interrupt_target_1): On non-stop targets, Mark infrun event-loop handler with mark_infrun_async_event_handler_interrupt_all instead of using target_interrupt. * infrun.c (interrupt_all_requested, switch_to_stop_thread) (sync_interrupt_all) (mark_infrun_async_event_handler_interrupt_all) (mark_infrun_async_event_handler_ctrl_c): New. (infrun_async_inferior_event_handler): Handle Ctrl-C/interrupt requests. * infrun.h (mark_infrun_async_event_handler_interrupt_all) (mark_infrun_async_event_handler_ctrl_c): Declare. * linux-nat.c (wait_for_signal): Don't handle Ctrl-C here. (linux_nat_wait_1): Handle it here, by marking the infrun event handler, and returning TARGET_WAITKIND_IGNORE with the quit flag still set. * target.c (maybe_pass_ctrlc): New. (target_terminal::inferior, target_terminal::restore_inferior): Use it. (target_pass_ctrlc): Ass there's no non-stop target pushed. gdb/testsuite/ChangeLog: yyyy-mm-dd Pedro Alves <pedro@palves.net> PR gdb/9425 PR gdb/14559 * gdb.base/bp-cmds-continue-ctrl-c.exp: Expect "stopped" in alternative to "signal SIGINT". * gdb.base/interrupt-daemon-attach.exp: Likewise. * gdb.base/interrupt-daemon.exp: Likewise. * gdb.base/interrupt-noterm.exp: Likewise. * gdb.base/interrupt.exp: Likewise. * gdb.base/random-signal.exp: Likewise. * gdb.base/range-stepping.exp: Likewise. * gdb.gdb/selftest.exp: Likewise. * gdb.mi/new-ui-mi-sync.exp: Likewise. * gdb.multi/multi-target-interrupt.exp: Likewise. * gdb.multi/multi-target-no-resumed.exp: Likewise. * gdb.multi/multi-term-settings.exp: Likewise. * gdb.server/reconnect-ctrl-c.exp: Likewise. * gdb.threads/async.exp: Likewise. * gdb.threads/continue-pending-status.exp: Likewise. * gdb.threads/leader-exit.exp: Likewise. * gdb.threads/manythreads.exp: Likewise. * gdb.threads/pthreads.exp: Likewise. * gdb.threads/schedlock.exp: Likewise. * gdb.threads/sigthread.exp: Likewise. * lib/gdb.exp (can_interrupt_blocked_sigint): New. * gdb.base/sigint-masked-out.exp (test_ctrl_c) (test_interrupt_cmd): Use can_interrupt_blocked_sigint, and don't kfail if true. * gdb.base/sigint-sigwait.exp (test_ctrl_c, test_interrupt_cmd): Likewise. Change-Id: I83c1b6a20deea1f1909156adde1d60b8f6f2629b
This commit is contained in:
parent
86a5798736
commit
814fb49ba3
@ -1002,7 +1002,15 @@ default_quit_handler (void)
|
||||
if (target_terminal::is_ours ())
|
||||
quit ();
|
||||
else
|
||||
target_pass_ctrlc ();
|
||||
{
|
||||
/* Let the even loop handle the quit/interrupt. In some
|
||||
modes (e.g., "set non-stop off" + "maint set
|
||||
target-non-stop on"), it's not safe to request an
|
||||
interrupt right now, as we may be in the middle of
|
||||
handling some other event, and target_stop changes infrun
|
||||
state. */
|
||||
mark_infrun_async_event_handler_ctrl_c ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
34
gdb/infcmd.c
34
gdb/infcmd.c
@ -2846,7 +2846,39 @@ interrupt_target_1 (bool all_threads)
|
||||
stop_current_target_threads_ns (inferior_ptid);
|
||||
}
|
||||
else
|
||||
target_interrupt ();
|
||||
{
|
||||
if (exists_non_stop_target ())
|
||||
{
|
||||
/* Ignore the interrupt request if everything is already
|
||||
stopped. */
|
||||
auto any_resumed = [] ()
|
||||
{
|
||||
for (thread_info *thr : all_non_exited_threads ())
|
||||
{
|
||||
if (thr->executing)
|
||||
return true;
|
||||
if (thr->suspend.waitstatus_pending_p)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (any_resumed ())
|
||||
{
|
||||
/* Stop all threads, and report one single stop for all
|
||||
threads. Since the "interrupt" command works
|
||||
asynchronously on all other modes (non-stop or true
|
||||
all-stop + stopping with SIGINT), i.e., the command
|
||||
finishes and GDB prints the prompt before the target
|
||||
actually stops, make this mode work the same, by
|
||||
deferring the actual synchronous stopping work to the
|
||||
event loop. */
|
||||
mark_infrun_async_event_handler_interrupt_all ();
|
||||
}
|
||||
}
|
||||
else
|
||||
target_interrupt ();
|
||||
}
|
||||
|
||||
disable_commit_resumed.reset_and_commit ();
|
||||
}
|
||||
|
149
gdb/infrun.c
149
gdb/infrun.c
@ -9440,13 +9440,158 @@ static const struct internalvar_funcs siginfo_funcs =
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Callback for infrun's target events source. This is marked when a
|
||||
thread has a pending status to process. */
|
||||
/* True if the user used the "interrupt" command and we want to handle
|
||||
the interruption via the event loop instead of immediately. This
|
||||
is so that "interrupt" always stops the program asynchronously in
|
||||
all the different execution modes. In particular, in "set non-stop
|
||||
off" + "maint set target-non-stop on" mode, we want to
|
||||
synchronously stop all threads with stop_all_threads, so we delay
|
||||
doing that to the event loop, so that "interrupt" presents a prompt
|
||||
immediately, and then presents the stop afterwards, just like what
|
||||
happens in non-stop mode, or if the target is in true all-stop mode
|
||||
and the interrupting is done by sending a SIGINT to the inferior
|
||||
process. */
|
||||
static bool interrupt_all_requested = false;
|
||||
|
||||
/* Pick the thread to report the stop on and to switch to it. */
|
||||
|
||||
static void
|
||||
switch_to_stop_thread ()
|
||||
{
|
||||
thread_info *stop_thr = nullptr;
|
||||
|
||||
if (previous_thread != nullptr && previous_thread->state == THREAD_RUNNING)
|
||||
stop_thr = previous_thread.get ();
|
||||
else
|
||||
{
|
||||
for (thread_info *thr : all_non_exited_threads ())
|
||||
if (thr->state == THREAD_RUNNING)
|
||||
{
|
||||
stop_thr = thr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (stop_thr != nullptr);
|
||||
|
||||
switch_to_thread (stop_thr);
|
||||
}
|
||||
|
||||
/* Synchronously stop all threads, saving interesting events as
|
||||
pending events, and present a normal stop on one of the threads.
|
||||
Preference is given to the "previous thread", which was the thread
|
||||
that the user last resumed. This is used in "set non-stop off" +
|
||||
"maint set target-non-stop on" mode to stop the target in response
|
||||
to Ctrl-C or the "interrupt" command. */
|
||||
|
||||
static void
|
||||
sync_interrupt_all ()
|
||||
{
|
||||
/* Events are always processed with the main UI as current UI. This
|
||||
way, warnings, debug output, etc. are always consistently sent to
|
||||
the main console. */
|
||||
scoped_restore save_ui = make_scoped_restore (¤t_ui, main_ui);
|
||||
|
||||
/* Exposed by gdb.base/paginate-after-ctrl-c-running.exp. */
|
||||
|
||||
/* Temporarily disable pagination. Otherwise, the user would be
|
||||
given an option to press 'q' to quit, which would cause an early
|
||||
exit and could leave GDB in a half-baked state. */
|
||||
scoped_restore save_pagination
|
||||
= make_scoped_restore (&pagination_enabled, false);
|
||||
|
||||
scoped_disable_commit_resumed disable_commit_resumed ("stopping for ctrl-c");
|
||||
|
||||
gdb_assert (!non_stop);
|
||||
|
||||
/* Stop all threads before picking which one to present the stop on
|
||||
-- this is safer than the other way around because otherwise the
|
||||
thread we pick could exit just while we try to stop it. */
|
||||
stop_all_threads ();
|
||||
|
||||
switch_to_stop_thread ();
|
||||
|
||||
target_waitstatus ws;
|
||||
ws.kind = TARGET_WAITKIND_STOPPED;
|
||||
ws.value.sig = GDB_SIGNAL_0;
|
||||
set_last_target_status (current_inferior ()->process_target (),
|
||||
inferior_ptid, ws);
|
||||
stopped_by_random_signal = true;
|
||||
stop_print_frame = true;
|
||||
normal_stop ();
|
||||
|
||||
inferior_event_handler (INF_EXEC_COMPLETE);
|
||||
|
||||
/* If a UI was in sync execution mode, and now isn't, restore its
|
||||
prompt (a synchronous execution command has finished, and we're
|
||||
ready for input). */
|
||||
all_uis_check_sync_execution_done ();
|
||||
}
|
||||
|
||||
/* See infrun.h. */
|
||||
|
||||
void
|
||||
mark_infrun_async_event_handler_interrupt_all ()
|
||||
{
|
||||
mark_infrun_async_event_handler ();
|
||||
interrupt_all_requested = true;
|
||||
}
|
||||
|
||||
/* See infrun.h. */
|
||||
|
||||
void
|
||||
mark_infrun_async_event_handler_ctrl_c ()
|
||||
{
|
||||
mark_infrun_async_event_handler ();
|
||||
set_quit_flag ();
|
||||
}
|
||||
|
||||
/* Callback for infrun's target events source. This is marked either
|
||||
when a thread has a pending status to process, or a target
|
||||
interrupt was requested, either with Ctrl-C or the "interrupt"
|
||||
command and target is in non-stop mode. */
|
||||
|
||||
static void
|
||||
infrun_async_inferior_event_handler (gdb_client_data data)
|
||||
{
|
||||
/* Handle a Ctrl-C while the inferior has the terminal, or an
|
||||
"interrupt" cmd request. */
|
||||
if ((!target_terminal::is_ours () && check_quit_flag ())
|
||||
|| interrupt_all_requested)
|
||||
{
|
||||
interrupt_all_requested = false;
|
||||
|
||||
if (exists_non_stop_target ())
|
||||
{
|
||||
if (non_stop)
|
||||
{
|
||||
/* Stop one thread, like it would happen if we were
|
||||
stopping with SIGINT sent to the foreground
|
||||
process. */
|
||||
switch_to_stop_thread ();
|
||||
interrupt_target_1 (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Stop all threads, and report one single stop for all
|
||||
threads. */
|
||||
sync_interrupt_all ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pass a Ctrl-C request to the target. Usually this means
|
||||
sending a SIGINT to the inferior process. */
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* Don't clear the event handler yet -- there may be pending
|
||||
events to process. */
|
||||
return;
|
||||
}
|
||||
|
||||
clear_async_event_handler (infrun_async_inferior_event_token);
|
||||
|
||||
inferior_event_handler (INF_REG_EVENT);
|
||||
}
|
||||
|
||||
|
12
gdb/infrun.h
12
gdb/infrun.h
@ -255,6 +255,18 @@ extern void infrun_async (int enable);
|
||||
loop. */
|
||||
extern void mark_infrun_async_event_handler (void);
|
||||
|
||||
/* Like mark_infrun_async_event_handler, and ask the event loop to
|
||||
stop all threads, in response to an "interrupt" command. */
|
||||
extern void mark_infrun_async_event_handler_interrupt_all ();
|
||||
|
||||
/* Like mark_infrun_async_event_handler, and ask the event loop to
|
||||
handle a "Ctrl-C" interruption request. In some modes (e.g., "set
|
||||
non-stop off" + "maint set target-non-stop on"), we interrupt the
|
||||
target with target_stop, and it's not safe to use that right away,
|
||||
as we may be in the middle of handling some other event, and
|
||||
target_stop changes infrun state. */
|
||||
extern void mark_infrun_async_event_handler_ctrl_c ();
|
||||
|
||||
/* The global chain of threads that need to do a step-over operation
|
||||
to get past e.g., a breakpoint. */
|
||||
extern struct thread_info *global_thread_step_over_chain_head;
|
||||
|
@ -2142,19 +2142,6 @@ wait_for_signal ()
|
||||
{
|
||||
linux_nat_debug_printf ("about to sigsuspend");
|
||||
sigsuspend (&suspend_mask);
|
||||
|
||||
/* If the quit flag is set, it means that the user pressed Ctrl-C
|
||||
and we're debugging a process that is running on a separate
|
||||
terminal, so we must forward the Ctrl-C to the inferior. (If the
|
||||
inferior is sharing GDB's terminal, then the Ctrl-C reaches the
|
||||
inferior directly.) We must do this here because functions that
|
||||
need to block waiting for a signal loop forever until there's an
|
||||
event to report before returning back to the event loop. */
|
||||
if (!target_terminal::is_ours ())
|
||||
{
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
|
||||
@ -3317,8 +3304,32 @@ linux_nat_wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
|
||||
/* We shouldn't end up here unless we want to try again. */
|
||||
gdb_assert (lp == NULL);
|
||||
|
||||
/* Block until we get an event reported with SIGCHLD. */
|
||||
/* Block until we get an event reported with SIGCHLD or a SIGINT
|
||||
interrupt. */
|
||||
wait_for_signal ();
|
||||
|
||||
/* If the quit flag is set, it means that the user pressed
|
||||
Ctrl-C and we're debugging a process that is running on a
|
||||
separate terminal, so we must forward the Ctrl-C to the
|
||||
inferior. (If the inferior is sharing GDB's terminal, then
|
||||
the Ctrl-C reaches the inferior directly.) If we were
|
||||
interrupted by Ctrl-C, return back to the event loop and let
|
||||
it handle interrupting the target (or targets). */
|
||||
|
||||
if (!target_terminal::is_ours () && check_quit_flag ())
|
||||
{
|
||||
mark_infrun_async_event_handler_ctrl_c ();
|
||||
|
||||
linux_nat_debug_printf ("exit (quit flag)");
|
||||
|
||||
/* If we got a SIGCHLD, need to end up here again. */
|
||||
async_file_mark ();
|
||||
|
||||
ourstatus->kind = TARGET_WAITKIND_IGNORE;
|
||||
|
||||
restore_child_signals_mask (&prev_mask);
|
||||
return minus_one_ptid;
|
||||
}
|
||||
}
|
||||
|
||||
gdb_assert (lp);
|
||||
|
24
gdb/target.c
24
gdb/target.c
@ -927,6 +927,21 @@ target_terminal::init (void)
|
||||
m_terminal_state = target_terminal_state::is_ours;
|
||||
}
|
||||
|
||||
/* Called after switching the terminal to the inferior. If the user
|
||||
hit C-c before, pretend that it was hit right here. Non-stop
|
||||
targets however are more complicated though, as interruption with
|
||||
"set non-stop off" + "maint set target-non-stop on" wants to stop
|
||||
all threads individually with target_stop (and synchronously wait
|
||||
for the stops), so we let the event loop handle it, when it's
|
||||
recursion-safe to do so. */
|
||||
|
||||
static void
|
||||
maybe_pass_ctrlc ()
|
||||
{
|
||||
if (!exists_non_stop_target () && check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* See target/target.h. */
|
||||
|
||||
void
|
||||
@ -961,8 +976,7 @@ target_terminal::inferior (void)
|
||||
|
||||
/* If the user hit C-c before, pretend that it was hit right
|
||||
here. */
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
maybe_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* See target/target.h. */
|
||||
@ -998,8 +1012,7 @@ target_terminal::restore_inferior (void)
|
||||
|
||||
/* If the user hit C-c before, pretend that it was hit right
|
||||
here. */
|
||||
if (check_quit_flag ())
|
||||
target_pass_ctrlc ();
|
||||
maybe_pass_ctrlc ();
|
||||
}
|
||||
|
||||
/* Switch terminal state to DESIRED_STATE, either is_ours, or
|
||||
@ -3797,6 +3810,9 @@ target_interrupt ()
|
||||
void
|
||||
target_pass_ctrlc (void)
|
||||
{
|
||||
/* Non-stop targets interrupt programs with target_stop instead. */
|
||||
gdb_assert (!exists_non_stop_target ());
|
||||
|
||||
/* Pass the Ctrl-C to the first target that has a thread
|
||||
running. */
|
||||
for (inferior *inf : all_inferiors ())
|
||||
|
@ -87,6 +87,9 @@ proc do_test {} {
|
||||
-re "Program received signal SIGINT.*\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (SIGINT)\n"
|
||||
}
|
||||
-re "Program stopped.*\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (stopped)\n"
|
||||
}
|
||||
-re "Quit\r\n$gdb_prompt $" {
|
||||
send_log "$internal_pass (Quit)\n"
|
||||
|
||||
|
@ -81,7 +81,7 @@ proc do_test {} {
|
||||
}
|
||||
|
||||
after 500 {send_gdb "\003"}
|
||||
gdb_test "" "(Program|Thread .*) received signal SIGINT.*" \
|
||||
gdb_test "" "(Program|Thread .*) (received signal SIGINT|stopped).*" \
|
||||
"stop with control-c"
|
||||
|
||||
remote_exec host "kill -9 $child_pid"
|
||||
|
@ -53,7 +53,7 @@ proc do_test {} {
|
||||
|
||||
set test "ctrl-c stops process"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "received signal SIGINT.*\r\n$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
@ -79,7 +79,7 @@ proc do_test {} {
|
||||
|
||||
set test "interrupt cmd stops process"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "received signal SIGINT" {
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ gdb_test_multiple $test $test {
|
||||
|
||||
set test "inferior received SIGINT"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "\r\nProgram received signal SIGINT.*" {
|
||||
-re "\r\nProgram (received signal SIGINT|stopped).*" {
|
||||
# This appears after the prompt, which was already consumed
|
||||
# above.
|
||||
pass $test
|
||||
|
@ -82,7 +82,7 @@ if ![file exists $binfile] then {
|
||||
send_gdb "\003"
|
||||
set msg "send_gdb control C"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ if ![file exists $binfile] then {
|
||||
set msg "Send Control-C, second time"
|
||||
send_gdb "\003"
|
||||
gdb_test_multiple "" "$msg" {
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$msg"
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ proc do_test {} {
|
||||
# For this to work we must be sure to consume the "Continuing."
|
||||
# message first, or GDB's signal handler may not be in place.
|
||||
after 500 {send_gdb "\003"}
|
||||
gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Program (received signal SIGINT|stopped).*" \
|
||||
"stop with control-c"
|
||||
}
|
||||
|
||||
# With native debugging and "run" (with job control), the ctrl-c
|
||||
|
@ -193,7 +193,7 @@ if ![target_info exists gdb,nointerrupts] {
|
||||
incr vcont_r_counter
|
||||
exp_continue
|
||||
}
|
||||
-re "Program received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ proc_with_prefix test_ctrl_c {} {
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue" "" {
|
||||
-re "Continuing" {
|
||||
pass $gdb_test_name
|
||||
@ -52,7 +54,9 @@ proc_with_prefix test_ctrl_c {} {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
timeout {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (timeout)"
|
||||
}
|
||||
}
|
||||
@ -71,6 +75,8 @@ proc_with_prefix test_interrupt_cmd {} {
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue&" "" {
|
||||
-re "Continuing\\.\r\n$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
@ -91,7 +97,9 @@ proc_with_prefix test_interrupt_cmd {} {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
timeout {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (timeout)"
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ proc_with_prefix test_ctrl_c {} {
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue" "" {
|
||||
-re "Continuing" {
|
||||
pass $gdb_test_name
|
||||
@ -54,7 +56,9 @@ proc_with_prefix test_ctrl_c {} {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
-re -wrap "Inferior.*exited normally.*" {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/9425" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (the program exited)"
|
||||
}
|
||||
}
|
||||
@ -73,6 +77,8 @@ proc_with_prefix test_interrupt_cmd {} {
|
||||
return
|
||||
}
|
||||
|
||||
set can_interrupt [can_interrupt_blocked_sigint]
|
||||
|
||||
gdb_test_multiple "continue&" "" {
|
||||
-re "Continuing\\.\r\n$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
@ -95,7 +101,9 @@ proc_with_prefix test_interrupt_cmd {} {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
-re "Inferior.*exited normally" {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
if {!$can_interrupt} {
|
||||
setup_kfail "gdb/14559" *-*-*
|
||||
}
|
||||
fail "$gdb_test_name (the program exited)"
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ proc test_with_self { } {
|
||||
send_gdb "\003"
|
||||
# "Thread 1" is displayed iff Guile support is linked in.
|
||||
gdb_expect {
|
||||
-re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$description"
|
||||
}
|
||||
-re ".*$gdb_prompt $" {
|
||||
@ -119,7 +119,7 @@ proc test_with_self { } {
|
||||
set description "send ^C to child process again"
|
||||
send_gdb "\003"
|
||||
gdb_expect {
|
||||
-re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$description"
|
||||
}
|
||||
-re ".*$gdb_prompt $" {
|
||||
|
@ -92,7 +92,7 @@ proc do_test {sync_command} {
|
||||
gdb_test_multiple "interrupt" "$message" {
|
||||
-re "$gdb_prompt " {
|
||||
gdb_test_multiple "" "$message" {
|
||||
-re "received signal SIGINT" {
|
||||
-re "(received signal SIGINT|stopped)" {
|
||||
pass $message
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ proc test_ctrlc {} {
|
||||
|
||||
set msg "send_gdb control C"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ proc test_no_resumed_infs {inf_A inf_B} {
|
||||
# Now stop the program (all targets).
|
||||
send_gdb "\003"
|
||||
gdb_test_multiple "" "send_gdb control C" {
|
||||
-re "received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "(received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ proc coretest {inf1_how inf2_how} {
|
||||
if {$expect_ttou} {
|
||||
gdb_test "" "Quit" "stop with control-c (Quit)"
|
||||
} else {
|
||||
gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)"
|
||||
gdb_test "" "(received signal SIGINT|stopped).*" "stop with control-c (SIGINT)"
|
||||
}
|
||||
|
||||
# Useful for debugging in case the Ctrl-C above fails.
|
||||
|
@ -49,7 +49,7 @@ with_test_prefix "preparation" {
|
||||
gdb_test "disconnect" ".*"
|
||||
}
|
||||
|
||||
# Connect, continue, send Ctrl-C and expect a SIGINT stop.
|
||||
# Connect, continue, send Ctrl-C and expect a stop.
|
||||
|
||||
proc connect_continue_ctrl_c {} {
|
||||
global gdbserver_protocol gdbserver_gdbport
|
||||
@ -67,7 +67,7 @@ proc connect_continue_ctrl_c {} {
|
||||
}
|
||||
|
||||
after 1000 {send_gdb "\003"}
|
||||
gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Program (received signal SIGINT|stopped).*" "stop with control-c"
|
||||
}
|
||||
|
||||
with_test_prefix "first" {
|
||||
|
@ -80,7 +80,7 @@ proc test_current_thread {expected_thr} {
|
||||
gdb_test_multiple $test $test {
|
||||
-re "^interrupt\r\n$gdb_prompt " {
|
||||
gdb_test_multiple "" $test {
|
||||
-re "Thread .* received signal SIGINT, Interrupt\\." {
|
||||
-re "Thread .* (received signal SIGINT, Interrupt|stopped)\\." {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ for {set i 0} {$i < $attempts} {incr i} {
|
||||
|
||||
set msg "caught interrupt"
|
||||
gdb_test_multiple "" $msg {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $msg
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ send_gdb "\003"
|
||||
|
||||
set test "caught interrupt"
|
||||
gdb_test_multiple "" $test {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ gdb_test_multiple "continue" "first continue" {
|
||||
# we don't lose GDB's output while we do it.
|
||||
remote_expect host 1 { timeout { } }
|
||||
|
||||
# Send a Ctrl-C and wait for the SIGINT.
|
||||
# Send a Ctrl-C and wait for the stop.
|
||||
|
||||
proc interrupt_and_wait { message } {
|
||||
global gdb_prompt
|
||||
@ -70,7 +70,7 @@ proc interrupt_and_wait { message } {
|
||||
-re "\\\[\[^\]\]* exited\\\]\r\n" {
|
||||
exp_continue
|
||||
}
|
||||
-re " received signal SIGINT.*$gdb_prompt $" {
|
||||
-re " (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass "$message"
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
|
@ -204,7 +204,7 @@ proc check_control_c {} {
|
||||
send_gdb "\003"
|
||||
set description "Stopped with a ^C"
|
||||
gdb_expect {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $description
|
||||
}
|
||||
-re "Quit.*$gdb_prompt $" {
|
||||
|
@ -68,14 +68,9 @@ proc stop_process { description } {
|
||||
# For this to work we must be sure to consume the "Continuing."
|
||||
# message first, or GDB's signal handler may not be in place.
|
||||
after 1000 {send_gdb "\003"}
|
||||
gdb_expect {
|
||||
-re "Thread .* received signal SIGINT.*$gdb_prompt $"
|
||||
{
|
||||
pass $description
|
||||
}
|
||||
timeout
|
||||
{
|
||||
fail "$description (timeout)"
|
||||
gdb_test_multiple "" $description {
|
||||
-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
|
||||
pass $description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,4 +49,4 @@ after 500 {send_gdb "\003"}
|
||||
|
||||
# Make sure we do not get an internal error from hitting Control-C
|
||||
# while many signals are flying back and forth.
|
||||
gdb_test "" "Thread .* received signal SIGINT.*" "stop with control-c"
|
||||
gdb_test "" "Thread .* (received signal SIGINT|stopped).*" "stop with control-c"
|
||||
|
@ -2870,6 +2870,25 @@ gdb_caching_proc supports_memtag {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Return true if the target is able to interrupt a program that blocks
|
||||
# SIGINT.
|
||||
proc can_interrupt_blocked_sigint {} {
|
||||
if {[istarget *-*-linux*]} {
|
||||
gdb_test_multiple "maint show target-non-stop" "" {
|
||||
-re "(is|currently) on.*$::gdb_prompt $" {
|
||||
}
|
||||
-re "(is|currently) off.*$::gdb_prompt $" {
|
||||
# GDBserver still tries to interrupt programs with
|
||||
# SIGINT.
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Assume possible.
|
||||
return 1
|
||||
}
|
||||
|
||||
# Return 1 if the target supports hardware single stepping.
|
||||
|
||||
proc can_hardware_single_step {} {
|
||||
|
Loading…
Reference in New Issue
Block a user