1# Copyright (C) 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 16load_lib gdb-python.exp 17 18require allow_python_tests 19 20standard_testfile 21 22if {[build_executable "failed to prepare" ${testfile} ${srcfile}]} { 23 return -1 24} 25 26# Remove debug information from BINFILE and place it into 27# BINFILE.debug. 28if {[gdb_gnu_strip_debug $binfile]} { 29 unsupported "cannot produce separate debug info files" 30 return -1 31} 32 33set remote_python_file \ 34 [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] 35 36set debug_filename ${binfile}.debug 37set hidden_filename ${binfile}.hidden 38 39# Start GDB. 40clean_restart 41 42# Some initial sanity checks; initially, we can find the debug information 43# (this will use the .gnu_debuglink), then after we move the debug 44# information, reload the executable, now the debug can't be found. 45with_test_prefix "initial checks" { 46 # Load BINFILE, we should find the separate debug information. 47 gdb_file_cmd $binfile 48 gdb_assert {$gdb_file_cmd_debug_info == "debug"} \ 49 "debug info is found" 50 51 # Rename the debug information file, re-load BINFILE, GDB should fail 52 # to find the debug information 53 remote_exec build "mv $debug_filename $hidden_filename" 54 gdb_file_cmd $binfile 55 gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ 56 "debug info no longer found" 57} 58 59# Load the Python script into GDB. 60gdb_test "source $remote_python_file" "^Success" \ 61 "source python script" 62 63# Setup the separate debug info directory. This isn't actually needed until 64# some of the later tests, but might as well get this done now. 65set debug_directory [standard_output_file "debug-dir"] 66remote_exec build "mkdir -p $debug_directory" 67gdb_test_no_output "set debug-file-directory $debug_directory" \ 68 "set debug-file-directory" 69 70# Initially the missing debug handler we install is in a mode where it 71# returns None, indicating that it can't help locate the debug information. 72# Check this works as expected. 73with_test_prefix "handler returning None" { 74 gdb_test_no_output \ 75 "python gdb.missing_debug.register_handler(None, handler_obj)" \ 76 "register the initial handler" 77 78 gdb_file_cmd $binfile 79 gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ 80 "debug info not found" 81 82 # Check the handler was only called once. 83 gdb_test "python print(handler_obj.call_count)" "^1" \ 84 "check handler was only called once" 85} 86 87# Now configure the handler to move the debug file back to the 88# .gnu_debuglink location and then return True, this will cause GDB to 89# recheck, at which point it should find the debug info. 90with_test_prefix "handler in gnu_debuglink mode" { 91 gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \ 92 \"$hidden_filename\", \ 93 \"$debug_filename\")" \ 94 "confirgure handler" 95 gdb_file_cmd $binfile 96 gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" 97 98 # Check the handler was only called once. 99 gdb_test "python print(handler_obj.call_count)" "^1" \ 100 "check handler was only called once" 101} 102 103# Setup a directory structure based on the build-id of BINFILE, but don't 104# move the debug information into place just yet. 105# 106# Instead, configure the handler to move the debug info into the build-id 107# directory. 108# 109# Reload BINFILE, at which point the handler will move the debug info into 110# the build-id directory and return True, GDB will then recheck for the 111# debug information, and should find it. 112with_test_prefix "handler in build-id mode" { 113 # Move the debug file out of the way once more. 114 remote_exec build "mv $debug_filename $hidden_filename" 115 116 # Create the build-id based directory in which the debug information 117 # will be placed. 118 set build_id_filename \ 119 $debug_directory/[build_id_debug_filename_get $binfile] 120 remote_exec build "mkdir -p [file dirname $build_id_filename]" 121 122 # Configure the handler to move the debug info into the build-id dir. 123 gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \ 124 \"$hidden_filename\", \ 125 \"$build_id_filename\")" \ 126 "confirgure handler" 127 128 # Reload the binary and check the debug information is found. 129 gdb_file_cmd $binfile 130 gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" 131 132 # Check the handler was only called once. 133 gdb_test "python print(handler_obj.call_count)" "^1" \ 134 "check handler was only called once" 135} 136 137# Move the debug information back to a hidden location and configure the 138# handler to return the filename of the hidden debug info location. GDB 139# should immediately use this file as the debug information. 140with_test_prefix "handler returning a string" { 141 remote_exec build "mv $build_id_filename $hidden_filename" 142 143 # Configure the handler return a filename string. 144 gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_STRING, \ 145 \"$hidden_filename\")" \ 146 "confirgure handler" 147 148 # Reload the binary and check the debug information is found. 149 gdb_file_cmd $binfile 150 gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" 151 152 # Check the handler was only called once. 153 gdb_test "python print(handler_obj.call_count)" "^1" \ 154 "check handler was only called once" 155} 156 157# Register another global handler, this one raises an exception. Reload the 158# debug information, the bad handler should be invoked first, which raises 159# an excetption, at which point GDB should skip further Python handlers. 160with_test_prefix "handler raises an exception" { 161 gdb_test_no_output \ 162 "python gdb.missing_debug.register_handler(None, rhandler)" 163 164 foreach_with_prefix exception_type {gdb.GdbError TypeError} { 165 gdb_test_no_output \ 166 "python rhandler.exception_type = $exception_type" 167 168 gdb_file_cmd $binfile 169 gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ 170 "debug info not found" 171 172 set re [string_to_regexp \ 173 "Python Exception <class '$exception_type'>: message"] 174 gdb_assert {[regexp $re $gdb_file_cmd_msg]} \ 175 "check for exception in file command output" 176 177 # Our original handler is still registered, but should not have been 178 # called again (as the exception occurs first). 179 gdb_test "python print(handler_obj.call_count)" "^1" \ 180 "check good handler hasn't been called again" 181 } 182} 183 184gdb_test "info missing-debug-handlers" \ 185 [multi_line \ 186 "Global:" \ 187 " exception_handler" \ 188 " handler"] \ 189 "check both handlers are visible" 190 191# Re-start GDB. 192clean_restart 193 194# Load the Python script into GDB. 195gdb_test "source $remote_python_file" "^Success" \ 196 "source python script for bad handler name checks" 197 198# Attempt to register a missing-debug-handler with NAME. The expectation is 199# that this should fail as NAME contains some invalid characters. 200proc check_bad_name {name} { 201 set name_re [string_to_regexp $name] 202 set re \ 203 [multi_line \ 204 "ValueError.*: invalid character '.' in handler name: $name_re" \ 205 "Error occurred in Python.*"] 206 207 gdb_test "python register(\"$name\")" $re \ 208 "check that '$name' is not accepted" 209} 210 211# We don't attempt to be exhaustive here, just check a few random examples 212# of invalid names. 213check_bad_name "!! Bad Name" 214check_bad_name "Bad Name" 215check_bad_name "(Bad Name)" 216check_bad_name "Bad \[Name\]" 217check_bad_name "Bad,Name" 218check_bad_name "Bad;Name" 219 220# Check that there are no handlers registered. 221gdb_test_no_output "info missing-debug-handlers" \ 222 "check no handlers are registered" 223 224# Check we can use the enable/disable commands where there are no handlers 225# registered. 226gdb_test "enable missing-debug-handler foo" \ 227 "^0 missing debug handlers enabled" 228gdb_test "disable missing-debug-handler foo" \ 229 "^0 missing debug handlers disabled" 230 231# Grab the current program space object, used for registering handler later. 232gdb_test_no_output "python pspace = gdb.selected_inferior().progspace" 233 234# Now register some handlers. 235foreach hspec {{\"Foo\" None} 236 {\"-bar\" None} 237 {\"baz-\" pspace} 238 {\"abc-def\" pspace}} { 239 lassign $hspec name locus 240 gdb_test "python register($name, $locus)" 241} 242 243with_test_prefix "all handlers enabled" { 244 gdb_test "info missing-debug-handlers" \ 245 [multi_line \ 246 "Current Progspace:" \ 247 " abc-def" \ 248 " baz-" \ 249 "Global:" \ 250 " -bar" \ 251 " Foo"] 252 253 gdb_file_cmd $binfile 254 gdb_test "python print(handler_call_log)" \ 255 [string_to_regexp {['abc-def', 'baz-', '-bar', 'Foo']}] 256 gdb_test_no_output "python handler_call_log = \[\]" \ 257 "reset call log" 258} 259 260with_test_prefix "disable 'baz-'" { 261 gdb_test "disable missing-debug-handler progspace baz-" \ 262 "^1 missing debug handler disabled" 263 264 gdb_test "info missing-debug-handlers" \ 265 [multi_line \ 266 "Progspace \[^\r\n\]+:" \ 267 " abc-def" \ 268 " baz- \\\[disabled\\\]" \ 269 "Global:" \ 270 " -bar" \ 271 " Foo"] 272 273 gdb_file_cmd $binfile 274 gdb_test "python print(handler_call_log)" \ 275 [string_to_regexp {['abc-def', '-bar', 'Foo']}] 276 gdb_test_no_output "python handler_call_log = \[\]" \ 277 "reset call log" 278} 279 280with_test_prefix "disable 'Foo'" { 281 gdb_test "disable missing-debug-handler .* Foo" \ 282 "^1 missing debug handler disabled" 283 284 gdb_test "info missing-debug-handlers" \ 285 [multi_line \ 286 "Progspace \[^\r\n\]+:" \ 287 " abc-def" \ 288 " baz- \\\[disabled\\\]" \ 289 "Global:" \ 290 " -bar" \ 291 " Foo \\\[disabled\\\]"] 292 293 gdb_file_cmd $binfile 294 gdb_test "python print(handler_call_log)" \ 295 [string_to_regexp {['abc-def', '-bar']}] 296 gdb_test_no_output "python handler_call_log = \[\]" \ 297 "reset call log" 298} 299 300with_test_prefix "disable everything" { 301 gdb_test "disable missing-debug-handler .* .*" \ 302 "^2 missing debug handlers disabled" 303 304 gdb_test "info missing-debug-handlers" \ 305 [multi_line \ 306 "Progspace \[^\r\n\]+:" \ 307 " abc-def \\\[disabled\\\]" \ 308 " baz- \\\[disabled\\\]" \ 309 "Global:" \ 310 " -bar \\\[disabled\\\]" \ 311 " Foo \\\[disabled\\\]"] 312 313 gdb_file_cmd $binfile 314 gdb_test "python print(handler_call_log)" \ 315 [string_to_regexp {[]}] 316 gdb_test_no_output "python handler_call_log = \[\]" \ 317 "reset call log" 318} 319 320with_test_prefix "enable 'abc-def'" { 321 set re [string_to_regexp $binfile] 322 323 gdb_test "enable missing-debug-handler \"$re\" abc-def" \ 324 "^1 missing debug handler enabled" \ 325 "enable missing-debug-handler" 326 327 gdb_test "info missing-debug-handlers" \ 328 [multi_line \ 329 "Progspace \[^\r\n\]+:" \ 330 " abc-def" \ 331 " baz- \\\[disabled\\\]" \ 332 "Global:" \ 333 " -bar \\\[disabled\\\]" \ 334 " Foo \\\[disabled\\\]"] 335 336 gdb_file_cmd $binfile 337 gdb_test "python print(handler_call_log)" \ 338 [string_to_regexp {['abc-def']}] 339 gdb_test_no_output "python handler_call_log = \[\]" \ 340 "reset call log" 341} 342 343with_test_prefix "enable global handlers" { 344 set re [string_to_regexp $binfile] 345 346 gdb_test "enable missing-debug-handler global" \ 347 "^2 missing debug handlers enabled" 348 349 gdb_test "info missing-debug-handlers" \ 350 [multi_line \ 351 "Progspace \[^\r\n\]+:" \ 352 " abc-def" \ 353 " baz- \\\[disabled\\\]" \ 354 "Global:" \ 355 " -bar" \ 356 " Foo"] 357 358 gdb_file_cmd $binfile 359 gdb_test "python print(handler_call_log)" \ 360 [string_to_regexp {['abc-def', '-bar', 'Foo']}] 361 gdb_test_no_output "python handler_call_log = \[\]" \ 362 "reset call log" 363} 364 365# Add handler_obj to the global handler list, and configure it to 366# return False. We should call all of the program space specific 367# handlers (which return None), and then call handler_obj from the 368# global list, which returns False, at which point we shouldn't call 369# anyone else. 370with_test_prefix "return False handler in progspace list" { 371 gdb_test "enable missing-debug-handler progspace" \ 372 "^1 missing debug handler enabled" 373 374 gdb_test_no_output \ 375 "python gdb.missing_debug.register_handler(None, handler_obj)" \ 376 "register the initial handler" 377 378 gdb_test "info missing-debug-handlers" \ 379 [multi_line \ 380 "Progspace \[^\r\n\]+:" \ 381 " abc-def" \ 382 " baz-" \ 383 "Global:" \ 384 " handler" \ 385 " -bar" \ 386 " Foo"] 387 388 gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_FALSE)" \ 389 "confirgure handler" 390 391 gdb_file_cmd $binfile 392 gdb_test "python print(handler_call_log)" \ 393 [string_to_regexp {['abc-def', 'baz-', 'handler']}] 394 gdb_test_no_output "python handler_call_log = \[\]" \ 395 "reset call log" 396} 397 398# Now add handler_obj to the current program space's handler list. We 399# use the same handler object here, that's fine. We should only see a 400# call to the first handler object in the call log. 401with_test_prefix "return False handler in global list" { 402 gdb_test_no_output \ 403 "python gdb.missing_debug.register_handler(pspace, handler_obj)" \ 404 "register the initial handler" 405 406 gdb_test "info missing-debug-handlers" \ 407 [multi_line \ 408 "Progspace \[^\r\n\]+:" \ 409 " handler" \ 410 " abc-def" \ 411 " baz-" \ 412 "Global:" \ 413 " handler" \ 414 " -bar" \ 415 " Foo"] 416 417 gdb_file_cmd $binfile 418 gdb_test "python print(handler_call_log)" \ 419 [string_to_regexp {['handler']}] 420 gdb_test_no_output "python handler_call_log = \[\]" \ 421 "reset call log" 422} 423 424with_test_prefix "check handler replacement" { 425 # First, check we can have the same name appear in both program 426 # space and global lists without giving an error. 427 gdb_test_no_output "python register(\"Foo\", pspace)" 428 429 gdb_test "info missing-debug-handlers" \ 430 [multi_line \ 431 "Progspace \[^\r\n\]+:" \ 432 " Foo" \ 433 " handler" \ 434 " abc-def" \ 435 " baz-" \ 436 "Global:" \ 437 " handler" \ 438 " -bar" \ 439 " Foo"] 440 441 # Now check that we get an error if we try to add a handler with 442 # the same name. 443 gdb_test "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"))" \ 444 [multi_line \ 445 "RuntimeError.*: Handler Foo already exists\\." \ 446 "Error occurred in Python.*"] 447 448 gdb_test "python gdb.missing_debug.register_handler(handler=log_handler(\"Foo\"), locus=pspace)" \ 449 [multi_line \ 450 "RuntimeError.*: Handler Foo already exists\\." \ 451 "Error occurred in Python.*"] 452 453 # And now try again, but this time with 'replace=True', we 454 # shouldn't get an error in this case. 455 gdb_test_no_output \ 456 "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"), replace=True)" 457 458 gdb_test_no_output \ 459 "python gdb.missing_debug.register_handler(handler=log_handler(\"Foo\"), locus=None, replace=True)" 460 461 # Now disable a handler and check we still need to use 'replace=True'. 462 gdb_test "disable missing-debug-handler progspace Foo" \ 463 "^1 missing debug handler disabled" 464 465 gdb_test "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"))" \ 466 [multi_line \ 467 "RuntimeError.*: Handler Foo already exists\\." \ 468 "Error occurred in Python.*"] \ 469 "still get an error when handler is disabled" 470 471 gdb_test_no_output \ 472 "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"), replace=True)" \ 473 "can replace a disabled handler" 474} 475