1# This testcase is part of GDB, the GNU debugger.
2
3# Copyright 2017-2020 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
26standard_testfile
27
28if ![can_spawn_for_attach] {
29    return 0
30}
31
32if [build_executable "failed to prepare" $testfile $srcfile {debug}] {
33    return -1
34}
35
36# Start the programs running and then wait for a bit, to be sure that
37# they can be attached to.  We start these processes upfront in order
38# to reuse them for the different iterations.  This makes the testcase
39# faster, compared to calling spawn_wait_for_attach for each iteration
40# in the testing matrix.
41
42set spawn_id_list [spawn_wait_for_attach [list $binfile $binfile]]
43set attach_spawn_id1 [lindex $spawn_id_list 0]
44set attach_spawn_id2 [lindex $spawn_id_list 1]
45set attach_pid1 [spawn_id_get_pid $attach_spawn_id1]
46set attach_pid2 [spawn_id_get_pid $attach_spawn_id2]
47
48# Create inferior WHICH_INF.  INF_HOW is how to create it.  This can
49# be one of:
50#
51#  - "run" - for "run" command.
52#  - "tty" - for "run" + "tty TTY".
53#  - "attach" - for attaching to an existing process.
54proc create_inferior {which_inf inf_how} {
55    global binfile
56
57    # Run to main and delete breakpoints.
58    proc my_runto_main {} {
59	if ![runto_main] {
60	    fail "run to main"
61	    return 0
62	} else {
63	    # Delete breakpoints otherwise GDB would try to step over
64	    # the breakpoint at 'main' without resuming the other
65	    # inferior, possibly masking the issue we're trying to
66	    # test.
67	    delete_breakpoints
68	    return 1
69	}
70    }
71
72    if {$inf_how == "run"} {
73	if [my_runto_main] {
74	    global inferior_spawn_id
75	    return $inferior_spawn_id
76	}
77    } elseif {$inf_how == "tty"} {
78	# Create the new PTY for the inferior.
79	spawn -pty
80	set inf_spawn_id $spawn_id
81	set inf_tty_name $spawn_out(slave,name)
82	gdb_test_no_output "tty $inf_tty_name" "tty TTY"
83
84	if [my_runto_main] {
85	    return $inf_spawn_id
86	}
87    } elseif {$inf_how == "attach"} {
88
89	# Reuse the inferiors spawned at the start of the testcase (to
90	# avoid having to wait the delay imposed by
91	# spawn_wait_for_attach).
92	global attach_spawn_id$which_inf
93	set test_spawn_id [set attach_spawn_id$which_inf]
94	set testpid [spawn_id_get_pid $test_spawn_id]
95	if {[gdb_test "attach $testpid" \
96		 "Attaching to program: .*, process $testpid.*(in|at).*" \
97		 "attach"] == 0} {
98	    return $test_spawn_id
99	}
100    } else {
101	error "unhandled inf_how: $inf_how"
102    }
103
104    return ""
105}
106
107# Print debug output.
108
109proc send_debug {str} {
110    # Uncomment for debugging.
111    #send_user $str
112}
113
114# The core of the testcase.  See intro.  Creates two inferiors that
115# both loop changing their terminal's settings and printing output,
116# and checks whether they receive SIGTTOU.  INF1_HOW and INF2_HOW
117# indicate how inferiors 1 and 2 should be created, respectively.  See
118# 'create_inferior' above for what arguments these parameters accept.
119
120proc coretest {inf1_how inf2_how} {
121    global binfile
122    global inferior_spawn_id
123    global gdb_spawn_id
124    global decimal
125
126    clean_restart $binfile
127
128    with_test_prefix "inf1" {
129	set inf1_spawn_id [create_inferior 1 $inf1_how]
130    }
131
132    set num 2
133    gdb_test "add-inferior" "Added inferior $num.*" \
134	"add empty inferior $num"
135    gdb_test "inferior $num" "Switching to inferior $num.*" \
136	"switch to inferior $num"
137    gdb_file_cmd ${binfile}
138
139    with_test_prefix "inf2" {
140	set inf2_spawn_id [create_inferior 2 $inf2_how]
141    }
142
143    gdb_test_no_output "set schedule-multiple on"
144
145    # "run" makes each inferior be a process group leader.  When we
146    # run both inferiors in GDB's terminal/session, only one can end
147    # up as the terminal's foreground process group, so it's expected
148    # that the other receives a SIGTTOU.
149    set expect_ttou [expr {$inf1_how == "run" && $inf2_how == "run"}]
150
151    global gdb_prompt
152
153    set any "\[^\r\n\]*"
154
155    set pid1 -1
156    set pid2 -1
157
158    set test "info inferiors"
159    gdb_test_multiple $test $test {
160	-re "1${any}process ($decimal)${any}\r\n" {
161	    set pid1 $expect_out(1,string)
162	    send_debug "pid1=$pid1\n"
163	    exp_continue
164	}
165	-re "2${any}process ($decimal)${any}\r\n" {
166	    set pid2 $expect_out(1,string)
167	    send_debug "pid2=$pid2\n"
168	    exp_continue
169	}
170	-re "$gdb_prompt $" {
171	    pass $test
172	}
173    }
174
175    # Helper for the gdb_test_multiple call below.  Issues a PASS if
176    # we've seen each inferior print at least 3 times, otherwise
177    # continues waiting for inferior output.
178    proc pass_or_exp_continue {} {
179	uplevel 1 {
180	    if {$count1 >= 3 && $count2 >= 3} {
181		if $expect_ttou {
182		    fail "$test (expected SIGTTOU)"
183		} else {
184		    pass $test
185		}
186	    } else {
187		exp_continue
188	    }
189	}
190    }
191
192    set infs_spawn_ids [list $inf1_spawn_id $inf2_spawn_id]
193    send_debug "infs_spawn_ids=$infs_spawn_ids\n"
194
195    set count1 0
196    set count2 0
197
198    set test "continue"
199    gdb_test_multiple $test $test {
200	-i $infs_spawn_ids -re "pid=$pid1, count=" {
201	    incr count1
202	    pass_or_exp_continue
203	}
204	-i $infs_spawn_ids -re "pid=$pid2, count=" {
205	    incr count2
206	    pass_or_exp_continue
207	}
208	-i $gdb_spawn_id -re "received signal SIGTTOU.*$gdb_prompt " {
209	    if $expect_ttou {
210		pass "$test (expected SIGTTOU)"
211	    } else {
212		fail "$test (SIGTTOU)"
213	    }
214	}
215    }
216
217    send_gdb "\003"
218    if {$expect_ttou} {
219	gdb_test "" "Quit" "stop with control-c (Quit)"
220    } else {
221	gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)"
222    }
223
224    # Useful for debugging in case the Ctrl-C above fails.
225    with_test_prefix "final" {
226	gdb_test "info inferiors"
227	gdb_test "info threads"
228    }
229}
230
231set how_modes {"run" "attach" "tty"}
232
233foreach_with_prefix inf1_how $how_modes {
234    foreach_with_prefix inf2_how $how_modes {
235	if {($inf1_how == "tty" || $inf2_how == "tty")
236	    && [target_info gdb_protocol] == "extended-remote"} {
237	    # No way to specify the inferior's tty in the remote
238	    # protocol.
239	    unsupported "no support for \"tty\" in the RSP"
240	    continue
241	}
242
243	coretest $inf1_how $inf2_how
244    }
245}
246
247kill_wait_spawned_process $attach_spawn_id1
248kill_wait_spawned_process $attach_spawn_id2
249