Home | History | Annotate | Line # | Download | only in gdb.multi
      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