1 # Copyright 2023-2024 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 # Tests related to pending breakpoints in a multi-inferior environment. 17 18 require allow_shlib_tests !use_gdb_stub 19 20 standard_testfile 21 22 set libname $testfile-lib 23 set srcfile_lib $srcdir/$subdir/$libname.c 24 set binfile_lib [standard_output_file $libname.so] 25 26 if { [gdb_compile_shlib $srcfile_lib $binfile_lib {}] != "" } { 27 untested "failed to compile shared library 1" 28 return -1 29 } 30 31 set binfile_lib_target [gdb_download_shlib $binfile_lib] 32 33 if { [build_executable "failed to prepare" $testfile $srcfile \ 34 [list debug \ 35 additional_flags=-DSHLIB_NAME=\"$binfile_lib_target\" \ 36 shlib_load]] } { 37 return -1 38 } 39 40 # Start two inferiors, both running the same test binary. The arguments 41 # INF_1_STOP and INF_2_STOP are source code patterns that are passed to 42 # gdb_get_line_number to figure out where each inferior should be stopped. 43 # 44 # This proc does a clean_restart and leaves inferior 2 selected. Also the 45 # 'breakpoint pending' flag is enabled, so pending breakpoints can be created 46 # without GDB prompting the user. 47 proc do_test_setup { inf_1_stop inf_2_stop } { 48 clean_restart ${::binfile} 49 50 gdb_locate_shlib $::binfile_lib 51 52 if {![runto_main]} { 53 return false 54 } 55 56 gdb_breakpoint [gdb_get_line_number ${inf_1_stop}] temporary 57 gdb_continue_to_breakpoint "move inferior 1 into position" 58 59 gdb_test "add-inferior -exec ${::binfile}" \ 60 "Added inferior 2.*" "add inferior 2" 61 gdb_test "inferior 2" "Switching to inferior 2 .*" "switch to inferior 2" 62 63 if {![runto_main]} { 64 return false 65 } 66 67 gdb_breakpoint [gdb_get_line_number ${inf_2_stop}] temporary 68 gdb_continue_to_breakpoint "move inferior 2 into position" 69 70 gdb_test_no_output "set breakpoint pending on" 71 72 return true 73 } 74 75 # Create a breakpoint on the function 'foo' in THREAD. It is expected 76 # that the breakpoint created will be pending, this is checked by 77 # running the 'info breakpoints' command. 78 # 79 # Returns the number for the newly created breakpoint. 80 proc do_create_pending_foo_breakpoint { {thread "1.1"} } { 81 gdb_test "break foo thread $thread" \ 82 [multi_line \ 83 "Function \"foo\" not defined\\." \ 84 "Breakpoint $::decimal \\(foo\\) pending\."] \ 85 "set pending thread-specific breakpoint" 86 set bpnum [get_integer_valueof "\$bpnum" "*INVALID*" \ 87 "get number for thread-specific breakpoint on foo"] 88 gdb_test "info breakpoints $bpnum" \ 89 [multi_line \ 90 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ 91 "\\s+stop only in thread [string_to_regexp $thread]"] \ 92 "check thread-specific breakpoint is initially pending" 93 94 return $bpnum 95 } 96 97 # Create a breakpoint on the function 'foo' in THREAD. It is expected 98 # that the breakpoint created will not be pending, this is checked by 99 # running the 'info breakpoints' command. 100 # 101 # Returns the number for the newly created breakpoint. 102 proc do_create_foo_breakpoint { {thread "1.1"} } { 103 gdb_test "break foo thread $thread" \ 104 "Breakpoint $::decimal at $::hex" \ 105 "set thread-specific breakpoint" 106 set bpnum [get_integer_valueof "\$bpnum" "*INVALID*" \ 107 "get number for thread-specific breakpoint on foo"] 108 gdb_test "info breakpoints $bpnum" \ 109 [multi_line \ 110 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s+<foo\[^>\]*> inf $::decimal" \ 111 "\\s+stop only in thread [string_to_regexp $thread]"] \ 112 "check thread-specific breakpoint is initially pending" 113 114 return $bpnum 115 } 116 117 # Check that when a breakpoint is in the pending state, but that breakpoint 118 # does have some locations (those locations themselves are pending), GDB 119 # doesn't display the inferior list in the 'info breakpoints' output. 120 proc_with_prefix test_no_inf_display {} { 121 do_test_setup "Break before open" "Break before open" 122 123 # Create a breakpoint on 'foo'. As the shared library (that 124 # contains foo) has not been loaded into any inferior yet, then 125 # there will be no locations and the breakpoint will be created 126 # pending. Pass the 'allow-pending' flag so the gdb_breakpoint 127 # correctly expects the new breakpoint to be pending. 128 gdb_breakpoint "foo" allow-pending 129 set bpnum [get_integer_valueof "\$bpnum" "*INVALID*" \ 130 "get foo breakpoint number"] 131 132 # Check the 'info breakpoints' output; the breakpoint is pending with 133 # no 'inf X' appearing at the end of the line. 134 gdb_test "info breakpoint $bpnum" \ 135 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ 136 "check info bp before locations have been created" 137 138 # Now select inferior 1 and allow the inferior to run forward to the 139 # point where a breakpoint location for foo will have been created. 140 gdb_test "inferior 1" "Switching to inferior 1 .*" 141 gdb_breakpoint [gdb_get_line_number "Break after open"] temporary 142 gdb_continue_to_breakpoint \ 143 "move inferior 1 until a location has been added" 144 145 # Check the 'info breakpoints' output. Notice we display the inferior 146 # list at the end of the breakpoint line. 147 gdb_test "info breakpoint $bpnum" \ 148 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s+<foo\[^>\]*>\\s+inf 1" \ 149 "check info breakpoints while breakpoint is inserted" 150 151 # Continue inferior 1 until the shared library has been unloaded. The 152 # breakpoint on 'foo' will return to the pending state. We will need to 153 # 'continue' twice as the first time will hit the 'foo' breakpoint. 154 gdb_breakpoint [gdb_get_line_number "Break after close"] temporary 155 gdb_continue_to_breakpoint "hit the breakpoint in foo" 156 gdb_continue_to_breakpoint "after close library" 157 158 # Check the 'info breakpoints' output, check there is no 'inf 1' at the 159 # end of the breakpoint line. 160 gdb_test "info breakpoint $bpnum" \ 161 [multi_line \ 162 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ 163 "\\s+breakpoint already hit 1 time"] \ 164 "check info breakpoints while breakpoint is pending" 165 } 166 167 # Setup two inferiors. In #1 the symbol 'foo' has not yet been 168 # loaded, while in #2 the symbol 'foo' has been loaded. 169 # 170 # Create a thread-specific breakpoint on 'foo' tied to a thread in 171 # inferior #1, the breakpoint should be pending -- 'foo' is not yet 172 # loaded in #1. 173 # 174 # Now move inferior #1 forward until 'foo' is loaded, check the 175 # breakpoint is no longer pending. 176 # 177 # Move inferior #1 forward more until 'foo' is unloaded, check that 178 # the breakpoint returns to the pending state. 179 proc_with_prefix test_pending_toggle { } { 180 181 do_test_setup "Break before open" "Break before close" 182 183 set bpnum [do_create_pending_foo_breakpoint] 184 185 # Now return to inferior 1 and continue until the shared library is 186 # loaded, the breakpoint should become non-pending. 187 gdb_test "inferior 1" "Switching to inferior 1 .*" \ 188 "switch back to inferior 1" 189 gdb_continue_to_breakpoint "stop in foo in inferior 1" "foo \\(\\) .*" 190 191 gdb_test "info breakpoint $bpnum" \ 192 [multi_line \ 193 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex <foo\[^>\]*> inf 1" \ 194 "\\s+stop only in thread 1\\.1" \ 195 "\\s+breakpoint already hit 1 time"] \ 196 "check thread-specific breakpoint is no longer pending" 197 198 gdb_breakpoint [gdb_get_line_number "Break after close"] temporary 199 gdb_continue_to_breakpoint "close library" 200 gdb_test "info breakpoints $bpnum" \ 201 [multi_line \ 202 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ 203 "\\s+stop only in thread 1\\.1" \ 204 "\\s+breakpoint already hit 1 time"] \ 205 "check thread-specific breakpoint is pending again" 206 } 207 208 # Create a Python variable VAR and set it to the gdb.Breakpoint object 209 # corresponding to the breakpoint numbered BPNUM. If THREAD is not 210 # the empty string then THREAD should be an integer, check that 211 # gdb.Breakpoint.thread is set to the value of THREAD. Otherwise, if 212 # THREAD is the empty string, check that gdb.Breakpoint.thread is set 213 # to None. 214 proc py_find_breakpoint { var bpnum {thread ""} } { 215 gdb_test_no_output \ 216 "python ${var}=\[b for b in gdb.breakpoints() if b.number == $bpnum\]\[0\]" \ 217 "find Python gdb.Breakpoint object" 218 if { $thread ne "" } { 219 gdb_test_no_output "python assert(${var}.thread == ${thread})" \ 220 "check thread attribute is currently correct" 221 } else { 222 gdb_test_no_output "python assert(${var}.thread is None)" \ 223 "check thread attribute is currently correct" 224 } 225 } 226 227 # Setup two inferiors. In #1 the symbol 'foo' has not yet been 228 # loaded, while in #2 the symbol 'foo' has been loaded. 229 # 230 # Create a thread-specific breakpoint on 'foo' tied to a thread in 231 # inferior #1, the breakpoint should be pending -- 'foo' is not yet 232 # loaded in #1. 233 # 234 # Use Python to change the thread of the thread-specific breakpoint to 235 # a thread in inferior #2, at this point the thread should gain a 236 # location and become non-pending. 237 # 238 # Set the thread back to a thread in inferior #1, the breakpoint 239 # should return to the pending state. 240 proc_with_prefix py_test_toggle_thread {} { 241 do_test_setup "Break before open" "Break after open" 242 243 set bpnum [do_create_pending_foo_breakpoint] 244 245 py_find_breakpoint "bp" $bpnum 1 246 247 gdb_test_no_output "python bp.thread = 2" \ 248 "change thread on thread-specific breakpoint" 249 gdb_test "info breakpoint $bpnum" \ 250 [multi_line \ 251 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex <foo\[^>\]*> inf 2" \ 252 "\\s+stop only in thread 2\\.1"] \ 253 "check thread-specific breakpoint now has a location" 254 255 gdb_test_no_output "set call_count = 2" "set call_count in inferior 2" 256 gdb_continue_to_breakpoint "stop at foo in inferior 2" "foo \\(\\) .*" 257 258 gdb_test_no_output "python bp.thread = 1" \ 259 "restore thread on thread-specific breakpoint" 260 gdb_test "info breakpoints $bpnum" \ 261 [multi_line \ 262 "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \ 263 "\\s+stop only in thread 1\\.1" \ 264 "\\s+breakpoint already hit 1 time"] \ 265 "check thread-specific breakpoint has returned to pending" 266 267 gdb_breakpoint [gdb_get_line_number "Break after close"] temporary 268 gdb_continue_to_breakpoint "stop after close in inferior 2" \ 269 ".* Break after close\\. .*" 270 271 gdb_test "inferior 1" "Switching to inferior 1 .*" \ 272 "switch to inferior 1" 273 gdb_continue_to_breakpoint "stop at foo in inferior 1" "foo \\(\\) .*" 274 } 275 276 # Setup two inferiors. Both inferiors have the symbol 'foo' 277 # available. 278 # 279 # Create a thread-specific breakpoint on 'foo' tied to a thread in 280 # inferior #1, the breakpoint should not be pending, but will only 281 # have a single location, the location in inferior #1. 282 # 283 # Use Python to change the thread of the thread-specific breakpoint to 284 # None. At this point the breakpoint should gain a second location, a 285 # location in inferior #2. 286 proc_with_prefix py_test_clear_thread {} { 287 do_test_setup "Break after open" "Break after open" 288 289 set bpnum [do_create_foo_breakpoint] 290 291 py_find_breakpoint "bp" $bpnum 1 292 293 gdb_test_no_output "python bp.thread = None" \ 294 "clear thread on thread-specific breakpoint" 295 gdb_test "info breakpoints $bpnum" \ 296 [multi_line \ 297 "${bpnum}\\s+breakpoint\\s+keep y\\s+<MULTIPLE>\\s*" \ 298 "${bpnum}\\.1\\s+y\\s+${::hex}\\s+<foo\[^>\]*> inf $::decimal" \ 299 "${bpnum}\\.2\\s+y\\s+${::hex}\\s+<foo\[^>\]*> inf $::decimal"] \ 300 "check for a location in both inferiors" 301 302 gdb_continue_to_breakpoint "stop at foo in inferior 2" "foo \\(\\) .*" 303 gdb_test_no_output "set call_count = 2" "set call_count in inferior 2" 304 305 gdb_test "inferior 1" "Switching to inferior 1 .*" \ 306 "switch to inferior 1" 307 gdb_continue_to_breakpoint "stop at foo in inferior 1" "foo \\(\\) .*" 308 gdb_test_no_output "set call_count = 2" "set call_count in inferior 1" 309 310 gdb_test_no_output "python bp.thread = 2" 311 gdb_test "info breakpoints $bpnum" \ 312 [multi_line \ 313 "${bpnum}\\s+breakpoint\\s+keep y\\s+${::hex}\\s+<foo\[^>\]*> inf 2" \ 314 "\\s+stop only in thread 2\\.1" \ 315 "\\s+breakpoint already hit 2 times"] \ 316 "check for a location only in inferior 2" 317 318 gdb_breakpoint [gdb_get_line_number "Break after close"] temporary 319 gdb_continue_to_breakpoint "stop after close in inferior 1" \ 320 ".* Break after close\\. .*" 321 322 gdb_test "inferior 2" "Switching to inferior 2 .*" \ 323 "switch back to inferior 2" 324 gdb_continue_to_breakpoint "stop at foo again in inferior 2" \ 325 "foo \\(\\) .*" 326 } 327 328 # Run all the tests. 329 test_no_inf_display 330 test_pending_toggle 331 py_test_toggle_thread 332 py_test_clear_thread 333