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.
+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.
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
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)" \
+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.
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
$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
* 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.
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
@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.
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
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
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
@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
@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.
#
# 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()
--- /dev/null
+# 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/>.
+
+
--- /dev/null
+# 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()
--- /dev/null
+# 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
+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.
--- /dev/null
+/* 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 */
+}
--- /dev/null
+# 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"
--- /dev/null
+# 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())