# 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'\\."