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