# Copyright 2022-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Test inferior-specific breakpoints. standard_testfile -1.c -2.c if {[use_gdb_stub]} { return } set srcfile1 ${srcfile} set binfile1 ${binfile}-1 set binfile2 ${binfile}-2 if {[build_executable ${testfile}.exp ${binfile1} "${srcfile1}"] != 0} { return -1 } if {[build_executable ${testfile}.exp ${binfile2} "${srcfile2}"] != 0} { return -1 } # Start the first inferior. clean_restart ${binfile1} if {![runto_main]} { return } # Add a second inferior, and start this one too. gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2" gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2" gdb_load $binfile2 if {![runto_main]} { return } # Try to create a breakpoint using both the 'inferior' and 'thread' keywords, # this should fail. Try with the keywords in both orders just in case the # parser has a bug. gdb_test "break foo thread 1.1 inferior 1" \ "You can specify only one of thread, inferior, or task\\." gdb_test "break foo inferior 1 thread 1.1" \ "You can specify only one of thread, inferior, or task\\." # Try to create a breakpoint using the 'inferior' keyword multiple times. gdb_test "break foo inferior 1 inferior 2" \ "You can specify only one inferior\\." # Clear out any other breakpoints. delete_breakpoints # Create an inferior specific breakpoint and then change the inferior # using the Python API. Use 'info breakpoint' to check that the # breakpoint was updated as we expect. if { [allow_python_tests] } { with_test_prefix "update breakpoint inferior" { # Create the b/p and grab its number. gdb_breakpoint "bar inferior 1" set bpnum [get_integer_valueof "\$bpnum" "INVALID" \ "get b/p number for breakpoint on bar"] # Get the line number for the two locations, the first in # inferior 1, the second in inferior 2. set bar_lineno_1 \ [gdb_get_line_number "First location of bar" $srcfile] set bar_lineno_2 \ [gdb_get_line_number "Second location of bar" $srcfile2] # Check the b/p was created with a single location where we # expect it. gdb_test "info breakpoint $bpnum" \ [multi_line \ "" \ "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$hex\\s+in bar at \[^\r\n\]+/$srcfile:$bar_lineno_1 inf 1" \ "\\s+stop only in inferior 1"] \ "check original details for breakpoint on bar" # Use the Python API to update the b/p's inferior. gdb_test_no_output "python bp = gdb.breakpoints()\[0\]" gdb_test_no_output "python bp.inferior = 2" # We should still only have a single location, but now in # inferior 2. gdb_test "info breakpoint $bpnum" \ [multi_line \ "" \ "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$hex\\s+in bar at \[^\r\n\]+/$srcfile2:$bar_lineno_2 inf 2" \ "\\s+stop only in inferior 2"] \ "check updated details for breakpoint on bar" # Use the Python API to remove the inferior restriction on the # breakpoint. gdb_test_no_output "python bp.inferior = None" # The breakpoint should now have multiple locations. gdb_test "info breakpoint $bpnum" \ [multi_line \ "" \ "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+\\s*" \ "$bpnum.1\\s+y\\s+$hex\\s+in bar at\[^\r\n\]+$srcfile:$bar_lineno_1 inf 1" \ "$bpnum.2\\s+y\\s+$hex\\s+in bar at\[^\r\n\]+$srcfile2:$bar_lineno_2 inf 2"] \ "check breakpoint bar now inferior requirement is gone" # Finally, add the inferior requirement back. gdb_test_no_output "python bp.inferior = 1" # Check the original location and restriction is restored. gdb_test "info breakpoint $bpnum" \ [multi_line \ "" \ "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$hex\\s+in bar at \[^\r\n\]+/$srcfile:$bar_lineno_1 inf 1" \ "\\s+stop only in inferior 1"] \ "check original details for breakpoint on bar are back" delete_breakpoints } } # Use 'info breakpoint' to check that the inferior specific breakpoint is # present in the breakpoint list. TESTNAME is the name used for this test, # BP_NUMBER is the number for the breakpoint, and EXPECTED_LOC_COUNT is the # number of locations we expect for that breakpoint. proc check_info_breakpoints { testname bp_number expected_loc_count } { gdb_test_multiple "info breakpoints $bp_number" $testname { -re "\r\nNum\\s+\[^\r\n\]+\r\n" { exp_continue } -re "^$bp_number\\s+breakpoint\\s+keep\\s+y\\s+\\s*\r\n" { set saw_header true exp_continue } -re "^\\s+stop only in inferior 1\r\n" { set saw_inf_cond true exp_continue } -re "^\\s+breakpoint already hit $::decimal times\r\n" { exp_continue } -re "^$bp_number\\.\[123\]\\s+y\\s+ $::hex in foo at \[^\r\n\]+(?: inf \[12\])?\r\n" { incr location_count exp_continue } -re "^$::gdb_prompt $" { with_test_prefix $gdb_test_name { gdb_assert { $saw_header \ && $location_count == $expected_loc_count \ && $saw_inf_cond } \ $gdb_test_name } } } } # Create an inferior-specific breakpoint. Use gdb_test instead of # gdb_breakpoint here as we want to check the breakpoint was placed in # multiple locations. gdb_test "break foo inferior 1" \ "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)" set bp_number [get_integer_valueof "\$bpnum" "INVALID" \ "get b/p number for inferior specific breakpoint"] set saw_header false set location_count 0 set saw_inf_cond false check_info_breakpoints "first check for inferior specific breakpoint" \ $bp_number 2 # Create a multi-inferior breakpoint to stop at. gdb_breakpoint "stop_breakpt" message set stop_bp_num [get_integer_valueof "\$bpnum" "INVALID" \ "get b/p number for stop_breakpt"] # Now resume inferior 2, this should reach 'stop_breakpt'. gdb_test "continue" \ "hit Breakpoint $stop_bp_num\.$decimal, stop_breakpt \\(\\) .*" \ "continue in inferior 2" # Switch to inferior 1, and try there. gdb_test "inferior 1" ".*" \ "select inferior 1 to check the inferior-specific b/p works" gdb_test "continue " \ "Thread 1\\.${decimal}\[^\r\n\]* hit Breakpoint\ $bp_number\.$decimal, foo \\(\\) .*" \ "first continue in inferior 1" # Now back to inferior 2, let the inferior exit, and then remove the # inferior, the inferior-specific breakpoint should not be deleted. gdb_test "inferior 2" ".*" \ "switch back to allow inferior 2 to exit" gdb_test "continue" "\\\[Inferior 2 \[^\r\n\]+ exited normally\\\]" \ "allow inferior 2 to exit" gdb_test "inferior 1" ".*" \ "back to inferior 1 so inferior 2 can be deleted" gdb_test_no_output "remove-inferiors 2" gdb_test "continue " "hit Breakpoint $bp_number\.$decimal, foo \\(\\) .*" \ "second continue in inferior 1" gdb_test "continue " "hit Breakpoint $stop_bp_num, stop_breakpt \\(\\) .*" \ "third continue in inferior 1" # Now allow inferior 1 to exit, the inferior specific breakpoint # should not be deleted. gdb_test "continue" \ "\\\[Inferior 1 \[^\r\n\]+ exited normally\\\]" \ "allow inferior 1 to exit" check_info_breakpoints "second check for inferior specific breakpoint" \ $bp_number 2 # Now create another new inferior, then remove inferior 1. As a result of # this removal, the inferior specific breakpoint should be deleted. gdb_test "add-inferior" "Added inferior 3.*" "add empty inferior 3" gdb_test "inferior 3" "Switching to inferior 3.*" "switch to inferior 3" gdb_test "remove-inferiors 1" \ "Inferior-specific breakpoint $bp_number deleted - inferior 1 has been removed\\." # Now check 'info breakpoints' to ensure the breakpoint is gone. gdb_test "info breakpoints $bp_number" \ "No breakpoint, watchpoint, tracepoint, or catchpoint matching '$bp_number'\\."