1 # Copyright (C) 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 import os 17 import shutil 18 from enum import Enum 19 20 import gdb 21 from gdb.missing_objfile import MissingObjfileHandler 22 23 # A global log that is filled in by instances of the LOG_HANDLER class 24 # when they are called. 25 handler_call_log = [] 26 27 # A global holding a string, the build-id of the last missing objfile 28 # which triggered the 'handler' class below. This is set in the 29 # __call__ method of the 'handler' class and then checked from the 30 # expect script. 31 handler_last_buildid = None 32 33 34 # A global holding a string, the filename of the last missing objfile 35 # which triggered the 'handler' class below. This is set in the 36 # __call__ method of the 'handler' class and then checked from the 37 # expect script. 38 handler_last_filename = None 39 40 41 # A helper function that makes some assertions about the arguments 42 # passed to a MissingObjfileHandler.__call__() method. 43 def check_args(pspace, buildid, filename): 44 assert type(filename) == str 45 assert filename != "" 46 assert type(pspace) == gdb.Progspace 47 assert type(buildid) == str 48 assert buildid != "" 49 50 51 # Enum used to configure the 'handler' class from the test script. 52 class Mode(Enum): 53 RETURN_NONE = 0 54 RETURN_TRUE = 1 55 RETURN_FALSE = 2 56 RETURN_STRING = 3 57 58 59 # A missing objfile handler which can be configured to return each of 60 # the different possible return types. 61 class handler(MissingObjfileHandler): 62 def __init__(self): 63 super().__init__("handler") 64 self._call_count = 0 65 self._mode = Mode.RETURN_NONE 66 67 def __call__(self, pspace, buildid, filename): 68 global handler_call_log, handler_last_buildid, handler_last_filename 69 check_args(pspace, buildid, filename) 70 handler_call_log.append(self.name) 71 handler_last_buildid = buildid 72 handler_last_filename = filename 73 self._call_count += 1 74 if self._mode == Mode.RETURN_NONE: 75 return None 76 77 if self._mode == Mode.RETURN_TRUE: 78 shutil.copy(self._src, self._dest) 79 80 # If we're using the fission-dwp board then there will 81 # also be a .dwp file that needs to be copied. 82 dwp_src = self._src + ".dwp" 83 if os.path.exists(dwp_src): 84 dwp_dest = self._dest + ".dwp" 85 shutil.copy(dwp_src, dwp_dest) 86 87 return True 88 89 if self._mode == Mode.RETURN_FALSE: 90 return False 91 92 if self._mode == Mode.RETURN_STRING: 93 return self._dest 94 95 assert False 96 97 @property 98 def call_count(self): 99 """Return a count, the number of calls to __call__ since the last 100 call to set_mode. 101 """ 102 return self._call_count 103 104 def set_mode(self, mode, *args): 105 self._call_count = 0 106 self._mode = mode 107 108 if mode == Mode.RETURN_NONE: 109 assert len(args) == 0 110 return 111 112 if mode == Mode.RETURN_TRUE: 113 assert len(args) == 2 114 self._src = args[0] 115 self._dest = args[1] 116 return 117 118 if mode == Mode.RETURN_FALSE: 119 assert len(args) == 0 120 return 121 122 if mode == Mode.RETURN_STRING: 123 assert len(args) == 1 124 self._dest = args[0] 125 return 126 127 assert False 128 129 130 # A missing objfile handler which raises an exception. The type of 131 # exception to be raised is configured from the test script. 132 class exception_handler(MissingObjfileHandler): 133 def __init__(self): 134 super().__init__("exception_handler") 135 self.exception_type = None 136 137 def __call__(self, pspace, buildid, filename): 138 global handler_call_log 139 check_args(pspace, buildid, filename) 140 handler_call_log.append(self.name) 141 assert self.exception_type is not None 142 raise self.exception_type("message") 143 144 145 # A very simple logging missing objfile handler. Always returns None 146 # so that GDB will try any other registered handlers, but first logs 147 # the name of this handler into the global HANDLER_CALL_LOG, which can 148 # then be checked from the test script. 149 class log_handler(MissingObjfileHandler): 150 def __call__(self, pspace, buildid, filename): 151 global handler_call_log 152 check_args(pspace, buildid, filename) 153 handler_call_log.append(self.name) 154 return None 155 156 157 # A basic helper function, this keeps lines shorter in the TCL script. 158 def register(name, locus=None): 159 gdb.missing_objfile.register_handler(locus, log_handler(name)) 160 161 162 # Create instances of the handlers, but don't install any. We install 163 # these as needed from the TCL script. 164 rhandler = exception_handler() 165 handler_obj = handler() 166 167 print("Success") 168