| # This testcase is part of GDB, the GNU debugger. |
| |
| # Copyright 2017-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/>. |
| |
| # This testcase exercises GDB's terminal ownership management |
| # (terminal_ours/terminal_inferior, etc.) when debugging multiple |
| # inferiors. It tests debugging multiple inferiors started with |
| # different combinations of 'run'/'run+tty'/'attach', and ensures that |
| # the process that is sharing GDB's terminal/session is properly made |
| # the GDB terminal's foregound process group (i.e., "given the |
| # terminal"). |
| |
| standard_testfile |
| |
| if ![can_spawn_for_attach] { |
| return 0 |
| } |
| |
| if [build_executable "failed to prepare" $testfile $srcfile {debug}] { |
| return -1 |
| } |
| |
| # Start the programs running and then wait for a bit, to be sure that |
| # they can be attached to. We start these processes upfront in order |
| # to reuse them for the different iterations. This makes the testcase |
| # faster, compared to calling spawn_wait_for_attach for each iteration |
| # in the testing matrix. |
| |
| set spawn_id_list [spawn_wait_for_attach [list $binfile $binfile]] |
| set attach_spawn_id1 [lindex $spawn_id_list 0] |
| set attach_spawn_id2 [lindex $spawn_id_list 1] |
| set attach_pid1 [spawn_id_get_pid $attach_spawn_id1] |
| set attach_pid2 [spawn_id_get_pid $attach_spawn_id2] |
| |
| # Create inferior WHICH_INF. INF_HOW is how to create it. This can |
| # be one of: |
| # |
| # - "run" - for "run" command. |
| # - "tty" - for "run" + "tty TTY". |
| # - "attach" - for attaching to an existing process. |
| proc create_inferior {which_inf inf_how} { |
| global binfile |
| |
| # Run to main and delete breakpoints. |
| proc my_runto_main {} { |
| if ![runto_main] { |
| return 0 |
| } else { |
| # Delete breakpoints otherwise GDB would try to step over |
| # the breakpoint at 'main' without resuming the other |
| # inferior, possibly masking the issue we're trying to |
| # test. |
| delete_breakpoints |
| return 1 |
| } |
| } |
| |
| if {$inf_how == "run"} { |
| if [my_runto_main] { |
| global inferior_spawn_id |
| return $inferior_spawn_id |
| } |
| } elseif {$inf_how == "tty"} { |
| # Create the new PTY for the inferior. |
| spawn -pty |
| set inf_spawn_id $spawn_id |
| set inf_tty_name $spawn_out(slave,name) |
| gdb_test_no_output "tty $inf_tty_name" "tty TTY" |
| |
| if [my_runto_main] { |
| return $inf_spawn_id |
| } |
| } elseif {$inf_how == "attach"} { |
| |
| # Reuse the inferiors spawned at the start of the testcase (to |
| # avoid having to wait the delay imposed by |
| # spawn_wait_for_attach). |
| global attach_spawn_id$which_inf |
| set test_spawn_id [set attach_spawn_id$which_inf] |
| set testpid [spawn_id_get_pid $test_spawn_id] |
| if {[gdb_test "attach $testpid" \ |
| "Attaching to program: .*, process $testpid.*(in|at).*" \ |
| "attach"] == 0} { |
| |
| # The program is now stopped, but if testing against |
| # gdbserver, then the inferior's output emmitted before it |
| # stopped isn't flushed unless we explicitly do so, |
| # because it is on a different spawn_id. Do it now, to |
| # avoid confusing tests further below. |
| gdb_test_multiple "" "flush inferior output" { |
| -timeout 1 |
| -i $test_spawn_id -re "pid=" { |
| exp_continue |
| } |
| timeout { |
| pass $gdb_test_name |
| } |
| } |
| |
| return $test_spawn_id |
| } |
| } else { |
| error "unhandled inf_how: $inf_how" |
| } |
| |
| return "" |
| } |
| |
| # Print debug output. |
| |
| proc send_debug {str} { |
| # Uncomment for debugging. |
| #send_user $str |
| } |
| |
| # The core of the testcase. See intro. Creates two inferiors that |
| # both loop changing their terminal's settings and printing output, |
| # and checks whether they receive SIGTTOU. INF1_HOW and INF2_HOW |
| # indicate how inferiors 1 and 2 should be created, respectively. See |
| # 'create_inferior' above for what arguments these parameters accept. |
| |
| proc coretest {inf1_how inf2_how} { |
| global binfile |
| global inferior_spawn_id |
| global gdb_spawn_id |
| global decimal |
| |
| clean_restart $binfile |
| |
| with_test_prefix "inf1" { |
| set inf1_spawn_id [create_inferior 1 $inf1_how] |
| } |
| |
| set num 2 |
| gdb_test "add-inferior" "Added inferior $num.*" \ |
| "add empty inferior $num" |
| gdb_test "inferior $num" "Switching to inferior $num.*" \ |
| "switch to inferior $num" |
| gdb_file_cmd ${binfile} |
| |
| with_test_prefix "inf2" { |
| set inf2_spawn_id [create_inferior 2 $inf2_how] |
| } |
| |
| gdb_test_no_output "set schedule-multiple on" |
| |
| # "run" makes each inferior be a process group leader. When we |
| # run both inferiors in GDB's terminal/session, only one can end |
| # up as the terminal's foreground process group, so it's expected |
| # that the other receives a SIGTTOU. |
| set expect_ttou [expr {$inf1_how == "run" && $inf2_how == "run"}] |
| |
| global gdb_prompt |
| |
| set any "\[^\r\n\]*" |
| |
| set pid1 -1 |
| set pid2 -1 |
| |
| set test "info inferiors" |
| gdb_test_multiple $test $test { |
| -re "1${any}process ($decimal)${any}\r\n" { |
| set pid1 $expect_out(1,string) |
| send_debug "pid1=$pid1\n" |
| exp_continue |
| } |
| -re "2${any}process ($decimal)${any}\r\n" { |
| set pid2 $expect_out(1,string) |
| send_debug "pid2=$pid2\n" |
| exp_continue |
| } |
| -re "$gdb_prompt $" { |
| pass $test |
| } |
| } |
| |
| # Helper for the gdb_test_multiple call below. Issues a PASS if |
| # we've seen each inferior print at least 3 times, otherwise |
| # continues waiting for inferior output. |
| proc pass_or_exp_continue {} { |
| uplevel 1 { |
| if {$count1 >= 3 && $count2 >= 3} { |
| if $expect_ttou { |
| fail "$gdb_test_name (expected SIGTTOU)" |
| } else { |
| pass $gdb_test_name |
| } |
| } else { |
| exp_continue |
| } |
| } |
| } |
| |
| set infs_spawn_ids [list $inf1_spawn_id $inf2_spawn_id] |
| send_debug "infs_spawn_ids=$infs_spawn_ids\n" |
| |
| set count1 0 |
| set count2 0 |
| |
| # We're going to interrupt with Ctrl-C. For this to work we must |
| # be sure to consume the "Continuing." message first, or GDB may |
| # still own the terminal. Also, note that in the attach case, we |
| # flushed inferior output right after attaching, so that we're |
| # sure that the "pid=" lines we see are emitted by the inferior |
| # after it is continued, instead of having been emitted before it |
| # was attached to. |
| gdb_test_multiple "continue" "continue, hand over terminal" { |
| -re "Continuing" { |
| pass $gdb_test_name |
| } |
| } |
| |
| gdb_test_multiple "" "continue" { |
| -i $infs_spawn_ids -re "pid=$pid1, count=" { |
| incr count1 |
| pass_or_exp_continue |
| } |
| -i $infs_spawn_ids -re "pid=$pid2, count=" { |
| incr count2 |
| pass_or_exp_continue |
| } |
| -i $gdb_spawn_id -re "received signal SIGTTOU.*$gdb_prompt " { |
| if $expect_ttou { |
| pass "$gdb_test_name (expected SIGTTOU)" |
| } else { |
| fail "$gdb_test_name (SIGTTOU)" |
| } |
| } |
| } |
| |
| send_gdb "\003" |
| if {$expect_ttou} { |
| gdb_test "" "Quit" "stop with control-c (Quit)" |
| } else { |
| gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)" |
| } |
| |
| # Useful for debugging in case the Ctrl-C above fails. |
| with_test_prefix "final" { |
| gdb_test "info inferiors" |
| gdb_test "info threads" |
| } |
| } |
| |
| set how_modes {"run" "attach" "tty"} |
| |
| foreach_with_prefix inf1_how $how_modes { |
| foreach_with_prefix inf2_how $how_modes { |
| if {($inf1_how == "tty" || $inf2_how == "tty") |
| && [target_info gdb_protocol] == "extended-remote"} { |
| # No way to specify the inferior's tty in the remote |
| # protocol. |
| unsupported "no support for \"tty\" in the RSP" |
| continue |
| } |
| |
| coretest $inf1_how $inf2_how |
| } |
| } |
| |
| kill_wait_spawned_process $attach_spawn_id1 |
| kill_wait_spawned_process $attach_spawn_id2 |