1 # Copyright 2016-2025 Free Software Foundation, Inc. 2 3 # This program is free software; you can redistribute it and/or modify 4 # it under the terms of the GNU General Public License as published by 5 # the Free Software Foundation; either version 3 of the License, or 6 # (at your option) any later version. 7 # 8 # This program is distributed in the hope that it will be useful, 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 # GNU General Public License for more details. 12 # 13 # You should have received a copy of the GNU General Public License 14 # along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 # Test that -exec-run works as expected. Exercises various testing 17 # axes: 18 # 19 # - MI running on main UI vs separate UI. 20 # 21 # - inferior tty set to main tty vs separate tty. 22 # 23 # - forking the child failing and sending output to the right inferior 24 # terminal, vs the child not failing to start. 25 26 load_lib mi-support.exp 27 set MIFLAGS "-i=mi" 28 29 # The purpose of this testcase is to test the -exec-run command. If we 30 # cannot use it, then there is no point in running this testcase. 31 require !use_gdb_stub 32 33 set have_startup_shell [have_startup_shell] 34 35 standard_testfile mi-start.c 36 37 if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { 38 untested "could not build mi-exec-run" 39 return -1 40 } 41 42 # The test proper. INFTTY_MODE determines whether "set inferior-tty" 43 # is in effect. MI_MODE determines whether MI is run on the main UI, 44 # or as a separate UI. FORCE_FAIL is true when we want -exec-run to 45 # fail and cause inferior output be sent to the inferior tty. 46 47 proc test {inftty_mode mi_mode force_fail} { 48 global srcdir subdir binfile srcfile 49 global gdb_spawn_id gdb_main_spawn_id mi_spawn_id inferior_spawn_id 50 global decimal 51 52 mi_gdb_exit 53 54 set start_ops {} 55 if {$inftty_mode == "separate"} { 56 lappend start_ops "separate-inferior-tty" 57 } 58 if {$mi_mode == "separate"} { 59 lappend start_ops "separate-mi-tty" 60 } 61 62 if [mi_gdb_start $start_ops] { 63 return 64 } 65 66 if {$force_fail} { 67 # Disable the shell so that it's the first exec that fails, 68 # instead of the shell starting and then failing with some 69 # unspecified output. 70 mi_gdb_test "-gdb-set startup-with-shell off" ".*" 71 set bin $binfile.nox 72 } else { 73 set bin $binfile 74 } 75 76 mi_delete_breakpoints 77 mi_gdb_reinitialize_dir $srcdir/$subdir 78 mi_gdb_load ${bin} 79 80 # Useful for debugging: 81 verbose -log "Channels:" 82 verbose -log " inferior_spawn_id=$inferior_spawn_id" 83 verbose -log " gdb_spawn_id=$gdb_spawn_id" 84 verbose -log " gdb_main_spawn_id=$gdb_main_spawn_id" 85 verbose -log " mi_spawn_id=$mi_spawn_id" 86 87 if {$force_fail} { 88 set saw_perm_error 0 89 set saw_mi_error 0 90 set already_failed 0 91 set test "run failure detected" 92 send_gdb "-exec-run --start\n" 93 94 # Redirect through SPAWN_LIST global. If the 95 # inferior_spawn_id is not the same as gdb_spawn_id, e.g. when 96 # testing with gdbserver, the gdbserver can exit after 97 # emitting it's error message. 98 # 99 # If inferior_spawn_id exits then we may see the eof from that 100 # spawn-id before we see the pattern from the gdb_spawn_id, 101 # which will kick us out of the gdb_expect, and cause us to 102 # fail the test. 103 # 104 # Instead we clean SPAWN_LIST once we've seen the expected 105 # pattern from that spawn-id, and after that we no longer care 106 # when gdbserver exits. 107 global spawn_list 108 set spawn_list "$inferior_spawn_id" 109 110 gdb_expect { 111 -i spawn_list 112 -re ".*Cannot exec.*Permission denied" { 113 set saw_perm_error 1 114 set spawn_list "" 115 verbose -log "saw perm error" 116 if {!$saw_mi_error} { 117 exp_continue 118 } 119 } 120 -i "$gdb_spawn_id" 121 -re "\\^error,msg=\"(During startup program exited with code 127|Running .* on the remote target failed)" { 122 set saw_mi_error 1 123 verbose -log "saw mi error" 124 if {!$saw_perm_error} { 125 exp_continue 126 } 127 } 128 timeout { 129 set already_failed 1 130 fail "$test (timeout)" 131 } 132 -i "$gdb_main_spawn_id" 133 eof { 134 set already_failed 1 135 fail "$test (eof)" 136 } 137 } 138 139 if {$saw_perm_error && $saw_mi_error} { 140 pass $test 141 } elseif {!$already_failed} { 142 verbose -log "saw_perm_error=$saw_perm_error; saw_mi_error=$saw_mi_error" 143 fail $test 144 } 145 } else { 146 mi_run_cmd "--start" 147 mi_expect_stop "breakpoint-hit" "main" "" ".*$srcfile" "$decimal" \ 148 { "" "disp=\"del\"" } "breakpoint hit reported on mi" 149 150 if {$mi_mode == "separate"} { 151 # Check that the breakpoint hit is reported on the main 152 # UI/CLI. Note no prompt is expected. 153 switch_gdb_spawn_id $gdb_main_spawn_id 154 155 set test "breakpoint hit reported on console" 156 gdb_test_multiple "" $test { 157 -re "Temporary breakpoint .*, main \\(\\) at .*$srcfile:$decimal.*return 0;" { 158 pass $test 159 } 160 } 161 162 # Switch back to the MI UI. 163 global mi_spawn_id 164 switch_gdb_spawn_id $mi_spawn_id 165 } 166 } 167 } 168 169 # Create a not-executable copy of the program, in order to exercise 170 # vfork->exec failing. 171 gdb_remote_download host $binfile $binfile.nox 172 remote_exec target "chmod \"a-x\" $binfile.nox" 173 174 foreach_with_prefix inferior-tty {"main" "separate"} { 175 foreach_with_prefix mi {"main" "separate"} { 176 foreach_with_prefix force-fail {0 1} { 177 if { ${force-fail} && $have_startup_shell == -1 } { 178 continue 179 } 180 test ${inferior-tty} ${mi} ${force-fail} 181 } 182 } 183 } 184 185 mi_gdb_exit 186