forked from Imagelibrary/binutils-gdb
This commit:
commit 8f6c452b5a
Date: Sun Oct 15 22:48:42 2023 +0100
gdb: implement missing debug handler hook for Python
introduced a use of str.isascii(), which was only added in Python 3.7.
This commit switches to use curses.ascii.isascii(), as this was
available in 3.6.
The same is true for str.isalnum(), which is replaced with
curses.ascii.isalnum().
There should be no user visible changes after this commit.
171 lines
5.5 KiB
Python
171 lines
5.5 KiB
Python
# Copyright (C) 2023 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/>.
|
|
|
|
"""
|
|
MissingDebugHandler base class, and register_handler function.
|
|
"""
|
|
|
|
import gdb
|
|
from curses.ascii import isascii, isalnum
|
|
|
|
|
|
def _validate_name(name):
|
|
"""Validate a missing debug handler name string.
|
|
|
|
If name is valid as a missing debug handler name, then this
|
|
function does nothing. If name is not valid then an exception is
|
|
raised.
|
|
|
|
Arguments:
|
|
name: A string, the name of a missing debug handler.
|
|
|
|
Returns:
|
|
Nothing.
|
|
|
|
Raises:
|
|
ValueError: If name is invalid as a missing debug handler
|
|
name.
|
|
"""
|
|
for ch in name:
|
|
if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
|
|
raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
|
|
|
|
|
|
class MissingDebugHandler(object):
|
|
"""Base class for missing debug handlers written in Python.
|
|
|
|
A missing debug handler has a single method __call__ along with
|
|
the read/write attribute enabled, and a read-only attribute name.
|
|
|
|
Attributes:
|
|
name: Read-only attribute, the name of this handler.
|
|
enabled: When true this handler is enabled.
|
|
"""
|
|
|
|
def __init__(self, name):
|
|
"""Constructor.
|
|
|
|
Args:
|
|
name: An identifying name for this handler.
|
|
|
|
Raises:
|
|
TypeError: name is not a string.
|
|
ValueError: name contains invalid characters.
|
|
"""
|
|
|
|
if not isinstance(name, str):
|
|
raise TypeError("incorrect type for name: %s" % type(name))
|
|
|
|
_validate_name(name)
|
|
|
|
self._name = name
|
|
self._enabled = True
|
|
|
|
@property
|
|
def name(self):
|
|
return self._name
|
|
|
|
@property
|
|
def enabled(self):
|
|
return self._enabled
|
|
|
|
@enabled.setter
|
|
def enabled(self, value):
|
|
if not isinstance(value, bool):
|
|
raise TypeError("incorrect type for enabled attribute: %s" % type(value))
|
|
self._enabled = value
|
|
|
|
def __call__(self, objfile):
|
|
"""GDB handle missing debug information for an objfile.
|
|
|
|
Arguments:
|
|
objfile: A gdb.Objfile for which GDB could not find any
|
|
debug information.
|
|
|
|
Returns:
|
|
True: GDB should try again to locate the debug information
|
|
for objfile, the handler may have installed the
|
|
missing information.
|
|
False: GDB should move on without the debug information
|
|
for objfile.
|
|
A string: GDB should load the file at the given path; it
|
|
contains the debug information for objfile.
|
|
None: This handler can't help with objfile. GDB should
|
|
try any other registered handlers.
|
|
"""
|
|
raise NotImplementedError("MissingDebugHandler.__call__()")
|
|
|
|
|
|
def register_handler(locus, handler, replace=False):
|
|
"""Register handler in given locus.
|
|
|
|
The handler is prepended to the locus's missing debug handlers
|
|
list. The name of handler should be unique (or replace must be
|
|
True).
|
|
|
|
Arguments:
|
|
locus: Either a progspace, or None (in which case the unwinder
|
|
is registered globally).
|
|
handler: An object of a gdb.MissingDebugHandler subclass.
|
|
|
|
replace: If True, replaces existing handler with the same name
|
|
within locus. Otherwise, raises RuntimeException if
|
|
unwinder with the same name already exists.
|
|
|
|
Returns:
|
|
Nothing.
|
|
|
|
Raises:
|
|
RuntimeError: The name of handler is not unique.
|
|
TypeError: Bad locus type.
|
|
AttributeError: Required attributes of handler are missing.
|
|
"""
|
|
|
|
if locus is None:
|
|
if gdb.parameter("verbose"):
|
|
gdb.write("Registering global %s handler ...\n" % handler.name)
|
|
locus = gdb
|
|
elif isinstance(locus, gdb.Progspace):
|
|
if gdb.parameter("verbose"):
|
|
gdb.write(
|
|
"Registering %s handler for %s ...\n" % (handler.name, locus.filename)
|
|
)
|
|
else:
|
|
raise TypeError("locus should be gdb.Progspace or None")
|
|
|
|
# Some sanity checks on HANDLER. Calling getattr will raise an
|
|
# exception if the attribute doesn't exist, which is what we want.
|
|
# These checks are not exhaustive; we don't check the attributes
|
|
# have the correct types, or the method has the correct signature,
|
|
# but this should catch some basic mistakes.
|
|
getattr(handler, "name")
|
|
getattr(handler, "enabled")
|
|
call_method = getattr(handler, "__call__")
|
|
if not callable(call_method):
|
|
raise AttributeError(
|
|
"'%s' object's '__call__' attribute is not callable"
|
|
% type(handler).__name__
|
|
)
|
|
|
|
i = 0
|
|
for needle in locus.missing_debug_handlers:
|
|
if needle.name == handler.name:
|
|
if replace:
|
|
del locus.missing_debug_handlers[i]
|
|
else:
|
|
raise RuntimeError("Handler %s already exists." % handler.name)
|
|
i += 1
|
|
locus.missing_debug_handlers.insert(0, handler)
|