|
| 1 | +# Copyright (C) 2013-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 gdb |
| 17 | + |
| 18 | + |
| 19 | +class _FrameDecoratorBase(object): |
| 20 | + """Base class of frame decorators.""" |
| 21 | + |
| 22 | + # 'base' can refer to a gdb.Frame or another frame decorator. In |
| 23 | + # the latter case, the child class will have called the super |
| 24 | + # method and _base will be an object conforming to the Frame Filter |
| 25 | + # class. |
| 26 | + def __init__(self, base): |
| 27 | + self._base = base |
| 28 | + |
| 29 | + @staticmethod |
| 30 | + def __is_limited_frame(frame): |
| 31 | + """Internal utility to determine if the frame is special or |
| 32 | + limited.""" |
| 33 | + sal = frame.find_sal() |
| 34 | + |
| 35 | + if ( |
| 36 | + not sal.symtab |
| 37 | + or not sal.symtab.filename |
| 38 | + or frame.type() == gdb.DUMMY_FRAME |
| 39 | + or frame.type() == gdb.SIGTRAMP_FRAME |
| 40 | + ): |
| 41 | + return True |
| 42 | + |
| 43 | + return False |
| 44 | + |
| 45 | + def elided(self): |
| 46 | + """Return any elided frames that this class might be |
| 47 | + wrapping, or None.""" |
| 48 | + if hasattr(self._base, "elided"): |
| 49 | + return self._base.elided() |
| 50 | + |
| 51 | + return None |
| 52 | + |
| 53 | + def function(self): |
| 54 | + """Return the name of the frame's function or an address of |
| 55 | + the function of the frame. First determine if this is a |
| 56 | + special frame. If not, try to determine filename from GDB's |
| 57 | + frame internal function API. Finally, if a name cannot be |
| 58 | + determined return the address. If this function returns an |
| 59 | + address, GDB will attempt to determine the function name from |
| 60 | + its internal minimal symbols store (for example, for inferiors |
| 61 | + without debug-info).""" |
| 62 | + |
| 63 | + # Both gdb.Frame, and FrameDecorator have a method called |
| 64 | + # "function", so determine which object this is. |
| 65 | + if not isinstance(self._base, gdb.Frame): |
| 66 | + if hasattr(self._base, "function"): |
| 67 | + # If it is not a gdb.Frame, and there is already a |
| 68 | + # "function" method, use that. |
| 69 | + return self._base.function() |
| 70 | + |
| 71 | + frame = self.inferior_frame() |
| 72 | + |
| 73 | + if frame.type() == gdb.DUMMY_FRAME: |
| 74 | + return "<function called from gdb>" |
| 75 | + elif frame.type() == gdb.SIGTRAMP_FRAME: |
| 76 | + return "<signal handler called>" |
| 77 | + |
| 78 | + func = frame.name() |
| 79 | + if not isinstance(func, str): |
| 80 | + func = "???" |
| 81 | + return func |
| 82 | + |
| 83 | + def address(self): |
| 84 | + """Return the address of the frame's pc""" |
| 85 | + |
| 86 | + if hasattr(self._base, "address"): |
| 87 | + return self._base.address() |
| 88 | + |
| 89 | + frame = self.inferior_frame() |
| 90 | + return frame.pc() |
| 91 | + |
| 92 | + def frame_args(self): |
| 93 | + """Return an iterable of frame arguments for this frame, if |
| 94 | + any. The iterable object contains objects conforming with the |
| 95 | + Symbol/Value interface. If there are no frame arguments, or |
| 96 | + if this frame is deemed to be a special case, return None.""" |
| 97 | + |
| 98 | + if hasattr(self._base, "frame_args"): |
| 99 | + return self._base.frame_args() |
| 100 | + |
| 101 | + frame = self.inferior_frame() |
| 102 | + if self.__is_limited_frame(frame): |
| 103 | + return None |
| 104 | + |
| 105 | + args = FrameVars(frame) |
| 106 | + return args.fetch_frame_args() |
| 107 | + |
| 108 | + def frame_locals(self): |
| 109 | + """Return an iterable of local variables for this frame, if |
| 110 | + any. The iterable object contains objects conforming with the |
| 111 | + Symbol/Value interface. If there are no frame locals, or if |
| 112 | + this frame is deemed to be a special case, return None.""" |
| 113 | + |
| 114 | + if hasattr(self._base, "frame_locals"): |
| 115 | + return self._base.frame_locals() |
| 116 | + |
| 117 | + frame = self.inferior_frame() |
| 118 | + if self.__is_limited_frame(frame): |
| 119 | + return None |
| 120 | + |
| 121 | + args = FrameVars(frame) |
| 122 | + return args.fetch_frame_locals() |
| 123 | + |
| 124 | + def line(self): |
| 125 | + """Return line number information associated with the frame's |
| 126 | + pc. If symbol table/line information does not exist, or if |
| 127 | + this frame is deemed to be a special case, return None""" |
| 128 | + |
| 129 | + if hasattr(self._base, "line"): |
| 130 | + return self._base.line() |
| 131 | + |
| 132 | + frame = self.inferior_frame() |
| 133 | + if self.__is_limited_frame(frame): |
| 134 | + return None |
| 135 | + |
| 136 | + sal = frame.find_sal() |
| 137 | + if sal: |
| 138 | + return sal.line |
| 139 | + else: |
| 140 | + return None |
| 141 | + |
| 142 | + def inferior_frame(self): |
| 143 | + """Return the gdb.Frame underpinning this frame decorator.""" |
| 144 | + |
| 145 | + # If 'base' is a frame decorator, we want to call its inferior |
| 146 | + # frame method. If '_base' is a gdb.Frame, just return that. |
| 147 | + if hasattr(self._base, "inferior_frame"): |
| 148 | + return self._base.inferior_frame() |
| 149 | + return self._base |
| 150 | + |
| 151 | + |
| 152 | +class FrameDecorator(_FrameDecoratorBase): |
| 153 | + """Basic implementation of a Frame Decorator |
| 154 | +
|
| 155 | + This base frame decorator decorates a frame or another frame |
| 156 | + decorator, and provides convenience methods. If this object is |
| 157 | + wrapping a frame decorator, defer to that wrapped object's method |
| 158 | + if it has one. This allows for frame decorators that have |
| 159 | + sub-classed FrameDecorator object, but also wrap other frame |
| 160 | + decorators on the same frame to correctly execute. |
| 161 | +
|
| 162 | + E.g |
| 163 | +
|
| 164 | + If the result of frame filters running means we have one gdb.Frame |
| 165 | + wrapped by multiple frame decorators, all sub-classed from |
| 166 | + FrameDecorator, the resulting hierarchy will be: |
| 167 | +
|
| 168 | + Decorator1 |
| 169 | + -- (wraps) Decorator2 |
| 170 | + -- (wraps) FrameDecorator |
| 171 | + -- (wraps) gdb.Frame |
| 172 | +
|
| 173 | + In this case we have two frame decorators, both of which are |
| 174 | + sub-classed from FrameDecorator. If Decorator1 just overrides the |
| 175 | + 'function' method, then all of the other methods are carried out |
| 176 | + by the super-class FrameDecorator. But Decorator2 may have |
| 177 | + overriden other methods, so FrameDecorator will look at the |
| 178 | + 'base' parameter and defer to that class's methods. And so on, |
| 179 | + down the chain.""" |
| 180 | + |
| 181 | + def filename(self): |
| 182 | + """Return the filename associated with this frame, detecting |
| 183 | + and returning the appropriate library name is this is a shared |
| 184 | + library.""" |
| 185 | + |
| 186 | + if hasattr(self._base, "filename"): |
| 187 | + return self._base.filename() |
| 188 | + |
| 189 | + frame = self.inferior_frame() |
| 190 | + sal = frame.find_sal() |
| 191 | + if not sal.symtab or not sal.symtab.filename: |
| 192 | + pc = frame.pc() |
| 193 | + return gdb.solib_name(pc) |
| 194 | + else: |
| 195 | + return sal.symtab.filename |
| 196 | + |
| 197 | + |
| 198 | +class DAPFrameDecorator(_FrameDecoratorBase): |
| 199 | + """Like FrameDecorator, but has slightly different results |
| 200 | + for the "filename" method.""" |
| 201 | + |
| 202 | + def filename(self): |
| 203 | + """Return the filename associated with this frame, detecting |
| 204 | + and returning the appropriate library name is this is a shared |
| 205 | + library.""" |
| 206 | + |
| 207 | + if hasattr(self._base, "filename"): |
| 208 | + return self._base.filename() |
| 209 | + |
| 210 | + frame = self.inferior_frame() |
| 211 | + sal = frame.find_sal() |
| 212 | + if sal.symtab is not None: |
| 213 | + return sal.symtab.fullname() |
| 214 | + return None |
| 215 | + |
| 216 | + def frame_locals(self): |
| 217 | + """Return an iterable of local variables for this frame, if |
| 218 | + any. The iterable object contains objects conforming with the |
| 219 | + Symbol/Value interface. If there are no frame locals, or if |
| 220 | + this frame is deemed to be a special case, return None.""" |
| 221 | + |
| 222 | + if hasattr(self._base, "frame_locals"): |
| 223 | + return self._base.frame_locals() |
| 224 | + |
| 225 | + frame = self.inferior_frame() |
| 226 | + args = FrameVars(frame) |
| 227 | + return args.fetch_frame_locals(True) |
| 228 | + |
| 229 | + |
| 230 | +class SymValueWrapper(object): |
| 231 | + """A container class conforming to the Symbol/Value interface |
| 232 | + which holds frame locals or frame arguments.""" |
| 233 | + |
| 234 | + # The FRAME argument is needed here because gdb.Symbol doesn't |
| 235 | + # carry the block with it, and so read_var can't find symbols from |
| 236 | + # outer (static link) frames. |
| 237 | + def __init__(self, frame, symbol): |
| 238 | + self.frame = frame |
| 239 | + self.sym = symbol |
| 240 | + |
| 241 | + def value(self): |
| 242 | + """Return the value associated with this symbol, or None""" |
| 243 | + if self.frame is None: |
| 244 | + return None |
| 245 | + return self.frame.read_var(self.sym) |
| 246 | + |
| 247 | + def symbol(self): |
| 248 | + """Return the symbol, or Python text, associated with this |
| 249 | + symbol, or None""" |
| 250 | + return self.sym |
| 251 | + |
| 252 | + |
| 253 | +class FrameVars(object): |
| 254 | + """Utility class to fetch and store frame local variables, or |
| 255 | + frame arguments.""" |
| 256 | + |
| 257 | + def __init__(self, frame): |
| 258 | + self.frame = frame |
| 259 | + |
| 260 | + def fetch_frame_locals(self, follow_link=False): |
| 261 | + """Public utility method to fetch frame local variables for |
| 262 | + the stored frame. Frame arguments are not fetched. If there |
| 263 | + are no frame local variables, return an empty list.""" |
| 264 | + lvars = [] |
| 265 | + |
| 266 | + frame = self.frame |
| 267 | + try: |
| 268 | + block = frame.block() |
| 269 | + except RuntimeError: |
| 270 | + block = None |
| 271 | + |
| 272 | + traversed_link = False |
| 273 | + while block is not None: |
| 274 | + if block.is_global or block.is_static: |
| 275 | + break |
| 276 | + for sym in block: |
| 277 | + # Exclude arguments from the innermost function, but |
| 278 | + # if we found and traversed a static link, just treat |
| 279 | + # all such variables as "local". |
| 280 | + if sym.is_argument: |
| 281 | + if not traversed_link: |
| 282 | + continue |
| 283 | + elif not sym.is_variable: |
| 284 | + # We use an 'elif' here because is_variable |
| 285 | + # returns False for arguments as well. Anyway, |
| 286 | + # don't include non-variables here. |
| 287 | + continue |
| 288 | + lvars.append(SymValueWrapper(frame, sym)) |
| 289 | + |
| 290 | + if block.function is not None: |
| 291 | + if not follow_link: |
| 292 | + break |
| 293 | + # If the frame has a static link, follow it here. |
| 294 | + traversed_link = True |
| 295 | + frame = frame.static_link() |
| 296 | + if frame is None: |
| 297 | + break |
| 298 | + try: |
| 299 | + block = frame.block() |
| 300 | + except RuntimeError: |
| 301 | + block = None |
| 302 | + else: |
| 303 | + block = block.superblock |
| 304 | + |
| 305 | + return lvars |
| 306 | + |
| 307 | + def fetch_frame_args(self): |
| 308 | + """Public utility method to fetch frame arguments for the |
| 309 | + stored frame. Frame arguments are the only type fetched. If |
| 310 | + there are no frame argument variables, return an empty list.""" |
| 311 | + |
| 312 | + args = [] |
| 313 | + |
| 314 | + try: |
| 315 | + block = self.frame.block() |
| 316 | + except RuntimeError: |
| 317 | + block = None |
| 318 | + |
| 319 | + while block is not None: |
| 320 | + if block.is_global or block.is_static: |
| 321 | + break |
| 322 | + for sym in block: |
| 323 | + if not sym.is_argument: |
| 324 | + continue |
| 325 | + args.append(SymValueWrapper(None, sym)) |
| 326 | + |
| 327 | + # Stop when the function itself is seen, to avoid showing |
| 328 | + # variables from outer functions in a nested function. |
| 329 | + # Note that we don't traverse the static link for |
| 330 | + # arguments, only for locals. |
| 331 | + if block.function is not None: |
| 332 | + break |
| 333 | + |
| 334 | + block = block.superblock |
| 335 | + |
| 336 | + return args |
0 commit comments