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