Home | History | Annotate | Line # | Download | only in gdb.multi
      1 # This testcase is part of GDB, the GNU debugger.
      2 
      3 # Copyright 2017-2024 Free Software Foundation, Inc.
      4 
      5 # This program is free software; you can redistribute it and/or modify
      6 # it under the terms of the GNU General Public License as published by
      7 # the Free Software Foundation; either version 3 of the License, or
      8 # (at your option) any later version.
      9 #
     10 # This program is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU General Public License
     16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 # This testcase exercises GDB's terminal ownership management
     19 # (terminal_ours/terminal_inferior, etc.) when debugging multiple
     20 # inferiors.  It tests debugging multiple inferiors started with
     21 # different combinations of 'run'/'run+tty'/'attach', and ensures that
     22 # the process that is sharing GDB's terminal/session is properly made
     23 # the GDB terminal's foregound process group (i.e., "given the
     24 # terminal").
     25 
     26 standard_testfile
     27 
     28 require can_spawn_for_attach
     29 
     30 if [build_executable "failed to prepare" $testfile $srcfile {debug}] {
     31     return -1
     32 }
     33 
     34 # Start the programs running and then wait for a bit, to be sure that
     35 # they can be attached to.  We start these processes upfront in order
     36 # to reuse them for the different iterations.  This makes the testcase
     37 # faster, compared to calling spawn_wait_for_attach for each iteration
     38 # in the testing matrix.
     39 
     40 set spawn_id_list [spawn_wait_for_attach [list $binfile $binfile]]
     41 set attach_spawn_id1 [lindex $spawn_id_list 0]
     42 set attach_spawn_id2 [lindex $spawn_id_list 1]
     43 set attach_pid1 [spawn_id_get_pid $attach_spawn_id1]
     44 set attach_pid2 [spawn_id_get_pid $attach_spawn_id2]
     45 
     46 # Create inferior WHICH_INF.  INF_HOW is how to create it.  This can
     47 # be one of:
     48 #
     49 #  - "run" - for "run" command.
     50 #  - "tty" - for "run" + "tty TTY".
     51 #  - "attach" - for attaching to an existing process.
     52 proc create_inferior {which_inf inf_how} {
     53     global binfile
     54 
     55     # Run to main and delete breakpoints.
     56     proc my_runto_main {} {
     57 	if ![runto_main] {
     58 	    return 0
     59 	} else {
     60 	    # Delete breakpoints otherwise GDB would try to step over
     61 	    # the breakpoint at 'main' without resuming the other
     62 	    # inferior, possibly masking the issue we're trying to
     63 	    # test.
     64 	    delete_breakpoints
     65 	    return 1
     66 	}
     67     }
     68 
     69     if {$inf_how == "run"} {
     70 	if [my_runto_main] {
     71 	    global inferior_spawn_id
     72 	    return $inferior_spawn_id
     73 	}
     74     } elseif {$inf_how == "tty"} {
     75 	# Create the new PTY for the inferior.
     76 	spawn -pty
     77 	set inf_spawn_id $spawn_id
     78 	set inf_tty_name $spawn_out(slave,name)
     79 	gdb_test_no_output "tty $inf_tty_name" "tty TTY"
     80 
     81 	if [my_runto_main] {
     82 	    return $inf_spawn_id
     83 	}
     84     } elseif {$inf_how == "attach"} {
     85 
     86 	# Reuse the inferiors spawned at the start of the testcase (to
     87 	# avoid having to wait the delay imposed by
     88 	# spawn_wait_for_attach).
     89 	global attach_spawn_id$which_inf
     90 	set test_spawn_id [set attach_spawn_id$which_inf]
     91 	set testpid [spawn_id_get_pid $test_spawn_id]
     92 	if {[gdb_test "attach $testpid" \
     93 		 "Attaching to program: .*, process $testpid.*(in|at).*" \
     94 		 "attach"] == 0} {
     95 
     96 	    # The program is now stopped, but if testing against
     97 	    # gdbserver, then the inferior's output emmitted before it
     98 	    # stopped isn't flushed unless we explicitly do so,
     99 	    # because it is on a different spawn_id.  Do it now, to
    100 	    # avoid confusing tests further below.
    101 	    gdb_test_multiple "" "flush inferior output" {
    102 		-timeout 1
    103 		-i $test_spawn_id -re "pid=" {
    104 		    exp_continue
    105 		}
    106 		timeout {
    107 		    pass $gdb_test_name
    108 		}
    109 	    }
    110 
    111 	    return $test_spawn_id
    112 	}
    113     } else {
    114 	error "unhandled inf_how: $inf_how"
    115     }
    116 
    117     return ""
    118 }
    119 
    120 # Print debug output.
    121 
    122 proc send_debug {str} {
    123     # Uncomment for debugging.
    124     #send_user $str
    125 }
    126 
    127 # The core of the testcase.  See intro.  Creates two inferiors that
    128 # both loop changing their terminal's settings and printing output,
    129 # and checks whether they receive SIGTTOU.  INF1_HOW and INF2_HOW
    130 # indicate how inferiors 1 and 2 should be created, respectively.  See
    131 # 'create_inferior' above for what arguments these parameters accept.
    132 
    133 proc coretest {inf1_how inf2_how} {
    134     global binfile
    135     global inferior_spawn_id
    136     global gdb_spawn_id
    137     global decimal
    138 
    139     clean_restart $binfile
    140 
    141     with_test_prefix "inf1" {
    142 	set inf1_spawn_id [create_inferior 1 $inf1_how]
    143     }
    144 
    145     set num 2
    146     gdb_test "add-inferior" "Added inferior $num.*" \
    147 	"add empty inferior $num"
    148     gdb_test "inferior $num" "Switching to inferior $num.*" \
    149 	"switch to inferior $num"
    150     gdb_file_cmd ${binfile}
    151 
    152     with_test_prefix "inf2" {
    153 	set inf2_spawn_id [create_inferior 2 $inf2_how]
    154     }
    155 
    156     gdb_test_no_output "set schedule-multiple on"
    157 
    158     # "run" makes each inferior be a process group leader.  When we
    159     # run both inferiors in GDB's terminal/session, only one can end
    160     # up as the terminal's foreground process group, so it's expected
    161     # that the other receives a SIGTTOU.
    162     set expect_ttou [expr {$inf1_how == "run" && $inf2_how == "run"}]
    163 
    164     global gdb_prompt
    165 
    166     set any "\[^\r\n\]*"
    167 
    168     set pid1 -1
    169     set pid2 -1
    170 
    171     set test "info inferiors"
    172     gdb_test_multiple $test $test {
    173 	-re "1${any}process ($decimal)${any}\r\n" {
    174 	    set pid1 $expect_out(1,string)
    175 	    send_debug "pid1=$pid1\n"
    176 	    exp_continue
    177 	}
    178 	-re "2${any}process ($decimal)${any}\r\n" {
    179 	    set pid2 $expect_out(1,string)
    180 	    send_debug "pid2=$pid2\n"
    181 	    exp_continue
    182 	}
    183 	-re "$gdb_prompt $" {
    184 	    pass $test
    185 	}
    186     }
    187 
    188     # Helper for the gdb_test_multiple call below.  Issues a PASS if
    189     # we've seen each inferior print at least 3 times, otherwise
    190     # continues waiting for inferior output.
    191     proc pass_or_exp_continue {} {
    192 	uplevel 1 {
    193 	    if {$count1 >= 3 && $count2 >= 3} {
    194 		if $expect_ttou {
    195 		    fail "$gdb_test_name (expected SIGTTOU)"
    196 		} else {
    197 		    pass $gdb_test_name
    198 		}
    199 	    } else {
    200 		exp_continue
    201 	    }
    202 	}
    203     }
    204 
    205     set infs_spawn_ids [list $inf1_spawn_id $inf2_spawn_id]
    206     send_debug "infs_spawn_ids=$infs_spawn_ids\n"
    207 
    208     set count1 0
    209     set count2 0
    210 
    211     # We're going to interrupt with Ctrl-C.  For this to work we must
    212     # be sure to consume the "Continuing." message first, or GDB may
    213     # still own the terminal.  Also, note that in the attach case, we
    214     # flushed inferior output right after attaching, so that we're
    215     # sure that the "pid=" lines we see are emitted by the inferior
    216     # after it is continued, instead of having been emitted before it
    217     # was attached to.
    218     gdb_test_multiple "continue" "continue, hand over terminal" {
    219 	-re "Continuing" {
    220 	    pass $gdb_test_name
    221 	}
    222     }
    223 
    224     gdb_test_multiple "" "continue" {
    225 	-i $infs_spawn_ids -re "pid=$pid1, count=" {
    226 	    incr count1
    227 	    pass_or_exp_continue
    228 	}
    229 	-i $infs_spawn_ids -re "pid=$pid2, count=" {
    230 	    incr count2
    231 	    pass_or_exp_continue
    232 	}
    233 	-i $gdb_spawn_id -re "received signal SIGTTOU.*$gdb_prompt " {
    234 	    if $expect_ttou {
    235 		pass "$gdb_test_name (expected SIGTTOU)"
    236 	    } else {
    237 		fail "$gdb_test_name (SIGTTOU)"
    238 	    }
    239 	}
    240     }
    241 
    242     send_gdb "\003"
    243     if {$expect_ttou} {
    244 	gdb_test "" "Quit" "stop with control-c (Quit)"
    245     } else {
    246 	gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)"
    247     }
    248 
    249     # Useful for debugging in case the Ctrl-C above fails.
    250     with_test_prefix "final" {
    251 	gdb_test "info inferiors"
    252 	gdb_test "info threads"
    253     }
    254 }
    255 
    256 set how_modes {"run" "attach" "tty"}
    257 
    258 foreach_with_prefix inf1_how $how_modes {
    259     foreach_with_prefix inf2_how $how_modes {
    260 	if {($inf1_how == "tty" || $inf2_how == "tty")
    261 	    && [target_info gdb_protocol] == "extended-remote"} {
    262 	    # No way to specify the inferior's tty in the remote
    263 	    # protocol.
    264 	    unsupported "no support for \"tty\" in the RSP"
    265 	    continue
    266 	}
    267 
    268 	coretest $inf1_how $inf2_how
    269     }
    270 }
    271 
    272 kill_wait_spawned_process $attach_spawn_id1
    273 kill_wait_spawned_process $attach_spawn_id2
    274