mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-12-19 01:19:41 +08:00
The commit:
commit 29c7078711
Date: Sun Sep 8 07:46:09 2024 +0200
[gdb/testsuite] Handle missing curses in gdb.python/py-missing-debug.exp
Highlighted that in some cases we might be running on a system with an
older version of Python (earlier than 3.7), and on a system for which
the curses library has not been installed.
In these circumstances the gdb.missing_debug module will not load as
it uses curses to provide isalnum() and isascii() functions.
To avoid this problem I propose that we copy the isalnum() and
isascii() from the Python curses library. These functions are
basically trivial and removing the curses dependency means GDB will
work in more cases without increasing its dependencies.
I did consider keeping the uses of curses and only having the function
definitions be a fallback for when the curses library failed to load,
but this felt like overkill. The function definitions are both tiny
and I think "obvious" given their specifications, so I figure we might
as well just use our own definitions if they are not available as
builtin methods on the str class.
For testing I changed this line:
if sys.version_info >= (3, 7):
to
if sys.version_info >= (3, 7) and False:
then reran gdb.python/py-missing-debug.exp, there were no failures.
Approved-By: Tom de Vries <tdevries@suse.de>
210 lines
6.5 KiB
Python
210 lines
6.5 KiB
Python
# Copyright (C) 2023-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/>.
|
|
|
|
"""
|
|
MissingDebugHandler base class, and register_handler function.
|
|
"""
|
|
|
|
import sys
|
|
|
|
import gdb
|
|
|
|
if sys.version_info >= (3, 7):
|
|
# Functions str.isascii() and str.isalnum are available starting Python
|
|
# 3.7.
|
|
def isascii(ch):
|
|
return ch.isascii()
|
|
|
|
def isalnum(ch):
|
|
return ch.isalnum()
|
|
|
|
else:
|
|
# Older version of Python doesn't have str.isascii() and
|
|
# str.isalnum() so provide our own.
|
|
#
|
|
# We could import isalnum() and isascii() from the curses library,
|
|
# but that adds an extra dependency. Given these functions are
|
|
# both small and trivial lets implement them here.
|
|
#
|
|
# These definitions are based on those in the curses library, but
|
|
# simplified as we know C will always be a single character 'str'.
|
|
|
|
def isdigit(c):
|
|
return 48 <= ord(c) <= 57
|
|
|
|
def islower(c):
|
|
return 97 <= ord(c) <= 122
|
|
|
|
def isupper(c):
|
|
return 65 <= ord(c) <= 90
|
|
|
|
def isalpha(c):
|
|
return isupper(c) or islower(c)
|
|
|
|
def isalnum(c):
|
|
return isalpha(c) or isdigit(c)
|
|
|
|
def isascii(c):
|
|
return 0 <= ord(c) <= 127
|
|
|
|
|
|
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)
|