* NEWS: Update.
* data-directory/Makefile.in (PYTHON_FILES): Add type_printers.py. * python/lib/gdb/command/type_printers.py: New file. * python/lib/gdb/command/types.py (TypePrinter): New class. (_get_some_type_recognizers, get_type_recognizers, apply_type_recognizers, register_type_printer): New functions. * python/py-objfile.c (objfile_object) <type_printers>: New field. (objfpy_dealloc): Decref new field. (objfpy_new): Set new field. (objfpy_get_type_printers, objfpy_set_type_printers): New functions. (objfile_to_objfile_object): Set new field. (objfile_getset): Add "type_printers". * python/py-progspace.c (pspace_object) <type_printers>: New field. (pspy_dealloc): Decref new field. (pspy_new): Set new field. (pspy_get_type_printers, pspy_set_type_printers): New functions. (pspace_to_pspace_object): Set new field. (pspace_getset): Add "type_printers". * python/python.c (start_type_printers, apply_type_printers, free_type_printers): New functions. (_initialize_python): Set gdb.type_printers. * python/python.h (start_type_printers, apply_type_printers, free_type_printers): Declare. * typeprint.c (type_print_raw_options, default_ptype_flags): Update for new fields. (do_free_global_table, create_global_typedef_table, find_global_typedef): New functions. (find_typedef_in_hash): Use find_global_typedef. (whatis_exp): Use create_global_typedef_table. Change cleanup handling. * typeprint.h (struct type_print_options) <global_typedefs, global_printers>: New fields. doc * gdb.texinfo (Symbols): Document "info type-printers", "enable type-printer" and "disable type-printer". (Python API): Add new node to menu. (Type Printing API): New node. (Progspaces In Python): Document type_printers field. (Objfiles In Python): Likewise. (gdb.types) <get_type_recognizers, apply_type_recognizers, register_type_printer, TypePrinter>: Document. testsuite * gdb.base/completion.exp: Update for "info type-printers". * gdb.python/py-typeprint.cc: New file. * gdb.python/py-typeprint.exp: New file. * gdb.python/py-typeprint.py: New file.
This commit is contained in:
parent
bd69fc683f
commit
18a9fc1261
19 changed files with 874 additions and 17 deletions
|
@ -70,6 +70,9 @@ sys.argv = ['']
|
|||
# Initial pretty printers.
|
||||
pretty_printers = []
|
||||
|
||||
# Initial type printers.
|
||||
type_printers = []
|
||||
|
||||
# Convenience variable to GDB's python directory
|
||||
PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
|
125
gdb/python/lib/gdb/command/type_printers.py
Normal file
125
gdb/python/lib/gdb/command/type_printers.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
# Type printer commands.
|
||||
# Copyright (C) 2010-2012 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 copy
|
||||
import gdb
|
||||
|
||||
"""GDB commands for working with type-printers."""
|
||||
|
||||
class InfoTypePrinter(gdb.Command):
|
||||
"""GDB command to list all registered type-printers.
|
||||
|
||||
Usage: info type-printers
|
||||
"""
|
||||
|
||||
def __init__ (self):
|
||||
super(InfoTypePrinter, self).__init__("info type-printers",
|
||||
gdb.COMMAND_DATA)
|
||||
|
||||
def list_type_printers(self, type_printers):
|
||||
"""Print a list of type printers."""
|
||||
# A potential enhancement is to provide an option to list printers in
|
||||
# "lookup order" (i.e. unsorted).
|
||||
sorted_type_printers = copy.copy(type_printers)
|
||||
sorted_type_printers.sort(lambda x, y: cmp(x.name, y.name))
|
||||
for printer in sorted_type_printers:
|
||||
if printer.enabled:
|
||||
enabled = ''
|
||||
else:
|
||||
enabled = " [disabled]"
|
||||
print " %s%s" % (printer.name, enabled)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
"""GDB calls this to perform the command."""
|
||||
sep = ''
|
||||
for objfile in gdb.objfiles():
|
||||
if objfile.type_printers:
|
||||
print "%sType printers for %s:" % (sep, objfile.name)
|
||||
self.list_type_printers(objfile.type_printers)
|
||||
sep = '\n'
|
||||
if gdb.current_progspace().type_printers:
|
||||
print "%sType printers for program space:" % sep
|
||||
self.list_type_printers(gdb.current_progspace().type_printers)
|
||||
sep = '\n'
|
||||
if gdb.type_printers:
|
||||
print "%sGlobal type printers:" % sep
|
||||
self.list_type_printers(gdb.type_printers)
|
||||
|
||||
class _EnableOrDisableCommand(gdb.Command):
|
||||
def __init__(self, setting, name):
|
||||
super(_EnableOrDisableCommand, self).__init__(name, gdb.COMMAND_DATA)
|
||||
self.setting = setting
|
||||
|
||||
def set_some(self, name, printers):
|
||||
result = False
|
||||
for p in printers:
|
||||
if name == p.name:
|
||||
p.enabled = self.setting
|
||||
result = True
|
||||
return result
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
"""GDB calls this to perform the command."""
|
||||
for name in arg.split():
|
||||
ok = False
|
||||
for objfile in gdb.objfiles():
|
||||
if self.set_some(name, objfile.type_printers):
|
||||
ok = True
|
||||
if self.set_some(name, gdb.current_progspace().type_printers):
|
||||
ok = True
|
||||
if self.set_some(name, gdb.type_printers):
|
||||
ok = True
|
||||
if not ok:
|
||||
print "No type printer named '%s'" % name
|
||||
|
||||
def add_some(self, result, word, printers):
|
||||
for p in printers:
|
||||
if p.name.startswith(word):
|
||||
result.append(p.name)
|
||||
|
||||
def complete(self, text, word):
|
||||
result = []
|
||||
for objfile in gdb.objfiles():
|
||||
self.add_some(result, word, objfile.type_printers)
|
||||
self.add_some(result, word, gdb.current_progspace().type_printers)
|
||||
self.add_some(result, word, gdb.type_printers)
|
||||
return result
|
||||
|
||||
class EnableTypePrinter(_EnableOrDisableCommand):
|
||||
"""GDB command to enable the specified type printer.
|
||||
|
||||
Usage: enable type-printer NAME
|
||||
|
||||
NAME is the name of the type-printer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(EnableTypePrinter, self).__init__(True, "enable type-printer")
|
||||
|
||||
class DisableTypePrinter(_EnableOrDisableCommand):
|
||||
"""GDB command to disable the specified type-printer.
|
||||
|
||||
Usage: disable type-printer NAME
|
||||
|
||||
NAME is the name of the type-printer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(DisableTypePrinter, self).__init__(False, "disable type-printer")
|
||||
|
||||
InfoTypePrinter()
|
||||
EnableTypePrinter()
|
||||
DisableTypePrinter()
|
|
@ -109,3 +109,68 @@ def deep_items (type_):
|
|||
else:
|
||||
for i in deep_items (v.type):
|
||||
yield i
|
||||
|
||||
class TypePrinter(object):
|
||||
"""The base class for type printers.
|
||||
|
||||
Instances of this type can be used to substitute type names during
|
||||
'ptype'.
|
||||
|
||||
A type printer must have at least 'name' and 'enabled' attributes,
|
||||
and supply an 'instantiate' method.
|
||||
|
||||
The 'instantiate' method must either return None, or return an
|
||||
object which has a 'recognize' method. This method must accept a
|
||||
gdb.Type argument and either return None, meaning that the type
|
||||
was not recognized, or a string naming the type.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.enabled = True
|
||||
|
||||
def instantiate(self):
|
||||
return None
|
||||
|
||||
# Helper function for computing the list of type recognizers.
|
||||
def _get_some_type_recognizers(result, plist):
|
||||
for printer in plist:
|
||||
if printer.enabled:
|
||||
inst = printer.instantiate()
|
||||
if inst is not None:
|
||||
result.append(inst)
|
||||
return None
|
||||
|
||||
def get_type_recognizers():
|
||||
"Return a list of the enabled type recognizers for the current context."
|
||||
result = []
|
||||
|
||||
# First try the objfiles.
|
||||
for objfile in gdb.objfiles():
|
||||
_get_some_type_recognizers(result, objfile.type_printers)
|
||||
# Now try the program space.
|
||||
_get_some_type_recognizers(result, gdb.current_progspace().type_printers)
|
||||
# Finally, globals.
|
||||
_get_some_type_recognizers(result, gdb.type_printers)
|
||||
|
||||
return result
|
||||
|
||||
def apply_type_recognizers(recognizers, type_obj):
|
||||
"""Apply the given list of type recognizers to the type TYPE_OBJ.
|
||||
If any recognizer in the list recognizes TYPE_OBJ, returns the name
|
||||
given by the recognizer. Otherwise, this returns None."""
|
||||
for r in recognizers:
|
||||
result = r.recognize(type_obj)
|
||||
if result is not None:
|
||||
return result
|
||||
return None
|
||||
|
||||
def register_type_printer(locus, printer):
|
||||
"""Register a type printer.
|
||||
PRINTER is the type printer instance.
|
||||
LOCUS is either an objfile, a program space, or None, indicating
|
||||
global registration."""
|
||||
|
||||
if locus is None:
|
||||
locus = gdb
|
||||
locus.type_printers.insert(0, printer)
|
||||
|
|
|
@ -32,6 +32,9 @@ typedef struct
|
|||
|
||||
/* The pretty-printer list of functions. */
|
||||
PyObject *printers;
|
||||
|
||||
/* The type-printer list. */
|
||||
PyObject *type_printers;
|
||||
} objfile_object;
|
||||
|
||||
static PyTypeObject objfile_object_type;
|
||||
|
@ -58,6 +61,7 @@ objfpy_dealloc (PyObject *o)
|
|||
objfile_object *self = (objfile_object *) o;
|
||||
|
||||
Py_XDECREF (self->printers);
|
||||
Py_XDECREF (self->type_printers);
|
||||
self->ob_type->tp_free ((PyObject *) self);
|
||||
}
|
||||
|
||||
|
@ -76,6 +80,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
|
|||
Py_DECREF (self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->type_printers = PyList_New (0);
|
||||
if (!self->type_printers)
|
||||
{
|
||||
Py_DECREF (self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return (PyObject *) self;
|
||||
}
|
||||
|
@ -118,6 +129,48 @@ objfpy_set_printers (PyObject *o, PyObject *value, void *ignore)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get the 'type_printers' attribute. */
|
||||
|
||||
static PyObject *
|
||||
objfpy_get_type_printers (PyObject *o, void *ignore)
|
||||
{
|
||||
objfile_object *self = (objfile_object *) o;
|
||||
|
||||
Py_INCREF (self->type_printers);
|
||||
return self->type_printers;
|
||||
}
|
||||
|
||||
/* Set the 'type_printers' attribute. */
|
||||
|
||||
static int
|
||||
objfpy_set_type_printers (PyObject *o, PyObject *value, void *ignore)
|
||||
{
|
||||
PyObject *tmp;
|
||||
objfile_object *self = (objfile_object *) o;
|
||||
|
||||
if (! value)
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
_("Cannot delete the type_printers attribute."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (! PyList_Check (value))
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
_("The type_printers attribute must be a list."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Take care in case the LHS and RHS are related somehow. */
|
||||
tmp = self->type_printers;
|
||||
Py_INCREF (value);
|
||||
self->type_printers = value;
|
||||
Py_XDECREF (tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implementation of gdb.Objfile.is_valid (self) -> Boolean.
|
||||
Returns True if this object file still exists in GDB. */
|
||||
|
||||
|
@ -172,6 +225,13 @@ objfile_to_objfile_object (struct objfile *objfile)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
object->type_printers = PyList_New (0);
|
||||
if (!object->type_printers)
|
||||
{
|
||||
Py_DECREF (object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_objfile_data (objfile, objfpy_objfile_data_key, object);
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +270,8 @@ static PyGetSetDef objfile_getset[] =
|
|||
"The objfile's filename, or None.", NULL },
|
||||
{ "pretty_printers", objfpy_get_printers, objfpy_set_printers,
|
||||
"Pretty printers.", NULL },
|
||||
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
|
||||
"Type printers.", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ typedef struct
|
|||
|
||||
/* The pretty-printer list of functions. */
|
||||
PyObject *printers;
|
||||
|
||||
/* The type-printer list. */
|
||||
PyObject *type_printers;
|
||||
} pspace_object;
|
||||
|
||||
static PyTypeObject pspace_object_type;
|
||||
|
@ -66,6 +69,7 @@ pspy_dealloc (PyObject *self)
|
|||
pspace_object *ps_self = (pspace_object *) self;
|
||||
|
||||
Py_XDECREF (ps_self->printers);
|
||||
Py_XDECREF (ps_self->type_printers);
|
||||
self->ob_type->tp_free (self);
|
||||
}
|
||||
|
||||
|
@ -84,6 +88,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
|
|||
Py_DECREF (self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->type_printers = PyList_New (0);
|
||||
if (!self->type_printers)
|
||||
{
|
||||
Py_DECREF (self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return (PyObject *) self;
|
||||
}
|
||||
|
@ -126,6 +137,48 @@ pspy_set_printers (PyObject *o, PyObject *value, void *ignore)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get the 'type_printers' attribute. */
|
||||
|
||||
static PyObject *
|
||||
pspy_get_type_printers (PyObject *o, void *ignore)
|
||||
{
|
||||
pspace_object *self = (pspace_object *) o;
|
||||
|
||||
Py_INCREF (self->type_printers);
|
||||
return self->type_printers;
|
||||
}
|
||||
|
||||
/* Set the 'type_printers' attribute. */
|
||||
|
||||
static int
|
||||
pspy_set_type_printers (PyObject *o, PyObject *value, void *ignore)
|
||||
{
|
||||
PyObject *tmp;
|
||||
pspace_object *self = (pspace_object *) o;
|
||||
|
||||
if (! value)
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
"cannot delete the type_printers attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (! PyList_Check (value))
|
||||
{
|
||||
PyErr_SetString (PyExc_TypeError,
|
||||
"the type_printers attribute must be a list");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Take care in case the LHS and RHS are related somehow. */
|
||||
tmp = self->type_printers;
|
||||
Py_INCREF (value);
|
||||
self->type_printers = value;
|
||||
Py_XDECREF (tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Clear the PSPACE pointer in a Pspace object and remove the reference. */
|
||||
|
@ -168,6 +221,13 @@ pspace_to_pspace_object (struct program_space *pspace)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
object->type_printers = PyList_New (0);
|
||||
if (!object->type_printers)
|
||||
{
|
||||
Py_DECREF (object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_program_space_data (pspace, pspy_pspace_data_key, object);
|
||||
}
|
||||
}
|
||||
|
@ -197,6 +257,8 @@ static PyGetSetDef pspace_getset[] =
|
|||
"The progspace's main filename, or None.", NULL },
|
||||
{ "pretty_printers", pspy_get_printers, pspy_set_printers,
|
||||
"Pretty printers.", NULL },
|
||||
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
|
||||
"Type printers.", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -1181,6 +1181,125 @@ gdbpy_objfiles (PyObject *unused1, PyObject *unused2)
|
|||
return list;
|
||||
}
|
||||
|
||||
/* Compute the list of active type printers and return it. The result
|
||||
of this function can be passed to apply_type_printers, and should
|
||||
be freed by free_type_printers. */
|
||||
|
||||
void *
|
||||
start_type_printers (void)
|
||||
{
|
||||
struct cleanup *cleanups;
|
||||
PyObject *type_module, *func, *result_obj;
|
||||
|
||||
cleanups = ensure_python_env (get_current_arch (), current_language);
|
||||
|
||||
type_module = PyImport_ImportModule ("gdb.types");
|
||||
if (type_module == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (type_module);
|
||||
|
||||
func = PyObject_GetAttrString (type_module, "get_type_recognizers");
|
||||
if (func == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (func);
|
||||
|
||||
result_obj = PyObject_CallFunctionObjArgs (func, (char *) NULL);
|
||||
if (result_obj == NULL)
|
||||
gdbpy_print_stack ();
|
||||
|
||||
done:
|
||||
do_cleanups (cleanups);
|
||||
return result_obj;
|
||||
}
|
||||
|
||||
/* If TYPE is recognized by some type printer, return a newly
|
||||
allocated string holding the type's replacement name. The caller
|
||||
is responsible for freeing the string. Otherwise, return NULL.
|
||||
|
||||
This function has a bit of a funny name, since it actually applies
|
||||
recognizers, but this seemed clearer given the start_type_printers
|
||||
and free_type_printers functions. */
|
||||
|
||||
char *
|
||||
apply_type_printers (void *printers, struct type *type)
|
||||
{
|
||||
struct cleanup *cleanups;
|
||||
PyObject *type_obj, *type_module, *func, *result_obj;
|
||||
PyObject *printers_obj = printers;
|
||||
char *result = NULL;
|
||||
|
||||
if (printers_obj == NULL)
|
||||
return NULL;
|
||||
|
||||
cleanups = ensure_python_env (get_current_arch (), current_language);
|
||||
|
||||
type_obj = type_to_type_object (type);
|
||||
if (type_obj == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (type_obj);
|
||||
|
||||
type_module = PyImport_ImportModule ("gdb.types");
|
||||
if (type_module == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (type_module);
|
||||
|
||||
func = PyObject_GetAttrString (type_module, "apply_type_recognizers");
|
||||
if (func == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (func);
|
||||
|
||||
result_obj = PyObject_CallFunctionObjArgs (func, printers_obj,
|
||||
type_obj, (char *) NULL);
|
||||
if (result_obj == NULL)
|
||||
{
|
||||
gdbpy_print_stack ();
|
||||
goto done;
|
||||
}
|
||||
make_cleanup_py_decref (result_obj);
|
||||
|
||||
if (result_obj != Py_None)
|
||||
{
|
||||
result = python_string_to_host_string (result_obj);
|
||||
if (result == NULL)
|
||||
gdbpy_print_stack ();
|
||||
}
|
||||
|
||||
done:
|
||||
do_cleanups (cleanups);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Free the result of start_type_printers. */
|
||||
|
||||
void
|
||||
free_type_printers (void *arg)
|
||||
{
|
||||
struct cleanup *cleanups;
|
||||
PyObject *printers = arg;
|
||||
|
||||
if (printers == NULL)
|
||||
return;
|
||||
|
||||
cleanups = ensure_python_env (get_current_arch (), current_language);
|
||||
Py_DECREF (printers);
|
||||
do_cleanups (cleanups);
|
||||
}
|
||||
|
||||
#else /* HAVE_PYTHON */
|
||||
|
||||
/* Dummy implementation of the gdb "python-interactive" and "python"
|
||||
|
@ -1238,6 +1357,23 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
|
|||
"scripting is not supported."));
|
||||
}
|
||||
|
||||
void *
|
||||
start_type_printers (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
apply_type_printers (void *ignore, struct type *type)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
free_type_printers (void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* HAVE_PYTHON */
|
||||
|
||||
|
||||
|
|
|
@ -49,4 +49,10 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
|
|||
|
||||
int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
|
||||
|
||||
void *start_type_printers (void);
|
||||
|
||||
char *apply_type_printers (void *, struct type *type);
|
||||
|
||||
void free_type_printers (void *arg);
|
||||
|
||||
#endif /* GDB_PYTHON_H */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue