998d452ac8
Running gdb.threads/fork-plus-threads.exp against gdbserver in extended-remote mode, even though the test passes, we still see broken behavior: (gdb) PASS: gdb.threads/fork-plus-threads.exp: set detach-on-fork off continue & Continuing. (gdb) PASS: gdb.threads/fork-plus-threads.exp: continue & [New Thread 28092.28092] [Thread 28092.28092] #2 stopped. [New Thread 28094.28094] [Inferior 2 (process 28092) exited normally] [New Thread 28094.28105] [New Thread 28094.28109] ... [Thread 28174.28174] #18 stopped. [New Thread 28185.28185] [Inferior 10 (process 28174) exited normally] [New Thread 28185.28196] [Thread 28185.28185] #20 stopped. Cannot remove breakpoints because program is no longer writable. Further execution is probably impossible. [Inferior 11 (process 28185) exited normally] [Inferior 1 (process 28091) exited normally] PASS: gdb.threads/fork-plus-threads.exp: reached breakpoint info threads No threads. (gdb) PASS: gdb.threads/fork-plus-threads.exp: no threads left info inferiors Num Description Executable * 1 <null> /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/fork-plus-threads (gdb) PASS: gdb.threads/fork-plus-threads.exp: only inferior 1 left All the "[Thread FOO] #NN stopped." above are bogus, as well as the "Cannot remove breakpoints because program is no longer writable.", which is a consequence. The problem is that when we intercept a fork event, we should report the event for the parent, only, and leave the child stopped, but not report its stop event. GDB later decides whether to follow the parent or the child. But because handle_extended_wait does not set the child's last_status.kind to TARGET_WAITKIND_STOPPED, a stop_all_threads/unstop_all_lwps sequence (e.g., from trying to access memory) by mistake ends up queueing a SIGSTOP on the child, resuming it, and then when that SIGSTOP is intercepted, because the LWP has last_resume_kind set to resume_stop, gdbserver reports the stop to GDB, as GDB_SIGNAL_0: ... >>>> entering unstop_all_lwps unstopping all lwps proceed_one_lwp: lwp 1600 client wants LWP to remain 1600 stopped proceed_one_lwp: lwp 1828 Client wants LWP 1828 to stop. Making sure it has a SIGSTOP pending ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sending sigstop to lwp 1828 pc is 0x3615ebc7cc Resuming lwp 1828 (continue, signal 0, stop expected) continue from pc 0x3615ebc7cc unstop_all_lwps done sigchld_handler <<<< exiting unstop_all_lwps handling possible target event >>>> entering linux_wait_1 linux_wait_1: [<all threads>] my_waitpid (-1, 0x40000001) my_waitpid (-1, 0x1): status(137f), 1828 LWFE: waitpid(-1, ...) returned 1828, ERRNO-OK LLW: waitpid 1828 received Stopped (signal) (stopped) pc is 0x3615ebc7cc Expected stop. LLW: resume_stop SIGSTOP caught for LWP 1828.1828. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... linux_wait_1 ret = LWP 1828.1828, 1, 0 <<<< exiting linux_wait_1 Writing resume reply for LWP 1828.1828:1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tested on x86_64 Fedora 20, extended-remote. gdb/gdbserver/ChangeLog: 2015-07-30 Pedro Alves <palves@redhat.com> * linux-low.c (handle_extended_wait): Set the child's last reported status to TARGET_WAITKIND_STOPPED.
105 lines
3.2 KiB
Text
105 lines
3.2 KiB
Text
# Copyright (C) 2015 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/>.
|
|
|
|
# This test verifies that threads created by the child fork are
|
|
# properly handled. Specifically, GDB used to have a bug where it
|
|
# would leave child fork threads stuck stopped, even though "info
|
|
# threads" would show them running.
|
|
#
|
|
# See https://sourceware.org/bugzilla/show_bug.cgi?id=18600
|
|
|
|
standard_testfile
|
|
|
|
proc do_test { detach_on_fork } {
|
|
global GDBFLAGS
|
|
global srcfile testfile
|
|
global gdb_prompt
|
|
|
|
set saved_gdbflags $GDBFLAGS
|
|
set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop on\""]
|
|
|
|
if {[prepare_for_testing "failed to prepare" \
|
|
$testfile $srcfile {debug pthreads}] == -1} {
|
|
set GDBFLAGS $saved_gdbflags
|
|
return -1
|
|
}
|
|
|
|
set GDBFLAGS $saved_gdbflags
|
|
|
|
if ![runto_main] then {
|
|
fail "Can't run to main"
|
|
return 0
|
|
}
|
|
|
|
gdb_test_no_output "set detach-on-fork $detach_on_fork"
|
|
set test "continue &"
|
|
gdb_test_multiple $test $test {
|
|
-re "$gdb_prompt " {
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
# gdbserver had a bug that resulted in reporting the fork child's
|
|
# initial stop to gdb, which gdb does not expect, in turn
|
|
# resulting in a broken session, like:
|
|
#
|
|
# [Thread 31536.31536] #16 stopped. <== BAD
|
|
# [New Thread 31547.31547]
|
|
# [Inferior 10 (process 31536) exited normally]
|
|
# [New Thread 31547.31560]
|
|
#
|
|
# [Thread 31547.31547] #18 stopped. <== BAD
|
|
# Cannot remove breakpoints because program is no longer writable. <== BAD
|
|
# Further execution is probably impossible. <== BAD
|
|
# [Inferior 11 (process 31547) exited normally]
|
|
# [Inferior 1 (process 31454) exited normally]
|
|
#
|
|
# These variables track whether we see such broken behavior.
|
|
set saw_cannot_remove_breakpoints 0
|
|
set saw_thread_stopped 0
|
|
|
|
set test "inferior 1 exited"
|
|
gdb_test_multiple "" $test {
|
|
-re "Cannot remove breakpoints" {
|
|
set saw_cannot_remove_breakpoints 1
|
|
exp_continue
|
|
}
|
|
-re "Thread \[^\r\n\]+ stopped\\." {
|
|
set saw_thread_stopped 1
|
|
exp_continue
|
|
}
|
|
-re "Inferior 1 \(\[^\r\n\]+\) exited normally" {
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
gdb_assert !$saw_cannot_remove_breakpoints \
|
|
"no failure to remove breakpoints"
|
|
gdb_assert !$saw_thread_stopped \
|
|
"no spurious thread stop"
|
|
|
|
gdb_test "info threads" "No threads\." \
|
|
"no threads left"
|
|
|
|
gdb_test "info inferiors" \
|
|
"Num\[ \t\]+Description\[ \t\]+Executable\[ \t\]+\r\n\\* 1 \[^\r\n\]+" \
|
|
"only inferior 1 left"
|
|
}
|
|
|
|
foreach detach_on_fork {"on" "off"} {
|
|
with_test_prefix "detach-on-fork=$detach_on_fork" {
|
|
do_test $detach_on_fork
|
|
}
|
|
}
|