| # 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 os | |
| import shutil | |
| 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") |