* [RFA, doc RFA]: New printing module and info/disable/enable commands
@ 2010-11-01 2:47 Doug Evans
2010-11-01 4:07 ` Eli Zaretskii
2010-11-01 15:09 ` Doug Evans
0 siblings, 2 replies; 5+ messages in thread
From: Doug Evans @ 2010-11-01 2:47 UTC (permalink / raw)
To: gdb-patches
Hi.
This patch adds two things: A new python module to assist in writing
pretty-printers (mostly to formalize some of the administrivia aspects
to support the new commands), and three new commands:
info|enable|disable pretty-printer.
This is a revised version of the patch I posted earlier
(several months ago IIRC).
Ok to check in?
2010-10-31 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.
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.
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.407
diff -u -p -r1.407 NEWS
--- NEWS 13 Oct 2010 20:08:42 -0000 1.407
+++ NEWS 1 Nov 2010 02:10:06 -0000
@@ -16,6 +16,15 @@
It contains a collection of utilities for working with gdb.Types objects:
get_basic_type, has_field, make_enum_dict.
+ ** Module gdb.printing has been added.
+ It contains utilities for writing and registering pretty-printers.
+ New classes: PrettyPrinter, SubPrettyPrinter,
+ RegexpCollectionPrettyPrinter.
+ New function: register_pretty_printer.
+
+ ** New commands "info pretty-printers", "enable pretty-printer" and
+ "disable pretty-printer" have been added.
+
* C++ Improvements:
** GDB now puts template parameters in scope when debugging in an
Index: data-directory/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/data-directory/Makefile.in,v
retrieving revision 1.4
diff -u -p -r1.4 Makefile.in
--- data-directory/Makefile.in 21 Oct 2010 23:50:45 -0000 1.4
+++ data-directory/Makefile.in 1 Nov 2010 02:10:07 -0000
@@ -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: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.770
diff -u -p -r1.770 gdb.texinfo
--- doc/gdb.texinfo 30 Oct 2010 04:39:47 -0000 1.770
+++ doc/gdb.texinfo 1 Nov 2010 02:10:07 -0000
@@ -8127,8 +8127,59 @@ Show whether C@t{++} virtual function ta
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's type. 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.
+For example, the @samp{info pretty-printer} command will list all the
+installed pretty-printers.
+For pretty-printers that handle multiple types, we call the printer
+of each individual type a @code{subprinter}, and each subprinter is named.
+The format of the name is @samp{printer-name}:@samp{subprinter-name}.
+
+Pretty-printers are installed by @samp{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 +8204,89 @@ With a pretty-printer for @code{std::str
$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
+
+@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 to list.
+Objects are @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 the pretty-printer 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 the pretty-printer 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 @code{subprinter}.
@node Value History
@section Value History
@@ -20484,7 +20616,7 @@ situation, a Python @code{KeyboardInterr
* 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 +21481,13 @@ printer exists, then this returns @code{
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 +21495,8 @@ cannot create a pretty-printer for the v
@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 +21509,43 @@ given list, functions are always invoked
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 +21553,13 @@ And here is an example showing how a loo
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 +21595,8 @@ To continue the @code{std::string} examp
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 +21604,92 @@ And then the corresponding contents of t
@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.
+When a printer handles several types we call the printer for each type
+a @code{subprinter}.
-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,12 +23138,11 @@ top of the source tree to the source sea
@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.types:: Utilities for working with types.
+* gdb.printing:: Building and registering pretty-printers.
@end menu
@node gdb.types
@@ -22966,6 +23183,31 @@ Return @code{True} if @var{type}, assume
Return a Python @code{dictionary} type produced from @var{enum_type}.
@end table
+@node gdb.printing
+@subsubsection 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 Interpreters
@chapter Command Interpreters
@cindex command interpreters
Index: python/lib/gdb/__init__.py
===================================================================
RCS file: /cvs/src/src/gdb/python/lib/gdb/__init__.py,v
retrieving revision 1.1
diff -u -p -r1.1 __init__.py
--- python/lib/gdb/__init__.py 6 Oct 2010 16:02:44 -0000 1.1
+++ python/lib/gdb/__init__.py 1 Nov 2010 02:10:07 -0000
@@ -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()
Index: python/lib/gdb/printing.py
===================================================================
RCS file: python/lib/gdb/printing.py
diff -N python/lib/gdb/printing.py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ python/lib/gdb/printing.py 1 Nov 2010 02:10:07 -0000
@@ -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: python/lib/gdb/command/__init__.py
===================================================================
RCS file: python/lib/gdb/command/__init__.py
diff -N python/lib/gdb/command/__init__.py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ python/lib/gdb/command/__init__.py 1 Nov 2010 02:10:07 -0000
@@ -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/>.
+
+
Index: python/lib/gdb/command/pretty_printers.py
===================================================================
RCS file: python/lib/gdb/command/pretty_printers.py
diff -N python/lib/gdb/command/pretty_printers.py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ python/lib/gdb/command/pretty_printers.py 1 Nov 2010 02:10:07 -0000
@@ -0,0 +1,368 @@
+# 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."""
+ # TODO: Provide 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()
Index: testsuite/gdb.python/py-pp-maint.c
===================================================================
RCS file: testsuite/gdb.python/py-pp-maint.c
diff -N testsuite/gdb.python/py-pp-maint.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.python/py-pp-maint.c 1 Nov 2010 02:10:07 -0000
@@ -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 */
+}
Index: testsuite/gdb.python/py-pp-maint.exp
===================================================================
RCS file: testsuite/gdb.python/py-pp-maint.exp
diff -N testsuite/gdb.python/py-pp-maint.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.python/py-pp-maint.exp 1 Nov 2010 02:10:07 -0000
@@ -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"
Index: testsuite/gdb.python/py-pp-maint.py
===================================================================
RCS file: testsuite/gdb.python/py-pp-maint.py
diff -N testsuite/gdb.python/py-pp-maint.py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.python/py-pp-maint.py 1 Nov 2010 02:10:07 -0000
@@ -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())
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [RFA, doc RFA]: New printing module and info/disable/enable commands 2010-11-01 2:47 [RFA, doc RFA]: New printing module and info/disable/enable commands Doug Evans @ 2010-11-01 4:07 ` Eli Zaretskii 2010-11-02 17:35 ` Doug Evans 2010-11-01 15:09 ` Doug Evans 1 sibling, 1 reply; 5+ messages in thread From: Eli Zaretskii @ 2010-11-01 4:07 UTC (permalink / raw) To: Doug Evans; +Cc: gdb-patches > Date: Sun, 31 Oct 2010 19:47:04 -0700 (PDT) > From: dje@google.com (Doug Evans) > > This patch adds two things: A new python module to assist in writing > pretty-printers (mostly to formalize some of the administrivia aspects > to support the new commands), and three new commands: > info|enable|disable pretty-printer. Thanks. > + ** 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. This is okay. > +Pretty-printers are normally named. This makes them easy to manage. > +For example, the @samp{info pretty-printer} command will list all the > +installed pretty-printers. It's not clear how "info pretty-printers" is an example of giving names to pretty-printers. How about The @samp{info pretty-printer} command will list all the installed pretty-printers with their names. > +For pretty-printers that handle multiple types, we call the printer > +of each individual type a @code{subprinter}, and each subprinter is named. Suggest to rephrase: 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 @samp{printer-name}:@samp{subprinter-name}. Please use @var here, not @code, since both of these stand for something else, they are not literal symbols. > +Pretty-printers are installed by @samp{registering} them with @value{GDBN}. ^^^^^^^^^^^^^^^^^^ @dfn, not @samp. You are defining new terminology. > +@node Pretty-Printer Commands > +@subsection Pretty-Printer Commands A @cindex entry here would be good. > +@var{object-regexp} is a regular expression matching the objects to list. "Objects to list" or "objects whose pretty-printers to list"? > +Objects are @code{global}, the program space's file ^^^^^^^^^^^ "Objects can be ...", perhaps? > +@item disable pretty-printer @var{object-regexp} @var{name-regexp} > +Disable the pretty-printer matching @var{object-regexp} and @var{name-regexp}. ^^^^^^^^^^^^^^^^^^^^^^^^^^ "Disable pretty-printers", in plural. > +@item enable pretty-printer @var{object-regexp} @var{name-regexp} > +Enable the pretty-printer matching @var{object-regexp} and @var{name-regexp}. ^^^^^^^^^^^^^^^^^^^^^^^^^ Ditto. > +Note that for @code{bar} the entire printer can be disabled, > +as can each individual @code{subprinter}. Why is "subprinter" in @code here? > +When a printer handles several types we call the printer for each type > +a @code{subprinter}. @dfn, and I suggest to rephrase as above. > +@node gdb.printing > +@subsubsection gdb.printing An index entry here, please. Thanks. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFA, doc RFA]: New printing module and info/disable/enable commands 2010-11-01 4:07 ` Eli Zaretskii @ 2010-11-02 17:35 ` Doug Evans 2010-11-02 20:31 ` Tom Tromey 0 siblings, 1 reply; 5+ messages in thread From: Doug Evans @ 2010-11-02 17:35 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches [-- Attachment #1: Type: text/plain, Size: 4589 bytes --] On Sun, Oct 31, 2010 at 9:07 PM, Eli Zaretskii <eliz@gnu.org> wrote: >> Date: Sun, 31 Oct 2010 19:47:04 -0700 (PDT) >> From: dje@google.com (Doug Evans) >> >> This patch adds two things: A new python module to assist in writing >> pretty-printers (mostly to formalize some of the administrivia aspects >> to support the new commands), and three new commands: >> info|enable|disable pretty-printer. > > Thanks. > >> + ** 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. > > This is okay. > >> +Pretty-printers are normally named. This makes them easy to manage. >> +For example, the @samp{info pretty-printer} command will list all the >> +installed pretty-printers. > > It's not clear how "info pretty-printers" is an example of giving > names to pretty-printers. How about > > The @samp{info pretty-printer} command will list all the installed > pretty-printers with their names. > >> +For pretty-printers that handle multiple types, we call the printer >> +of each individual type a @code{subprinter}, and each subprinter is named. > > Suggest to rephrase: > > 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 @samp{printer-name}:@samp{subprinter-name}. > > Please use @var here, not @code, since both of these stand for > something else, they are not literal symbols. > >> +Pretty-printers are installed by @samp{registering} them with @value{GDBN}. > ^^^^^^^^^^^^^^^^^^ > @dfn, not @samp. You are defining new terminology. > >> +@node Pretty-Printer Commands >> +@subsection Pretty-Printer Commands > > A @cindex entry here would be good. > >> +@var{object-regexp} is a regular expression matching the objects to list. > > "Objects to list" or "objects whose pretty-printers to list"? > >> +Objects are @code{global}, the program space's file > ^^^^^^^^^^^ > "Objects can be ...", perhaps? > >> +@item disable pretty-printer @var{object-regexp} @var{name-regexp} >> +Disable the pretty-printer matching @var{object-regexp} and @var{name-regexp}. > ^^^^^^^^^^^^^^^^^^^^^^^^^^ > "Disable pretty-printers", in plural. > >> +@item enable pretty-printer @var{object-regexp} @var{name-regexp} >> +Enable the pretty-printer matching @var{object-regexp} and @var{name-regexp}. > ^^^^^^^^^^^^^^^^^^^^^^^^^ > Ditto. > >> +Note that for @code{bar} the entire printer can be disabled, >> +as can each individual @code{subprinter}. > > Why is "subprinter" in @code here? > >> +When a printer handles several types we call the printer for each type >> +a @code{subprinter}. > > @dfn, and I suggest to rephrase as above. > >> +@node gdb.printing >> +@subsubsection gdb.printing > > An index entry here, please. > > Thanks. Thanks. This applies all your suggestions. And I still need approval for the code part. 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. 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. [-- Attachment #2: gdb-101102-pp-maint-3.patch.txt --] [-- Type: text/plain, Size: 51421 bytes --] 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. 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. Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.407 diff -u -p -r1.407 NEWS --- NEWS 13 Oct 2010 20:08:42 -0000 1.407 +++ NEWS 2 Nov 2010 17:15:21 -0000 @@ -16,6 +16,15 @@ It contains a collection of utilities for working with gdb.Types objects: get_basic_type, has_field, make_enum_dict. + ** Module gdb.printing has been added. + It contains utilities for writing and registering pretty-printers. + New classes: PrettyPrinter, SubPrettyPrinter, + RegexpCollectionPrettyPrinter. + New function: register_pretty_printer. + + ** New commands "info pretty-printers", "enable pretty-printer" and + "disable pretty-printer" have been added. + * C++ Improvements: ** GDB now puts template parameters in scope when debugging in an Index: data-directory/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/data-directory/Makefile.in,v retrieving revision 1.4 diff -u -p -r1.4 Makefile.in --- data-directory/Makefile.in 21 Oct 2010 23:50:45 -0000 1.4 +++ data-directory/Makefile.in 2 Nov 2010 17:15:21 -0000 @@ -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: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.770 diff -u -p -r1.770 gdb.texinfo --- doc/gdb.texinfo 30 Oct 2010 04:39:47 -0000 1.770 +++ doc/gdb.texinfo 2 Nov 2010 17:15:21 -0000 @@ -8127,8 +8127,60 @@ Show whether C@t{++} virtual function ta 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's type. 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::str $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{KeyboardInterr * 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{ 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 v @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 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 loo 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} examp 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 t @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 sea @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: python/lib/gdb/__init__.py =================================================================== RCS file: /cvs/src/src/gdb/python/lib/gdb/__init__.py,v retrieving revision 1.1 diff -u -p -r1.1 __init__.py --- python/lib/gdb/__init__.py 6 Oct 2010 16:02:44 -0000 1.1 +++ python/lib/gdb/__init__.py 2 Nov 2010 17:15:22 -0000 @@ -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() Index: python/lib/gdb/printing.py =================================================================== RCS file: python/lib/gdb/printing.py diff -N python/lib/gdb/printing.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ python/lib/gdb/printing.py 2 Nov 2010 17:15:22 -0000 @@ -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: python/lib/gdb/command/__init__.py =================================================================== RCS file: python/lib/gdb/command/__init__.py diff -N python/lib/gdb/command/__init__.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ python/lib/gdb/command/__init__.py 2 Nov 2010 17:15:22 -0000 @@ -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/>. + + Index: python/lib/gdb/command/pretty_printers.py =================================================================== RCS file: python/lib/gdb/command/pretty_printers.py diff -N python/lib/gdb/command/pretty_printers.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ python/lib/gdb/command/pretty_printers.py 2 Nov 2010 17:15:22 -0000 @@ -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() Index: testsuite/gdb.python/py-pp-maint.c =================================================================== RCS file: testsuite/gdb.python/py-pp-maint.c diff -N testsuite/gdb.python/py-pp-maint.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.python/py-pp-maint.c 2 Nov 2010 17:15:22 -0000 @@ -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 */ +} Index: testsuite/gdb.python/py-pp-maint.exp =================================================================== RCS file: testsuite/gdb.python/py-pp-maint.exp diff -N testsuite/gdb.python/py-pp-maint.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.python/py-pp-maint.exp 2 Nov 2010 17:15:22 -0000 @@ -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" Index: testsuite/gdb.python/py-pp-maint.py =================================================================== RCS file: testsuite/gdb.python/py-pp-maint.py diff -N testsuite/gdb.python/py-pp-maint.py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.python/py-pp-maint.py 2 Nov 2010 17:15:22 -0000 @@ -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()) ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFA, doc RFA]: New printing module and info/disable/enable commands 2010-11-02 17:35 ` Doug Evans @ 2010-11-02 20:31 ` Tom Tromey 0 siblings, 0 replies; 5+ messages in thread From: Tom Tromey @ 2010-11-02 20:31 UTC (permalink / raw) To: Doug Evans; +Cc: Eli Zaretskii, gdb-patches >>>>> "Doug" == Doug Evans <dje@google.com> writes: Doug> +When @value{GDBN} prints a value, it first sees if there is a pretty-printer Doug> +registered for the value's type. Actually, a printer can recognize a value based on whatever it likes, not just the value's type. Doug> +import gdb.command.pretty_printers Doug> + Doug> +gdb.command.pretty_printers.register_pretty_printer_commands() I think we should have command modules auto-register the commands on import. That way we can make __init__.py import all the command modules it finds and not worry about what functions to call to initialize. I don't think you need to change anything here, unless you really want. Phil is working on a patch in this area right now. Thanks for doing this. It looks good to me. I will try to remember to update the libstdc++ printers to (conditionally) use the new machinery when I get back. I think this will let us finally give users the ability to separately disable the iterator printers, which some folks have asked for. Tom ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFA, doc RFA]: New printing module and info/disable/enable commands 2010-11-01 2:47 [RFA, doc RFA]: New printing module and info/disable/enable commands Doug Evans 2010-11-01 4:07 ` Eli Zaretskii @ 2010-11-01 15:09 ` Doug Evans 1 sibling, 0 replies; 5+ messages in thread From: Doug Evans @ 2010-11-01 15:09 UTC (permalink / raw) To: gdb-patches On Sun, Oct 31, 2010 at 7:47 PM, Doug Evans <dje@google.com> wrote: > Hi. > > This patch adds two things: A new python module to assist in writing > pretty-printers (mostly to formalize some of the administrivia aspects > to support the new commands), and three new commands: > info|enable|disable pretty-printer. > > This is a revised version of the patch I posted earlier > (several months ago IIRC). > > Ok to check in? > > 2010-10-31 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. > > 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. There's one aspect of the coding standards that I violate here, which I forgot to mention and bring up for discussion. I added this to the python coding standards in gdbint.texinfo based on discussions in IRC: "Use @code{FIXME} instead of @code{TODO}." There are two issues with this: - the meaning of the two words can be quite different - new FIXMEs are prohibited in gdb in the first place This raises two questions: 1) What's the proper way to document thoughts on future improvements? This matters to the current patch here: + def list_pretty_printers(self, pretty_printers, name_re, subname_re): + """Print a list of pretty-printers.""" + # TODO: Provide option to list printers in "lookup order" + # (i.e. unsorted). The option is not present in the current patch for fear of feechuritis. 2) Should I reword the above text in gdbint.texinfo to say "TODO is prohibited." ? ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2010-11-02 20:31 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2010-11-01 2:47 [RFA, doc RFA]: New printing module and info/disable/enable commands Doug Evans 2010-11-01 4:07 ` Eli Zaretskii 2010-11-02 17:35 ` Doug Evans 2010-11-02 20:31 ` Tom Tromey 2010-11-01 15:09 ` Doug Evans
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox