Home | History | Annotate | Line # | Download | only in gdb.base
      1 # Copyright 2024-2025 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 # Breakpoint locations can be marked as "disabled due to their
     17 # condition".  This test sets up a breakpoint which depends on reading
     18 # a variable in a shared library.  We then check that the b/p is
     19 # correctly disabled before the shared library is loaded, becomes
     20 # enabled once the shared library is loaded.  And becomes disabled
     21 # again when the shared libraries are unloaded.
     22 
     23 standard_testfile .c -lib.c
     24 
     25 set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
     26 
     27 # Build the library and copy it to the target.
     28 set libname ${testfile}-lib
     29 set libfile [standard_output_file $libname]
     30 if { [build_executable "build shlib" $libfile $srcfile2 {debug shlib}] == -1} {
     31     return
     32 }
     33 set libfile_on_target [gdb_download_shlib $libfile]
     34 
     35 # Build the executable.
     36 set opts [list debug shlib_load additional_flags=-DSHLIB_NAME=\"${libname}\"]
     37 if { [build_executable "build exec" $binfile $srcfile $opts] == -1} {
     38     return
     39 }
     40 
     41 set other_bp_line [gdb_get_line_number "Other BP location" $srcfile]
     42 set cond_bp_line [gdb_get_line_number "Conditional BP location" $srcfile]
     43 set exit_bp_line [gdb_get_line_number "BP before exit" $srcfile]
     44 
     45 # Setup a conditional b/p, the condition of which depends on reading a
     46 # variable from a shared library.  The b/p will initially be created
     47 # disabled (due to the condition).
     48 #
     49 # Continue the inferior, when the shared library is loaded GDB should
     50 # make the b/p enabled.
     51 #
     52 # Restart the inferior, which should unload the shared library, GDB
     53 # should mark the b/p as disabled due to its condition again.
     54 proc run_test { hit_cond } {
     55     clean_restart
     56     gdb_load $::binfile
     57 
     58     if {![runto_main]} {
     59 	return
     60     }
     61 
     62     # Setup breakpoints.
     63     gdb_breakpoint $::srcfile:$::other_bp_line
     64     set other_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
     65 			  "get number of other b/p"]
     66 
     67     gdb_breakpoint $::srcfile:$::exit_bp_line
     68     set exit_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
     69 			 "get number of exit b/p"]
     70 
     71     gdb_breakpoint $::srcfile:$::cond_bp_line
     72     set cond_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
     73 			"get number of conditional b/p"]
     74 
     75     if { $hit_cond } {
     76 	set lib_global_val 0
     77     } else {
     78 	set lib_global_val 1
     79     }
     80 
     81     # Set the condition.  Use 'force' as we're referencing a variable in
     82     # the shared library, which hasn't been loaded yet.  The breakpoint
     83     # will immediately be marked as disabled_by_cond.
     84     gdb_test "condition -force $cond_bp_num lib_global == $lib_global_val" \
     85 	[multi_line \
     86 	     "warning: failed to validate condition at location $cond_bp_num\\.1, disabling:" \
     87 	     "  No symbol \"lib_global\" in current context\\."] \
     88 	"set b/p condition, it will be disabled"
     89 
     90     # Source Python script if supported.
     91     if { [allow_python_tests] } {
     92 	gdb_test_no_output "source $::pyfile" "import python scripts"
     93 	gdb_test "python print(bp_modified_list)" "\\\[\\\]" \
     94 	    "check b/p modified observer has not yet triggered"
     95     }
     96 
     97     # Check the b/p is indeed marked as disabled (based on its condition).
     98     gdb_test "info breakpoint $cond_bp_num" \
     99 	[multi_line \
    100 	     "\r\n$cond_bp_num\\.1\\s+N\\*\\s+$::hex in main at \[^\r\n\]+" \
    101 	     "\\(\\*\\): Breakpoint condition is invalid at this location\\."] \
    102 	"conditional breakpoint is disabled based on condition"
    103 
    104     if { $hit_cond } {
    105 	# Continue the inferior.  The shared library is loaded and the
    106 	# breakpoint condition should become valid.  We should then stop at
    107 	# the conditional breakpoint.
    108 	gdb_test "continue" \
    109 	    [multi_line \
    110 		 "Breakpoint $cond_bp_num, main \\(\\) at \[^\r\n\]+:$::cond_bp_line" \
    111 		 "$::cond_bp_line\\s+breakpt \\(\\);\\s+/\\* Conditional BP location\\.  \\*/"] \
    112 	    "continue until conditional b/p is hit"
    113     } else {
    114 	# Continue the inferior.  The shared library is loaded and the
    115 	# breakpoint condition should become valid.  As the condition
    116 	# is going to be false GDB will stop at the other line.
    117 	gdb_test "continue" \
    118 	    [multi_line \
    119 		 "Breakpoint $other_bp_num, main \\(\\) at \[^\r\n\]+:$::other_bp_line" \
    120 		 "$::other_bp_line\\s+breakpt \\(\\);\\s+/\\* Other BP location\\.  \\*/"] \
    121 	    "continue until conditional b/p is hit"
    122     }
    123 
    124     if { [allow_python_tests] } {
    125 	# We're going to look at the list of b/p that have been
    126 	# modified since we loaded the Python script.  The first b/p
    127 	# modified will be the conditional b/p, this occurs when the
    128 	# b/p condition became valid.
    129 	#
    130 	# The second b/p will be whichever b/p we hit (the hit count
    131 	# increased).  So figure out which b/p we are going to hit.
    132 	if { $hit_cond } {
    133 	    set second_bp_num $cond_bp_num
    134 	} else {
    135 	    set second_bp_num $other_bp_num
    136 	}
    137 
    138 	# Now check the list of modified b/p.
    139 	gdb_test "python print(bp_modified_list)" \
    140 	    "\\\[$cond_bp_num, $second_bp_num\\\]" \
    141 	    "check b/p modified observer was triggered"
    142     }
    143 
    144     if {[gdb_protocol_is_remote]} {
    145 	set evals_re "(?: \\(\[^) \]+ evals\\))?"
    146     } else {
    147 	set evals_re ""
    148     }
    149 
    150     # Check the b/p is no longer marked as disabled.  The output is
    151     # basically the same here whether the b/p was hit or not.  It's
    152     # just the hit counter line that we need to append or not.
    153     set re_list \
    154 	[list \
    155 	     "$cond_bp_num\\s+breakpoint\\s+keep\\s+y\\s+$::hex in main at \[^\r\n\]+:$::cond_bp_line" \
    156 	     "\\s+stop only if lib_global == $lib_global_val$evals_re"]
    157     if { $hit_cond } {
    158 	lappend re_list "\\s+breakpoint already hit 1 time"
    159     }
    160     set re [multi_line {*}$re_list]
    161     gdb_test "info breakpoint $cond_bp_num" $re \
    162 	"conditional breakpoint is now enabled"
    163 
    164     if { $hit_cond } {
    165 	gdb_test "continue" \
    166 	    [multi_line \
    167 		 "Breakpoint $other_bp_num, main \\(\\) at \[^\r\n\]+:$::other_bp_line" \
    168 		 "$::other_bp_line\\s+breakpt \\(\\);\\s+/\\* Other BP location\\.  \\*/"] \
    169 	    "continue to other b/p"
    170     }
    171 
    172     if {[allow_python_tests]} {
    173 	# Clear out the list of modified b/p.  This makes the results
    174 	# (see below) clearer.
    175 	gdb_test_no_output "python bp_modified_list=\[\]" \
    176 	    "clear bp_modified_list"
    177     }
    178 
    179     gdb_test "continue" \
    180 	[multi_line \
    181 	     "Breakpoint $exit_bp_num, main \\(\\) at \[^\r\n\]+:$::exit_bp_line" \
    182 	     "$::exit_bp_line\\s+breakpt \\(\\);\\s+/\\* BP before exit\\.  \\*/"] \
    183 	"continue b/p before exit"
    184 
    185     # Check the b/p is once again marked as disabled based on its
    186     # condition.
    187     gdb_test "info breakpoint $cond_bp_num" \
    188 	[multi_line \
    189 	     "\r\n$cond_bp_num\\.1\\s+N\\*\\s+$::hex in main at \[^\r\n\]+" \
    190 	     "\\(\\*\\): Breakpoint condition is invalid at this location\\."] \
    191 	"conditional breakpoint is again disabled based on condition"
    192 
    193     if { [allow_python_tests] } {
    194 	# The condition breakpoint will have been modified (moved to
    195 	# the disabled state) when GDB unloaded the shared libraries.
    196 	# And the b/p in main will have been modified in that it's hit
    197 	# count will have gone up.
    198 	gdb_test "python print(bp_modified_list)" \
    199 	    "\\\[$cond_bp_num, $exit_bp_num\\\]" \
    200 	    "check b/p modified observer was triggered during restart"
    201     }
    202 }
    203 
    204 # The tests.
    205 foreach_with_prefix hit_cond { true false } {
    206     run_test $hit_cond
    207 }
    208