forked from Imagelibrary/binutils-gdb
I noticed that in commit:
commit 5cabc8098e
Date: Wed Jul 31 15:55:57 2024 +0100
gdb/python: implement Python find_exec_by_build_id hook
I managed to typo 'unsupported' as 'unsupport'. If you run the test
on a target that doesn't support core file creation then you'll get a
TCL error.
Fixed in this commit.
566 lines
18 KiB
Plaintext
566 lines
18 KiB
Plaintext
# Copyright (C) 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 <http://www.gnu.org/licenses/>.
|
|
|
|
load_lib gdb-python.exp
|
|
|
|
require allow_python_tests
|
|
require {!is_remote host}
|
|
|
|
standard_testfile .c -lib.c
|
|
|
|
# Build the library.
|
|
set libname ${testfile}-lib
|
|
set libfile [standard_output_file $libname]
|
|
if { [build_executable "build shlib" $libfile $srcfile2 \
|
|
{debug shlib build-id}] == -1} {
|
|
return
|
|
}
|
|
|
|
# Build the executable.
|
|
set opts [list debug build-id shlib=${libfile}]
|
|
if { [build_executable "build exec" $binfile $srcfile $opts] == -1} {
|
|
return
|
|
}
|
|
|
|
# The cc-with-gnu-debuglink board will split the debug out into the
|
|
# .debug directory. This test script relies on having GDB lookup the
|
|
# objfile and debug via the build-id, which this test sets up. Trying
|
|
# to do that, while also supporting the cc-with-gnu-debuglink board is
|
|
# just too complicated.
|
|
if {[file isdirectory [standard_output_file ".debug"]]} {
|
|
unsupported "split debug testing not supported"
|
|
return
|
|
}
|
|
|
|
set remote_python_file \
|
|
[gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
|
|
|
|
# Generate a core file.
|
|
set corefile [core_find $binfile {}]
|
|
if {$corefile == ""} {
|
|
unsupported "core file not generated"
|
|
return 0
|
|
}
|
|
|
|
# Create a directory named DIRNAME for use as the
|
|
# debug-file-directory. Populate the directory with links (based on
|
|
# the build-ids) to each file in the list FILES.
|
|
#
|
|
# Return the full filename of DIRNAME on the host.
|
|
proc setup_debugdir { dirname files } {
|
|
set debugdir [host_standard_output_file $dirname]
|
|
|
|
# Create basic empty directory structure (in case FILES is empty).
|
|
remote_exec host "mkdir -p $debugdir/.build-id/"
|
|
|
|
foreach file $files {
|
|
set build_id_filename [build_id_debug_filename_get $file ""]
|
|
|
|
remote_exec host "mkdir -p $debugdir/[file dirname $build_id_filename]"
|
|
remote_exec host "ln -s $file $debugdir/$build_id_filename"
|
|
}
|
|
|
|
return $debugdir
|
|
}
|
|
|
|
# Query some symbols in the inferior to see if GDB managed to find the
|
|
# executable (when EXEC_LOADED is true) and/or the library (when LIB_LOADED
|
|
# is true).
|
|
proc check_loaded_debug { exec_loaded lib_loaded } {
|
|
if { $exec_loaded } {
|
|
gdb_test "whatis global_exec_var" "^type = volatile struct exec_type"
|
|
|
|
if { $lib_loaded } {
|
|
gdb_test "whatis global_lib_var" "^type = volatile struct lib_type"
|
|
} else {
|
|
gdb_test "whatis global_lib_var" \
|
|
"^No symbol \"global_lib_var\" in current context\\."
|
|
}
|
|
} else {
|
|
gdb_test "whatis global_exec_var" \
|
|
"^No symbol table is loaded\\. Use the \"file\" command\\."
|
|
gdb_test "whatis global_lib_var" \
|
|
"^No symbol table is loaded\\. Use the \"file\" command\\."
|
|
}
|
|
}
|
|
|
|
# Load the global corefile. The EXTRA_RE is checked for prior to GDB
|
|
# announcing that the core-file has been loaded.
|
|
proc load_core_file { {extra_re ".*"} } {
|
|
gdb_test "core-file $::corefile" \
|
|
[multi_line \
|
|
"$extra_re" \
|
|
"Core was generated by \[^\r\n\]+" \
|
|
"Program terminated with signal SIGABRT, Aborted\\." \
|
|
"\[^\r\n\]+(?:\r\n\[^\r\n\]+)?"] \
|
|
"loaded the core file"
|
|
}
|
|
|
|
# Set the debug-file-directory to DIRNAME.
|
|
proc set_debug_file_dir { dirname } {
|
|
gdb_test_no_output "set debug-file-directory $dirname" \
|
|
"set debug-file-directory"
|
|
}
|
|
|
|
# Restart GDB and load the support Python script.
|
|
proc clean_restart_load_python {} {
|
|
clean_restart
|
|
gdb_test "source $::remote_python_file" "^Success" \
|
|
"load python script"
|
|
}
|
|
|
|
# For sanity, lets check that we can load the specify the executable
|
|
# and then load the core-file the easy way.
|
|
with_test_prefix "initial sanity check" {
|
|
clean_restart $binfile
|
|
load_core_file
|
|
check_loaded_debug true true
|
|
}
|
|
|
|
# Move the executable and library into a location that the core-file
|
|
# can't possibly know about. After this the only way GDB can track
|
|
# down these files will be by looking in the debug-file-directory.
|
|
set hidden_dir [host_standard_output_file "hidden"]
|
|
set hidden_binfile "$hidden_dir/$testfile"
|
|
set hidden_libfile "$hidden_dir/$libname"
|
|
remote_exec host "mkdir -p $hidden_dir"
|
|
remote_exec host "mv $libfile $hidden_libfile"
|
|
remote_exec host "mv $binfile $hidden_binfile"
|
|
|
|
# If using the fission-dwp board then we'll have .dwp files that also
|
|
# need to be moved.
|
|
if {[remote_file host exists ${libfile}.dwp]} {
|
|
remote_exec host "mv ${libfile}.dwp ${hidden_libfile}.dwp"
|
|
}
|
|
|
|
if {[remote_file host exists ${binfile}.dwp]} {
|
|
remote_exec host "mv ${binfile}.dwp ${hidden_binfile}.dwp"
|
|
}
|
|
|
|
with_test_prefix "no objfiles, no debug-file-directory" {
|
|
clean_restart
|
|
load_core_file
|
|
check_loaded_debug false false
|
|
}
|
|
|
|
# Setup some debug-file-directories.
|
|
set debugdir_no_lib \
|
|
[setup_debugdir "debugdir.no-lib" [list "$hidden_binfile"]]
|
|
set debugdir_empty \
|
|
[setup_debugdir "debugdir.empty" {}]
|
|
set debugdir_all \
|
|
[setup_debugdir "debugdir.all" [list "$hidden_libfile" \
|
|
"$hidden_binfile"]]
|
|
|
|
with_test_prefix "no objfiles available" {
|
|
# Another sanity check that GDB can find the files via the
|
|
# debug-file-directory.
|
|
clean_restart
|
|
set_debug_file_dir $debugdir_empty
|
|
load_core_file
|
|
check_loaded_debug false false
|
|
}
|
|
|
|
with_test_prefix "all objfiles available" {
|
|
# Another sanity check that GDB can find the files via the
|
|
# debug-file-directory.
|
|
set_debug_file_dir $debugdir_all
|
|
load_core_file
|
|
check_loaded_debug true true
|
|
}
|
|
|
|
with_test_prefix "lib objfile missing" {
|
|
# Another sanity check that GDB can find the files via the
|
|
# debug-file-directory.
|
|
set_debug_file_dir $debugdir_no_lib
|
|
load_core_file
|
|
check_loaded_debug true false
|
|
}
|
|
|
|
with_test_prefix "all objfiles missing, handler returns None" {
|
|
clean_restart_load_python
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(None, handler_obj)" \
|
|
"register initial handler"
|
|
load_core_file
|
|
|
|
check_loaded_debug false false
|
|
|
|
# The handler should be called three times, once for the
|
|
# mapped-file, once for the core-file's exec, and once for the
|
|
# shared library.
|
|
gdb_test "python print(handler_obj.call_count)" "^3" \
|
|
"check handler was called three times"
|
|
}
|
|
|
|
with_test_prefix "lib objfile missing, handler returns None" {
|
|
# Reset handler_obj.
|
|
gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_NONE)"
|
|
|
|
set_debug_file_dir $debugdir_no_lib
|
|
load_core_file
|
|
check_loaded_debug true false
|
|
|
|
# The handler will be called twice, once when GDB tries to
|
|
# load the shared library during the memory-mapped file phase,
|
|
# then again for the shared library loading.
|
|
gdb_test "python print(handler_obj.call_count)" "^2" \
|
|
"check handler was called three times"
|
|
}
|
|
|
|
with_test_prefix "handler installs lib objfile" {
|
|
set build_id_filename [build_id_debug_filename_get \
|
|
$hidden_libfile ""]
|
|
remote_exec host \
|
|
"mkdir -p $debugdir_no_lib/[file dirname $build_id_filename]"
|
|
gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \
|
|
\"$hidden_libfile\", \"$debugdir_no_lib/$build_id_filename\")" \
|
|
"configure handler"
|
|
|
|
load_core_file
|
|
check_loaded_debug true true
|
|
|
|
# Cleanup so the test can be reproduced again later if needed.
|
|
remote_exec host "rm $debugdir_no_lib/$build_id_filename"
|
|
}
|
|
|
|
with_test_prefix "handler points to lib objfile" {
|
|
set build_id_filename [build_id_debug_filename_get \
|
|
$hidden_libfile ""]
|
|
remote_exec host \
|
|
"mkdir -p $debugdir_no_lib/[file dirname $build_id_filename]"
|
|
gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_STRING, \
|
|
\"$hidden_libfile\")" \
|
|
"configure handler"
|
|
|
|
load_core_file
|
|
check_loaded_debug true true
|
|
|
|
# Cleanup so the test can be reproduced again later if needed.
|
|
remote_exec host "rm $debugdir_no_lib/$build_id_filename"
|
|
|
|
# The handler will only have been called once when loading the
|
|
# memory-mapped file. GDB is smart enough to reuse the previously
|
|
# discovered BFD object as the shared library.
|
|
gdb_test "python print(handler_obj.call_count)" "^1" \
|
|
"check good handler hasn't been called again"
|
|
|
|
# Validate the filename and build-id arguments passed to the handler.
|
|
set expected_buildid [get_build_id $hidden_libfile]
|
|
gdb_test "python print(handler_last_buildid)" "^$expected_buildid"
|
|
gdb_test "python print(handler_last_filename)" \
|
|
"^[string_to_regexp $libfile]"
|
|
}
|
|
|
|
# Register another global handler, this one raises an exception. Reload the
|
|
# core-file, the bad handler should be invoked first, which raises an
|
|
# excetption, at which point GDB should skip further Python handlers.
|
|
with_test_prefix "handler raises an exception" {
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(None, rhandler)"
|
|
|
|
foreach_with_prefix exception_type {gdb.GdbError TypeError} {
|
|
gdb_test_no_output \
|
|
"python rhandler.exception_type = $exception_type"
|
|
|
|
# Load the core file. We expect the exception message to appear at
|
|
# least once in the output.
|
|
set re [string_to_regexp \
|
|
"Python Exception <class '$exception_type'>: message"]
|
|
load_core_file "${re}.*"
|
|
|
|
# Our original handler is still registered, but should not have been
|
|
# called again (as the exception occurs first).
|
|
gdb_test "python print(handler_obj.call_count)" "^1" \
|
|
"check good handler hasn't been called again"
|
|
}
|
|
}
|
|
|
|
# Re-start GDB.
|
|
clean_restart_load_python
|
|
|
|
# Attempt to register a missing-debug-handler with NAME. The expectation is
|
|
# that this should fail as NAME contains some invalid characters.
|
|
proc check_bad_name {name} {
|
|
set name_re [string_to_regexp $name]
|
|
set re \
|
|
[multi_line \
|
|
"ValueError.*: invalid character '.' in handler name: $name_re" \
|
|
"Error occurred in Python.*"]
|
|
|
|
gdb_test "python register(\"$name\")" $re \
|
|
"check that '$name' is not accepted"
|
|
}
|
|
|
|
# We don't attempt to be exhaustive here, just check a few random examples
|
|
# of invalid names.
|
|
check_bad_name "!! Bad Name"
|
|
check_bad_name "Bad Name"
|
|
check_bad_name "(Bad Name)"
|
|
check_bad_name "Bad \[Name\]"
|
|
check_bad_name "Bad,Name"
|
|
check_bad_name "Bad;Name"
|
|
|
|
# Check that there are no handlers registered.
|
|
gdb_test_no_output "info missing-objfile-handlers" \
|
|
"check no handlers are registered"
|
|
|
|
# Grab the current program space object, used for registering handler later.
|
|
gdb_test_no_output "python pspace = gdb.selected_inferior().progspace"
|
|
|
|
# Now register some handlers.
|
|
foreach hspec {{\"Foo\" None}
|
|
{\"-bar\" None}
|
|
{\"baz-\" pspace}
|
|
{\"abc-def\" pspace}} {
|
|
lassign $hspec name locus
|
|
gdb_test "python register($name, $locus)"
|
|
}
|
|
|
|
with_test_prefix "all handlers enabled" {
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Current Progspace:" \
|
|
" abc-def" \
|
|
" baz-" \
|
|
"Global:" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
set_debug_file_dir $debugdir_no_lib
|
|
load_core_file
|
|
|
|
# As we perform two look ups, first for the mapped-file then for the
|
|
# shared library, each handler will be called twice.
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', 'baz-', '-bar', 'Foo', 'abc-def', 'baz-', '-bar', 'Foo']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "disable 'baz-'" {
|
|
gdb_test "disable missing-objfile-handler progspace baz-" \
|
|
"^1 missing objfile handler disabled"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def" \
|
|
" baz- \\\[disabled\\\]" \
|
|
"Global:" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', '-bar', 'Foo', 'abc-def', '-bar', 'Foo']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "disable 'Foo'" {
|
|
gdb_test "disable missing-objfile-handler .* Foo" \
|
|
"^1 missing objfile handler disabled"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def" \
|
|
" baz- \\\[disabled\\\]" \
|
|
"Global:" \
|
|
" -bar" \
|
|
" Foo \\\[disabled\\\]"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', '-bar', 'abc-def', '-bar']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "disable everything" {
|
|
gdb_test "disable missing-objfile-handler .* .*" \
|
|
"^2 missing objfile handlers disabled"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def \\\[disabled\\\]" \
|
|
" baz- \\\[disabled\\\]" \
|
|
"Global:" \
|
|
" -bar \\\[disabled\\\]" \
|
|
" Foo \\\[disabled\\\]"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {[]}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "enable 'abc-def'" {
|
|
set re [string_to_regexp $hidden_binfile]
|
|
|
|
gdb_test "enable missing-objfile-handler \"$re\" abc-def" \
|
|
"^1 missing objfile handler enabled" \
|
|
"enable missing-objfile-handler"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def" \
|
|
" baz- \\\[disabled\\\]" \
|
|
"Global:" \
|
|
" -bar \\\[disabled\\\]" \
|
|
" Foo \\\[disabled\\\]"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', 'abc-def']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "enable global handlers" {
|
|
gdb_test "enable missing-objfile-handler global" \
|
|
"^2 missing objfile handlers enabled"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def" \
|
|
" baz- \\\[disabled\\\]" \
|
|
"Global:" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', '-bar', 'Foo', 'abc-def', '-bar', 'Foo']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
# Add handler_obj to the global handler list, and configure it to
|
|
# return False. We should call all of the program space specific
|
|
# handlers (which return None), and then call handler_obj from the
|
|
# global list, which returns False, at which point we shouldn't call
|
|
# anyone else.
|
|
with_test_prefix "return False handler in global list" {
|
|
gdb_test "enable missing-objfile-handler progspace" \
|
|
"^1 missing objfile handler enabled"
|
|
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(None, handler_obj)" \
|
|
"register handler_obj in global list"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" abc-def" \
|
|
" baz-" \
|
|
"Global:" \
|
|
" handler" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_FALSE)" \
|
|
"confirgure handler"
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['abc-def', 'baz-', 'handler', 'abc-def', 'baz-', 'handler']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
# Now add handler_obj to the current program space's handler list. We
|
|
# use the same handler object here, that's fine. We should only see a
|
|
# call to the first handler object in the call log.
|
|
with_test_prefix "return False handler in progspace list" {
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(pspace, handler_obj)" \
|
|
"register handler_obj in progspace list"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" handler" \
|
|
" abc-def" \
|
|
" baz-" \
|
|
"Global:" \
|
|
" handler" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
load_core_file
|
|
gdb_test "python print(handler_call_log)" \
|
|
[string_to_regexp {['handler', 'handler']}]
|
|
gdb_test_no_output "python handler_call_log = \[\]" \
|
|
"reset call log"
|
|
}
|
|
|
|
with_test_prefix "check handler replacement" {
|
|
# First, check we can have the same name appear in both program
|
|
# space and global lists without giving an error.
|
|
gdb_test_no_output "python register(\"Foo\", pspace)"
|
|
|
|
gdb_test "info missing-objfile-handlers" \
|
|
[multi_line \
|
|
"Progspace \[^\r\n\]+:" \
|
|
" Foo" \
|
|
" handler" \
|
|
" abc-def" \
|
|
" baz-" \
|
|
"Global:" \
|
|
" handler" \
|
|
" -bar" \
|
|
" Foo"]
|
|
|
|
# Now check that we get an error if we try to add a handler with
|
|
# the same name.
|
|
gdb_test "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"))" \
|
|
[multi_line \
|
|
"RuntimeError.*: Handler Foo already exists\\." \
|
|
"Error occurred in Python.*"]
|
|
|
|
gdb_test "python gdb.missing_objfile.register_handler(handler=log_handler(\"Foo\"), locus=pspace)" \
|
|
[multi_line \
|
|
"RuntimeError.*: Handler Foo already exists\\." \
|
|
"Error occurred in Python.*"]
|
|
|
|
# And now try again, but this time with 'replace=True', we
|
|
# shouldn't get an error in this case.
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"), replace=True)"
|
|
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(handler=log_handler(\"Foo\"), locus=None, replace=True)"
|
|
|
|
# Now disable a handler and check we still need to use 'replace=True'.
|
|
gdb_test "disable missing-objfile-handler progspace Foo" \
|
|
"^1 missing objfile handler disabled"
|
|
|
|
gdb_test "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"))" \
|
|
[multi_line \
|
|
"RuntimeError.*: Handler Foo already exists\\." \
|
|
"Error occurred in Python.*"] \
|
|
"still get an error when handler is disabled"
|
|
|
|
gdb_test_no_output \
|
|
"python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"), replace=True)" \
|
|
"can replace a disabled handler"
|
|
}
|