aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.python/py-missing-objfile.py
diff options
context:
space:
mode:
authorAndrew Burgess <aburgess@redhat.com>2024-07-31 15:55:57 +0100
committerAndrew Burgess <aburgess@redhat.com>2024-11-10 10:18:23 +0000
commit5cabc8098e65ac22d4245232ad20b19fa4729802 (patch)
treebdbda2463e12abd122031830602df12ed43f3a28 /gdb/testsuite/gdb.python/py-missing-objfile.py
parentef1a41f20b7ea4799e09b37312dc051245435fd1 (diff)
downloadbinutils-5cabc8098e65ac22d4245232ad20b19fa4729802.zip
binutils-5cabc8098e65ac22d4245232ad20b19fa4729802.tar.gz
binutils-5cabc8098e65ac22d4245232ad20b19fa4729802.tar.bz2
gdb/python: implement Python find_exec_by_build_id hook
Implement extension_language_ops::find_objfile_from_buildid within GDB's Python API. Doing this allows users to write Python extensions that can help locate missing objfiles when GDB opens a core file. A handler might perform some project- or site-specific actions to find a missing objfile. Or might provide some project- or site-specific advice to the user on how they can obtain the missing objfile. The implementation is very similar to the approach taken in: commit 8f6c452b5a4e50fbb55ff1d13328b392ad1fd416 Date: Sun Oct 15 22:48:42 2023 +0100 gdb: implement missing debug handler hook for Python The following new commands are added as commands implemented in Python, this is similar to how the Python missing debug and unwinder commands are implemented: info missing-objfile-handlers enable missing-objfile-handler LOCUS HANDLER disable missing-objfile-handler LOCUS HANDLER To make use of this extension hook a user will create missing objfile handler objects, and registers these handlers with GDB. When GDB opens a core file and encounters a missing objfile each handler is called in turn until one is able to help. Here is a minimal handler that does nothing useful: import gdb import gdb.missing_objfile class MyFirstHandler(gdb.missing_objfile.MissingObjfileHandler): def __init__(self): super().__init__("my_first_handler") def __call__(self, pspace, build_id, filename): # This handler does nothing useful. return None gdb.missing_objfile.register_handler(None, MyFirstHandler()) Returning None from the __call__ method tells GDB that this handler was unable to find the missing objfile, and GDB should ask any other registered handlers. Possible return values from a handler: - None: This means the handler couldn't help. GDB will call other registered handlers to see if they can help instead. - False: The handler has done all it can, but the objfile couldn't be found. GDB will not call any other handlers, and will continue without the objfile. - True: The handler has installed the objfile into a location where GDB would normally expect to find it. GDB should repeat its normal lookup process and the objfile should now be found. - A string: The handler can return a filename, which is the missing objfile. GDB will load this file. Handlers can be registered globally, or per program space. GDB checks the handlers for the current program space first, and then all of the global handles. The first handler that returns a value that is not None, has "handled" the missing objfile, at which point GDB continues. The implementation of this feature is mostly straight forward. I have reworked some of the missing debug file related code so that it can be shared with this feature. E.g. gdb/python/lib/gdb/missing_files.py is mostly content moved from gdb/python/lib/gdb/missing_debug.py, but updated to be more generic. Now gdb/python/lib/gdb/missing_debug.py and the new file gdb/python/lib/gdb/missing_objfile.py both call into the missing_files.py file. For gdb/python/lib/gdb/command/missing_files.py this is even more extreme, gdb/python/lib/gdb/command/missing_debug.py is completely gone now and gdb/python/lib/gdb/command/missing_files.py provides all of the new commands in a generic way. I have made one change to the existing Python API, I renamed the attribute Progspace.missing_debug_handlers to Progspace.missing_file_handlers. I don't see this as too problematic. This attribute was only used to implement the missing debug feature and was never documented beyond the fact that it existed. There was no reason for users to be touching this attribute. Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Diffstat (limited to 'gdb/testsuite/gdb.python/py-missing-objfile.py')
-rw-r--r--gdb/testsuite/gdb.python/py-missing-objfile.py167
1 files changed, 167 insertions, 0 deletions
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")