411 lines
13 KiB
Plaintext
411 lines
13 KiB
Plaintext
# Copyright 1997-2016 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/>.
|
|
|
|
# Until "set follow-fork-mode" and "catch fork" are implemented on
|
|
# other targets...
|
|
#
|
|
if { ![istarget "*-*-linux*"] && ![istarget "*-*-openbsd*"] } then {
|
|
continue
|
|
}
|
|
|
|
standard_testfile
|
|
|
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
|
|
return -1
|
|
}
|
|
|
|
proc check_fork_catchpoints {} {
|
|
global gdb_prompt
|
|
|
|
# Verify that the system supports "catch fork".
|
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "insert first fork catchpoint"
|
|
set has_fork_catchpoints 0
|
|
gdb_test_multiple "continue" "continue to first fork catchpoint" {
|
|
-re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" {
|
|
unsupported "continue to first fork catchpoint"
|
|
}
|
|
-re ".*Catchpoint.*$gdb_prompt $" {
|
|
set has_fork_catchpoints 1
|
|
pass "continue to first fork catchpoint"
|
|
}
|
|
}
|
|
|
|
if {$has_fork_catchpoints == 0} {
|
|
unsupported "fork catchpoints"
|
|
return -code return
|
|
}
|
|
}
|
|
|
|
# Test follow-fork to ensure that the correct process is followed, that
|
|
# the followed process stops where it is expected to stop, that processes
|
|
# are detached (or not) as expected, and that the inferior list has the
|
|
# expected contents after following the fork. WHO is the argument to
|
|
# the 'set follow-fork-mode' command, DETACH is the argument to the
|
|
# 'set detach-on-fork' command, and CMD is the GDB command used to
|
|
# execute the program past the fork. If the value of WHO or DETACH is
|
|
# 'default', the corresponding GDB command is skipped for that test.
|
|
# The value of CMD must be either 'next 2' or 'continue'.
|
|
proc test_follow_fork { who detach cmd } {
|
|
global gdb_prompt
|
|
global srcfile
|
|
global testfile
|
|
|
|
with_test_prefix "follow $who, detach $detach, command \"$cmd\"" {
|
|
|
|
# Start a new debugger session each time so defaults are legitimate.
|
|
clean_restart $testfile
|
|
|
|
if ![runto_main] {
|
|
untested "could not run to main"
|
|
return -1
|
|
}
|
|
|
|
# The "Detaching..." and "Attaching..." messages may be hidden by
|
|
# default.
|
|
gdb_test_no_output "set verbose"
|
|
|
|
# Set follow-fork-mode if we aren't using the default.
|
|
if {$who == "default"} {
|
|
set who "parent"
|
|
} else {
|
|
gdb_test_no_output "set follow-fork $who"
|
|
}
|
|
|
|
gdb_test "show follow-fork" \
|
|
"Debugger response to a program call of fork or vfork is \"$who\"." \
|
|
"show follow-fork"
|
|
|
|
# Set detach-on-fork mode if we aren't using the default.
|
|
if {$detach == "default"} {
|
|
set detach "on"
|
|
} else {
|
|
gdb_test_no_output "set detach-on-fork $detach"
|
|
}
|
|
|
|
gdb_test "show detach-on-fork" \
|
|
"Whether gdb will detach.* fork is $detach." \
|
|
"show detach-on-fork"
|
|
|
|
# Set a breakpoint after the fork if we aren't single-stepping
|
|
# past the fork.
|
|
if {$cmd == "continue"} {
|
|
set bp_after_fork [gdb_get_line_number "set breakpoint here"]
|
|
gdb_test "break ${srcfile}:$bp_after_fork" \
|
|
"Breakpoint.*, line $bp_after_fork.*" \
|
|
"set breakpoint after fork"
|
|
}
|
|
|
|
# Set up the output we expect to see after we run.
|
|
set expected_re ""
|
|
if {$who == "child"} {
|
|
set expected_re "Attaching after.* fork to.*"
|
|
if {$detach == "on"} {
|
|
append expected_re "Detaching after fork from .*"
|
|
}
|
|
append expected_re "set breakpoint here.*"
|
|
} elseif {$who == "parent" && $detach == "on"} {
|
|
set expected_re "Detaching after fork from .*set breakpoint here.*"
|
|
} else {
|
|
set expected_re ".*set breakpoint here.*"
|
|
}
|
|
|
|
# Test running past and following the fork, using the parameters
|
|
# set above.
|
|
gdb_test $cmd $expected_re "$cmd past fork"
|
|
|
|
# Check that we have the inferiors arranged correctly after
|
|
# following the fork.
|
|
set resume_unfollowed 0
|
|
if {$who == "parent" && $detach == "on"} {
|
|
|
|
# Follow parent / detach child: the only inferior is the parent.
|
|
gdb_test "info inferiors" "\\* 1 .* process.*" \
|
|
"info inferiors"
|
|
|
|
} elseif {$who == "parent" && $detach == "off"} {
|
|
|
|
# Follow parent / keep child: two inferiors under debug, the
|
|
# parent is the current inferior.
|
|
gdb_test "info inferiors" "\\* 1 .*process.* 2 .*process.*" \
|
|
"info inferiors"
|
|
|
|
gdb_test "inferior 2" "Switching to inferior 2 .*"
|
|
set resume_unfollowed 1
|
|
|
|
} elseif {$who == "child" && $detach == "on"} {
|
|
|
|
# Follow child / detach parent: the child is under debug and is
|
|
# the current inferior. The parent is listed but is not under
|
|
# debug.
|
|
gdb_test "info inferiors" " 1 .*<null>.*\\* 2 .*process.*" \
|
|
"info inferiors"
|
|
|
|
} elseif {$who == "child" && $detach == "off"} {
|
|
|
|
# Follow child / keep parent: two inferiors under debug, the
|
|
# child is the current inferior.
|
|
gdb_test "info inferiors" " 1 .*process.*\\* 2 .*process.*" \
|
|
"info inferiors"
|
|
|
|
gdb_test "inferior 1" "Switching to inferior 1 .*"
|
|
set resume_unfollowed 1
|
|
}
|
|
|
|
if {$resume_unfollowed == 1} {
|
|
if {$cmd == "next 2"} {
|
|
|
|
gdb_continue_to_end "continue unfollowed inferior to end"
|
|
|
|
} elseif {$cmd == "continue"} {
|
|
|
|
gdb_continue_to_breakpoint \
|
|
"continue unfollowed inferior to bp" \
|
|
".* set breakpoint here.*"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
proc catch_fork_child_follow {} {
|
|
global gdb_prompt
|
|
global srcfile
|
|
|
|
set bp_after_fork [gdb_get_line_number "set breakpoint here"]
|
|
|
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" \
|
|
"explicit child follow, set catch fork"
|
|
|
|
# Verify that the catchpoint is mentioned in an "info breakpoints",
|
|
# and further that the catchpoint mentions no process id.
|
|
#
|
|
set test_name "info shows catchpoint without pid"
|
|
gdb_test_multiple "info breakpoints" "$test_name" {
|
|
-re ".*catchpoint.*keep y.*fork\[\r\n\]+$gdb_prompt $" {
|
|
pass "$test_name"
|
|
}
|
|
}
|
|
|
|
gdb_test "continue" \
|
|
"Catchpoint \[0-9\]* \\(forked process \[0-9\]*\\),.*" \
|
|
"explicit child follow, catch fork"
|
|
|
|
# Verify that the catchpoint is mentioned in an "info breakpoints",
|
|
# and further that the catchpoint managed to capture a process id.
|
|
#
|
|
set test_name "info shows catchpoint without pid"
|
|
gdb_test_multiple "info breakpoints" "$test_name" {
|
|
-re ".*catchpoint.*keep y.*fork, process.*$gdb_prompt $" {
|
|
pass "$test_name"
|
|
}
|
|
}
|
|
|
|
gdb_test_no_output "set follow-fork child"
|
|
|
|
gdb_test "tbreak ${srcfile}:$bp_after_fork" \
|
|
"Temporary breakpoint.*, line $bp_after_fork.*" \
|
|
"set follow-fork child, tbreak"
|
|
|
|
set expected_re "Attaching after.* fork to.*Detaching after fork from"
|
|
append expected_re ".* at .*$bp_after_fork.*"
|
|
gdb_test "continue" $expected_re "set follow-fork child, hit tbreak"
|
|
|
|
# The parent has been detached; allow time for any output it might
|
|
# generate to arrive, so that output doesn't get confused with
|
|
# any expected debugger output from a subsequent testpoint.
|
|
#
|
|
exec sleep 1
|
|
|
|
gdb_test "delete breakpoints" \
|
|
"" \
|
|
"set follow-fork child, cleanup" \
|
|
"Delete all breakpoints. \\(y or n\\) $" \
|
|
"y"
|
|
}
|
|
|
|
proc catch_fork_unpatch_child {} {
|
|
global gdb_prompt
|
|
global srcfile
|
|
|
|
set bp_exit [gdb_get_line_number "at exit"]
|
|
|
|
gdb_test "break callee" "file .*$srcfile, line .*" \
|
|
"unpatch child, break at callee"
|
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" \
|
|
"unpatch child, set catch fork"
|
|
|
|
gdb_test "continue" \
|
|
"Catchpoint \[0-9\]* \\(forked process \[0-9\]*\\),.*" \
|
|
"unpatch child, catch fork"
|
|
|
|
# Delete all breakpoints and catchpoints.
|
|
delete_breakpoints
|
|
|
|
# Force $srcfile as the current GDB source can be in glibc sourcetree.
|
|
gdb_test "break $srcfile:$bp_exit" \
|
|
"Breakpoint .*file .*$srcfile, line .*" \
|
|
"unpatch child, breakpoint at exit call"
|
|
|
|
gdb_test_no_output "set follow-fork child" \
|
|
"unpatch child, set follow-fork child"
|
|
|
|
set test "unpatch child, unpatched parent breakpoints from child"
|
|
gdb_test_multiple "continue" $test {
|
|
-re "at exit.*$gdb_prompt $" {
|
|
pass "$test"
|
|
}
|
|
-re "SIGTRAP.*$gdb_prompt $" {
|
|
fail "$test"
|
|
|
|
# Explicitly kill this child, so we can continue gracefully
|
|
# with further testing...
|
|
send_gdb "kill\n"
|
|
gdb_expect {
|
|
-re ".*Kill the program being debugged.*y or n. $" {
|
|
send_gdb "y\n"
|
|
gdb_expect -re "$gdb_prompt $" {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
proc tcatch_fork_parent_follow {} {
|
|
global gdb_prompt
|
|
global srcfile
|
|
|
|
set bp_after_fork [gdb_get_line_number "set breakpoint here"]
|
|
|
|
gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" \
|
|
"explicit parent follow, set tcatch fork"
|
|
|
|
# ??rehrauer: I don't yet know how to get the id of the tcatch
|
|
# via this script, so that I can add a -do list to it. For now,
|
|
# do the follow stuff after the catch happens.
|
|
|
|
gdb_test "continue" \
|
|
"Catchpoint \[0-9\]* \\(forked process \[0-9\]*\\),.*" \
|
|
"explicit parent follow, tcatch fork"
|
|
|
|
gdb_test_no_output "set follow-fork parent"
|
|
|
|
gdb_test "tbreak ${srcfile}:$bp_after_fork" \
|
|
"Temporary breakpoint.*, line $bp_after_fork.*" \
|
|
"set follow-fork parent, tbreak"
|
|
|
|
gdb_test "continue" \
|
|
"Detaching after fork from.* at .*$bp_after_fork.*" \
|
|
"set follow-fork parent, hit tbreak"
|
|
|
|
# The child has been detached; allow time for any output it might
|
|
# generate to arrive, so that output doesn't get confused with
|
|
# any expected debugger output from a subsequent testpoint.
|
|
#
|
|
exec sleep 1
|
|
|
|
gdb_test "delete breakpoints" \
|
|
"" \
|
|
"set follow-fork parent, cleanup" \
|
|
"Delete all breakpoints. \\(y or n\\) $" \
|
|
"y"
|
|
}
|
|
|
|
proc do_fork_tests {} {
|
|
global gdb_prompt
|
|
global testfile
|
|
|
|
# Verify that help is available for "set follow-fork-mode".
|
|
#
|
|
gdb_test "help set follow-fork-mode" \
|
|
"Set debugger response to a program call of fork or vfork..*
|
|
A fork or vfork creates a new process. follow-fork-mode can be:.*
|
|
.*parent - the original process is debugged after a fork.*
|
|
.*child - the new process is debugged after a fork.*
|
|
The unfollowed process will continue to run..*
|
|
By default, the debugger will follow the parent process..*" \
|
|
"help set follow-fork"
|
|
|
|
# Verify that we can set follow-fork-mode, using an abbreviation
|
|
# for both the flag and its value.
|
|
#
|
|
gdb_test_no_output "set follow-fork ch"
|
|
|
|
gdb_test "show follow-fork" \
|
|
"Debugger response to a program call of fork or vfork is \"child\".*" \
|
|
"set follow-fork, using abbreviations"
|
|
|
|
# Verify that we cannot set follow-fork-mode to nonsense.
|
|
#
|
|
gdb_test "set follow-fork chork" "Undefined item: \"chork\".*" \
|
|
"set follow-fork to nonsense is prohibited"
|
|
|
|
gdb_test_no_output "set follow-fork parent" "reset parent"
|
|
|
|
# Check that fork catchpoints are supported, as an indicator for whether
|
|
# fork-following is supported.
|
|
if [runto_main] then { check_fork_catchpoints }
|
|
|
|
# Test the basic follow-fork functionality using all combinations of
|
|
# values for follow-fork-mode and detach-on-fork, using either a
|
|
# breakpoint or single-step to execute past the fork.
|
|
#
|
|
# The first loop should be sufficient to test the defaults. There
|
|
# is no need to test using the defaults in other permutations (e.g.
|
|
# "default" "on", "parent" "default", etc.).
|
|
foreach cmd {"next 2" "continue"} {
|
|
test_follow_fork "default" "default" $cmd
|
|
}
|
|
|
|
# Now test all explicit permutations.
|
|
foreach who {"parent" "child"} {
|
|
foreach detach {"on" "off"} {
|
|
foreach cmd {"next 2" "continue"} {
|
|
test_follow_fork $who $detach $cmd
|
|
}
|
|
}
|
|
}
|
|
|
|
# Catchpoint tests.
|
|
|
|
# Restart to eliminate any effects of the follow-fork tests.
|
|
clean_restart $testfile
|
|
gdb_test_no_output "set verbose"
|
|
|
|
# Test the ability to catch a fork, specify that the child be
|
|
# followed, and continue. Make the catchpoint permanent.
|
|
#
|
|
if [runto_main] then { catch_fork_child_follow }
|
|
|
|
# Test that parent breakpoints are successfully detached from the
|
|
# child at fork time, even if the user removes them from the
|
|
# breakpoints list after stopping at a fork catchpoint.
|
|
if [runto_main] then { catch_fork_unpatch_child }
|
|
|
|
# Test the ability to catch a fork, specify via a -do clause that
|
|
# the parent be followed, and continue. Make the catchpoint temporary.
|
|
#
|
|
if [runto_main] then { tcatch_fork_parent_follow }
|
|
}
|
|
|
|
# The "Detaching..." and "Attaching..." messages may be hidden by
|
|
# default.
|
|
gdb_test_no_output "set verbose"
|
|
|
|
# This is a test of gdb's ability to follow the parent, child or both
|
|
# parent and child of a Unix fork() system call.
|
|
#
|
|
do_fork_tests
|
|
|
|
return 0
|