mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-07-29 04:43:31 +08:00

New in this version: - Add a PY_MAJOR_VERSION check in configure.ac / AC_TRY_LIBPYTHON. If the user passes --with-python=python2, this will cause a configure failure saying that GDB only supports Python 3. Support for Python 2 is a maintenance burden for any patches touching Python support. Among others, the differences between Python 2 and 3 string and integer types are subtle. It requires a lot of effort and thinking to get something that behaves correctly on both. And that's if the author and reviewer of the patch even remember to test with Python 2. See this thread for an example: https://sourceware.org/pipermail/gdb-patches/2021-December/184260.html So, remove Python 2 support. Update the documentation to state that GDB can be built against Python 3 (as opposed to Python 2 or 3). Update all the spots that use: - sys.version_info - IS_PY3K - PY_MAJOR_VERSION - gdb_py_is_py3k ... to only keep the Python 3 portions and drop the use of some now-removed compatibility macros. I did not update the configure script more than just removing the explicit references to Python 2. We could maybe do more there, like check the Python version and reject it if that version is not supported. Otherwise (with this patch), things will only fail at compile time, so it won't really be clear to the user that they are trying to use an unsupported Python version. But I'm a bit lost in the configure code that checks for Python, so I kept that for later. Change-Id: I75b0f79c148afbe3c07ac664cfa9cade052c0c62
275 lines
11 KiB
Python
275 lines
11 KiB
Python
# Python side of the support for xmethods.
|
|
# Copyright (C) 2013-2022 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/>.
|
|
|
|
"""Utilities for defining xmethods"""
|
|
|
|
import gdb
|
|
import re
|
|
import sys
|
|
|
|
|
|
class XMethod(object):
|
|
"""Base class (or a template) for an xmethod description.
|
|
|
|
Currently, the description requires only the 'name' and 'enabled'
|
|
attributes. Description objects are managed by 'XMethodMatcher'
|
|
objects (see below). Note that this is only a template for the
|
|
interface of the XMethodMatcher.methods objects. One could use
|
|
this class or choose to use an object which supports this exact same
|
|
interface. Also, an XMethodMatcher can choose not use it 'methods'
|
|
attribute. In such cases this class (or an equivalent) is not used.
|
|
|
|
Attributes:
|
|
name: The name of the xmethod.
|
|
enabled: A boolean indicating if the xmethod is enabled.
|
|
"""
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.enabled = True
|
|
|
|
|
|
class XMethodMatcher(object):
|
|
"""Abstract base class for matching an xmethod.
|
|
|
|
When looking for xmethods, GDB invokes the `match' method of a
|
|
registered xmethod matcher to match the object type and method name.
|
|
The `match' method in concrete classes derived from this class should
|
|
return an `XMethodWorker' object, or a list of `XMethodWorker'
|
|
objects if there is a match (see below for 'XMethodWorker' class).
|
|
|
|
Attributes:
|
|
name: The name of the matcher.
|
|
enabled: A boolean indicating if the matcher is enabled.
|
|
methods: A sequence of objects of type 'XMethod', or objects
|
|
which have at least the attributes of an 'XMethod' object.
|
|
This list is used by the 'enable'/'disable'/'info' commands to
|
|
enable/disable/list the xmethods registered with GDB. See
|
|
the 'match' method below to know how this sequence is used.
|
|
This attribute is None if the matcher chooses not have any
|
|
xmethods managed by it.
|
|
"""
|
|
|
|
def __init__(self, name):
|
|
"""
|
|
Args:
|
|
name: An identifying name for the xmethod or the group of
|
|
xmethods returned by the `match' method.
|
|
"""
|
|
self.name = name
|
|
self.enabled = True
|
|
self.methods = None
|
|
|
|
def match(self, class_type, method_name):
|
|
"""Match class type and method name.
|
|
|
|
In derived classes, it should return an XMethodWorker object, or a
|
|
sequence of 'XMethodWorker' objects. Only those xmethod workers
|
|
whose corresponding 'XMethod' descriptor object is enabled should be
|
|
returned.
|
|
|
|
Args:
|
|
class_type: The class type (gdb.Type object) to match.
|
|
method_name: The name (string) of the method to match.
|
|
"""
|
|
raise NotImplementedError("XMethodMatcher match")
|
|
|
|
|
|
class XMethodWorker(object):
|
|
"""Base class for all xmethod workers defined in Python.
|
|
|
|
An xmethod worker is an object which matches the method arguments, and
|
|
invokes the method when GDB wants it to. Internally, GDB first invokes the
|
|
'get_arg_types' method to perform overload resolution. If GDB selects to
|
|
invoke this Python xmethod, then it invokes it via the overridden
|
|
'__call__' method. The 'get_result_type' method is used to implement
|
|
'ptype' on the xmethod.
|
|
|
|
Derived classes should override the 'get_arg_types', 'get_result_type'
|
|
and '__call__' methods.
|
|
"""
|
|
|
|
def get_arg_types(self):
|
|
"""Return arguments types of an xmethod.
|
|
|
|
A sequence of gdb.Type objects corresponding to the arguments of the
|
|
xmethod are returned. If the xmethod takes no arguments, then 'None'
|
|
or an empty sequence is returned. If the xmethod takes only a single
|
|
argument, then a gdb.Type object or a sequence with a single gdb.Type
|
|
element is returned.
|
|
"""
|
|
raise NotImplementedError("XMethodWorker get_arg_types")
|
|
|
|
def get_result_type(self, *args):
|
|
"""Return the type of the result of the xmethod.
|
|
|
|
Args:
|
|
args: Arguments to the method. Each element of the tuple is a
|
|
gdb.Value object. The first element is the 'this' pointer
|
|
value. These are the same arguments passed to '__call__'.
|
|
|
|
Returns:
|
|
A gdb.Type object representing the type of the result of the
|
|
xmethod.
|
|
"""
|
|
raise NotImplementedError("XMethodWorker get_result_type")
|
|
|
|
def __call__(self, *args):
|
|
"""Invoke the xmethod.
|
|
|
|
Args:
|
|
args: Arguments to the method. Each element of the tuple is a
|
|
gdb.Value object. The first element is the 'this' pointer
|
|
value.
|
|
|
|
Returns:
|
|
A gdb.Value corresponding to the value returned by the xmethod.
|
|
Returns 'None' if the method does not return anything.
|
|
"""
|
|
raise NotImplementedError("XMethodWorker __call__")
|
|
|
|
|
|
class SimpleXMethodMatcher(XMethodMatcher):
|
|
"""A utility class to implement simple xmethod mathers and workers.
|
|
|
|
See the __init__ method below for information on how instances of this
|
|
class can be used.
|
|
|
|
For simple classes and methods, one can choose to use this class. For
|
|
complex xmethods, which need to replace/implement template methods on
|
|
possibly template classes, one should implement their own xmethod
|
|
matchers and workers. See py-xmethods.py in testsuite/gdb.python
|
|
directory of the GDB source tree for examples.
|
|
"""
|
|
|
|
class SimpleXMethodWorker(XMethodWorker):
|
|
def __init__(self, method_function, arg_types):
|
|
self._arg_types = arg_types
|
|
self._method_function = method_function
|
|
|
|
def get_arg_types(self):
|
|
return self._arg_types
|
|
|
|
def __call__(self, *args):
|
|
return self._method_function(*args)
|
|
|
|
def __init__(
|
|
self, name, class_matcher, method_matcher, method_function, *arg_types
|
|
):
|
|
"""
|
|
Args:
|
|
name: Name of the xmethod matcher.
|
|
class_matcher: A regular expression used to match the name of the
|
|
class whose method this xmethod is implementing/replacing.
|
|
method_matcher: A regular expression used to match the name of the
|
|
method this xmethod is implementing/replacing.
|
|
method_function: A Python callable which would be called via the
|
|
'invoke' method of the worker returned by the objects of this
|
|
class. This callable should accept the object (*this) as the
|
|
first argument followed by the rest of the arguments to the
|
|
method. All arguments to this function should be gdb.Value
|
|
objects.
|
|
arg_types: The gdb.Type objects corresponding to the arguments that
|
|
this xmethod takes. It can be None, or an empty sequence,
|
|
or a single gdb.Type object, or a sequence of gdb.Type objects.
|
|
"""
|
|
XMethodMatcher.__init__(self, name)
|
|
assert callable(method_function), (
|
|
"The 'method_function' argument to 'SimpleXMethodMatcher' "
|
|
"__init__ method should be a callable."
|
|
)
|
|
self._method_function = method_function
|
|
self._class_matcher = class_matcher
|
|
self._method_matcher = method_matcher
|
|
self._arg_types = arg_types
|
|
|
|
def match(self, class_type, method_name):
|
|
cm = re.match(self._class_matcher, str(class_type.unqualified().tag))
|
|
mm = re.match(self._method_matcher, method_name)
|
|
if cm and mm:
|
|
return SimpleXMethodMatcher.SimpleXMethodWorker(
|
|
self._method_function, self._arg_types
|
|
)
|
|
|
|
|
|
# A helper function for register_xmethod_matcher which returns an error
|
|
# object if MATCHER is not having the requisite attributes in the proper
|
|
# format.
|
|
|
|
|
|
def _validate_xmethod_matcher(matcher):
|
|
if not hasattr(matcher, "match"):
|
|
return TypeError("Xmethod matcher is missing method: match")
|
|
if not hasattr(matcher, "name"):
|
|
return TypeError("Xmethod matcher is missing attribute: name")
|
|
if not hasattr(matcher, "enabled"):
|
|
return TypeError("Xmethod matcher is missing attribute: enabled")
|
|
if not isinstance(matcher.name, str):
|
|
return TypeError("Attribute 'name' of xmethod matcher is not a " "string")
|
|
if matcher.name.find(";") >= 0:
|
|
return ValueError("Xmethod matcher name cannot contain ';' in it")
|
|
|
|
|
|
# A helper function for register_xmethod_matcher which looks up an
|
|
# xmethod matcher with NAME in LOCUS. Returns the index of the xmethod
|
|
# matcher in 'xmethods' sequence attribute of the LOCUS. If NAME is not
|
|
# found in LOCUS, then -1 is returned.
|
|
|
|
|
|
def _lookup_xmethod_matcher(locus, name):
|
|
for i in range(0, len(locus.xmethods)):
|
|
if locus.xmethods[i].name == name:
|
|
return i
|
|
return -1
|
|
|
|
|
|
def register_xmethod_matcher(locus, matcher, replace=False):
|
|
"""Registers a xmethod matcher MATCHER with a LOCUS.
|
|
|
|
Arguments:
|
|
locus: The locus in which the xmethods should be registered.
|
|
It can be 'None' to indicate that the xmethods should be
|
|
registered globally. Or, it could be a gdb.Objfile or a
|
|
gdb.Progspace object in which the xmethods should be
|
|
registered.
|
|
matcher: The xmethod matcher to register with the LOCUS. It
|
|
should be an instance of 'XMethodMatcher' class.
|
|
replace: If True, replace any existing xmethod matcher with the
|
|
same name in the locus. Otherwise, if a matcher with the same name
|
|
exists in the locus, raise an exception.
|
|
"""
|
|
err = _validate_xmethod_matcher(matcher)
|
|
if err:
|
|
raise err
|
|
if not locus:
|
|
locus = gdb
|
|
if locus == gdb:
|
|
locus_name = "global"
|
|
else:
|
|
locus_name = locus.filename
|
|
index = _lookup_xmethod_matcher(locus, matcher.name)
|
|
if index >= 0:
|
|
if replace:
|
|
del locus.xmethods[index]
|
|
else:
|
|
raise RuntimeError(
|
|
"Xmethod matcher already registered with "
|
|
"%s: %s" % (locus_name, matcher.name)
|
|
)
|
|
if gdb.parameter("verbose"):
|
|
gdb.write("Registering xmethod matcher '%s' with %s' ...\n")
|
|
locus.xmethods.insert(0, matcher)
|