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 16 load_lib gdb-python.exp 17 18 require allow_python_tests 19 20 standard_testfile 21 22 if {[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. 28 if {[gdb_gnu_strip_debug $binfile]} { 29 unsupported "cannot produce separate debug info files" 30 return -1 31 } 32 33 set remote_python_file \ 34 [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] 35 36 set debug_filename ${binfile}.debug 37 set hidden_filename ${binfile}.hidden 38 39 # Start GDB. 40 clean_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. 45 with_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. 60 gdb_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. 65 set debug_directory [standard_output_file "debug-dir"] 66 remote_exec build "mkdir -p $debug_directory" 67 gdb_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. 73 with_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. 90 with_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. 112 with_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. 140 with_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. 160 with_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 184 gdb_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. 192 clean_restart 193 194 # Load the Python script into GDB. 195 gdb_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. 200 proc 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. 213 check_bad_name "!! Bad Name" 214 check_bad_name "Bad Name" 215 check_bad_name "(Bad Name)" 216 check_bad_name "Bad \[Name\]" 217 check_bad_name "Bad,Name" 218 check_bad_name "Bad;Name" 219 220 # Check that there are no handlers registered. 221 gdb_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. 226 gdb_test "enable missing-debug-handler foo" \ 227 "^0 missing debug handlers enabled" 228 gdb_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. 232 gdb_test_no_output "python pspace = gdb.selected_inferior().progspace" 233 234 # Now register some handlers. 235 foreach 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 243 with_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 260 with_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 280 with_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 300 with_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 320 with_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 343 with_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. 370 with_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. 401 with_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 424 with_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