Xmethod support in Python.

* python/py-xmethods.c: New file.
	* python/py-objfile.c (objfile_object): New field 'xmethods'.
	(objfpy_dealloc): XDECREF on the new xmethods field.
	(objfpy_new, objfile_to_objfile_object): Initialize xmethods
	field.
	(objfpy_get_xmethods): New function.
	(objfile_getset): New entry 'xmethods'.
	* python/py-progspace.c (pspace_object): New field 'xmethods'.
	(pspy_dealloc): XDECREF on the new xmethods field.
	(pspy_new, pspace_to_pspace_object): Initialize	xmethods
	field.
	(pspy_get_xmethods): New function.
	(pspace_getset): New entry 'xmethods'.
	* python/python-internal.h: Add declarations for new functions.
	* python/python.c (_initialize_python): Invoke
	gdbpy_initialize_xmethods.
	* python/lib/gdb/__init__.py (xmethods): New
	attribute.
	* python/lib/gdb/xmethod.py: New file.
	* python/lib/gdb/command/xmethods.py: New file.

	testuite/
	* gdb.python/py-xmethods.cc: New testcase to test xmethods.
	* gdb.python/py-xmethods.exp: New tests to test xmethods.
	* gdb.python/py-xmethods.py: Python script supporting the
	new testcase and tests.
This commit is contained in:
Siva Chandra 2014-05-20 06:53:04 -07:00
parent 58992dc550
commit 883964a75e
15 changed files with 1821 additions and 2 deletions

View file

@ -67,6 +67,8 @@ pretty_printers = []
# Initial type printers.
type_printers = []
# Initial xmethod matchers.
xmethods = []
# Initial frame filters.
frame_filters = {}

View file

@ -0,0 +1,272 @@
# Xmethod commands.
# Copyright 2013-2014 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/>.
import gdb
import re
"""GDB commands for working with xmethods."""
def validate_xm_regexp(part_name, regexp):
try:
return re.compile(regexp)
except SyntaxError:
raise SyntaxError("Invalid %s regexp: %s", part_name, regexp)
def parse_xm_command_args(arg):
"""Parses the arguments passed to a xmethod command.
Arguments:
arg: The argument string passed to a xmethod command.
Returns:
A 3-tuple: (<locus matching regular expression>,
<matcher matching regular expression>,
<name matching regular experession>)
"""
argv = gdb.string_to_argv(arg)
argc = len(argv)
if argc > 2:
raise SyntaxError("Too many arguments to command.")
locus_regexp = ""
matcher_name_regexp = ""
xm_name_regexp = None
if argc >= 1:
locus_regexp = argv[0]
if argc == 2:
parts = argv[1].split(";", 1)
matcher_name_regexp = parts[0]
if len(parts) > 1:
xm_name_regexp = parts[1]
if xm_name_regexp:
name_re = validate_xm_regexp("xmethod name", xm_name_regexp)
else:
name_re = None
return (validate_xm_regexp("locus", locus_regexp),
validate_xm_regexp("matcher name", matcher_name_regexp),
name_re)
def get_global_method_matchers(locus_re, matcher_re):
"""Returns a dict of matching globally registered xmethods.
Arguments:
locus_re: Even though only globally registered xmethods are
looked up, they will be looked up only if 'global' matches
LOCUS_RE.
matcher_re: The regular expression matching the names of xmethods.
Returns:
A dict of matching globally registered xmethod matchers. The only
key in the dict will be 'global'.
"""
locus_str = "global"
xm_dict = { locus_str: [] }
if locus_re.match("global"):
xm_dict[locus_str].extend(
[m for m in gdb.xmethods if matcher_re.match(m.name)])
return xm_dict
def get_method_matchers_in_loci(loci, locus_re, matcher_re):
"""Returns a dict of matching registered xmethods in the LOCI.
Arguments:
loci: The list of loci to lookup matching xmethods in.
locus_re: Xmethod matchers will be looked up in a particular locus
only if its filename matches the regular expression LOCUS_RE.
matcher_re: The regular expression to match the xmethod matcher
names.
Returns:
A dict of matching xmethod matchers. The keys of the dict are the
filenames of the loci the xmethod matchers belong to.
"""
xm_dict = {}
for locus in loci:
if isinstance(locus, gdb.Progspace):
if (not locus_re.match(locus.filename) and
not locus_re.match('progspace')):
continue
locus_type = "progspace"
else:
if not locus_re.match(locus.filename):
continue
locus_type = "objfile"
locus_str = "%s %s" % (locus_type, locus.filename)
xm_dict[locus_str] = [
m for m in locus.xmethods if matcher_re.match(m.name)]
return xm_dict
def print_xm_info(xm_dict, name_re):
"""Print a dictionary of xmethods."""
def get_status_string(method):
if not m.enabled:
return " [disabled]"
else:
return ""
if not xm_dict:
return
for locus_str in xm_dict:
if not xm_dict[locus_str]:
continue
print ("Xmethods in %s:" % locus_str)
for matcher in xm_dict[locus_str]:
print (" %s" % matcher.name)
if not matcher.methods:
continue
for m in matcher.methods:
if name_re is None or name_re.match(m.name):
print (" %s%s" % (m.name, get_status_string(m)))
def set_xm_status1(xm_dict, name_re, status):
"""Set the status (enabled/disabled) of a dictionary of xmethods."""
for locus_str, matchers in xm_dict.iteritems():
for matcher in matchers:
if not name_re:
# If the name regex is missing, then set the status of the
# matcher and move on.
matcher.enabled = status
continue
if not matcher.methods:
# The methods attribute could be None. Move on.
continue
for m in matcher.methods:
if name_re.match(m.name):
m.enabled = status
def set_xm_status(arg, status):
"""Set the status (enabled/disabled) of xmethods matching ARG.
This is a helper function for enable/disable commands. ARG is the
argument string passed to the commands.
"""
locus_re, matcher_re, name_re = parse_xm_command_args(arg)
set_xm_status1(get_global_method_matchers(locus_re, matcher_re), name_re,
status)
set_xm_status1(
get_method_matchers_in_loci(
[gdb.current_progspace()], locus_re, matcher_re),
name_re,
status)
set_xm_status1(
get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re),
name_re,
status)
class InfoXMethod(gdb.Command):
"""GDB command to list registered xmethod matchers.
Usage: info xmethod [locus-regexp [name-regexp]]
LOCUS-REGEXP is a regular expression matching the location of the
xmethod matchers. If it is omitted, all registered xmethod matchers
from all loci are listed. A locus could be 'global', a regular expression
matching the current program space's filename, or a regular expression
matching filenames of objfiles. Locus could be 'progspace' to specify that
only xmethods from the current progspace should be listed.
NAME-REGEXP is a regular expression matching the names of xmethod
matchers. If this omitted for a specified locus, then all registered
xmethods in the locus are listed. To list only a certain xmethods
managed by a single matcher, the name regexp can be specified as
matcher-name-regexp;xmethod-name-regexp.
"""
def __init__(self):
super(InfoXMethod, self).__init__("info xmethod",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
locus_re, matcher_re, name_re = parse_xm_command_args(arg)
print_xm_info(get_global_method_matchers(locus_re, matcher_re),
name_re)
print_xm_info(
get_method_matchers_in_loci(
[gdb.current_progspace()], locus_re, matcher_re),
name_re)
print_xm_info(
get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re),
name_re)
class EnableXMethod(gdb.Command):
"""GDB command to enable a specified (group of) xmethod(s).
Usage: enable xmethod [locus-regexp [name-regexp]]
LOCUS-REGEXP is a regular expression matching the location of the
xmethod matchers. If it is omitted, all registered xmethods matchers
from all loci are enabled. A locus could be 'global', a regular expression
matching the current program space's filename, or a regular expression
matching filenames of objfiles. Locus could be 'progspace' to specify that
only xmethods from the current progspace should be enabled.
NAME-REGEXP is a regular expression matching the names of xmethods
within a given locus. If this omitted for a specified locus, then all
registered xmethod matchers in the locus are enabled. To enable only
a certain xmethods managed by a single matcher, the name regexp can be
specified as matcher-name-regexp;xmethod-name-regexp.
"""
def __init__(self):
super(EnableXMethod, self).__init__("enable xmethod",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
set_xm_status(arg, True)
class DisableXMethod(gdb.Command):
"""GDB command to disable a specified (group of) xmethod(s).
Usage: disable xmethod [locus-regexp [name-regexp]]
LOCUS-REGEXP is a regular expression matching the location of the
xmethod matchers. If it is omitted, all registered xmethod matchers
from all loci are disabled. A locus could be 'global', a regular
expression matching the current program space's filename, or a regular
expression filenames of objfiles. Locus could be 'progspace' to specify
that only xmethods from the current progspace should be disabled.
NAME-REGEXP is a regular expression matching the names of xmethods
within a given locus. If this omitted for a specified locus, then all
registered xmethod matchers in the locus are disabled. To disable
only a certain xmethods managed by a single matcher, the name regexp
can be specified as matcher-name-regexp;xmethod-name-regexp.
"""
def __init__(self):
super(DisableXMethod, self).__init__("disable xmethod",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
set_xm_status(arg, False)
def register_xmethod_commands():
"""Installs the xmethod commands."""
InfoXMethod()
EnableXMethod()
DisableXMethod()
register_xmethod_commands()

View file

@ -0,0 +1,259 @@
# Python side of the support for xmethods.
# Copyright (C) 2013-2014 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
if sys.version_info[0] > 2:
# Python 3 removed basestring and long
basestring = str
long = int
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.
Derived classes should override the 'get_arg_types' 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 __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, basestring):
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)

View file

@ -37,6 +37,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
/* The debug method matcher list. */
PyObject *xmethods;
} objfile_object;
static PyTypeObject objfile_object_type
@ -67,6 +70,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->printers);
Py_XDECREF (self->frame_filters);
Py_XDECREF (self->type_printers);
Py_XDECREF (self->xmethods);
Py_TYPE (self)->tp_free (self);
}
@ -99,6 +103,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
self->xmethods = PyList_New (0);
if (self->xmethods == NULL)
{
Py_DECREF (self);
return NULL;
}
}
return (PyObject *) self;
}
@ -193,6 +204,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
/* Get the 'xmethods' attribute. */
PyObject *
objfpy_get_xmethods (PyObject *o, void *ignore)
{
objfile_object *self = (objfile_object *) o;
Py_INCREF (self->xmethods);
return self->xmethods;
}
/* Set the 'type_printers' attribute. */
static int
@ -292,6 +314,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
object->xmethods = PyList_New (0);
if (object->xmethods == NULL)
{
Py_DECREF (object);
return NULL;
}
set_objfile_data (objfile, objfpy_objfile_data_key, object);
}
}
@ -333,6 +362,8 @@ static PyGetSetDef objfile_getset[] =
objfpy_set_frame_filters, "Frame Filters.", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
{ "xmethods", objfpy_get_xmethods, NULL,
"Debug methods.", NULL },
{ NULL }
};

View file

@ -39,6 +39,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
/* The debug method list. */
PyObject *xmethods;
} pspace_object;
static PyTypeObject pspace_object_type
@ -75,6 +78,7 @@ pspy_dealloc (PyObject *self)
Py_XDECREF (ps_self->printers);
Py_XDECREF (ps_self->frame_filters);
Py_XDECREF (ps_self->type_printers);
Py_XDECREF (ps_self->xmethods);
Py_TYPE (self)->tp_free (self);
}
@ -107,6 +111,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
self->xmethods = PyList_New (0);
if (self->xmethods == NULL)
{
Py_DECREF (self);
return NULL;
}
}
return (PyObject *) self;
}
@ -201,6 +212,17 @@ pspy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
/* Get the 'xmethods' attribute. */
PyObject *
pspy_get_xmethods (PyObject *o, void *ignore)
{
pspace_object *self = (pspace_object *) o;
Py_INCREF (self->xmethods);
return self->xmethods;
}
/* Set the 'type_printers' attribute. */
static int
@ -297,6 +319,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
object->xmethods = PyList_New (0);
if (object->xmethods == NULL)
{
Py_DECREF (object);
return NULL;
}
set_program_space_data (pspace, pspy_pspace_data_key, object);
}
}
@ -329,6 +358,8 @@ static PyGetSetDef pspace_getset[] =
"Frame filters.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
{ "xmethods", pspy_get_xmethods, NULL,
"Debug methods.", NULL },
{ NULL }
};

642
gdb/python/py-xmethods.c Normal file
View file

@ -0,0 +1,642 @@
/* Support for debug methods in Python.
Copyright (C) 2013-2014 Free Software Foundation, Inc.
This file is part of GDB.
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/>. */
#include "defs.h"
#include "arch-utils.h"
#include "extension-priv.h"
#include "objfiles.h"
#include "value.h"
#include "language.h"
#include "python.h"
#include "python-internal.h"
static const char enabled_field_name[] = "enabled";
static const char match_method_name[] = "match";
static const char get_arg_types_method_name[] = "get_arg_types";
static const char invoke_method_name[] = "invoke";
static const char matchers_attr_str[] = "xmethods";
static PyObject *py_match_method_name = NULL;
static PyObject *py_get_arg_types_method_name = NULL;
static PyObject *py_invoke_method_name = NULL;
struct gdbpy_worker_data
{
PyObject *worker;
PyObject *this_type;
};
static struct xmethod_worker *new_python_xmethod_worker (PyObject *item,
PyObject *py_obj_type);
/* Implementation of free_xmethod_worker_data for Python. */
void
gdbpy_free_xmethod_worker_data (const struct extension_language_defn *extlang,
void *data)
{
struct gdbpy_worker_data *worker_data = data;
struct cleanup *cleanups;
gdb_assert (worker_data->worker != NULL && worker_data->this_type != NULL);
/* We don't do much here, but we still need the GIL. */
cleanups = ensure_python_env (get_current_arch (), current_language);
Py_DECREF (worker_data->worker);
Py_DECREF (worker_data->this_type);
xfree (worker_data);
do_cleanups (cleanups);
}
/* Implementation of clone_xmethod_worker_data for Python. */
void *
gdbpy_clone_xmethod_worker_data (const struct extension_language_defn *extlang,
void *data)
{
struct gdbpy_worker_data *worker_data = data, *new_data;
struct cleanup *cleanups;
gdb_assert (worker_data->worker != NULL && worker_data->this_type != NULL);
/* We don't do much here, but we still need the GIL. */
cleanups = ensure_python_env (get_current_arch (), current_language);
new_data = XCNEW (struct gdbpy_worker_data);
new_data->worker = worker_data->worker;
new_data->this_type = worker_data->this_type;
Py_INCREF (new_data->worker);
Py_INCREF (new_data->this_type);
do_cleanups (cleanups);
return new_data;
}
/* Invoke the "match" method of the MATCHER and return a new reference
to the result. Returns NULL on error. */
static PyObject *
invoke_match_method (PyObject *matcher, PyObject *py_obj_type,
const char *xmethod_name)
{
PyObject *py_xmethod_name;
PyObject *match_method, *enabled_field, *match_result;
struct cleanup *cleanups;
int enabled;
cleanups = make_cleanup (null_cleanup, NULL);
enabled_field = PyObject_GetAttrString (matcher, enabled_field_name);
if (enabled_field == NULL)
{
do_cleanups (cleanups);
return NULL;
}
make_cleanup_py_decref (enabled_field);
enabled = PyObject_IsTrue (enabled_field);
if (enabled == -1)
{
do_cleanups (cleanups);
return NULL;
}
if (enabled == 0)
{
/* Return 'None' if the matcher is not enabled. */
do_cleanups (cleanups);
Py_RETURN_NONE;
}
match_method = PyObject_GetAttrString (matcher, match_method_name);
if (match_method == NULL)
{
do_cleanups (cleanups);
return NULL;
}
make_cleanup_py_decref (match_method);
py_xmethod_name = PyString_FromString (xmethod_name);
if (py_xmethod_name == NULL)
{
do_cleanups (cleanups);
return NULL;
}
make_cleanup_py_decref (py_xmethod_name);
match_result = PyObject_CallMethodObjArgs (matcher,
py_match_method_name,
py_obj_type,
py_xmethod_name,
NULL);
do_cleanups (cleanups);
return match_result;
}
/* Implementation of get_matching_xmethod_workers for Python. */
enum ext_lang_rc
gdbpy_get_matching_xmethod_workers
(const struct extension_language_defn *extlang,
struct type *obj_type, const char *method_name,
xmethod_worker_vec **dm_vec)
{
struct cleanup *cleanups;
struct objfile *objfile;
VEC (xmethod_worker_ptr) *worker_vec = NULL;
PyObject *py_type, *py_progspace;
PyObject *py_xmethod_matcher_list = NULL, *list_iter, *matcher;
gdb_assert (obj_type != NULL && method_name != NULL);
cleanups = ensure_python_env (get_current_arch (), current_language);
py_type = type_to_type_object (obj_type);
if (py_type == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
make_cleanup_py_decref (py_type);
/* Create an empty list of debug methods. */
py_xmethod_matcher_list = PyList_New (0);
if (py_xmethod_matcher_list == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
/* Gather debug method matchers registered with the object files.
This could be done differently by iterating over each objfile's matcher
list individually, but there's no data yet to show it's needed. */
ALL_OBJFILES (objfile)
{
PyObject *py_objfile = objfile_to_objfile_object (objfile);
PyObject *objfile_matchers, *temp = py_xmethod_matcher_list;
if (py_objfile == NULL)
{
gdbpy_print_stack ();
Py_DECREF (py_xmethod_matcher_list);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
objfile_matchers = objfpy_get_xmethods (py_objfile, NULL);
py_xmethod_matcher_list = PySequence_Concat (temp, objfile_matchers);
Py_DECREF (temp);
Py_DECREF (objfile_matchers);
if (py_xmethod_matcher_list == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
}
/* Gather debug methods matchers registered with the current program
space. */
py_progspace = pspace_to_pspace_object (current_program_space);
if (py_progspace != NULL)
{
PyObject *temp = py_xmethod_matcher_list;
PyObject *pspace_matchers = pspy_get_xmethods (py_progspace, NULL);
py_xmethod_matcher_list = PySequence_Concat (temp, pspace_matchers);
Py_DECREF (temp);
Py_DECREF (pspace_matchers);
if (py_xmethod_matcher_list == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
}
else
{
gdbpy_print_stack ();
Py_DECREF (py_xmethod_matcher_list);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
/* Gather debug method matchers registered globally. */
if (gdb_python_module != NULL
&& PyObject_HasAttrString (gdb_python_module, matchers_attr_str))
{
PyObject *gdb_matchers;
PyObject *temp = py_xmethod_matcher_list;
gdb_matchers = PyObject_GetAttrString (gdb_python_module,
matchers_attr_str);
if (gdb_matchers != NULL)
{
py_xmethod_matcher_list = PySequence_Concat (temp, gdb_matchers);
Py_DECREF (temp);
Py_DECREF (gdb_matchers);
if (py_xmethod_matcher_list == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
}
else
{
gdbpy_print_stack ();
Py_DECREF (py_xmethod_matcher_list);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
}
/* Safe to make a cleanup for py_xmethod_matcher_list now as it
will not change any more. */
make_cleanup_py_decref (py_xmethod_matcher_list);
list_iter = PyObject_GetIter (py_xmethod_matcher_list);
if (list_iter == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
while ((matcher = PyIter_Next (list_iter)) != NULL)
{
PyObject *match_result = invoke_match_method (matcher, py_type,
method_name);
if (match_result == NULL)
{
gdbpy_print_stack ();
Py_DECREF (matcher);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
if (match_result == Py_None)
; /* This means there was no match. */
else if (PySequence_Check (match_result))
{
PyObject *iter = PyObject_GetIter (match_result);
PyObject *py_worker;
if (iter == NULL)
{
gdbpy_print_stack ();
Py_DECREF (matcher);
Py_DECREF (match_result);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
while ((py_worker = PyIter_Next (iter)) != NULL)
{
struct xmethod_worker *worker;
worker = new_python_xmethod_worker (py_worker, py_type);
VEC_safe_push (xmethod_worker_ptr, worker_vec, worker);
Py_DECREF (py_worker);
}
Py_DECREF (iter);
/* Report any error that could have occurred while iterating. */
if (PyErr_Occurred ())
{
gdbpy_print_stack ();
Py_DECREF (matcher);
Py_DECREF (match_result);
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
}
else
{
struct xmethod_worker *worker;
worker = new_python_xmethod_worker (match_result, py_type);
VEC_safe_push (xmethod_worker_ptr, worker_vec, worker);
}
Py_DECREF (match_result);
Py_DECREF (matcher);
}
Py_DECREF (list_iter);
/* Report any error that could have occurred while iterating. */
if (PyErr_Occurred ())
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
do_cleanups (cleanups);
*dm_vec = worker_vec;
return EXT_LANG_RC_OK;
}
/* Implementation of get_xmethod_arg_types for Python. */
enum ext_lang_rc
gdbpy_get_xmethod_arg_types (const struct extension_language_defn *extlang,
struct xmethod_worker *worker,
int *nargs, struct type ***arg_types)
{
struct gdbpy_worker_data *worker_data = worker->data;
PyObject *py_worker = worker_data->worker;
PyObject *get_arg_types_method;
PyObject *py_argtype_list, *list_iter = NULL, *item;
struct cleanup *cleanups;
struct type **type_array, *obj_type;
int i = 1, arg_count;
/* Set nargs to -1 so that any premature return from this function returns
an invalid/unusable number of arg types. */
*nargs = -1;
cleanups = ensure_python_env (get_current_arch (), current_language);
get_arg_types_method = PyObject_GetAttrString (py_worker,
get_arg_types_method_name);
if (get_arg_types_method == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
make_cleanup_py_decref (get_arg_types_method);
py_argtype_list = PyObject_CallMethodObjArgs (py_worker,
py_get_arg_types_method_name,
NULL);
if (py_argtype_list == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
make_cleanup_py_decref (py_argtype_list);
if (py_argtype_list == Py_None)
arg_count = 0;
else if (PySequence_Check (py_argtype_list))
{
arg_count = PySequence_Size (py_argtype_list);
if (arg_count == -1)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
list_iter = PyObject_GetIter (py_argtype_list);
if (list_iter == NULL)
{
gdbpy_print_stack ();
do_cleanups (cleanups);
return EXT_LANG_RC_ERROR;
}
make_cleanup_py_decref (list_iter);
}
else
arg_count = 1;
/* Include the 'this' argument in the size. */
type_array = XCNEWVEC (struct type *, arg_count + 1);
i = 1;
if (list_iter != NULL)
{
while ((item = PyIter_Next (list_iter)) != NULL)
{
struct type *arg_type = type_object_to_type (item);
Py_DECREF (item);
if (arg_type == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Arg type returned by the get_arg_types "
"method of a debug method worker object is "
"not a gdb.Type object."));
break;
}
type_array[i] = arg_type;
i++;
}
}
else if (arg_count == 1)
{
/* py_argtype_list is not actually a list but a single gdb.Type
object. */
struct type *arg_type = type_object_to_type (py_argtype_list);
if (arg_type == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Arg type returned by the get_arg_types method "
"of an xmethod worker object is not a gdb.Type "
"object."));
}
else
{
type_array[i] = arg_type;
i++;
}
}
if (PyErr_Occurred ())
{
gdbpy_print_stack ();
do_cleanups (cleanups);
xfree (type_array);
return EXT_LANG_RC_ERROR;
}
/* Add the type of 'this' as the first argument. The 'this' pointer should
be a 'const' value. Hence, create a 'const' variant of the 'this' pointer
type. */
obj_type = type_object_to_type (worker_data->this_type);
type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL);
*nargs = i;
*arg_types = type_array;
do_cleanups (cleanups);
return EXT_LANG_RC_OK;
}
/* Implementation of invoke_xmethod for Python. */
struct value *
gdbpy_invoke_xmethod (const struct extension_language_defn *extlang,
struct xmethod_worker *worker,
struct value *obj, struct value **args, int nargs)
{
int i;
struct cleanup *cleanups;
PyObject *py_value_obj, *py_arg_tuple, *py_result;
struct type *obj_type, *this_type;
struct value *res = NULL;
struct gdbpy_worker_data *worker_data = worker->data;
PyObject *xmethod_worker = worker_data->worker;
cleanups = ensure_python_env (get_current_arch (), current_language);
obj_type = check_typedef (value_type (obj));
this_type = check_typedef (type_object_to_type (worker_data->this_type));
if (TYPE_CODE (obj_type) == TYPE_CODE_PTR)
{
struct type *this_ptr = lookup_pointer_type (this_type);
if (!types_equal (obj_type, this_ptr))
obj = value_cast (this_ptr, obj);
}
else if (TYPE_CODE (obj_type) == TYPE_CODE_REF)
{
struct type *this_ref = lookup_reference_type (this_type);
if (!types_equal (obj_type, this_ref))
obj = value_cast (this_ref, obj);
}
else
{
if (!types_equal (obj_type, this_type))
obj = value_cast (this_type, obj);
}
py_value_obj = value_to_value_object (obj);
if (py_value_obj == NULL)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
make_cleanup_py_decref (py_value_obj);
py_arg_tuple = PyTuple_New (nargs + 1);
if (py_arg_tuple == NULL)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
make_cleanup_py_decref (py_arg_tuple);
/* PyTuple_SET_ITEM steals the reference of the element. Hence INCREF the
reference to the 'this' object as we have a cleanup to DECREF it. */
Py_INCREF (py_value_obj);
PyTuple_SET_ITEM (py_arg_tuple, 0, py_value_obj);
for (i = 0; i < nargs; i++)
{
PyObject *py_value_arg = value_to_value_object (args[i]);
if (py_value_arg == NULL)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
PyTuple_SET_ITEM (py_arg_tuple, i + 1, py_value_arg);
}
py_result = PyObject_CallObject (xmethod_worker, py_arg_tuple);
if (py_result == NULL)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
make_cleanup_py_decref (py_result);
if (py_result != Py_None)
{
res = convert_value_from_python (py_result);
if (res == NULL)
{
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
}
else
{
res = allocate_value (lookup_typename (python_language, python_gdbarch,
"void", NULL, 0));
}
do_cleanups (cleanups);
return res;
}
/* Creates a new Python xmethod_worker object.
The new object has data of type 'struct gdbpy_worker_data' composed
with the components PY_WORKER and THIS_TYPE. */
static struct xmethod_worker *
new_python_xmethod_worker (PyObject *py_worker, PyObject *this_type)
{
struct gdbpy_worker_data *data;
gdb_assert (py_worker != NULL && this_type != NULL);
data = XCNEW (struct gdbpy_worker_data);
data->worker = py_worker;
data->this_type = this_type;
Py_INCREF (py_worker);
Py_INCREF (this_type);
return new_xmethod_worker (&extension_language_python, data);
}
int
gdbpy_initialize_xmethods (void)
{
py_match_method_name = PyString_FromString (match_method_name);
if (py_match_method_name == NULL)
return -1;
py_invoke_method_name = PyString_FromString (invoke_method_name);
if (py_invoke_method_name == NULL)
return -1;
py_get_arg_types_method_name
= PyString_FromString (get_arg_types_method_name);
if (py_get_arg_types_method_name == NULL)
return -1;
return 1;
}

View file

@ -307,6 +307,24 @@ extern enum ext_lang_bp_stop gdbpy_breakpoint_cond_says_stop
(const struct extension_language_defn *, struct breakpoint *);
extern int gdbpy_breakpoint_has_cond (const struct extension_language_defn *,
struct breakpoint *b);
extern void *gdbpy_clone_xmethod_worker_data
(const struct extension_language_defn *extlang, void *data);
extern void gdbpy_free_xmethod_worker_data
(const struct extension_language_defn *extlang, void *data);
extern enum ext_lang_rc gdbpy_get_matching_xmethod_workers
(const struct extension_language_defn *extlang,
struct type *obj_type, const char *method_name,
xmethod_worker_vec **dm_vec);
extern enum ext_lang_rc gdbpy_get_xmethod_arg_types
(const struct extension_language_defn *extlang,
struct xmethod_worker *worker,
int *nargs,
struct type ***arg_types);
extern struct value *gdbpy_invoke_xmethod
(const struct extension_language_defn *extlang,
struct xmethod_worker *worker,
struct value *obj, struct value **args, int nargs);
PyObject *gdbpy_history (PyObject *self, PyObject *args);
PyObject *gdbpy_breakpoints (PyObject *, PyObject *);
@ -345,11 +363,13 @@ PyObject *pspace_to_pspace_object (struct program_space *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *pspy_get_printers (PyObject *, void *);
PyObject *pspy_get_frame_filters (PyObject *, void *);
PyObject *pspy_get_xmethods (PyObject *, void *);
PyObject *objfile_to_objfile_object (struct objfile *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *objfpy_get_printers (PyObject *, void *);
PyObject *objfpy_get_frame_filters (PyObject *, void *);
PyObject *objfpy_get_xmethods (PyObject *, void *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@ -430,6 +450,8 @@ int gdbpy_initialize_new_objfile_event (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_arch (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_xmethods (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_xdecref (PyObject *py);

View file

@ -186,7 +186,13 @@ static const struct extension_language_ops python_extension_ops =
gdbpy_set_quit_flag,
gdbpy_check_quit_flag,
gdbpy_before_prompt_hook
gdbpy_before_prompt_hook,
gdbpy_clone_xmethod_worker_data,
gdbpy_free_xmethod_worker_data,
gdbpy_get_matching_xmethod_workers,
gdbpy_get_xmethod_arg_types,
gdbpy_invoke_xmethod
};
/* Architecture and language to be used in callbacks from
@ -1752,7 +1758,8 @@ message == an error message without a stack will be printed."),
|| gdbpy_initialize_exited_event () < 0
|| gdbpy_initialize_thread_event () < 0
|| gdbpy_initialize_new_objfile_event () < 0
|| gdbpy_initialize_arch () < 0)
|| gdbpy_initialize_arch () < 0
|| gdbpy_initialize_xmethods () < 0)
goto fail;
gdbpy_to_string_cst = PyString_FromString ("to_string");