forked from Imagelibrary/binutils-gdb
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
99 lines
2.8 KiB
Plaintext
99 lines
2.8 KiB
Plaintext
# Copyright (C) 2019-2021 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/>.
|
|
|
|
standard_testfile
|
|
|
|
if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} {
|
|
return -1
|
|
}
|
|
|
|
# At this point GDB will be busy handling the breakpoint hits and
|
|
# re-resuming the program. Even if GDB internally switches thread
|
|
# context, the user should not notice it. The following part of the
|
|
# testcase ensures that.
|
|
|
|
# Switch to thread EXPECTED_THR, and then confirm that the thread
|
|
# stays selected.
|
|
|
|
proc test_current_thread {expected_thr} {
|
|
global decimal
|
|
global gdb_prompt
|
|
global binfile
|
|
|
|
clean_restart $binfile
|
|
|
|
if {![runto "all_started"]} {
|
|
fail "could not run to all_started"
|
|
return
|
|
}
|
|
|
|
# Set a breakpoint that continuously fires but doeesn't cause a stop.
|
|
gdb_breakpoint [concat [gdb_get_line_number "set breakpoint here"] " if 0"]
|
|
|
|
gdb_test "thread $expected_thr" "Switching to thread $expected_thr .*" \
|
|
"switch to thread $expected_thr"
|
|
|
|
# Continue the program in the background.
|
|
set test "continue&"
|
|
gdb_test_multiple "continue&" $test {
|
|
-re "Continuing\\.\r\n$gdb_prompt " {
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
set test "current thread is $expected_thr"
|
|
set fails 0
|
|
for {set i 0} {$i < 10} {incr i} {
|
|
after 200
|
|
|
|
set cur_thread 0
|
|
gdb_test_multiple "thread" $test {
|
|
-re "Current thread is ($decimal) .*$gdb_prompt " {
|
|
set cur_thread $expect_out(1,string)
|
|
}
|
|
}
|
|
|
|
if {$cur_thread != $expected_thr} {
|
|
incr fails
|
|
}
|
|
}
|
|
|
|
gdb_assert {$fails == 0} $test
|
|
|
|
# Explicitly interrupt the target, because in all-stop/remote,
|
|
# that's all we can do when the target is running. If we don't do
|
|
# this, we'd time out trying to kill the target, while bringing
|
|
# down gdb & gdbserver.
|
|
set test "interrupt"
|
|
gdb_test_multiple $test $test {
|
|
-re "^interrupt\r\n$gdb_prompt " {
|
|
gdb_test_multiple "" $test {
|
|
-re "Thread .* (received signal SIGINT, Interrupt|stopped)\\." {
|
|
pass $test
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Try once with each thread as current, to avoid missing a bug just
|
|
# because some part of GDB manages to switch to the right thread by
|
|
# chance.
|
|
for {set thr 1} {$thr <= 3} {incr thr} {
|
|
with_test_prefix "thread $thr" {
|
|
test_current_thread $thr
|
|
}
|
|
}
|