From 18a9fc1261b8a654fe6db7eea7e60b0c59296d96 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 12 Nov 2012 17:41:59 +0000 Subject: [PATCH] * 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) : 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) : 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) : 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) : 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. --- gdb/ChangeLog | 40 ++++++ gdb/NEWS | 6 + gdb/data-directory/Makefile.in | 1 + gdb/doc/ChangeLog | 11 ++ gdb/doc/gdb.texinfo | 122 +++++++++++++++++- gdb/python/lib/gdb/__init__.py | 3 + gdb/python/lib/gdb/command/type_printers.py | 125 ++++++++++++++++++ gdb/python/lib/gdb/types.py | 65 ++++++++++ gdb/python/py-objfile.c | 62 +++++++++ gdb/python/py-progspace.c | 62 +++++++++ gdb/python/python.c | 136 ++++++++++++++++++++ gdb/python/python.h | 6 + gdb/testsuite/ChangeLog | 7 + gdb/testsuite/gdb.base/completion.exp | 6 +- gdb/testsuite/gdb.python/py-typeprint.cc | 37 ++++++ gdb/testsuite/gdb.python/py-typeprint.exp | 53 ++++++++ gdb/testsuite/gdb.python/py-typeprint.py | 35 +++++ gdb/typeprint.c | 106 +++++++++++++-- gdb/typeprint.h | 8 ++ 19 files changed, 874 insertions(+), 17 deletions(-) create mode 100644 gdb/python/lib/gdb/command/type_printers.py create mode 100644 gdb/testsuite/gdb.python/py-typeprint.cc create mode 100644 gdb/testsuite/gdb.python/py-typeprint.exp create mode 100644 gdb/testsuite/gdb.python/py-typeprint.py diff --git a/gdb/ChangeLog b/gdb/ChangeLog index fcf5ffc13f1..583f1c02480 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,43 @@ +2012-11-12 Tom Tromey + + * 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) : 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) : 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) : New fields. + 2012-11-12 Tom Tromey * c-typeprint.c (find_typedef_for_canonicalize, diff --git a/gdb/NEWS b/gdb/NEWS index b7a92720efc..739a7b3d75a 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -17,6 +17,8 @@ ** Python's atexit.register now works in GDB. + ** Types can be pretty-printed via a Python API. + * New Python-based convenience functions: ** $_memeq(buf1, buf2, length) @@ -51,6 +53,10 @@ pi [command] py [command] "py" is a new alias for "python". +enable type-printer [name]... +disable type-printer [name]... + Enable or disable type printers. + * Removed commands ** For the Renesas Super-H architecture, the "regs" command has been removed diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 41947c63d29..2e2fbf9eb0e 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -57,6 +57,7 @@ PYTHON_FILES = \ gdb/printing.py \ gdb/prompt.py \ gdb/command/__init__.py \ + gdb/command/type_printers.py \ gdb/command/pretty_printers.py \ gdb/command/prompt.py \ gdb/command/explore.py \ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index f59b001e3f6..0410dbc4c4c 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,14 @@ +2012-11-12 Tom Tromey + + * 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) : Document. + 2012-11-12 Tom Tromey * gdb.texinfo (Symbols): Document "set print type methods", diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 6f9d3f654d2..86cfe8e2c57 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -15218,6 +15218,22 @@ This command differs from @code{ptype} in two ways: first, like @code{whatis}, it does not print a detailed description; second, it lists all source files where a type is defined. +@kindex info type-printers +@item info type-printers +Versions of @value{GDBN} that ship with Python scripting enabled may +have ``type printers'' available. When using @command{ptype} or +@command{whatis}, these printers are consulted when the name of a type +is needed. @xref{Type Printing API}, for more information on writing +type printers. + +@code{info type-printers} displays all the available type printers. + +@kindex enable type-printer +@kindex disable type-printer +@item enable type-printer @var{name}@dots{} +@item disable type-printer @var{name}@dots{} +These commands can be used to enable or disable type printers. + @kindex info scope @cindex local variables @item info scope @var{location} @@ -22671,6 +22687,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Pretty Printing API:: Pretty-printing values. * Selecting Pretty-Printers:: How GDB chooses a pretty-printer. * Writing a Pretty-Printer:: Writing a Pretty-Printer. +* Type Printing API:: Pretty-printing types. * Inferiors In Python:: Python representation of inferiors (processes) * Events In Python:: Listening for events from @value{GDBN}. * Threads In Python:: Accessing inferior threads from Python. @@ -23966,6 +23983,68 @@ my_library.so: bar @end smallexample +@node Type Printing API +@subsubsection Type Printing API +@cindex type printing API for Python + +@value{GDBN} provides a way for Python code to customize type display. +This is mainly useful for substituting canonical typedef names for +types. + +@cindex type printer +A @dfn{type printer} is just a Python object conforming to a certain +protocol. A simple base class implementing the protocol is provided; +see @ref{gdb.types}. A type printer must supply at least: + +@defivar type_printer enabled +A boolean which is True if the printer is enabled, and False +otherwise. This is manipulated by the @code{enable type-printer} +and @code{disable type-printer} commands. +@end defivar + +@defivar type_printer name +The name of the type printer. This must be a string. This is used by +the @code{enable type-printer} and @code{disable type-printer} +commands. +@end defivar + +@defmethod type_printer instantiate (self) +This is called by @value{GDBN} at the start of type-printing. It is +only called if the type printer is enabled. This method must return a +new object that supplies a @code{recognize} method, as described below. +@end defmethod + + +When displaying a type, say via the @code{ptype} command, @value{GDBN} +will compute a list of type recognizers. This is done by iterating +first over the per-objfile type printers (@pxref{Objfiles In Python}), +followed by the per-progspace type printers (@pxref{Progspaces In +Python}), and finally the global type printers. + +@value{GDBN} will call the @code{instantiate} method of each enabled +type printer. If this method returns @code{None}, then the result is +ignored; otherwise, it is appended to the list of recognizers. + +Then, when @value{GDBN} is going to display a type name, it iterates +over the list of recognizers. For each one, it calls the recognition +function, stopping if the function returns a non-@code{None} value. +The recognition function is defined as: + +@defmethod type_recognizer recognize (self, type) +If @var{type} is not recognized, return @code{None}. Otherwise, +return a string which is to be printed as the name of @var{type}. +@var{type} will be an instance of @code{gdb.Type} (@pxref{Types In +Python}). +@end defmethod + +@value{GDBN} uses this two-pass approach so that type printers can +efficiently cache information without holding on to it too long. For +example, it can be convenient to look up type information in a type +printer and hold it for a recognizer's lifetime; if a single pass were +done then type printers would have to make use of the event system in +order to avoid holding information that could become stale as the +inferior changed. + @node Inferiors In Python @subsubsection Inferiors In Python @cindex inferiors in Python @@ -24810,6 +24889,11 @@ which is used to format the value. @xref{Pretty Printing API}, for more information. @end defvar +@defvar Progspace.type_printers +The @code{type_printers} attribute is a list of type printer objects. +@xref{Type Printing API}, for more information. +@end defvar + @node Objfiles In Python @subsubsection Objfiles In Python @@ -24855,6 +24939,11 @@ which is used to format the value. @xref{Pretty Printing API}, for more information. @end defvar +@defvar Objfile.type_printers +The @code{type_printers} attribute is a list of type printer objects. +@xref{Type Printing API}, for more information. +@end defvar + A @code{gdb.Objfile} object has the following methods: @defun Objfile.is_valid () @@ -26057,7 +26146,7 @@ if a printer with the same name already exists. @cindex gdb.types This module provides a collection of utilities for working with -@code{gdb.Types} objects. +@code{gdb.Type} objects. @table @code @item get_basic_type (@var{type}) @@ -26118,6 +26207,37 @@ Then in @value{GDBN}: @{['a', 'b0', 'b1']@} @end smallexample +@item get_type_recognizers () +Return a list of the enabled type recognizers for the current context. +This is called by @value{GDBN} during the type-printing process +(@pxref{Type Printing API}). + +@item apply_type_recognizers (recognizers, type_obj) +Apply the type recognizers, @var{recognizers}, to the type object +@var{type_obj}. If any recognizer returns a string, return that +string. Otherwise, return @code{None}. This is called by +@value{GDBN} during the type-printing process (@pxref{Type Printing +API}). + +@item register_type_printer (locus, printer) +This is a convenience function to register a type printer. +@var{printer} is the type printer to register. It must implement the +type printer protocol. @var{locus} is either a @code{gdb.Objfile}, in +which case the printer is registered with that objfile; a +@code{gdb.Progspace}, in which case the printer is registered with +that progspace; or @code{None}, in which case the printer is +registered globally. + +@item TypePrinter +This is a base class that implements the type printer protocol. Type +printers are encouraged, but not required, to derive from this class. +It defines a constructor: + +@defmethod TypePrinter __init__ (self, name) +Initialize the type printer with the given name. The new printer +starts in the enabled state. +@end defmethod + @end table @node gdb.prompt diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 0e020fe4858..067152641b5 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -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__)) diff --git a/gdb/python/lib/gdb/command/type_printers.py b/gdb/python/lib/gdb/command/type_printers.py new file mode 100644 index 00000000000..b7d693048bf --- /dev/null +++ b/gdb/python/lib/gdb/command/type_printers.py @@ -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 . + +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() diff --git a/gdb/python/lib/gdb/types.py b/gdb/python/lib/gdb/types.py index 66c952896bb..37453830799 100644 --- a/gdb/python/lib/gdb/types.py +++ b/gdb/python/lib/gdb/types.py @@ -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) diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index 9fa6813f62e..5d2398f5471 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -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 } }; diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c index e4b029b2070..c1b1cac52a7 100644 --- a/gdb/python/py-progspace.c +++ b/gdb/python/py-progspace.c @@ -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 } }; diff --git a/gdb/python/python.c b/gdb/python/python.c index cd50e509759..359d2385662 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -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 */ diff --git a/gdb/python/python.h b/gdb/python/python.h index 0d072712af4..72872b0b116 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -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 */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 9e043268e82..5293c88c090 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2012-11-12 Tom Tromey + + * 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. + 2012-11-12 Tom Tromey * gdb.base/call-sc.exp: Use "ptype/r". diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp index ccdc199e670..8b1facb1ab3 100644 --- a/gdb/testsuite/gdb.base/completion.exp +++ b/gdb/testsuite/gdb.base/completion.exp @@ -161,7 +161,7 @@ gdb_test_multiple "" "$test" { -re "^info t foo\\\x07$" { send_gdb "\n" gdb_test_multiple "" "$test" { - -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" { + -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" { pass "$test" } } @@ -174,7 +174,7 @@ gdb_test_multiple "" "$test" { -re "^info t\\\x07$" { send_gdb "\n" gdb_test_multiple "" "$test" { - -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" { + -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" { pass "$test" } } @@ -187,7 +187,7 @@ gdb_test_multiple "" "$test" { -re "^info t \\\x07$" { send_gdb "\n" gdb_test_multiple "" "$test" { - -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $" { + -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, (type-printers, )?types\\..*$gdb_prompt $" { pass "$test" } } diff --git a/gdb/testsuite/gdb.python/py-typeprint.cc b/gdb/testsuite/gdb.python/py-typeprint.cc new file mode 100644 index 00000000000..65c06f08166 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-typeprint.cc @@ -0,0 +1,37 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008-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 . */ + +class basic_string +{ +}; + +template +class templ +{ +public: + T x; + templ *value; +}; + +templ s; + +basic_string bs; + +int main() +{ + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-typeprint.exp b/gdb/testsuite/gdb.python/py-typeprint.exp new file mode 100644 index 00000000000..ef0705519cc --- /dev/null +++ b/gdb/testsuite/gdb.python/py-typeprint.exp @@ -0,0 +1,53 @@ +# Copyright (C) 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 . + +if { [skip_cplus_tests] } { continue } + +load_lib gdb-python.exp +load_lib cp-support.exp + +standard_testfile .cc + +if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} { + return -1 +} + +if { [skip_python_tests] } { continue } + +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] + +gdb_test_no_output "python execfile ('${remote_python_file}')" + +cp_test_ptype_class s "basic test" "class" "templ" { + { field public "T x;" } + { field public "templ *value;" } +} "" {} "" + +cp_test_ptype_class s "raw test" "class" "templ" { + { field public "basic_string x;" } + { field public "templ *value;" } +} "" {} "/r" + +gdb_test_no_output "disable type-printer string" +gdb_test "whatis bs" "basic_string" "whatis with disabled printer" + +gdb_test "info type-printers" "Global type printers:.*string.*disabled.*" + +gdb_test_no_output "enable type-printer string" +gdb_test "whatis bs" "string" "whatis with enabled printer" + +gdb_test "whatis s" "templ" + +remote_file host delete ${remote_python_file} diff --git a/gdb/testsuite/gdb.python/py-typeprint.py b/gdb/testsuite/gdb.python/py-typeprint.py new file mode 100644 index 00000000000..a4351cdd504 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-typeprint.py @@ -0,0 +1,35 @@ +# Copyright (C) 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 . + +import gdb + +class Recognizer(object): + def __init__(self): + self.enabled = True + + def recognize(self, type_obj): + if type_obj.tag == 'basic_string': + return 'string' + return None + +class StringTypePrinter(object): + def __init__(self): + self.name = 'string' + self.enabled = True + + def instantiate(self): + return Recognizer() + +gdb.type_printers.append(StringTypePrinter()) diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 0e1c93c0a8c..cf3ba381a44 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -38,6 +38,7 @@ #include #include #include "cli/cli-utils.h" +#include "python/python.h" extern void _initialize_typeprint (void); @@ -52,7 +53,9 @@ const struct type_print_options type_print_raw_options = 1, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ - NULL /* local_typedefs */ + NULL, /* local_typedefs */ + NULL, /* global_table */ + NULL /* global_printers */ }; /* The default flags for 'ptype' and 'whatis'. */ @@ -62,7 +65,9 @@ static struct type_print_options default_ptype_flags = 0, /* raw */ 1, /* print_methods */ 1, /* print_typedefs */ - NULL /* local_typedefs */ + NULL, /* local_typedefs */ + NULL, /* global_table */ + NULL /* global_printers */ }; @@ -235,6 +240,74 @@ copy_typedef_hash (struct typedef_hash_table *table) return result; } +/* A cleanup to free the global typedef hash. */ + +static void +do_free_global_table (void *arg) +{ + struct type_print_options *flags = arg; + + free_typedef_hash (flags->global_typedefs); + free_type_printers (flags->global_printers); +} + +/* Create the global typedef hash. */ + +static struct cleanup * +create_global_typedef_table (struct type_print_options *flags) +{ + gdb_assert (flags->global_typedefs == NULL && flags->global_printers == NULL); + flags->global_typedefs = create_typedef_hash (); + flags->global_printers = start_type_printers (); + return make_cleanup (do_free_global_table, flags); +} + +/* Look up the type T in the global typedef hash. If it is found, + return the typedef name. If it is not found, apply the + type-printers, if any, given by start_type_printers and return the + result. A NULL return means that the name was not found. */ + +static const char * +find_global_typedef (const struct type_print_options *flags, + struct type *t) +{ + char *applied; + void **slot; + struct typedef_field tf, *new_tf; + + if (flags->global_typedefs == NULL) + return NULL; + + tf.name = NULL; + tf.type = t; + + slot = htab_find_slot (flags->global_typedefs->table, &tf, INSERT); + if (*slot != NULL) + { + new_tf = *slot; + return new_tf->name; + } + + /* Put an entry into the hash table now, in case apply_type_printers + recurses. */ + new_tf = XOBNEW (&flags->global_typedefs->storage, struct typedef_field); + new_tf->name = NULL; + new_tf->type = t; + + *slot = new_tf; + + applied = apply_type_printers (flags->global_printers, t); + + if (applied != NULL) + { + new_tf->name = obstack_copy0 (&flags->global_typedefs->storage, applied, + strlen (applied)); + xfree (applied); + } + + return new_tf->name; +} + /* Look up the type T in the typedef hash table in with FLAGS. If T is in the table, return its short (class-relative) typedef name. Otherwise return NULL. If the table is NULL, this always returns @@ -243,16 +316,19 @@ copy_typedef_hash (struct typedef_hash_table *table) const char * find_typedef_in_hash (const struct type_print_options *flags, struct type *t) { - struct typedef_field tf, *found; + if (flags->local_typedefs != NULL) + { + struct typedef_field tf, *found; - if (flags->local_typedefs == NULL) - return NULL; + tf.name = NULL; + tf.type = t; + found = htab_find (flags->local_typedefs->table, &tf); - tf.name = NULL; - tf.type = t; - found = htab_find (flags->local_typedefs->table, &tf); + if (found != NULL) + return found->name; + } - return found == NULL ? NULL : found->name; + return find_global_typedef (flags, t); } @@ -325,7 +401,7 @@ whatis_exp (char *exp, int show) { struct expression *expr; struct value *val; - struct cleanup *old_chain = NULL; + struct cleanup *old_chain; struct type *real_type = NULL; struct type *type; int full = 0; @@ -334,6 +410,8 @@ whatis_exp (char *exp, int show) struct value_print_options opts; struct type_print_options flags = default_ptype_flags; + old_chain = make_cleanup (null_cleanup, NULL); + if (exp) { if (*exp == '/') @@ -373,7 +451,7 @@ whatis_exp (char *exp, int show) } expr = parse_expression (exp); - old_chain = make_cleanup (free_current_contents, &expr); + make_cleanup (free_current_contents, &expr); val = evaluate_type (expr); } else @@ -394,6 +472,9 @@ whatis_exp (char *exp, int show) printf_filtered ("type = "); + if (!flags.raw) + create_global_typedef_table (&flags); + if (real_type) { printf_filtered ("/* real type = "); @@ -406,8 +487,7 @@ whatis_exp (char *exp, int show) LA_PRINT_TYPE (type, "", gdb_stdout, show, 0, &flags); printf_filtered ("\n"); - if (exp) - do_cleanups (old_chain); + do_cleanups (old_chain); } static void diff --git a/gdb/typeprint.h b/gdb/typeprint.h index 71bac018999..a201bddd01e 100644 --- a/gdb/typeprint.h +++ b/gdb/typeprint.h @@ -38,6 +38,14 @@ struct type_print_options /* If not NULL, a local typedef hash table used when printing a type. */ struct typedef_hash_table *local_typedefs; + + /* If not NULL, a global typedef hash table used when printing a + type. */ + struct typedef_hash_table *global_typedefs; + + /* The list of type printers associated with the global typedef + table. This is intentionally opaque. */ + void *global_printers; }; extern const struct type_print_options type_print_raw_options; -- 2.39.2