aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.python
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/testsuite/gdb.python')
-rw-r--r--gdb/testsuite/gdb.python/py-missing-objfile-lib.c35
-rw-r--r--gdb/testsuite/gdb.python/py-missing-objfile.c49
-rw-r--r--gdb/testsuite/gdb.python/py-missing-objfile.exp565
-rw-r--r--gdb/testsuite/gdb.python/py-missing-objfile.py167
4 files changed, 816 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.python/py-missing-objfile-lib.c b/gdb/testsuite/gdb.python/py-missing-objfile-lib.c
new file mode 100644
index 0000000..8d740b4
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-missing-objfile-lib.c
@@ -0,0 +1,35 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+struct lib_type
+{
+ int a;
+ int b;
+};
+
+volatile struct lib_type global_lib_var = { 0, 0 };
+
+int
+foo (void)
+{
+ int res = 0;
+
+ res += global_lib_var.a;
+ res += global_lib_var.b;
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.c b/gdb/testsuite/gdb.python/py-missing-objfile.c
new file mode 100644
index 0000000..953e1c0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-missing-objfile.c
@@ -0,0 +1,49 @@
+/* This test program is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <stdlib.h>
+
+struct exec_type
+{
+ int a;
+ int b;
+ int c;
+};
+
+volatile struct exec_type global_exec_var = { 0, 0, 0 };
+
+extern int foo (void);
+
+void
+dump_core (void)
+{
+ abort ();
+}
+
+int
+main (void)
+{
+ int res = foo ();
+
+ res += global_exec_var.a;
+ res += global_exec_var.b;
+ res += global_exec_var.c;
+
+ dump_core ();
+
+ return res;
+}
diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.exp b/gdb/testsuite/gdb.python/py-missing-objfile.exp
new file mode 100644
index 0000000..15a6952
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-missing-objfile.exp
@@ -0,0 +1,565 @@
+# 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 == ""} {
+ unsupport "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"
+}
diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.py b/gdb/testsuite/gdb.python/py-missing-objfile.py
new file mode 100644
index 0000000..9b15896
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-missing-objfile.py
@@ -0,0 +1,167 @@
+# 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/>.
+
+import shutil
+import os
+from enum import Enum
+
+import gdb
+from gdb.missing_objfile import MissingObjfileHandler
+
+# A global log that is filled in by instances of the LOG_HANDLER class
+# when they are called.
+handler_call_log = []
+
+# A global holding a string, the build-id of the last missing objfile
+# which triggered the 'handler' class below. This is set in the
+# __call__ method of the 'handler' class and then checked from the
+# expect script.
+handler_last_buildid = None
+
+
+# A global holding a string, the filename of the last missing objfile
+# which triggered the 'handler' class below. This is set in the
+# __call__ method of the 'handler' class and then checked from the
+# expect script.
+handler_last_filename = None
+
+
+# A helper function that makes some assertions about the arguments
+# passed to a MissingObjfileHandler.__call__() method.
+def check_args(pspace, buildid, filename):
+ assert type(filename) == str
+ assert filename != ""
+ assert type(pspace) == gdb.Progspace
+ assert type(buildid) == str
+ assert buildid != ""
+
+
+# Enum used to configure the 'handler' class from the test script.
+class Mode(Enum):
+ RETURN_NONE = 0
+ RETURN_TRUE = 1
+ RETURN_FALSE = 2
+ RETURN_STRING = 3
+
+
+# A missing objfile handler which can be configured to return each of
+# the different possible return types.
+class handler(MissingObjfileHandler):
+ def __init__(self):
+ super().__init__("handler")
+ self._call_count = 0
+ self._mode = Mode.RETURN_NONE
+
+ def __call__(self, pspace, buildid, filename):
+ global handler_call_log, handler_last_buildid, handler_last_filename
+ check_args(pspace, buildid, filename)
+ handler_call_log.append(self.name)
+ handler_last_buildid = buildid
+ handler_last_filename = filename
+ self._call_count += 1
+ if self._mode == Mode.RETURN_NONE:
+ return None
+
+ if self._mode == Mode.RETURN_TRUE:
+ shutil.copy(self._src, self._dest)
+
+ # If we're using the fission-dwp board then there will
+ # also be a .dwp file that needs to be copied.
+ dwp_src = self._src + ".dwp"
+ if os.path.exists(dwp_src):
+ dwp_dest = self._dest + ".dwp"
+ shutil.copy(dwp_src, dwp_dest)
+
+ return True
+
+ if self._mode == Mode.RETURN_FALSE:
+ return False
+
+ if self._mode == Mode.RETURN_STRING:
+ return self._dest
+
+ assert False
+
+ @property
+ def call_count(self):
+ """Return a count, the number of calls to __call__ since the last
+ call to set_mode.
+ """
+ return self._call_count
+
+ def set_mode(self, mode, *args):
+ self._call_count = 0
+ self._mode = mode
+
+ if mode == Mode.RETURN_NONE:
+ assert len(args) == 0
+ return
+
+ if mode == Mode.RETURN_TRUE:
+ assert len(args) == 2
+ self._src = args[0]
+ self._dest = args[1]
+ return
+
+ if mode == Mode.RETURN_FALSE:
+ assert len(args) == 0
+ return
+
+ if mode == Mode.RETURN_STRING:
+ assert len(args) == 1
+ self._dest = args[0]
+ return
+
+ assert False
+
+
+# A missing objfile handler which raises an exception. The type of
+# exception to be raised is configured from the test script.
+class exception_handler(MissingObjfileHandler):
+ def __init__(self):
+ super().__init__("exception_handler")
+ self.exception_type = None
+
+ def __call__(self, pspace, buildid, filename):
+ global handler_call_log
+ check_args(pspace, buildid, filename)
+ handler_call_log.append(self.name)
+ assert self.exception_type is not None
+ raise self.exception_type("message")
+
+
+# A very simple logging missing objfile handler. Always returns None
+# so that GDB will try any other registered handlers, but first logs
+# the name of this handler into the global HANDLER_CALL_LOG, which can
+# then be checked from the test script.
+class log_handler(MissingObjfileHandler):
+ def __call__(self, pspace, buildid, filename):
+ global handler_call_log
+ check_args(pspace, buildid, filename)
+ handler_call_log.append(self.name)
+ return None
+
+
+# A basic helper function, this keeps lines shorter in the TCL script.
+def register(name, locus=None):
+ gdb.missing_objfile.register_handler(locus, log_handler(name))
+
+
+# Create instances of the handlers, but don't install any. We install
+# these as needed from the TCL script.
+rhandler = exception_handler()
+handler_obj = handler()
+
+print("Success")