forked from Imagelibrary/binutils-gdb
While testing a WIP Cygwin GDB that supports non-stop, I noticed that gdb.threads/attach-non-stop.exp exposes that this: (gdb) attach PID& ... (gdb) detach ... hangs. And it turns out that it hangs in all-stop as well. This commits fixes that. After "attach &", the target is set running, we've called ContinueDebugEvent and the process_thread thread is waiting for WaitForDebugEvent events. It is the equivalent of "attach; c&". In windows_nat_target::detach, the first thing we do is unconditionally call windows_continue (for ContinueDebugEvent), which blocks in do_synchronously, until the process_thread sees an event out of WaitForDebugEvent. Unless the inferior happens to run into a breakpoint, etc., then this hangs indefinitely. If we've already called ContinueDebugEvent earlier, then we shouldn't be calling it again in ::detach. Still in windows_nat_target::detach, we have an interesting issue that ends up being the bulk of the patch -- only the process_thread thread can call DebugActiveProcessStop, but if it is blocked in WaitForDebugEvent, we need to somehow force it to break out of it. The only way to do that, is to force the inferior to do something that causes WaitForDebugEvent to return some event. This patch uses CreateRemoteThread to do it, which results in WaitForDebugEvent reporting CREATE_THREAD_DEBUG_EVENT. We then terminate the injected thread before it has a chance to run any userspace code. Note that Win32 functions like DebugBreakProcess and GenerateConsoleCtrlEvent would also inject a new thread in the inferior. I first used DebugBreakProcess, but that is actually more complicated to use, because we'd have to be sure to consume the breakpoint event before detaching, otherwise the inferior would likely die due a breakpoint exception being raised with no debugger around to intercept it. See the new break_out_process_thread method. So the fix has two parts: - Keep track of whether we've called ContinueDebugEvent and the process_thread thread is waiting for events, or whether WaitForDebugEvent already returned an event. - In windows_nat_target::detach, if the process_thread thread is waiting for events, unblock out of its WaitForDebugEvent, before proceeding with the actual detach. New test included. Passes cleanly on GNU/Linux native and gdbserver, and also passes cleanly on Cygwin and MinGW, with the fix. Before the fix, it would hang and fail with a timeout. Tested-By: Hannes Domani <ssbssa@yahoo.de> Reviewed-By: Tom Tromey <tom@tromey.com> Change-Id: Ifb91c58c08af1a9bcbafecedc93dfce001040905
96 lines
2.6 KiB
Plaintext
96 lines
2.6 KiB
Plaintext
# Copyright 2024 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/>.
|
|
|
|
# Test detaching while the inferior is running. Basically:
|
|
#
|
|
# (gdb) attach PID
|
|
# (gdb) c&
|
|
# (gdb) detach
|
|
|
|
require can_spawn_for_attach
|
|
|
|
standard_testfile
|
|
|
|
if {[build_executable "failed to prepare" $testfile $srcfile {debug}] == -1} {
|
|
return
|
|
}
|
|
|
|
# The test proper. See description above.
|
|
|
|
proc test {} {
|
|
global binfile gdb_prompt
|
|
|
|
# This test requires executing commands while the target is
|
|
# running, which, when testing with the remote target, requires
|
|
# non-stop remote protocol. Until that variant of the RSP is the
|
|
# default, force target non-stop mode on.
|
|
set is_remote \
|
|
[expr {[target_info exists gdb_protocol] \
|
|
&& ([target_info gdb_protocol] == "remote" \
|
|
|| [target_info gdb_protocol] == "extended-remote")}]
|
|
|
|
save_vars { ::GDBFLAGS } {
|
|
if {$is_remote} {
|
|
append ::GDBFLAGS " -ex \"maint set target-non-stop on\""
|
|
}
|
|
clean_restart ${binfile}
|
|
}
|
|
|
|
set test_spawn_id [spawn_wait_for_attach $binfile]
|
|
set testpid [spawn_id_get_pid $test_spawn_id]
|
|
|
|
set any "\[^\r\n\]*"
|
|
|
|
# Iterate more than once so that we test re-attaching after
|
|
# detaching, in case GDB incorrectly detaches and the process
|
|
# crashes after the detach.
|
|
set n_iters 2
|
|
for {set iter 1} {$iter <= $n_iters} {incr iter} {
|
|
with_test_prefix "iter=$iter" {
|
|
set attached 0
|
|
|
|
gdb_test_multiple "attach $testpid" "attach" {
|
|
-re "Attaching to program:${any}process $testpid\r\n.*$gdb_prompt " {
|
|
pass $gdb_test_name
|
|
set attached 1
|
|
}
|
|
}
|
|
|
|
if {!$attached} {
|
|
break
|
|
}
|
|
|
|
gdb_test_multiple "continue &" "" {
|
|
-re "Continuing\.\r\n$::gdb_prompt " {
|
|
pass $gdb_test_name
|
|
}
|
|
}
|
|
|
|
gdb_test "detach" "Detaching from.*"
|
|
|
|
# Sleep a bit before reattaching to let the detached
|
|
# process crash and exit if e.g., GDB managed to leave
|
|
# breakpoint traps behind.
|
|
if {$iter != $n_iters} {
|
|
sleep 1
|
|
}
|
|
}
|
|
}
|
|
|
|
kill_wait_spawned_process $test_spawn_id
|
|
}
|
|
|
|
test
|