1 # Copyright 2022-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 # Some simple tests of inferior function calls from breakpoint
17 # conditions, in multi-threaded inferiors.
19 # This test sets up a multi-threaded inferior, and places a breakpoint
20 # at a location that many of the threads will reach. We repeat the
21 # test with different conditions, sometimes a single thread should
22 # stop at the breakpoint, sometimes multiple threads should stop, and
23 # sometimes no threads should stop.
27 if { [build_executable "failed to prepare" ${binfile} "${srcfile}" \
28 {debug pthreads}] == -1 } {
32 set cond_bp_line [gdb_get_line_number "Breakpoint here"]
33 set stop_bp_line [gdb_get_line_number "Stop marker"]
34 set nested_bp_line [gdb_get_line_number "Nested breakpoint"]
35 set segv_line [gdb_get_line_number "Segfault happens here"]
37 # Start GDB based on TARGET_ASYNC and TARGET_NON_STOP, and then runto main.
38 proc start_gdb_and_runto_main { target_async target_non_stop } {
39 save_vars { ::GDBFLAGS } {
41 " -ex \"maint set target-non-stop $target_non_stop\""
43 " -ex \"maintenance set target-async ${target_async}\""
45 clean_restart ${::binfile}
48 if { ![runto_main] } {
55 # Run a test of GDB's conditional breakpoints, where the conditions include
56 # inferior function calls.
58 # CONDITION is the expression to be used as the breakpoint condition.
60 # N_EXPECTED_HITS is the number of threads that we expect to stop due to
63 # MESSAGE is used as a test name prefix.
64 proc run_condition_test { message n_expected_hits condition \
65 target_async target_non_stop } {
66 with_test_prefix $message {
68 if { [start_gdb_and_runto_main $target_async \
69 $target_non_stop] == -1 } {
73 # Use this convenience variable to track how often the
74 # breakpoint condition has been evaluated. This should be
76 gdb_test "set \$n_cond_eval = 0"
78 # Setup the conditional breakpoint.
80 "${::srcfile}:${::cond_bp_line} if ((++\$n_cond_eval) && (${condition}))"
82 # And a breakpoint that we hit when the test is over, this one is
83 # not conditional. Only the main thread gets here once all the
84 # other threads have finished.
85 gdb_breakpoint "${::srcfile}:${::stop_bp_line}"
87 # The number of times we stop at the conditional breakpoint.
90 # Now keep 'continue'-ing GDB until all the threads have finished
91 # and we reach the stop_marker breakpoint.
92 gdb_test_multiple "continue" "spot all breakpoint hits" {
93 -re " worker_func \[^\r\n\]+${::srcfile}:${::cond_bp_line}\r\n${::decimal}\\s+\[^\r\n\]+Breakpoint here\[^\r\n\]+\r\n${::gdb_prompt} $" {
99 -re " stop_marker \[^\r\n\]+${::srcfile}:${::stop_bp_line}\r\n${::decimal}\\s+\[^\r\n\]+Stop marker\[^\r\n\]+\r\n${::gdb_prompt} $" {
104 gdb_assert { $n_hit_condition == $n_expected_hits } \
105 "stopped at breakpoint the expected number of times"
107 # Ensure the breakpoint condition was evaluated once per thread.
108 gdb_test "print \$n_cond_eval" "= 3" \
109 "condition was evaluated in each thread"
113 # Check that after handling a conditional breakpoint (where the condition
114 # includes an inferior call), it is still possible to kill the running
115 # inferior, and then restart the inferior.
117 # At once point doing this would result in GDB giving an assertion error.
118 proc_with_prefix run_kill_and_restart_test { target_async target_non_stop } {
119 # This test relies on the 'start' command, which is not possible with
120 # the plain 'remote' target.
121 if { [target_info gdb_protocol] == "remote" } {
125 if { [start_gdb_and_runto_main $target_async \
126 $target_non_stop] == -1 } {
130 # Setup the conditional breakpoint.
132 "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 1))"
133 gdb_continue_to_breakpoint "worker_func"
135 # Now kill the program being debugged.
136 gdb_test "kill" "" "kill process" \
137 "Kill the program being debugged.*y or n. $" "y"
139 # Check we can restart the inferior. At one point this would trigger an
144 # Create a conditional breakpoint which includes a call to a function that
145 # segfaults. Run GDB and check what happens when the inferior segfaults
146 # during the inferior call.
147 proc_with_prefix run_bp_cond_segfaults { target_async target_non_stop } {
148 if { [start_gdb_and_runto_main $target_async \
149 $target_non_stop] == -1 } {
153 # This test relies on the inferior segfaulting when trying to
154 # access address zero.
155 if { [is_address_zero_readable] } {
159 # Setup the conditional breakpoint, include a call to
160 # 'function_that_segfaults', which triggers the segfault.
162 "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 0) && function_that_segfaults ())"
163 set bp_1_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
164 "get number of conditional breakpoint"]
166 gdb_test "continue" \
170 "Thread ${::decimal} \"infcall-from-bp\" received signal SIGSEGV, Segmentation fault\\." \
171 "${::hex} in function_that_segfaults \\(\\) at \[^\r\n\]+:${::segv_line}" \
172 "${::decimal}\\s+\[^\r\n\]+Segfault happens here\[^\r\n\]+" \
173 "Error in testing condition for breakpoint ${bp_1_num}:" \
174 "The program being debugged was signaled while in a function called from GDB\\." \
175 "GDB remains in the frame where the signal was received\\." \
176 "To change this behavior use \"set unwind-on-signal on\"\\." \
177 "Evaluation of the expression containing the function" \
178 "\\(function_that_segfaults\\) will be abandoned\\." \
179 "When the function is done executing, GDB will silently stop\\."]
182 # Create a conditional breakpoint which includes a call to a function that
183 # itself has a breakpoint set within it. Run GDB and check what happens
184 # when GDB hits the nested breakpoint.
185 proc_with_prefix run_bp_cond_hits_breakpoint { target_async target_non_stop } {
186 if { [start_gdb_and_runto_main $target_async \
187 $target_non_stop] == -1 } {
191 # Setup the conditional breakpoint, include a call to
192 # 'function_with_breakpoint' in which we will shortly place a
195 "${::srcfile}:${::cond_bp_line} if (is_matching_tid (arg, 0) && function_with_breakpoint ())"
196 set bp_1_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
197 "get number of conditional breakpoint"]
199 gdb_breakpoint "${::srcfile}:${::nested_bp_line}"
200 set bp_2_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
201 "get number of nested breakpoint"]
203 gdb_test "continue" \
207 "Thread ${::decimal} \"infcall-from-bp\" hit Breakpoint ${bp_2_num}, function_with_breakpoint \\(\\) at \[^\r\n\]+:${::nested_bp_line}" \
208 "${::decimal}\\s+\[^\r\n\]+Nested breakpoint\[^\r\n\]+" \
209 "Error in testing condition for breakpoint ${bp_1_num}:" \
210 "The program being debugged stopped while in a function called from GDB\\." \
211 "Evaluation of the expression containing the function" \
212 "\\(function_with_breakpoint\\) will be abandoned\\." \
213 "When the function is done executing, GDB will silently stop\\."]
216 foreach_with_prefix target_async { "on" "off" } {
217 foreach_with_prefix target_non_stop { "on" "off" } {
218 run_condition_test "exactly one thread is hit" \
219 1 "is_matching_tid (arg, 1)" \
220 $target_async $target_non_stop
221 run_condition_test "exactly two threads are hit" \
222 2 "(is_matching_tid (arg, 0) || is_matching_tid (arg, 2))" \
223 $target_async $target_non_stop
224 run_condition_test "all three threads are hit" \
226 $target_async $target_non_stop
227 run_condition_test "no thread is hit" \
228 0 "return_false ()" \
229 $target_async $target_non_stop
231 run_kill_and_restart_test $target_async $target_non_stop
232 run_bp_cond_segfaults $target_async $target_non_stop
233 run_bp_cond_hits_breakpoint $target_async $target_non_stop