1 1.1 christos # Copyright 2021-2024 Free Software Foundation, Inc. 2 1.1 christos 3 1.1 christos # This program is free software; you can redistribute it and/or modify 4 1.1 christos # it under the terms of the GNU General Public License as published by 5 1.1 christos # the Free Software Foundation; either version 3 of the License, or 6 1.1 christos # (at your option) any later version. 7 1.1 christos # 8 1.1 christos # This program is distributed in the hope that it will be useful, 9 1.1 christos # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 1.1 christos # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 1.1 christos # GNU General Public License for more details. 12 1.1 christos # 13 1.1 christos # You should have received a copy of the GNU General Public License 14 1.1 christos # along with this program. If not, see <http://www.gnu.org/licenses/>. 15 1.1 christos 16 1.1 christos # Test stepping over a breakpoint installed on an instruction that 17 1.1 christos # exits the thread. 18 1.1 christos 19 1.1 christos standard_testfile .c 20 1.1 christos 21 1.1 christos set syscalls_src $srcdir/lib/my-syscalls.S 22 1.1 christos 23 1.1 christos if { [build_executable "failed to prepare" $testfile \ 24 1.1 christos [list $srcfile $syscalls_src] {debug pthreads}] == -1 } { 25 1.1 christos return 26 1.1 christos } 27 1.1 christos 28 1.1 christos # Test stepping/continuing at an exit syscall instruction. 29 1.1 christos # 30 1.1 christos # Each argument is a different testing axis. 31 1.1 christos # 32 1.1 christos # STEP_OVER_MODE can be one of: 33 1.1 christos # 34 1.1 christos # - none: don't put a breakpoint on the exit syscall instruction. 35 1.1 christos # 36 1.1 christos # - inline: put a breakpoint on the exit syscall instruction, and 37 1.1 christos # use in-line stepping to step over it (disable 38 1.1 christos # displaced-stepping). 39 1.1 christos # 40 1.1 christos # - displaced: same, but use displaced stepping. 41 1.1 christos # 42 1.1 christos # SCHEDLOCK can be "on" or "off". 43 1.1 christos # 44 1.1 christos # CMD is the GDB command to run when at the exit syscall instruction. 45 1.1 christos # 46 1.1 christos # NS_STOP_ALL is only used if testing "set non-stop on", and indicates 47 1.1 christos # whether to have GDB explicitly stop all threads before continuing to 48 1.1 christos # thread exit. 49 1.1 christos # 50 1.1 christos proc test {step_over_mode non-stop target-non-stop schedlock cmd ns_stop_all} { 51 1.1 christos if {${non-stop} == "off" && $ns_stop_all} { 52 1.1 christos error "invalid arguments" 53 1.1 christos } 54 1.1 christos 55 1.1 christos save_vars ::GDBFLAGS { 56 1.1 christos append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" 57 1.1 christos append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" 58 1.1 christos clean_restart $::binfile 59 1.1 christos } 60 1.1 christos 61 1.1 christos if { $step_over_mode == "none" } { 62 1.1 christos # Nothing to do. 63 1.1 christos } elseif { $step_over_mode == "inline" } { 64 1.1 christos gdb_test_no_output "set displaced-stepping off" 65 1.1 christos } elseif { $step_over_mode == "displaced" } { 66 1.1 christos gdb_test_no_output "set displaced-stepping on" 67 1.1 christos } else { 68 1.1 christos error "Invalid step_over_mode value: $step_over_mode" 69 1.1 christos } 70 1.1 christos 71 1.1 christos if {$schedlock 72 1.1 christos || (${non-stop} == "on" && $ns_stop_all)} { 73 1.1 christos 74 1.1 christos gdb_test_no_output "set args 1" 75 1.1 christos 76 1.1 christos if { ![runto my_exit_syscall] } { 77 1.1 christos return 78 1.1 christos } 79 1.1 christos 80 1.1 christos if {${non-stop} == "on"} { 81 1.1 christos # The test only spawns one thread at a time, so this just 82 1.1 christos # stops the main thread. IOW, we only need to wait for 83 1.1 christos # one stop. 84 1.1 christos gdb_test_multiple "interrupt -a" "" { 85 1.1 christos -re "$::gdb_prompt " { 86 1.1 christos gdb_test_multiple "" $gdb_test_name { 87 1.1 christos -re "Thread 1 \[^\r\n\]*stopped." { 88 1.1 christos pass $gdb_test_name 89 1.1 christos } 90 1.1 christos } 91 1.1 christos } 92 1.1 christos } 93 1.1 christos 94 1.1 christos gdb_test "thread 2" "Switching to thread 2 .*" 95 1.1 christos } 96 1.1 christos 97 1.1 christos gdb_test_no_output "set scheduler-locking ${schedlock}" 98 1.1 christos 99 1.1 christos # If testing a step-over is requested, leave the breakpoint at 100 1.1 christos # the current instruction to force a step-over; otherwise, 101 1.1 christos # remove it. 102 1.1 christos if { $step_over_mode == "none" } { 103 1.1 christos delete_breakpoints 104 1.1 christos } 105 1.1 christos 106 1.1 christos if {$cmd == "continue"} { 107 1.1 christos gdb_test "continue" \ 108 1.1 christos "No unwaited-for children left." \ 109 1.1 christos "continue stops when thread exits" 110 1.1 christos } else { 111 1.1 christos gdb_test_multiple $cmd "command aborts when thread exits" { 112 1.1 christos -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " { 113 1.1 christos pass $gdb_test_name 114 1.1 christos } 115 1.1 christos } 116 1.1 christos } 117 1.1 christos } else { 118 1.1 christos # Schedlock is off here. 119 1.1 christos # 120 1.1 christos # With "continue" and no scheduler-locking, GDB doesn't stop 121 1.1 christos # with "Command aborted, thread exited." when the thread 122 1.1 christos # exits, it just lets the inferior continue running freely. 123 1.1 christos # So we test that we can move past the thread exit, and that 124 1.1 christos # other threads can be freely scheduled. We do that by 125 1.1 christos # spawning another thread as soon as the first exit. We test 126 1.1 christos # that a number of times. This should also exercise GDB's 127 1.1 christos # handling of inline or displaced step-overs, that GDB handles 128 1.1 christos # the related resource accounting correctly when the stepping 129 1.1 christos # thread exits, etc. 130 1.1 christos # 131 1.1 christos # With "continue" and $step_over_mode == "none" however, after 132 1.1 christos # the first my_exit_syscall breakpoint hit, we will remove the 133 1.1 christos # breakpoint, so no other thread would ever hit it again. So 134 1.1 christos # might as well just test one thread. 135 1.1 christos # 136 1.1 christos # With step/next, GDB aborts the execution command with 137 1.1 christos # "Command aborted, thread exited." when the stepping thread 138 1.1 christos # exits. If we let the main spawn another thread as soon as 139 1.1 christos # the first exits, it would be possible for that new thread to 140 1.1 christos # hit the exit syscall insn breakpoint quickly enough that it 141 1.1 christos # would be reported to be user before the first thread exit 142 1.1 christos # would be, which would confuse testing. To avoid that, we 143 1.1 christos # only spawn one thread, too. 144 1.1 christos # 145 1.1 christos if {$cmd != "continue" || $step_over_mode == "none"} { 146 1.1 christos set n_threads 1 147 1.1 christos } else { 148 1.1 christos set n_threads 100 149 1.1 christos } 150 1.1 christos 151 1.1 christos gdb_test_no_output "set args $n_threads" 152 1.1 christos 153 1.1 christos if { ![runto_main] } { 154 1.1 christos return 155 1.1 christos } 156 1.1 christos 157 1.1 christos gdb_breakpoint "my_exit_syscall" 158 1.1 christos 159 1.1 christos gdb_test_no_output "set scheduler-locking ${schedlock}" 160 1.1 christos 161 1.1 christos if {$cmd != "continue" || $step_over_mode == "none"} { 162 1.1 christos set thread "<unknown>" 163 1.1 christos gdb_test_multiple "continue" "" { 164 1.1 christos -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" { 165 1.1 christos set thread $expect_out(1,string) 166 1.1 christos } 167 1.1 christos } 168 1.1 christos if {${non-stop}} { 169 1.1 christos gdb_test -nopass "thread $thread" "Switching to thread .*" \ 170 1.1 christos "switch to event thread" 171 1.1 christos } 172 1.1 christos 173 1.1 christos # If testing a step-over is requested, leave the breakpoint at 174 1.1 christos # the current instruction to force a step-over; otherwise, 175 1.1 christos # remove it. 176 1.1 christos if { $step_over_mode == "none" } { 177 1.1 christos delete_breakpoints 178 1.1 christos } 179 1.1 christos 180 1.1 christos if {$cmd == "continue"} { 181 1.1 christos gdb_continue_to_end "continue to end" "continue" 1 182 1.1 christos } else { 183 1.1 christos gdb_test_multiple $cmd "command aborts when thread exits" { 184 1.1 christos -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " { 185 1.1 christos pass $gdb_test_name 186 1.1 christos } 187 1.1 christos } 188 1.1 christos gdb_test "p \$_thread == $thread" "= 1" \ 189 1.1 christos "selected thread didn't change" 190 1.1 christos } 191 1.1 christos } else { 192 1.1 christos for { set i 0 } { $i < 100 } { incr i } { 193 1.1 christos with_test_prefix "iter $i" { 194 1.1 christos set ok 0 195 1.1 christos set thread "<unknown>" 196 1.1 christos gdb_test_multiple "continue" "" { 197 1.1 christos -re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" { 198 1.1 christos set thread $expect_out(1,string) 199 1.1 christos set ok 1 200 1.1 christos } 201 1.1 christos } 202 1.1 christos if {!${ok}} { 203 1.1 christos # Exit if there's a failure to avoid lengthy 204 1.1 christos # timeouts. 205 1.1 christos break 206 1.1 christos } 207 1.1 christos 208 1.1 christos if {${non-stop}} { 209 1.1 christos gdb_test -nopass "thread $thread" "Switching to thread .*" \ 210 1.1 christos "switch to event thread" 211 1.1 christos } 212 1.1 christos } 213 1.1 christos } 214 1.1 christos } 215 1.1 christos } 216 1.1 christos } 217 1.1 christos 218 1.1 christos foreach_with_prefix step_over_mode {none inline displaced} { 219 1.1 christos foreach_with_prefix non-stop {off on} { 220 1.1 christos foreach_with_prefix target-non-stop {off on} { 221 1.1 christos if {${non-stop} == "on" && ${target-non-stop} == "off"} { 222 1.1 christos # Invalid combination. 223 1.1 christos continue 224 1.1 christos } 225 1.1 christos 226 1.1 christos foreach_with_prefix schedlock {off on} { 227 1.1 christos foreach_with_prefix cmd {"next" "continue"} { 228 1.1 christos if {${non-stop} == "on"} { 229 1.1 christos foreach_with_prefix ns_stop_all {0 1} { 230 1.1 christos test ${step_over_mode} ${non-stop} ${target-non-stop} \ 231 1.1 christos ${schedlock} ${cmd} ${ns_stop_all} 232 1.1 christos } 233 1.1 christos } else { 234 1.1 christos test ${step_over_mode} ${non-stop} ${target-non-stop} ${schedlock} ${cmd} 0 235 1.1 christos } 236 1.1 christos } 237 1.1 christos } 238 1.1 christos } 239 1.1 christos } 240 1.1 christos } 241