New python module gdb.printing, and new commands info pretty-printer,
authorDoug Evans <dje@google.com>
Tue, 2 Nov 2010 22:44:13 +0000 (22:44 +0000)
committerDoug Evans <dje@google.com>
Tue, 2 Nov 2010 22:44:13 +0000 (22:44 +0000)
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.

13 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/data-directory/Makefile.in
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/python/lib/gdb/__init__.py
gdb/python/lib/gdb/command/__init__.py [new file with mode: 0644]
gdb/python/lib/gdb/command/pretty_printers.py [new file with mode: 0644]
gdb/python/lib/gdb/printing.py [new file with mode: 0644]
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-pp-maint.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-pp-maint.exp [new file with mode: 0644]
gdb/testsuite/gdb.python/py-pp-maint.py [new file with mode: 0644]

index 1e764df6fa6930709422889fbd0d663652996444..684372252ab3ebd4f5f2efafff3e9dfcca514e5d 100644 (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.
index c870f01074bc4dc4cf1aa4764be401277bc99480..a71461cbf8472c3cdbcfa9c8e22c876e6f498b03 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
      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
index 9153a29a6ca5a3ba3b30636199a55a171dbd47a1..84c618c4a5ed6d860d5cebebd53177ca1291552e 100644 (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)" \
index 052f580909d2786434a7c91f2a68fccf7fa4744e..a8df07269664a71ce4da08f58ba2f907e5f10804 100644 (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.
index d4a04f778a3e66c039fd0b1f4efcae56a591e3d4..069dce4bb4f10c0c2af2b54e6dbdacdf20423173 100644 (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.
index 102939be32c8a2c6f690f9463a9d39fef8c57d52..d084fe4d3b1e634a82ff829cc594f1f6dcdcb30b 100644 (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()
diff --git a/gdb/python/lib/gdb/command/__init__.py b/gdb/python/lib/gdb/command/__init__.py
new file mode 100644 (file)
index 0000000..4263473
--- /dev/null
@@ -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/>.
+
+
diff --git a/gdb/python/lib/gdb/command/pretty_printers.py b/gdb/python/lib/gdb/command/pretty_printers.py
new file mode 100644 (file)
index 0000000..4e341a5
--- /dev/null
@@ -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()
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
new file mode 100644 (file)
index 0000000..0971375
--- /dev/null
@@ -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
index da64857b5ff2de14ee51f021c57f61e3a569186b..4b24a22ae823a6b6a43c188ee2006e52c6bfa7db 100644 (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.
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.c b/gdb/testsuite/gdb.python/py-pp-maint.c
new file mode 100644 (file)
index 0000000..e72bd41
--- /dev/null
@@ -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 */
+}
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.exp b/gdb/testsuite/gdb.python/py-pp-maint.exp
new file mode 100644 (file)
index 0000000..0aa7956
--- /dev/null
@@ -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"
diff --git a/gdb/testsuite/gdb.python/py-pp-maint.py b/gdb/testsuite/gdb.python/py-pp-maint.py
new file mode 100644 (file)
index 0000000..5638a42
--- /dev/null
@@ -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())