New python module gdb.printing, and new commands info pretty-printer,

enable pretty-printer, disable pretty-printer.
	* NEWS: Mention them.
	* data-directory/Makefile.in (PYTHON_FILES): Add gdb/printing.py,
	gdb/command/__init__.py, gdb/command/pretty_printers.py.
	* python/lib/gdb/__init__.py: Install pretty-printer commands.
	* python/lib/gdb/printing.py: New file.
	* python/lib/gdb/command/__init__.py: New file.
	* python/lib/gdb/command/pretty_printers.py: New file.

	doc/
	* gdb.texinfo (Pretty Printing): Expand into three sections,
	introduction, example, and commands.
	(Python API): Delete section Disabling Pretty-Printers, merge into
	Selecting Pretty-Printers.
	(Writing a Pretty-Printer): New section.  Move the pretty-printer
	example here, and reformat to match python coding style.  Add a second
	example using the gdb.printing module.
	(Python modules): Add gdb.printing.

	testsuite/
	* gdb.python/py-pp-maint.c: New file.
	* gdb.python/py-pp-maint.exp: New file.
	* gdb.python/py-pp-maint.py: New file.
This commit is contained in:
Doug Evans 2010-11-02 22:44:13 +00:00
parent 50c97f3812
commit 7b51bc51e1
13 changed files with 1184 additions and 42 deletions

View file

@ -1,3 +1,15 @@
2010-11-02 Doug Evans <dje@google.com>
New python module gdb.printing, and new commands info pretty-printer,
enable pretty-printer, disable pretty-printer.
* NEWS: Mention them.
* data-directory/Makefile.in (PYTHON_FILES): Add gdb/printing.py,
gdb/command/__init__.py, gdb/command/pretty_printers.py.
* python/lib/gdb/__init__.py: Install pretty-printer commands.
* python/lib/gdb/printing.py: New file.
* python/lib/gdb/command/__init__.py: New file.
* python/lib/gdb/command/pretty_printers.py: New file.
2010-11-02 Tom Tromey <tromey@redhat.com>
* NEWS: Mention Guile removal.

View file

@ -16,6 +16,15 @@
It contains a collection of utilities for working with gdb.Types objects:
get_basic_type, has_field, make_enum_dict.
** Module gdb.printing has been added.
It contains utilities for writing and registering pretty-printers.
New classes: PrettyPrinter, SubPrettyPrinter,
RegexpCollectionPrettyPrinter.
New function: register_pretty_printer.
** New commands "info pretty-printers", "enable pretty-printer" and
"disable pretty-printer" have been added.
* C++ Improvements:
** GDB now puts template parameters in scope when debugging in an

View file

@ -52,7 +52,10 @@ PYTHON_DIR = python
PYTHON_INSTALL_DIR = $(DESTDIR)/$(GDB_DATADIR)/$(PYTHON_DIR)
PYTHON_FILES = \
gdb/__init__.py \
gdb/types.py
gdb/types.py \
gdb/printing.py \
gdb/command/__init__.py \
gdb/command/pretty_printers.py
FLAGS_TO_PASS = \
"prefix=$(prefix)" \

View file

@ -1,3 +1,14 @@
2010-11-02 Doug Evans <dje@google.com>
* gdb.texinfo (Pretty Printing): Expand into three sections,
introduction, example, and commands.
(Python API): Delete section Disabling Pretty-Printers, merge into
Selecting Pretty-Printers.
(Writing a Pretty-Printer): New section. Move the pretty-printer
example here, and reformat to match python coding style. Add a second
example using the gdb.printing module.
(Python modules): Add gdb.printing.
2010-10-29 Doug Evans <dje@google.com>
* gdb.texinfo (Python): Fix long line.

View file

@ -8127,8 +8127,60 @@ Show whether C@t{++} virtual function tables are pretty printed, or not.
Python code. It greatly simplifies the display of complex objects. This
mechanism works for both MI and the CLI.
For example, here is how a C@t{++} @code{std::string} looks without a
pretty-printer:
@menu
* Pretty-Printer Introduction:: Introduction to pretty-printers
* Pretty-Printer Example:: An example pretty-printer
* Pretty-Printer Commands:: Pretty-printer commands
@end menu
@node Pretty-Printer Introduction
@subsection Pretty-Printer Introduction
When @value{GDBN} prints a value, it first sees if there is a pretty-printer
registered for the value. If there is then @value{GDBN} invokes the
pretty-printer to print the value. Otherwise the value is printed normally.
Pretty-printers are normally named. This makes them easy to manage.
The @samp{info pretty-printer} command will list all the installed
pretty-printers with their names.
If a pretty-printer can handle multiple data types, then its
@dfn{subprinters} are the printers for the individual data types.
Each such subprinter has its own name.
The format of the name is @var{printer-name}:@var{subprinter-name}.
Pretty-printers are installed by @dfn{registering} them with @value{GDBN}.
Typically they are automatically loaded and registered when the corresponding
debug information is loaded, thus making them available without having to
do anything special.
There are three places where a pretty-printer can be registered.
@itemize @bullet
@item
Pretty-printers registered globally are available when debugging
all inferiors.
@item
Pretty-printers registered with a program space are available only
when debugging that program.
@xref{Progspaces In Python}, for more details on program spaces in Python.
@item
Pretty-printers registered with an objfile are loaded and unloaded
with the corresponding objfile (e.g., shared library).
@xref{Objfiles In Python}, for more details on objfiles in Python.
@end itemize
@xref{Selecting Pretty-Printers}, for further information on how
pretty-printers are selected,
@xref{Writing a Pretty-Printer}, for implementing pretty printers
for new types.
@node Pretty-Printer Example
@subsection Pretty-Printer Example
Here is how a C@t{++} @code{std::string} looks without a pretty-printer:
@smallexample
(@value{GDBP}) print s
@ -8153,8 +8205,91 @@ With a pretty-printer for @code{std::string} only the contents are printed:
$2 = "abcd"
@end smallexample
For implementing pretty printers for new types you should read the Python API
details (@pxref{Pretty Printing API}).
@node Pretty-Printer Commands
@subsection Pretty-Printer Commands
@cindex pretty-printer commands
@table @code
@kindex info pretty-printer
@item info pretty-printer [@var{object-regexp} [@var{name-regexp}]]
Print the list of installed pretty-printers.
This includes disabled pretty-printers, which are marked as such.
@var{object-regexp} is a regular expression matching the objects
whose pretty-printers to list.
Objects can be @code{global}, the program space's file
(@pxref{Progspaces In Python}),
and the object files within that program space (@pxref{Objfiles In Python}).
@xref{Selecting Pretty-Printers}, for details on how @value{GDBN}
looks up a printer from these three objects.
@var{name-regexp} is a regular expression matching the name of the printers
to list.
@kindex disable pretty-printer
@item disable pretty-printer [@var{object-regexp} [@var{name-regexp}]]
Disable pretty-printers matching @var{object-regexp} and @var{name-regexp}.
A disabled pretty-printer is not forgotten, it may be enabled again later.
@kindex enable pretty-printer
@item enable pretty-printer [@var{object-regexp} [@var{name-regexp}]]
Enable pretty-printers matching @var{object-regexp} and @var{name-regexp}.
@end table
Example:
Suppose we have three pretty-printers installed: one from library1.so
named @code{foo} that prints objects of type @code{foo}, and
another from library2.so named @code{bar} that prints two types of objects,
@code{bar1} and @code{bar2}.
@smallexample
(gdb) info pretty-printer
library1.so:
foo
library2.so:
bar
bar1
bar2
(gdb) info pretty-printer library2
library2.so:
bar
bar1
bar2
(gdb) disable pretty-printer library1
1 printer disabled
2 of 3 printers enabled
(gdb) info pretty-printer
library1.so:
foo [disabled]
library2.so:
bar
bar1
bar2
(gdb) disable pretty-printer library2 bar:bar1
1 printer disabled
1 of 3 printers enabled
(gdb) info pretty-printer library2
library1.so:
foo [disabled]
library2.so:
bar
bar1 [disabled]
bar2
(gdb) disable pretty-printer library2 bar
1 printer disabled
0 of 3 printers enabled
(gdb) info pretty-printer library2
library1.so:
foo [disabled]
library2.so:
bar [disabled]
bar1 [disabled]
bar2
@end smallexample
Note that for @code{bar} the entire printer can be disabled,
as can each individual subprinter.
@node Value History
@section Value History
@ -20484,7 +20619,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
* Types In Python:: Python representation of types.
* Pretty Printing API:: Pretty-printing values.
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Disabling Pretty-Printers:: Disabling broken printers.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Inferiors In Python:: Python representation of inferiors (processes)
* Threads In Python:: Accessing inferior threads from Python.
* Commands In Python:: Implementing new commands in Python.
@ -21349,12 +21484,13 @@ printer exists, then this returns @code{None}.
The Python list @code{gdb.pretty_printers} contains an array of
functions or callable objects that have been registered via addition
as a pretty-printer.
as a pretty-printer. Printers in this list are called @code{global}
printers, they're available when debugging all inferiors.
Each @code{gdb.Progspace} contains a @code{pretty_printers} attribute.
Each @code{gdb.Objfile} also contains a @code{pretty_printers}
attribute.
A function on one of these lists is passed a single @code{gdb.Value}
Each function on these lists is passed a single @code{gdb.Value}
argument and should return a pretty-printer object conforming to the
interface definition above (@pxref{Pretty Printing API}). If a function
cannot create a pretty-printer for the value, it should return
@ -21362,9 +21498,8 @@ cannot create a pretty-printer for the value, it should return
@value{GDBN} first checks the @code{pretty_printers} attribute of each
@code{gdb.Objfile} in the current program space and iteratively calls
each enabled function (@pxref{Disabling Pretty-Printers})
in the list for that @code{gdb.Objfile} until it receives
a pretty-printer object.
each enabled lookup routine in the list for that @code{gdb.Objfile}
until it receives a pretty-printer object.
If no pretty-printer is found in the objfile lists, @value{GDBN} then
searches the pretty-printer list of the current program space,
calling each enabled function until an object is returned.
@ -21377,20 +21512,43 @@ given list, functions are always invoked from the head of the list,
and iterated over sequentially until the end of the list, or a printer
object is returned.
For various reasons a pretty-printer may not work.
For example, the underlying data structure may have changed and
the pretty-printer is out of date.
The consequences of a broken pretty-printer are severe enough that
@value{GDBN} provides support for enabling and disabling individual
printers. For example, if @code{print frame-arguments} is on,
a backtrace can become highly illegible if any argument is printed
with a broken printer.
Pretty-printers are enabled and disabled by attaching an @code{enabled}
attribute to the registered function or callable object. If this attribute
is present and its value is @code{False}, the printer is disabled, otherwise
the printer is enabled.
@node Writing a Pretty-Printer
@subsubsection Writing a Pretty-Printer
@cindex writing a pretty-printer
A pretty-printer consists of two parts: a lookup function to detect
if the type is supported, and the printer itself.
Here is an example showing how a @code{std::string} printer might be
written:
written. @xref{Pretty Printing API}, for details on the API this class
must provide.
@smallexample
class StdStringPrinter:
class StdStringPrinter(object):
"Print a std::string"
def __init__ (self, val):
def __init__(self, val):
self.val = val
def to_string (self):
def to_string(self):
return self.val['_M_dataplus']['_M_p']
def display_hint (self):
def display_hint(self):
return 'string'
@end smallexample
@ -21398,15 +21556,13 @@ And here is an example showing how a lookup function for the printer
example above might be written.
@smallexample
def str_lookup_function (val):
def str_lookup_function(val):
lookup_tag = val.type.tag
regex = re.compile ("^std::basic_string<char,.*>$")
if lookup_tag == None:
return None
if regex.match (lookup_tag):
return StdStringPrinter (val)
regex = re.compile("^std::basic_string<char,.*>$")
if regex.match(lookup_tag):
return StdStringPrinter(val)
return None
@end smallexample
@ -21442,8 +21598,8 @@ To continue the @code{std::string} example (@pxref{Pretty Printing API}),
this code might appear in @code{gdb.libstdcxx.v6}:
@smallexample
def register_printers (objfile):
objfile.pretty_printers.add (str_lookup_function)
def register_printers(objfile):
objfile.pretty_printers.add(str_lookup_function)
@end smallexample
@noindent
@ -21451,27 +21607,92 @@ And then the corresponding contents of the auto-load file would be:
@smallexample
import gdb.libstdcxx.v6
gdb.libstdcxx.v6.register_printers (gdb.current_objfile ())
gdb.libstdcxx.v6.register_printers(gdb.current_objfile())
@end smallexample
@node Disabling Pretty-Printers
@subsubsection Disabling Pretty-Printers
@cindex disabling pretty-printers
The previous example illustrates a basic pretty-printer.
There are a few things that can be improved on.
The printer doesn't have a name, making it hard to identify in a
list of installed printers. The lookup function has a name, but
lookup functions can have arbitrary, even identical, names.
For various reasons a pretty-printer may not work.
For example, the underlying data structure may have changed and
the pretty-printer is out of date.
Second, the printer only handles one type, whereas a library typically has
several types. One could install a lookup function for each desired type
in the library, but one could also have a single lookup function recognize
several types. The latter is the conventional way this is handled.
If a pretty-printer can handle multiple data types, then its
@dfn{subprinters} are the printers for the individual data types.
The consequences of a broken pretty-printer are severe enough that
@value{GDBN} provides support for enabling and disabling individual
printers. For example, if @code{print frame-arguments} is on,
a backtrace can become highly illegible if any argument is printed
with a broken printer.
The @code{gdb.printing} module provides a formal way of solving these
problems (@pxref{gdb.printing}).
Here is another example that handles multiple types.
Pretty-printers are enabled and disabled by attaching an @code{enabled}
attribute to the registered function or callable object. If this attribute
is present and its value is @code{False}, the printer is disabled, otherwise
the printer is enabled.
These are the types we are going to pretty-print:
@smallexample
struct foo @{ int a, b; @};
struct bar @{ struct foo x, y; @};
@end smallexample
Here are the printers:
@smallexample
class fooPrinter:
"""Print a foo object."""
def __init__(self, val):
self.val = val
def to_string(self):
return ("a=<" + str(self.val["a"]) +
"> b=<" + str(self.val["b"]) + ">")
class barPrinter:
"""Print a bar object."""
def __init__(self, val):
self.val = val
def to_string(self):
return ("x=<" + str(self.val["x"]) +
"> y=<" + str(self.val["y"]) + ">")
@end smallexample
This example doesn't need a lookup function, that is handled by the
@code{gdb.printing} module. Instead a function is provided to build up
the object that handles the lookup.
@smallexample
import gdb.printing
def build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter(
"my_library")
pp.add_printer('foo', '^foo$', fooPrinter)
pp.add_printer('bar', '^bar$', barPrinter)
return pp
@end smallexample
And here is the autoload support:
@smallexample
import gdb.printing
import my_library
gdb.printing.register_pretty_printer(
gdb.current_objfile(),
my_library.build_pretty_printer())
@end smallexample
Finally, when this printer is loaded into @value{GDBN}, here is the
corresponding output of @samp{info pretty-printer}:
@smallexample
(gdb) info pretty-printer
my_library.so:
my_library
foo
bar
@end smallexample
@node Inferiors In Python
@subsubsection Inferiors In Python
@ -22920,16 +23141,42 @@ top of the source tree to the source search path.
@subsection Python modules
@cindex python modules
@c It is assumed that at least one more module will be added before
@c the next release of gdb. Thus we use a menu here.
@value{GDBN} comes with a module to assist writing Python code.
@menu
* gdb.printing:: Building and registering pretty-printers.
* gdb.types:: Utilities for working with types.
@end menu
@node gdb.printing
@subsubsection gdb.printing
@cindex gdb.printing
This module provides a collection of utilities for working with
pretty-printers.
@table @code
@item PrettyPrinter (@var{name}, @var{subprinters}=None)
This class specifies the API that makes @samp{info pretty-printer},
@samp{enable pretty-printer} and @samp{disable pretty-printer} work.
Pretty-printers should generally inherit from this class.
@item SubPrettyPrinter (@var{name})
For printers that handle multiple types, this class specifies the
corresponding API for the subprinters.
@item RegexpCollectionPrettyPrinter (@var{name})
Utility class for handling multiple printers, all recognized via
regular expressions.
@xref{Writing a Pretty-Printer}, for an example.
@item register_pretty_printer (@var{obj}, @var{printer})
Register @var{printer} with the pretty-printer list of @var{obj}.
@end table
@node gdb.types
@subsubsection gdb.types
@cindex gdb.types
This module provides a collection of utilities for working with
@code{gdb.Types} objects.

View file

@ -12,3 +12,7 @@
#
# 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.command.pretty_printers
gdb.command.pretty_printers.register_pretty_printer_commands()

View file

@ -0,0 +1,16 @@
# Copyright (C) 2010 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/>.

View file

@ -0,0 +1,369 @@
# Pretty-printer commands.
# Copyright (C) 2010 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/>.
"""GDB commands for working with pretty-printers."""
import copy
import gdb
import re
def parse_printer_regexps(arg):
"""Internal utility to parse a pretty-printer command argv.
Arguments:
arg: The arguments to the command. The format is:
[object-regexp [name-regexp]].
Individual printers in a collection are named as
printer-name:subprinter-name.
Returns:
The result is a 3-tuple of compiled regular expressions, except that
the resulting compiled subprinter regexp is None if not provided.
Raises:
SyntaxError: an error processing ARG
"""
argv = gdb.string_to_argv(arg);
argc = len(argv)
object_regexp = "" # match everything
name_regexp = "" # match everything
subname_regexp = None
if argc > 3:
raise SyntaxError("too many arguments")
if argc >= 1:
object_regexp = argv[0]
if argc >= 2:
name_subname = argv[1].split(":", 1)
name_regexp = name_subname[0]
if len(name_subname) == 2:
subname_regexp = name_subname[1]
# That re.compile raises SyntaxError was determined empirically.
# We catch it and reraise it to provide a slightly more useful
# error message for the user.
try:
object_re = re.compile(object_regexp)
except SyntaxError:
raise SyntaxError("invalid object regexp: %s" % object_regexp)
try:
name_re = re.compile (name_regexp)
except SyntaxError:
raise SyntaxError("invalid name regexp: %s" % name_regexp)
if subname_regexp is not None:
try:
subname_re = re.compile(subname_regexp)
except SyntaxError:
raise SyntaxError("invalid subname regexp: %s" % subname_regexp)
else:
subname_re = None
return(object_re, name_re, subname_re)
def printer_enabled_p(printer):
"""Internal utility to see if printer (or subprinter) is enabled."""
if hasattr(printer, "enabled"):
return printer.enabled
else:
return True
class InfoPrettyPrinter(gdb.Command):
"""GDB command to list all registered pretty-printers.
Usage: info pretty-printer [object-regexp [name-regexp]]
OBJECT-REGEXP is a regular expression matching the objects to list.
Objects are "global", the program space's file, and the objfiles within
that program space.
NAME-REGEXP matches the name of the pretty-printer.
Individual printers in a collection are named as
printer-name:subprinter-name.
"""
def __init__ (self):
super(InfoPrettyPrinter, self).__init__("info pretty-printer",
gdb.COMMAND_DATA)
@staticmethod
def enabled_string(printer):
"""Return "" if PRINTER is enabled, otherwise " [disabled]"."""
if printer_enabled_p(printer):
return ""
else:
return " [disabled]"
@staticmethod
def printer_name(printer):
"""Return the printer's name."""
if hasattr(printer, "name"):
return printer.name
if hasattr(printer, "__name__"):
return printer.__name__
# This "shouldn't happen", but the public API allows for
# direct additions to the pretty-printer list, and we shouldn't
# crash because someone added a bogus printer.
# Plus we want to give the user a way to list unknown printers.
return "unknown"
def list_pretty_printers(self, pretty_printers, name_re, subname_re):
"""Print a list of pretty-printers."""
# A potential enhancement is to provide an option to list printers in
# "lookup order" (i.e. unsorted).
sorted_pretty_printers = copy.copy(pretty_printers)
sorted_pretty_printers.sort(lambda x, y:
cmp(self.printer_name(x),
self.printer_name(y)))
for printer in sorted_pretty_printers:
name = self.printer_name(printer)
enabled = self.enabled_string(printer)
if name_re.match(name):
print " %s%s" % (name, enabled)
if (hasattr(printer, "subprinters") and
printer.subprinters is not None):
sorted_subprinters = copy.copy(printer.subprinters)
sorted_subprinters.sort(lambda x, y:
cmp(self.printer_name(x),
self.printer_name(y)))
for subprinter in sorted_subprinters:
if (not subname_re or
subname_re.match(subprinter.name)):
print (" %s%s" %
(subprinter.name,
self.enabled_string(subprinter)))
def invoke1(self, title, printer_list,
obj_name_to_match, object_re, name_re, subname_re):
""""Subroutine of invoke to simplify it."""
if printer_list and object_re.match(obj_name_to_match):
print title
self.list_pretty_printers(printer_list, name_re, subname_re)
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
(object_re, name_re, subname_re) = parse_printer_regexps(arg)
self.invoke1("global pretty-printers:", gdb.pretty_printers,
"global", object_re, name_re, subname_re)
cp = gdb.current_progspace()
self.invoke1("progspace %s pretty-printers:" % cp.filename,
cp.pretty_printers, "progspace",
object_re, name_re, subname_re)
for objfile in gdb.objfiles():
self.invoke1(" objfile %s pretty-printers:" % objfile.filename,
objfile.pretty_printers, objfile.filename,
object_re, name_re, subname_re)
def count_enabled_printers(pretty_printers):
"""Return a 2-tuple of number of enabled and total printers."""
enabled = 0
total = 0
for printer in pretty_printers:
if (hasattr(printer, "subprinters")
and printer.subprinters is not None):
if printer_enabled_p(printer):
for subprinter in printer.subprinters:
if printer_enabled_p(subprinter):
enabled += 1
total += len(printer.subprinters)
else:
if printer_enabled_p(printer):
enabled += 1
total += 1
return (enabled, total)
def count_all_enabled_printers():
"""Return a 2-tuble of the enabled state and total number of all printers.
This includes subprinters.
"""
enabled_count = 0
total_count = 0
(t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers)
enabled_count += t_enabled
total_count += t_total
(t_enabled, t_total) = count_enabled_printers(gdb.current_progspace().pretty_printers)
enabled_count += t_enabled
total_count += t_total
for objfile in gdb.objfiles():
(t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers)
enabled_count += t_enabled
total_count += t_total
return (enabled_count, total_count)
def pluralize(text, n, suffix="s"):
"""Return TEXT pluralized if N != 1."""
if n != 1:
return "%s%s" % (text, suffix)
else:
return text
def show_pretty_printer_enabled_summary():
"""Print the number of printers enabled/disabled.
We count subprinters individually.
"""
(enabled_count, total_count) = count_all_enabled_printers()
print "%d of %d printers enabled" % (enabled_count, total_count)
def do_enable_pretty_printer_1 (pretty_printers, name_re, subname_re, flag):
"""Worker for enabling/disabling pretty-printers.
Arguments:
pretty_printers: list of pretty-printers
name_re: regular-expression object to select printers
subname_re: regular expression object to select subprinters or None
if all are affected
flag: True for Enable, False for Disable
Returns:
The number of printers affected.
This is just for informational purposes for the user.
"""
total = 0
for printer in pretty_printers:
if (hasattr(printer, "name") and name_re.match(printer.name) or
hasattr(printer, "__name__") and name_re.match(printer.__name__)):
if hasattr(printer, "subprinters"):
if not subname_re:
# Only record printers that change state.
if printer_enabled_p(printer) != flag:
for subprinter in printer.subprinters:
if printer_enabled_p(subprinter):
total += 1
# NOTE: We preserve individual subprinter settings.
printer.enabled = flag
else:
# NOTE: Whether this actually disables the subprinter
# depends on whether the printer's lookup function supports
# the "enable" API. We can only assume it does.
for subprinter in printer.subprinters:
if subname_re.match(subprinter.name):
# Only record printers that change state.
if (printer_enabled_p(printer) and
printer_enabled_p(subprinter) != flag):
total += 1
subprinter.enabled = flag
else:
# This printer has no subprinters.
# If the user does "disable pretty-printer .* .* foo"
# should we disable printers that don't have subprinters?
# How do we apply "foo" in this context? Since there is no
# "foo" subprinter it feels like we should skip this printer.
# There's still the issue of how to handle
# "disable pretty-printer .* .* .*", and every other variation
# that can match everything. For now punt and only support
# "disable pretty-printer .* .*" (i.e. subname is elided)
# to disable everything.
if not subname_re:
# Only record printers that change state.
if printer_enabled_p(printer) != flag:
total += 1
printer.enabled = flag
return total
def do_enable_pretty_printer (arg, flag):
"""Internal worker for enabling/disabling pretty-printers."""
(object_re, name_re, subname_re) = parse_printer_regexps(arg)
total = 0
if object_re.match("global"):
total += do_enable_pretty_printer_1(gdb.pretty_printers,
name_re, subname_re, flag)
cp = gdb.current_progspace()
if object_re.match("progspace"):
total += do_enable_pretty_printer_1(cp.pretty_printers,
name_re, subname_re, flag)
for objfile in gdb.objfiles():
if object_re.match(objfile.filename):
total += do_enable_pretty_printer_1(objfile.pretty_printers,
name_re, subname_re, flag)
if flag:
state = "enabled"
else:
state = "disabled"
print "%d %s %s" % (total, pluralize("printer", total), state)
# Print the total list of printers currently enabled/disabled.
# This is to further assist the user in determining whether the result
# is expected. Since we use regexps to select it's useful.
show_pretty_printer_enabled_summary()
# Enable/Disable one or more pretty-printers.
#
# This is intended for use when a broken pretty-printer is shipped/installed
# and the user wants to disable that printer without disabling all the other
# printers.
#
# A useful addition would be -v (verbose) to show each printer affected.
class EnablePrettyPrinter (gdb.Command):
"""GDB command to enable the specified pretty-printer.
Usage: enable pretty-printer [object-regexp [name-regexp]]
OBJECT-REGEXP is a regular expression matching the objects to examine.
Objects are "global", the program space's file, and the objfiles within
that program space.
NAME-REGEXP matches the name of the pretty-printer.
Individual printers in a collection are named as
printer-name:subprinter-name.
"""
def __init__(self):
super(EnablePrettyPrinter, self).__init__("enable pretty-printer",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
do_enable_pretty_printer(arg, True)
class DisablePrettyPrinter (gdb.Command):
"""GDB command to disable the specified pretty-printer.
Usage: disable pretty-printer [object-regexp [name-regexp]]
OBJECT-REGEXP is a regular expression matching the objects to examine.
Objects are "global", the program space's file, and the objfiles within
that program space.
NAME-REGEXP matches the name of the pretty-printer.
Individual printers in a collection are named as
printer-name:subprinter-name.
"""
def __init__(self):
super(DisablePrettyPrinter, self).__init__("disable pretty-printer",
gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
do_enable_pretty_printer(arg, False)
def register_pretty_printer_commands():
"""Call from a top level script to install the pretty-printer commands."""
InfoPrettyPrinter()
EnablePrettyPrinter()
DisablePrettyPrinter()

View file

@ -0,0 +1,197 @@
# Pretty-printer utilities.
# Copyright (C) 2010 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 working with pretty-printers."""
import gdb
import gdb.types
import re
class PrettyPrinter(object):
"""A basic pretty-printer.
Attributes:
name: A unique string among all printers for the context in which
it is defined (objfile, progspace, or global(gdb)), and should
meaningfully describe what can be pretty-printed.
E.g., "StringPiece" or "protobufs".
subprinters: An iterable object with each element having a `name'
attribute, and, potentially, "enabled" attribute.
Or this is None if there are no subprinters.
enabled: A boolean indicating if the printer is enabled.
Subprinters are for situations where "one" pretty-printer is actually a
collection of several printers. E.g., The libstdc++ pretty-printer has
a pretty-printer for each of several different types, based on regexps.
"""
# While one might want to push subprinters into the subclass, it's
# present here to formalize such support to simplify
# commands/pretty_printers.py.
def __init__(self, name, subprinters=None):
self.name = name
self.subprinters = subprinters
self.enabled = True
def __call__(self, val):
# The subclass must define this.
raise NotImplementedError("PrettyPrinter __call__")
class SubPrettyPrinter(object):
"""Baseclass for sub-pretty-printers.
Sub-pretty-printers needn't use this, but it formalizes what's needed.
Attributes:
name: The name of the subprinter.
enabled: A boolean indicating if the subprinter is enabled.
"""
def __init__(self, name):
self.name = name
self.enabled = True
def register_pretty_printer(obj, printer):
"""Register pretty-printer PRINTER with OBJ.
The printer is added to the front of the search list, thus one can override
an existing printer if one needs to.
Arguments:
obj: Either an objfile, progspace, or None (in which case the printer
is registered globally).
printer: Either a function of one argument (old way) or any object
which has attributes: name, enabled, __call__.
Returns:
Nothing.
Raises:
TypeError: A problem with the type of the printer.
ValueError: The printer's name contains a colon ":".
If the caller wants the printer to be listable and disableable, it must
follow the PrettyPrinter API. This applies to the old way (functions) too.
If printer is an object, __call__ is a method of two arguments:
self, and the value to be pretty-printed. See PrettyPrinter.
"""
# Watch for both __name__ and name.
# Functions get the former for free, but we don't want to use an
# attribute named __foo__ for pretty-printers-as-objects.
# If printer has both, we use `name'.
if not hasattr(printer, "__name__") and not hasattr(printer, "name"):
raise TypeError("printer missing attribute: name")
if hasattr(printer, "name") and not hasattr(printer, "enabled"):
raise TypeError("printer missing attribute: enabled")
if not hasattr(printer, "__call__"):
raise TypeError("printer missing attribute: __call__")
if obj is None:
if gdb.parameter("verbose"):
gdb.write("Registering global %s pretty-printer ...\n" % name)
obj = gdb
else:
if gdb.parameter("verbose"):
gdb.write("Registering %s pretty-printer for %s ...\n" %
(printer.name, obj.filename))
if hasattr(printer, "name"):
if not isinstance(printer.name, basestring):
raise TypeError("printer name is not a string")
# If printer provides a name, make sure it doesn't contain ":".
# Colon is used by the info/enable/disable pretty-printer commands
# to delimit subprinters.
if printer.name.find(":") >= 0:
raise ValueError("colon ':' in printer name")
# Also make sure the name is unique.
# Alas, we can't do the same for functions and __name__, they could
# all have a canonical name like "lookup_function".
# PERF: gdb records printers in a list, making this inefficient.
if (printer.name in
[p.name for p in obj.pretty_printers if hasattr(p, "name")]):
raise RuntimeError("pretty-printer already registered: %s" %
printer.name)
obj.pretty_printers.insert(0, printer)
class RegexpCollectionPrettyPrinter(PrettyPrinter):
"""Class for implementing a collection of regular-expression based pretty-printers.
Intended usage:
pretty_printer = RegexpCollectionPrettyPrinter("my_library")
pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer)
...
pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter)
register_pretty_printer(obj, pretty_printer)
"""
class RegexpSubprinter(SubPrettyPrinter):
def __init__(self, name, regexp, gen_printer):
super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name)
self.regexp = regexp
self.gen_printer = gen_printer
self.compiled_re = re.compile(regexp)
def __init__(self, name):
super(RegexpCollectionPrettyPrinter, self).__init__(name, [])
def add_printer(self, name, regexp, gen_printer):
"""Add a printer to the list.
The printer is added to the end of the list.
Arguments:
name: The name of the subprinter.
regexp: The regular expression, as a string.
gen_printer: A function/method that given a value returns an
object to pretty-print it.
Returns:
Nothing.
"""
# NOTE: A previous version made the name of each printer the regexp.
# That makes it awkward to pass to the enable/disable commands (it's
# cumbersome to make a regexp of a regexp). So now the name is a
# separate parameter.
self.subprinters.append(self.RegexpSubprinter(name, regexp,
gen_printer))
def __call__(self, val):
"""Lookup the pretty-printer for the provided value."""
# Get the type name.
typename = gdb.types.get_basic_type(val.type).tag
if not typename:
return None
# Iterate over table of type regexps to determine
# if a printer is registered for that type.
# Return an instantiation of the printer if found.
for printer in self.subprinters:
if printer.enabled and printer.compiled_re.search(typename):
return printer.gen_printer(val)
# Cannot find a pretty printer. Return None.
return None

View file

@ -1,3 +1,9 @@
2010-11-02 Doug Evans <dje@google.com>
* gdb.python/py-pp-maint.c: New file.
* gdb.python/py-pp-maint.exp: New file.
* gdb.python/py-pp-maint.py: New file.
2010-11-02 Tom Tromey <tromey@redhat.com>
* gdb.base/default.exp: Remove "scheme" from language list.

View file

@ -0,0 +1,68 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2010 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/>. */
#include <string.h>
struct function_lookup_test
{
int x,y;
};
void
init_flt (struct function_lookup_test *p, int x, int y)
{
p->x = x;
p->y = y;
}
struct s
{
int a;
int *b;
};
struct ss
{
struct s a;
struct s b;
};
void
init_s (struct s *s, int a)
{
s->a = a;
s->b = &s->a;
}
void
init_ss (struct ss *s, int a, int b)
{
init_s (&s->a, a);
init_s (&s->b, b);
}
int
main ()
{
struct function_lookup_test flt;
struct ss ss;
init_flt (&flt, 42, 43);
init_ss (&ss, 1, 2);
return 0; /* break to inspect */
}

View file

@ -0,0 +1,126 @@
# Copyright (C) 2010 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/>.
# This file is part of the GDB testsuite. It tests Python-based
# pretty-printing for the CLI.
if $tracelevel then {
strace $tracelevel
}
if [is_remote host] {
untested "py-pp-maint.exp can only be run locally"
return -1
}
load_lib gdb-python.exp
set testfile "py-pp-maint"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
# Start with a fresh gdb.
gdb_exit
gdb_start
# Skip all tests if Python scripting is not enabled.
if { [skip_python_tests] } { continue }
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug"] != "" } {
untested "Couldn't compile ${srcfile}"
return -1
}
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
if ![runto_main ] then {
fail "Can't run to main"
return -1
}
# Ensure sys.path, et.al. are initialized properly.
gdb_check_python_config
gdb_test "b [gdb_get_line_number {break to inspect} ${testfile}.c ]" \
".*Breakpoint.*"
gdb_test "continue" ".*Breakpoint.*"
set python_file ${srcdir}/${subdir}/${testfile}.py
gdb_test_no_output "python execfile ('${python_file}')" ""
gdb_test "info pretty-printer" \
{.*function_lookup_test.*pp-test.*struct ss.*}
gdb_test "info pretty-printer global .*function" \
{.*function_lookup_test.*}
gdb_test "info pretty-printer .* pp-test" \
{.*pp-test.*struct ss.*}
gdb_test "print flt" " = x=<42> y=<43>" \
"print flt enabled #1"
gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
"print ss enabled #1"
gdb_test "disable pretty-printer" \
"5 printers disabled.*0 of 5 printers enabled"
gdb_test "disable pretty-printer global" \
"0 printers disabled.*0 of 5 printers enabled"
gdb_test "disable pretty-printer global lookup_function_lookup_test" \
"0 printers disabled.*0 of 5 printers enabled"
gdb_test "disable pretty-printer global pp-test:.*" \
"0 printers disabled.*0 of 5 printers enabled"
gdb_test "info pretty-printer global .*function" \
{.*function_lookup_test \[disabled\].*}
gdb_test "info pretty-printer .* pp-test" \
{.*pp-test.*struct ss \[disabled\].*}
gdb_test "print flt" " = {x = 42, y = 43}" \
"print flt disabled"
gdb_test "print ss" " = {a = {a = 1, b = $hex}, b = {a = 2, b = $hex}}" \
"print ss disabled"
gdb_test "enable pretty-printer global lookup_function_lookup_test" \
"1 printer enabled.*1 of 5 printers enabled"
# This doesn't enable any printers because each subprinter in the collection
# is still individually disabled. But this is still needed, to enable the
# collection itself.
gdb_test "enable pretty-printer global pp-test" \
"0 printers enabled.*1 of 5 printers enabled"
gdb_test "enable pretty-printer global pp-test:.*ss.*" \
"2 printers enabled.*3 of 5 printers enabled"
gdb_test "enable pretty-printer global pp-test:.*s.*" \
"2 printers enabled.*5 of 5 printers enabled"
gdb_test "info pretty-printer" \
{.*function_lookup_test.*pp-test.*struct ss.*}
gdb_test "print flt" " = x=<42> y=<43>" \
"print flt re-enabled"
gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
"print ss re-enabled"

View file

@ -0,0 +1,74 @@
# Copyright (C) 2010 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/>.
# This file is part of the GDB testsuite. It tests python pretty
# printers.
import re
import gdb.types
import gdb.printing
def lookup_function_lookup_test(val):
class PrintFunctionLookup(object):
def __init__(self, val):
self.val = val
def to_string(self):
return ("x=<" + str(self.val["x"]) +
"> y=<" + str(self.val["y"]) + ">")
typename = gdb.types.get_basic_type(val.type).tag
# Note: typename could be None.
if typename == "function_lookup_test":
return PrintFunctionLookup(val)
return None
class pp_s:
def __init__(self, val):
self.val = val
def to_string(self):
a = self.val["a"]
b = self.val["b"]
if a.address != b:
raise Exception("&a(%s) != b(%s)" % (str(a.address), str(b)))
return "a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
class pp_ss:
def __init__(self, val):
self.val = val
def to_string(self):
return "a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
def build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter("pp-test")
pp.add_printer('struct s', '^struct s$', pp_s)
pp.add_printer('s', '^s$', pp_s)
# Use a lambda this time to exercise doing things this way.
pp.add_printer('struct ss', '^struct ss$', lambda val: pp_ss(val))
pp.add_printer('ss', '^ss$', lambda val: pp_ss(val))
return pp
gdb.printing.register_pretty_printer(gdb, lookup_function_lookup_test)
gdb.printing.register_pretty_printer(gdb, build_pretty_printer())