Files
binutils-gdb/gdb/testsuite/gdb.threads/async.exp
Pedro Alves 814fb49ba3 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
2021-06-14 22:20:45 +01:00

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
}
}