* 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:
Tom Tromey 2012-11-12 17:41:59 +00:00
parent bd69fc683f
commit 18a9fc1261
19 changed files with 874 additions and 17 deletions

View file

@ -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__))

View 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()

View file

@ -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)

View file

@ -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 }
};

View file

@ -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 }
};

View file

@ -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 */

View file

@ -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 */