New python module gdb.printing, and new commands info pretty-printer,
enable pretty-printer, disable pretty-printer. * NEWS: Mention them. * data-directory/Makefile.in (PYTHON_FILES): Add gdb/printing.py, gdb/command/__init__.py, gdb/command/pretty_printers.py. * python/lib/gdb/__init__.py: Install pretty-printer commands. * python/lib/gdb/printing.py: New file. * python/lib/gdb/command/__init__.py: New file. * python/lib/gdb/command/pretty_printers.py: New file. doc/ * gdb.texinfo (Pretty Printing): Expand into three sections, introduction, example, and commands. (Python API): Delete section Disabling Pretty-Printers, merge into Selecting Pretty-Printers. (Writing a Pretty-Printer): New section. Move the pretty-printer example here, and reformat to match python coding style. Add a second example using the gdb.printing module. (Python modules): Add gdb.printing. testsuite/ * gdb.python/py-pp-maint.c: New file. * gdb.python/py-pp-maint.exp: New file. * gdb.python/py-pp-maint.py: New file.
This commit is contained in:
parent
50c97f3812
commit
7b51bc51e1
13 changed files with 1184 additions and 42 deletions
|
@ -1,3 +1,15 @@
|
||||||
|
2010-11-02 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
|
New python module gdb.printing, and new commands info pretty-printer,
|
||||||
|
enable pretty-printer, disable pretty-printer.
|
||||||
|
* NEWS: Mention them.
|
||||||
|
* data-directory/Makefile.in (PYTHON_FILES): Add gdb/printing.py,
|
||||||
|
gdb/command/__init__.py, gdb/command/pretty_printers.py.
|
||||||
|
* python/lib/gdb/__init__.py: Install pretty-printer commands.
|
||||||
|
* python/lib/gdb/printing.py: New file.
|
||||||
|
* python/lib/gdb/command/__init__.py: New file.
|
||||||
|
* python/lib/gdb/command/pretty_printers.py: New file.
|
||||||
|
|
||||||
2010-11-02 Tom Tromey <tromey@redhat.com>
|
2010-11-02 Tom Tromey <tromey@redhat.com>
|
||||||
|
|
||||||
* NEWS: Mention Guile removal.
|
* NEWS: Mention Guile removal.
|
||||||
|
|
9
gdb/NEWS
9
gdb/NEWS
|
@ -16,6 +16,15 @@
|
||||||
It contains a collection of utilities for working with gdb.Types objects:
|
It contains a collection of utilities for working with gdb.Types objects:
|
||||||
get_basic_type, has_field, make_enum_dict.
|
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:
|
* C++ Improvements:
|
||||||
|
|
||||||
** GDB now puts template parameters in scope when debugging in an
|
** GDB now puts template parameters in scope when debugging in an
|
||||||
|
|
|
@ -52,7 +52,10 @@ PYTHON_DIR = python
|
||||||
PYTHON_INSTALL_DIR = $(DESTDIR)/$(GDB_DATADIR)/$(PYTHON_DIR)
|
PYTHON_INSTALL_DIR = $(DESTDIR)/$(GDB_DATADIR)/$(PYTHON_DIR)
|
||||||
PYTHON_FILES = \
|
PYTHON_FILES = \
|
||||||
gdb/__init__.py \
|
gdb/__init__.py \
|
||||||
gdb/types.py
|
gdb/types.py \
|
||||||
|
gdb/printing.py \
|
||||||
|
gdb/command/__init__.py \
|
||||||
|
gdb/command/pretty_printers.py
|
||||||
|
|
||||||
FLAGS_TO_PASS = \
|
FLAGS_TO_PASS = \
|
||||||
"prefix=$(prefix)" \
|
"prefix=$(prefix)" \
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
2010-11-02 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
|
* gdb.texinfo (Pretty Printing): Expand into three sections,
|
||||||
|
introduction, example, and commands.
|
||||||
|
(Python API): Delete section Disabling Pretty-Printers, merge into
|
||||||
|
Selecting Pretty-Printers.
|
||||||
|
(Writing a Pretty-Printer): New section. Move the pretty-printer
|
||||||
|
example here, and reformat to match python coding style. Add a second
|
||||||
|
example using the gdb.printing module.
|
||||||
|
(Python modules): Add gdb.printing.
|
||||||
|
|
||||||
2010-10-29 Doug Evans <dje@google.com>
|
2010-10-29 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
* gdb.texinfo (Python): Fix long line.
|
* gdb.texinfo (Python): Fix long line.
|
||||||
|
|
|
@ -8127,8 +8127,60 @@ Show whether C@t{++} virtual function tables are pretty printed, or not.
|
||||||
Python code. It greatly simplifies the display of complex objects. This
|
Python code. It greatly simplifies the display of complex objects. This
|
||||||
mechanism works for both MI and the CLI.
|
mechanism works for both MI and the CLI.
|
||||||
|
|
||||||
For example, here is how a C@t{++} @code{std::string} looks without a
|
@menu
|
||||||
pretty-printer:
|
* Pretty-Printer Introduction:: Introduction to pretty-printers
|
||||||
|
* Pretty-Printer Example:: An example pretty-printer
|
||||||
|
* Pretty-Printer Commands:: Pretty-printer commands
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Pretty-Printer Introduction
|
||||||
|
@subsection Pretty-Printer Introduction
|
||||||
|
|
||||||
|
When @value{GDBN} prints a value, it first sees if there is a pretty-printer
|
||||||
|
registered for the value. If there is then @value{GDBN} invokes the
|
||||||
|
pretty-printer to print the value. Otherwise the value is printed normally.
|
||||||
|
|
||||||
|
Pretty-printers are normally named. This makes them easy to manage.
|
||||||
|
The @samp{info pretty-printer} command will list all the installed
|
||||||
|
pretty-printers with their names.
|
||||||
|
If a pretty-printer can handle multiple data types, then its
|
||||||
|
@dfn{subprinters} are the printers for the individual data types.
|
||||||
|
Each such subprinter has its own name.
|
||||||
|
The format of the name is @var{printer-name}:@var{subprinter-name}.
|
||||||
|
|
||||||
|
Pretty-printers are installed by @dfn{registering} them with @value{GDBN}.
|
||||||
|
Typically they are automatically loaded and registered when the corresponding
|
||||||
|
debug information is loaded, thus making them available without having to
|
||||||
|
do anything special.
|
||||||
|
|
||||||
|
There are three places where a pretty-printer can be registered.
|
||||||
|
|
||||||
|
@itemize @bullet
|
||||||
|
@item
|
||||||
|
Pretty-printers registered globally are available when debugging
|
||||||
|
all inferiors.
|
||||||
|
|
||||||
|
@item
|
||||||
|
Pretty-printers registered with a program space are available only
|
||||||
|
when debugging that program.
|
||||||
|
@xref{Progspaces In Python}, for more details on program spaces in Python.
|
||||||
|
|
||||||
|
@item
|
||||||
|
Pretty-printers registered with an objfile are loaded and unloaded
|
||||||
|
with the corresponding objfile (e.g., shared library).
|
||||||
|
@xref{Objfiles In Python}, for more details on objfiles in Python.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
@xref{Selecting Pretty-Printers}, for further information on how
|
||||||
|
pretty-printers are selected,
|
||||||
|
|
||||||
|
@xref{Writing a Pretty-Printer}, for implementing pretty printers
|
||||||
|
for new types.
|
||||||
|
|
||||||
|
@node Pretty-Printer Example
|
||||||
|
@subsection Pretty-Printer Example
|
||||||
|
|
||||||
|
Here is how a C@t{++} @code{std::string} looks without a pretty-printer:
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
(@value{GDBP}) print s
|
(@value{GDBP}) print s
|
||||||
|
@ -8153,8 +8205,91 @@ With a pretty-printer for @code{std::string} only the contents are printed:
|
||||||
$2 = "abcd"
|
$2 = "abcd"
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
For implementing pretty printers for new types you should read the Python API
|
@node Pretty-Printer Commands
|
||||||
details (@pxref{Pretty Printing API}).
|
@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
|
@node Value History
|
||||||
@section Value History
|
@section Value History
|
||||||
|
@ -20484,7 +20619,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
|
||||||
* Types In Python:: Python representation of types.
|
* Types In Python:: Python representation of types.
|
||||||
* Pretty Printing API:: Pretty-printing values.
|
* Pretty Printing API:: Pretty-printing values.
|
||||||
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
|
* 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)
|
* Inferiors In Python:: Python representation of inferiors (processes)
|
||||||
* Threads In Python:: Accessing inferior threads from Python.
|
* Threads In Python:: Accessing inferior threads from Python.
|
||||||
* Commands In Python:: Implementing new commands in Python.
|
* Commands In Python:: Implementing new commands in Python.
|
||||||
|
@ -21349,12 +21484,13 @@ printer exists, then this returns @code{None}.
|
||||||
|
|
||||||
The Python list @code{gdb.pretty_printers} contains an array of
|
The Python list @code{gdb.pretty_printers} contains an array of
|
||||||
functions or callable objects that have been registered via addition
|
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.Progspace} contains a @code{pretty_printers} attribute.
|
||||||
Each @code{gdb.Objfile} also contains a @code{pretty_printers}
|
Each @code{gdb.Objfile} also contains a @code{pretty_printers}
|
||||||
attribute.
|
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
|
argument and should return a pretty-printer object conforming to the
|
||||||
interface definition above (@pxref{Pretty Printing API}). If a function
|
interface definition above (@pxref{Pretty Printing API}). If a function
|
||||||
cannot create a pretty-printer for the value, it should return
|
cannot create a pretty-printer for the value, it should return
|
||||||
|
@ -21362,9 +21498,8 @@ cannot create a pretty-printer for the value, it should return
|
||||||
|
|
||||||
@value{GDBN} first checks the @code{pretty_printers} attribute of each
|
@value{GDBN} first checks the @code{pretty_printers} attribute of each
|
||||||
@code{gdb.Objfile} in the current program space and iteratively calls
|
@code{gdb.Objfile} in the current program space and iteratively calls
|
||||||
each enabled function (@pxref{Disabling Pretty-Printers})
|
each enabled lookup routine in the list for that @code{gdb.Objfile}
|
||||||
in the list for that @code{gdb.Objfile} until it receives
|
until it receives a pretty-printer object.
|
||||||
a pretty-printer object.
|
|
||||||
If no pretty-printer is found in the objfile lists, @value{GDBN} then
|
If no pretty-printer is found in the objfile lists, @value{GDBN} then
|
||||||
searches the pretty-printer list of the current program space,
|
searches the pretty-printer list of the current program space,
|
||||||
calling each enabled function until an object is returned.
|
calling each enabled function until an object is returned.
|
||||||
|
@ -21377,20 +21512,43 @@ given list, functions are always invoked from the head of the list,
|
||||||
and iterated over sequentially until the end of the list, or a printer
|
and iterated over sequentially until the end of the list, or a printer
|
||||||
object is returned.
|
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
|
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
|
@smallexample
|
||||||
class StdStringPrinter:
|
class StdStringPrinter(object):
|
||||||
"Print a std::string"
|
"Print a std::string"
|
||||||
|
|
||||||
def __init__ (self, val):
|
def __init__(self, val):
|
||||||
self.val = val
|
self.val = val
|
||||||
|
|
||||||
def to_string (self):
|
def to_string(self):
|
||||||
return self.val['_M_dataplus']['_M_p']
|
return self.val['_M_dataplus']['_M_p']
|
||||||
|
|
||||||
def display_hint (self):
|
def display_hint(self):
|
||||||
return 'string'
|
return 'string'
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
|
@ -21398,15 +21556,13 @@ And here is an example showing how a lookup function for the printer
|
||||||
example above might be written.
|
example above might be written.
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
def str_lookup_function (val):
|
def str_lookup_function(val):
|
||||||
|
|
||||||
lookup_tag = val.type.tag
|
lookup_tag = val.type.tag
|
||||||
regex = re.compile ("^std::basic_string<char,.*>$")
|
|
||||||
if lookup_tag == None:
|
if lookup_tag == None:
|
||||||
return None
|
return None
|
||||||
if regex.match (lookup_tag):
|
regex = re.compile("^std::basic_string<char,.*>$")
|
||||||
return StdStringPrinter (val)
|
if regex.match(lookup_tag):
|
||||||
|
return StdStringPrinter(val)
|
||||||
return None
|
return None
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
|
@ -21442,8 +21598,8 @@ To continue the @code{std::string} example (@pxref{Pretty Printing API}),
|
||||||
this code might appear in @code{gdb.libstdcxx.v6}:
|
this code might appear in @code{gdb.libstdcxx.v6}:
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
def register_printers (objfile):
|
def register_printers(objfile):
|
||||||
objfile.pretty_printers.add (str_lookup_function)
|
objfile.pretty_printers.add(str_lookup_function)
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
|
@ -21451,27 +21607,92 @@ And then the corresponding contents of the auto-load file would be:
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
import gdb.libstdcxx.v6
|
import gdb.libstdcxx.v6
|
||||||
gdb.libstdcxx.v6.register_printers (gdb.current_objfile ())
|
gdb.libstdcxx.v6.register_printers(gdb.current_objfile())
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
@node Disabling Pretty-Printers
|
The previous example illustrates a basic pretty-printer.
|
||||||
@subsubsection Disabling Pretty-Printers
|
There are a few things that can be improved on.
|
||||||
@cindex disabling pretty-printers
|
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.
|
Second, the printer only handles one type, whereas a library typically has
|
||||||
For example, the underlying data structure may have changed and
|
several types. One could install a lookup function for each desired type
|
||||||
the pretty-printer is out of date.
|
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
|
The @code{gdb.printing} module provides a formal way of solving these
|
||||||
@value{GDBN} provides support for enabling and disabling individual
|
problems (@pxref{gdb.printing}).
|
||||||
printers. For example, if @code{print frame-arguments} is on,
|
Here is another example that handles multiple types.
|
||||||
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}
|
These are the types we are going to pretty-print:
|
||||||
attribute to the registered function or callable object. If this attribute
|
|
||||||
is present and its value is @code{False}, the printer is disabled, otherwise
|
@smallexample
|
||||||
the printer is enabled.
|
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
|
@node Inferiors In Python
|
||||||
@subsubsection Inferiors In Python
|
@subsubsection Inferiors In Python
|
||||||
|
@ -22920,16 +23141,42 @@ top of the source tree to the source search path.
|
||||||
@subsection Python modules
|
@subsection Python modules
|
||||||
@cindex 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.
|
@value{GDBN} comes with a module to assist writing Python code.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
|
* gdb.printing:: Building and registering pretty-printers.
|
||||||
* gdb.types:: Utilities for working with types.
|
* gdb.types:: Utilities for working with types.
|
||||||
@end menu
|
@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
|
@node gdb.types
|
||||||
@subsubsection gdb.types
|
@subsubsection gdb.types
|
||||||
|
@cindex gdb.types
|
||||||
|
|
||||||
This module provides a collection of utilities for working with
|
This module provides a collection of utilities for working with
|
||||||
@code{gdb.Types} objects.
|
@code{gdb.Types} objects.
|
||||||
|
|
|
@ -12,3 +12,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# 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()
|
||||||
|
|
16
gdb/python/lib/gdb/command/__init__.py
Normal file
16
gdb/python/lib/gdb/command/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
369
gdb/python/lib/gdb/command/pretty_printers.py
Normal file
369
gdb/python/lib/gdb/command/pretty_printers.py
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
# Pretty-printer commands.
|
||||||
|
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""GDB commands for working with pretty-printers."""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import gdb
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def parse_printer_regexps(arg):
|
||||||
|
"""Internal utility to parse a pretty-printer command argv.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
arg: The arguments to the command. The format is:
|
||||||
|
[object-regexp [name-regexp]].
|
||||||
|
Individual printers in a collection are named as
|
||||||
|
printer-name:subprinter-name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The result is a 3-tuple of compiled regular expressions, except that
|
||||||
|
the resulting compiled subprinter regexp is None if not provided.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SyntaxError: an error processing ARG
|
||||||
|
"""
|
||||||
|
|
||||||
|
argv = gdb.string_to_argv(arg);
|
||||||
|
argc = len(argv)
|
||||||
|
object_regexp = "" # match everything
|
||||||
|
name_regexp = "" # match everything
|
||||||
|
subname_regexp = None
|
||||||
|
if argc > 3:
|
||||||
|
raise SyntaxError("too many arguments")
|
||||||
|
if argc >= 1:
|
||||||
|
object_regexp = argv[0]
|
||||||
|
if argc >= 2:
|
||||||
|
name_subname = argv[1].split(":", 1)
|
||||||
|
name_regexp = name_subname[0]
|
||||||
|
if len(name_subname) == 2:
|
||||||
|
subname_regexp = name_subname[1]
|
||||||
|
# That re.compile raises SyntaxError was determined empirically.
|
||||||
|
# We catch it and reraise it to provide a slightly more useful
|
||||||
|
# error message for the user.
|
||||||
|
try:
|
||||||
|
object_re = re.compile(object_regexp)
|
||||||
|
except SyntaxError:
|
||||||
|
raise SyntaxError("invalid object regexp: %s" % object_regexp)
|
||||||
|
try:
|
||||||
|
name_re = re.compile (name_regexp)
|
||||||
|
except SyntaxError:
|
||||||
|
raise SyntaxError("invalid name regexp: %s" % name_regexp)
|
||||||
|
if subname_regexp is not None:
|
||||||
|
try:
|
||||||
|
subname_re = re.compile(subname_regexp)
|
||||||
|
except SyntaxError:
|
||||||
|
raise SyntaxError("invalid subname regexp: %s" % subname_regexp)
|
||||||
|
else:
|
||||||
|
subname_re = None
|
||||||
|
return(object_re, name_re, subname_re)
|
||||||
|
|
||||||
|
|
||||||
|
def printer_enabled_p(printer):
|
||||||
|
"""Internal utility to see if printer (or subprinter) is enabled."""
|
||||||
|
if hasattr(printer, "enabled"):
|
||||||
|
return printer.enabled
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class InfoPrettyPrinter(gdb.Command):
|
||||||
|
"""GDB command to list all registered pretty-printers.
|
||||||
|
|
||||||
|
Usage: info pretty-printer [object-regexp [name-regexp]]
|
||||||
|
|
||||||
|
OBJECT-REGEXP is a regular expression matching the objects to list.
|
||||||
|
Objects are "global", the program space's file, and the objfiles within
|
||||||
|
that program space.
|
||||||
|
|
||||||
|
NAME-REGEXP matches the name of the pretty-printer.
|
||||||
|
Individual printers in a collection are named as
|
||||||
|
printer-name:subprinter-name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
super(InfoPrettyPrinter, self).__init__("info pretty-printer",
|
||||||
|
gdb.COMMAND_DATA)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enabled_string(printer):
|
||||||
|
"""Return "" if PRINTER is enabled, otherwise " [disabled]"."""
|
||||||
|
if printer_enabled_p(printer):
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return " [disabled]"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def printer_name(printer):
|
||||||
|
"""Return the printer's name."""
|
||||||
|
if hasattr(printer, "name"):
|
||||||
|
return printer.name
|
||||||
|
if hasattr(printer, "__name__"):
|
||||||
|
return printer.__name__
|
||||||
|
# This "shouldn't happen", but the public API allows for
|
||||||
|
# direct additions to the pretty-printer list, and we shouldn't
|
||||||
|
# crash because someone added a bogus printer.
|
||||||
|
# Plus we want to give the user a way to list unknown printers.
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
def list_pretty_printers(self, pretty_printers, name_re, subname_re):
|
||||||
|
"""Print a list of pretty-printers."""
|
||||||
|
# A potential enhancement is to provide an option to list printers in
|
||||||
|
# "lookup order" (i.e. unsorted).
|
||||||
|
sorted_pretty_printers = copy.copy(pretty_printers)
|
||||||
|
sorted_pretty_printers.sort(lambda x, y:
|
||||||
|
cmp(self.printer_name(x),
|
||||||
|
self.printer_name(y)))
|
||||||
|
for printer in sorted_pretty_printers:
|
||||||
|
name = self.printer_name(printer)
|
||||||
|
enabled = self.enabled_string(printer)
|
||||||
|
if name_re.match(name):
|
||||||
|
print " %s%s" % (name, enabled)
|
||||||
|
if (hasattr(printer, "subprinters") and
|
||||||
|
printer.subprinters is not None):
|
||||||
|
sorted_subprinters = copy.copy(printer.subprinters)
|
||||||
|
sorted_subprinters.sort(lambda x, y:
|
||||||
|
cmp(self.printer_name(x),
|
||||||
|
self.printer_name(y)))
|
||||||
|
for subprinter in sorted_subprinters:
|
||||||
|
if (not subname_re or
|
||||||
|
subname_re.match(subprinter.name)):
|
||||||
|
print (" %s%s" %
|
||||||
|
(subprinter.name,
|
||||||
|
self.enabled_string(subprinter)))
|
||||||
|
|
||||||
|
def invoke1(self, title, printer_list,
|
||||||
|
obj_name_to_match, object_re, name_re, subname_re):
|
||||||
|
""""Subroutine of invoke to simplify it."""
|
||||||
|
if printer_list and object_re.match(obj_name_to_match):
|
||||||
|
print title
|
||||||
|
self.list_pretty_printers(printer_list, name_re, subname_re)
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
"""GDB calls this to perform the command."""
|
||||||
|
(object_re, name_re, subname_re) = parse_printer_regexps(arg)
|
||||||
|
self.invoke1("global pretty-printers:", gdb.pretty_printers,
|
||||||
|
"global", object_re, name_re, subname_re)
|
||||||
|
cp = gdb.current_progspace()
|
||||||
|
self.invoke1("progspace %s pretty-printers:" % cp.filename,
|
||||||
|
cp.pretty_printers, "progspace",
|
||||||
|
object_re, name_re, subname_re)
|
||||||
|
for objfile in gdb.objfiles():
|
||||||
|
self.invoke1(" objfile %s pretty-printers:" % objfile.filename,
|
||||||
|
objfile.pretty_printers, objfile.filename,
|
||||||
|
object_re, name_re, subname_re)
|
||||||
|
|
||||||
|
|
||||||
|
def count_enabled_printers(pretty_printers):
|
||||||
|
"""Return a 2-tuple of number of enabled and total printers."""
|
||||||
|
enabled = 0
|
||||||
|
total = 0
|
||||||
|
for printer in pretty_printers:
|
||||||
|
if (hasattr(printer, "subprinters")
|
||||||
|
and printer.subprinters is not None):
|
||||||
|
if printer_enabled_p(printer):
|
||||||
|
for subprinter in printer.subprinters:
|
||||||
|
if printer_enabled_p(subprinter):
|
||||||
|
enabled += 1
|
||||||
|
total += len(printer.subprinters)
|
||||||
|
else:
|
||||||
|
if printer_enabled_p(printer):
|
||||||
|
enabled += 1
|
||||||
|
total += 1
|
||||||
|
return (enabled, total)
|
||||||
|
|
||||||
|
|
||||||
|
def count_all_enabled_printers():
|
||||||
|
"""Return a 2-tuble of the enabled state and total number of all printers.
|
||||||
|
This includes subprinters.
|
||||||
|
"""
|
||||||
|
enabled_count = 0
|
||||||
|
total_count = 0
|
||||||
|
(t_enabled, t_total) = count_enabled_printers(gdb.pretty_printers)
|
||||||
|
enabled_count += t_enabled
|
||||||
|
total_count += t_total
|
||||||
|
(t_enabled, t_total) = count_enabled_printers(gdb.current_progspace().pretty_printers)
|
||||||
|
enabled_count += t_enabled
|
||||||
|
total_count += t_total
|
||||||
|
for objfile in gdb.objfiles():
|
||||||
|
(t_enabled, t_total) = count_enabled_printers(objfile.pretty_printers)
|
||||||
|
enabled_count += t_enabled
|
||||||
|
total_count += t_total
|
||||||
|
return (enabled_count, total_count)
|
||||||
|
|
||||||
|
|
||||||
|
def pluralize(text, n, suffix="s"):
|
||||||
|
"""Return TEXT pluralized if N != 1."""
|
||||||
|
if n != 1:
|
||||||
|
return "%s%s" % (text, suffix)
|
||||||
|
else:
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def show_pretty_printer_enabled_summary():
|
||||||
|
"""Print the number of printers enabled/disabled.
|
||||||
|
We count subprinters individually.
|
||||||
|
"""
|
||||||
|
(enabled_count, total_count) = count_all_enabled_printers()
|
||||||
|
print "%d of %d printers enabled" % (enabled_count, total_count)
|
||||||
|
|
||||||
|
|
||||||
|
def do_enable_pretty_printer_1 (pretty_printers, name_re, subname_re, flag):
|
||||||
|
"""Worker for enabling/disabling pretty-printers.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
pretty_printers: list of pretty-printers
|
||||||
|
name_re: regular-expression object to select printers
|
||||||
|
subname_re: regular expression object to select subprinters or None
|
||||||
|
if all are affected
|
||||||
|
flag: True for Enable, False for Disable
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of printers affected.
|
||||||
|
This is just for informational purposes for the user.
|
||||||
|
"""
|
||||||
|
total = 0
|
||||||
|
for printer in pretty_printers:
|
||||||
|
if (hasattr(printer, "name") and name_re.match(printer.name) or
|
||||||
|
hasattr(printer, "__name__") and name_re.match(printer.__name__)):
|
||||||
|
if hasattr(printer, "subprinters"):
|
||||||
|
if not subname_re:
|
||||||
|
# Only record printers that change state.
|
||||||
|
if printer_enabled_p(printer) != flag:
|
||||||
|
for subprinter in printer.subprinters:
|
||||||
|
if printer_enabled_p(subprinter):
|
||||||
|
total += 1
|
||||||
|
# NOTE: We preserve individual subprinter settings.
|
||||||
|
printer.enabled = flag
|
||||||
|
else:
|
||||||
|
# NOTE: Whether this actually disables the subprinter
|
||||||
|
# depends on whether the printer's lookup function supports
|
||||||
|
# the "enable" API. We can only assume it does.
|
||||||
|
for subprinter in printer.subprinters:
|
||||||
|
if subname_re.match(subprinter.name):
|
||||||
|
# Only record printers that change state.
|
||||||
|
if (printer_enabled_p(printer) and
|
||||||
|
printer_enabled_p(subprinter) != flag):
|
||||||
|
total += 1
|
||||||
|
subprinter.enabled = flag
|
||||||
|
else:
|
||||||
|
# This printer has no subprinters.
|
||||||
|
# If the user does "disable pretty-printer .* .* foo"
|
||||||
|
# should we disable printers that don't have subprinters?
|
||||||
|
# How do we apply "foo" in this context? Since there is no
|
||||||
|
# "foo" subprinter it feels like we should skip this printer.
|
||||||
|
# There's still the issue of how to handle
|
||||||
|
# "disable pretty-printer .* .* .*", and every other variation
|
||||||
|
# that can match everything. For now punt and only support
|
||||||
|
# "disable pretty-printer .* .*" (i.e. subname is elided)
|
||||||
|
# to disable everything.
|
||||||
|
if not subname_re:
|
||||||
|
# Only record printers that change state.
|
||||||
|
if printer_enabled_p(printer) != flag:
|
||||||
|
total += 1
|
||||||
|
printer.enabled = flag
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def do_enable_pretty_printer (arg, flag):
|
||||||
|
"""Internal worker for enabling/disabling pretty-printers."""
|
||||||
|
(object_re, name_re, subname_re) = parse_printer_regexps(arg)
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
if object_re.match("global"):
|
||||||
|
total += do_enable_pretty_printer_1(gdb.pretty_printers,
|
||||||
|
name_re, subname_re, flag)
|
||||||
|
cp = gdb.current_progspace()
|
||||||
|
if object_re.match("progspace"):
|
||||||
|
total += do_enable_pretty_printer_1(cp.pretty_printers,
|
||||||
|
name_re, subname_re, flag)
|
||||||
|
for objfile in gdb.objfiles():
|
||||||
|
if object_re.match(objfile.filename):
|
||||||
|
total += do_enable_pretty_printer_1(objfile.pretty_printers,
|
||||||
|
name_re, subname_re, flag)
|
||||||
|
|
||||||
|
if flag:
|
||||||
|
state = "enabled"
|
||||||
|
else:
|
||||||
|
state = "disabled"
|
||||||
|
print "%d %s %s" % (total, pluralize("printer", total), state)
|
||||||
|
|
||||||
|
# Print the total list of printers currently enabled/disabled.
|
||||||
|
# This is to further assist the user in determining whether the result
|
||||||
|
# is expected. Since we use regexps to select it's useful.
|
||||||
|
show_pretty_printer_enabled_summary()
|
||||||
|
|
||||||
|
|
||||||
|
# Enable/Disable one or more pretty-printers.
|
||||||
|
#
|
||||||
|
# This is intended for use when a broken pretty-printer is shipped/installed
|
||||||
|
# and the user wants to disable that printer without disabling all the other
|
||||||
|
# printers.
|
||||||
|
#
|
||||||
|
# A useful addition would be -v (verbose) to show each printer affected.
|
||||||
|
|
||||||
|
class EnablePrettyPrinter (gdb.Command):
|
||||||
|
"""GDB command to enable the specified pretty-printer.
|
||||||
|
|
||||||
|
Usage: enable pretty-printer [object-regexp [name-regexp]]
|
||||||
|
|
||||||
|
OBJECT-REGEXP is a regular expression matching the objects to examine.
|
||||||
|
Objects are "global", the program space's file, and the objfiles within
|
||||||
|
that program space.
|
||||||
|
|
||||||
|
NAME-REGEXP matches the name of the pretty-printer.
|
||||||
|
Individual printers in a collection are named as
|
||||||
|
printer-name:subprinter-name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(EnablePrettyPrinter, self).__init__("enable pretty-printer",
|
||||||
|
gdb.COMMAND_DATA)
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
"""GDB calls this to perform the command."""
|
||||||
|
do_enable_pretty_printer(arg, True)
|
||||||
|
|
||||||
|
|
||||||
|
class DisablePrettyPrinter (gdb.Command):
|
||||||
|
"""GDB command to disable the specified pretty-printer.
|
||||||
|
|
||||||
|
Usage: disable pretty-printer [object-regexp [name-regexp]]
|
||||||
|
|
||||||
|
OBJECT-REGEXP is a regular expression matching the objects to examine.
|
||||||
|
Objects are "global", the program space's file, and the objfiles within
|
||||||
|
that program space.
|
||||||
|
|
||||||
|
NAME-REGEXP matches the name of the pretty-printer.
|
||||||
|
Individual printers in a collection are named as
|
||||||
|
printer-name:subprinter-name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(DisablePrettyPrinter, self).__init__("disable pretty-printer",
|
||||||
|
gdb.COMMAND_DATA)
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
"""GDB calls this to perform the command."""
|
||||||
|
do_enable_pretty_printer(arg, False)
|
||||||
|
|
||||||
|
|
||||||
|
def register_pretty_printer_commands():
|
||||||
|
"""Call from a top level script to install the pretty-printer commands."""
|
||||||
|
InfoPrettyPrinter()
|
||||||
|
EnablePrettyPrinter()
|
||||||
|
DisablePrettyPrinter()
|
197
gdb/python/lib/gdb/printing.py
Normal file
197
gdb/python/lib/gdb/printing.py
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
# Pretty-printer utilities.
|
||||||
|
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Utilities for working with pretty-printers."""
|
||||||
|
|
||||||
|
import gdb
|
||||||
|
import gdb.types
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class PrettyPrinter(object):
|
||||||
|
"""A basic pretty-printer.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: A unique string among all printers for the context in which
|
||||||
|
it is defined (objfile, progspace, or global(gdb)), and should
|
||||||
|
meaningfully describe what can be pretty-printed.
|
||||||
|
E.g., "StringPiece" or "protobufs".
|
||||||
|
subprinters: An iterable object with each element having a `name'
|
||||||
|
attribute, and, potentially, "enabled" attribute.
|
||||||
|
Or this is None if there are no subprinters.
|
||||||
|
enabled: A boolean indicating if the printer is enabled.
|
||||||
|
|
||||||
|
Subprinters are for situations where "one" pretty-printer is actually a
|
||||||
|
collection of several printers. E.g., The libstdc++ pretty-printer has
|
||||||
|
a pretty-printer for each of several different types, based on regexps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# While one might want to push subprinters into the subclass, it's
|
||||||
|
# present here to formalize such support to simplify
|
||||||
|
# commands/pretty_printers.py.
|
||||||
|
|
||||||
|
def __init__(self, name, subprinters=None):
|
||||||
|
self.name = name
|
||||||
|
self.subprinters = subprinters
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
def __call__(self, val):
|
||||||
|
# The subclass must define this.
|
||||||
|
raise NotImplementedError("PrettyPrinter __call__")
|
||||||
|
|
||||||
|
|
||||||
|
class SubPrettyPrinter(object):
|
||||||
|
"""Baseclass for sub-pretty-printers.
|
||||||
|
|
||||||
|
Sub-pretty-printers needn't use this, but it formalizes what's needed.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: The name of the subprinter.
|
||||||
|
enabled: A boolean indicating if the subprinter is enabled.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
|
||||||
|
def register_pretty_printer(obj, printer):
|
||||||
|
"""Register pretty-printer PRINTER with OBJ.
|
||||||
|
|
||||||
|
The printer is added to the front of the search list, thus one can override
|
||||||
|
an existing printer if one needs to.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
obj: Either an objfile, progspace, or None (in which case the printer
|
||||||
|
is registered globally).
|
||||||
|
printer: Either a function of one argument (old way) or any object
|
||||||
|
which has attributes: name, enabled, __call__.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: A problem with the type of the printer.
|
||||||
|
ValueError: The printer's name contains a colon ":".
|
||||||
|
|
||||||
|
If the caller wants the printer to be listable and disableable, it must
|
||||||
|
follow the PrettyPrinter API. This applies to the old way (functions) too.
|
||||||
|
If printer is an object, __call__ is a method of two arguments:
|
||||||
|
self, and the value to be pretty-printed. See PrettyPrinter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Watch for both __name__ and name.
|
||||||
|
# Functions get the former for free, but we don't want to use an
|
||||||
|
# attribute named __foo__ for pretty-printers-as-objects.
|
||||||
|
# If printer has both, we use `name'.
|
||||||
|
if not hasattr(printer, "__name__") and not hasattr(printer, "name"):
|
||||||
|
raise TypeError("printer missing attribute: name")
|
||||||
|
if hasattr(printer, "name") and not hasattr(printer, "enabled"):
|
||||||
|
raise TypeError("printer missing attribute: enabled")
|
||||||
|
if not hasattr(printer, "__call__"):
|
||||||
|
raise TypeError("printer missing attribute: __call__")
|
||||||
|
|
||||||
|
if obj is None:
|
||||||
|
if gdb.parameter("verbose"):
|
||||||
|
gdb.write("Registering global %s pretty-printer ...\n" % name)
|
||||||
|
obj = gdb
|
||||||
|
else:
|
||||||
|
if gdb.parameter("verbose"):
|
||||||
|
gdb.write("Registering %s pretty-printer for %s ...\n" %
|
||||||
|
(printer.name, obj.filename))
|
||||||
|
|
||||||
|
if hasattr(printer, "name"):
|
||||||
|
if not isinstance(printer.name, basestring):
|
||||||
|
raise TypeError("printer name is not a string")
|
||||||
|
# If printer provides a name, make sure it doesn't contain ":".
|
||||||
|
# Colon is used by the info/enable/disable pretty-printer commands
|
||||||
|
# to delimit subprinters.
|
||||||
|
if printer.name.find(":") >= 0:
|
||||||
|
raise ValueError("colon ':' in printer name")
|
||||||
|
# Also make sure the name is unique.
|
||||||
|
# Alas, we can't do the same for functions and __name__, they could
|
||||||
|
# all have a canonical name like "lookup_function".
|
||||||
|
# PERF: gdb records printers in a list, making this inefficient.
|
||||||
|
if (printer.name in
|
||||||
|
[p.name for p in obj.pretty_printers if hasattr(p, "name")]):
|
||||||
|
raise RuntimeError("pretty-printer already registered: %s" %
|
||||||
|
printer.name)
|
||||||
|
|
||||||
|
obj.pretty_printers.insert(0, printer)
|
||||||
|
|
||||||
|
|
||||||
|
class RegexpCollectionPrettyPrinter(PrettyPrinter):
|
||||||
|
"""Class for implementing a collection of regular-expression based pretty-printers.
|
||||||
|
|
||||||
|
Intended usage:
|
||||||
|
|
||||||
|
pretty_printer = RegexpCollectionPrettyPrinter("my_library")
|
||||||
|
pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer)
|
||||||
|
...
|
||||||
|
pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter)
|
||||||
|
register_pretty_printer(obj, pretty_printer)
|
||||||
|
"""
|
||||||
|
|
||||||
|
class RegexpSubprinter(SubPrettyPrinter):
|
||||||
|
def __init__(self, name, regexp, gen_printer):
|
||||||
|
super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name)
|
||||||
|
self.regexp = regexp
|
||||||
|
self.gen_printer = gen_printer
|
||||||
|
self.compiled_re = re.compile(regexp)
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super(RegexpCollectionPrettyPrinter, self).__init__(name, [])
|
||||||
|
|
||||||
|
def add_printer(self, name, regexp, gen_printer):
|
||||||
|
"""Add a printer to the list.
|
||||||
|
|
||||||
|
The printer is added to the end of the list.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
name: The name of the subprinter.
|
||||||
|
regexp: The regular expression, as a string.
|
||||||
|
gen_printer: A function/method that given a value returns an
|
||||||
|
object to pretty-print it.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# NOTE: A previous version made the name of each printer the regexp.
|
||||||
|
# That makes it awkward to pass to the enable/disable commands (it's
|
||||||
|
# cumbersome to make a regexp of a regexp). So now the name is a
|
||||||
|
# separate parameter.
|
||||||
|
|
||||||
|
self.subprinters.append(self.RegexpSubprinter(name, regexp,
|
||||||
|
gen_printer))
|
||||||
|
|
||||||
|
def __call__(self, val):
|
||||||
|
"""Lookup the pretty-printer for the provided value."""
|
||||||
|
|
||||||
|
# Get the type name.
|
||||||
|
typename = gdb.types.get_basic_type(val.type).tag
|
||||||
|
if not typename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Iterate over table of type regexps to determine
|
||||||
|
# if a printer is registered for that type.
|
||||||
|
# Return an instantiation of the printer if found.
|
||||||
|
for printer in self.subprinters:
|
||||||
|
if printer.enabled and printer.compiled_re.search(typename):
|
||||||
|
return printer.gen_printer(val)
|
||||||
|
|
||||||
|
# Cannot find a pretty printer. Return None.
|
||||||
|
return None
|
|
@ -1,3 +1,9 @@
|
||||||
|
2010-11-02 Doug Evans <dje@google.com>
|
||||||
|
|
||||||
|
* gdb.python/py-pp-maint.c: New file.
|
||||||
|
* gdb.python/py-pp-maint.exp: New file.
|
||||||
|
* gdb.python/py-pp-maint.py: New file.
|
||||||
|
|
||||||
2010-11-02 Tom Tromey <tromey@redhat.com>
|
2010-11-02 Tom Tromey <tromey@redhat.com>
|
||||||
|
|
||||||
* gdb.base/default.exp: Remove "scheme" from language list.
|
* gdb.base/default.exp: Remove "scheme" from language list.
|
||||||
|
|
68
gdb/testsuite/gdb.python/py-pp-maint.c
Normal file
68
gdb/testsuite/gdb.python/py-pp-maint.c
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct function_lookup_test
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
init_flt (struct function_lookup_test *p, int x, int y)
|
||||||
|
{
|
||||||
|
p->x = x;
|
||||||
|
p->y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct s
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
int *b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ss
|
||||||
|
{
|
||||||
|
struct s a;
|
||||||
|
struct s b;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
init_s (struct s *s, int a)
|
||||||
|
{
|
||||||
|
s->a = a;
|
||||||
|
s->b = &s->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init_ss (struct ss *s, int a, int b)
|
||||||
|
{
|
||||||
|
init_s (&s->a, a);
|
||||||
|
init_s (&s->b, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
struct function_lookup_test flt;
|
||||||
|
struct ss ss;
|
||||||
|
|
||||||
|
init_flt (&flt, 42, 43);
|
||||||
|
init_ss (&ss, 1, 2);
|
||||||
|
|
||||||
|
return 0; /* break to inspect */
|
||||||
|
}
|
126
gdb/testsuite/gdb.python/py-pp-maint.exp
Normal file
126
gdb/testsuite/gdb.python/py-pp-maint.exp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# This file is part of the GDB testsuite. It tests Python-based
|
||||||
|
# pretty-printing for the CLI.
|
||||||
|
|
||||||
|
if $tracelevel then {
|
||||||
|
strace $tracelevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if [is_remote host] {
|
||||||
|
untested "py-pp-maint.exp can only be run locally"
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
load_lib gdb-python.exp
|
||||||
|
|
||||||
|
set testfile "py-pp-maint"
|
||||||
|
set srcfile ${testfile}.c
|
||||||
|
set binfile ${objdir}/${subdir}/${testfile}
|
||||||
|
|
||||||
|
# Start with a fresh gdb.
|
||||||
|
gdb_exit
|
||||||
|
gdb_start
|
||||||
|
|
||||||
|
# Skip all tests if Python scripting is not enabled.
|
||||||
|
if { [skip_python_tests] } { continue }
|
||||||
|
|
||||||
|
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug"] != "" } {
|
||||||
|
untested "Couldn't compile ${srcfile}"
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_reinitialize_dir $srcdir/$subdir
|
||||||
|
gdb_load ${binfile}
|
||||||
|
|
||||||
|
if ![runto_main ] then {
|
||||||
|
fail "Can't run to main"
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure sys.path, et.al. are initialized properly.
|
||||||
|
gdb_check_python_config
|
||||||
|
|
||||||
|
gdb_test "b [gdb_get_line_number {break to inspect} ${testfile}.c ]" \
|
||||||
|
".*Breakpoint.*"
|
||||||
|
gdb_test "continue" ".*Breakpoint.*"
|
||||||
|
|
||||||
|
set python_file ${srcdir}/${subdir}/${testfile}.py
|
||||||
|
|
||||||
|
gdb_test_no_output "python execfile ('${python_file}')" ""
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer" \
|
||||||
|
{.*function_lookup_test.*pp-test.*struct ss.*}
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer global .*function" \
|
||||||
|
{.*function_lookup_test.*}
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer .* pp-test" \
|
||||||
|
{.*pp-test.*struct ss.*}
|
||||||
|
|
||||||
|
gdb_test "print flt" " = x=<42> y=<43>" \
|
||||||
|
"print flt enabled #1"
|
||||||
|
|
||||||
|
gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
|
||||||
|
"print ss enabled #1"
|
||||||
|
|
||||||
|
gdb_test "disable pretty-printer" \
|
||||||
|
"5 printers disabled.*0 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "disable pretty-printer global" \
|
||||||
|
"0 printers disabled.*0 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "disable pretty-printer global lookup_function_lookup_test" \
|
||||||
|
"0 printers disabled.*0 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "disable pretty-printer global pp-test:.*" \
|
||||||
|
"0 printers disabled.*0 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer global .*function" \
|
||||||
|
{.*function_lookup_test \[disabled\].*}
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer .* pp-test" \
|
||||||
|
{.*pp-test.*struct ss \[disabled\].*}
|
||||||
|
|
||||||
|
gdb_test "print flt" " = {x = 42, y = 43}" \
|
||||||
|
"print flt disabled"
|
||||||
|
|
||||||
|
gdb_test "print ss" " = {a = {a = 1, b = $hex}, b = {a = 2, b = $hex}}" \
|
||||||
|
"print ss disabled"
|
||||||
|
|
||||||
|
gdb_test "enable pretty-printer global lookup_function_lookup_test" \
|
||||||
|
"1 printer enabled.*1 of 5 printers enabled"
|
||||||
|
|
||||||
|
# This doesn't enable any printers because each subprinter in the collection
|
||||||
|
# is still individually disabled. But this is still needed, to enable the
|
||||||
|
# collection itself.
|
||||||
|
gdb_test "enable pretty-printer global pp-test" \
|
||||||
|
"0 printers enabled.*1 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "enable pretty-printer global pp-test:.*ss.*" \
|
||||||
|
"2 printers enabled.*3 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "enable pretty-printer global pp-test:.*s.*" \
|
||||||
|
"2 printers enabled.*5 of 5 printers enabled"
|
||||||
|
|
||||||
|
gdb_test "info pretty-printer" \
|
||||||
|
{.*function_lookup_test.*pp-test.*struct ss.*}
|
||||||
|
|
||||||
|
gdb_test "print flt" " = x=<42> y=<43>" \
|
||||||
|
"print flt re-enabled"
|
||||||
|
|
||||||
|
gdb_test "print ss" " = a=<a=<1> b=<$hex>> b=<a=<2> b=<$hex>>" \
|
||||||
|
"print ss re-enabled"
|
74
gdb/testsuite/gdb.python/py-pp-maint.py
Normal file
74
gdb/testsuite/gdb.python/py-pp-maint.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Copyright (C) 2010 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# This file is part of the GDB testsuite. It tests python pretty
|
||||||
|
# printers.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import gdb.types
|
||||||
|
import gdb.printing
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_function_lookup_test(val):
|
||||||
|
class PrintFunctionLookup(object):
|
||||||
|
def __init__(self, val):
|
||||||
|
self.val = val
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return ("x=<" + str(self.val["x"]) +
|
||||||
|
"> y=<" + str(self.val["y"]) + ">")
|
||||||
|
|
||||||
|
typename = gdb.types.get_basic_type(val.type).tag
|
||||||
|
# Note: typename could be None.
|
||||||
|
if typename == "function_lookup_test":
|
||||||
|
return PrintFunctionLookup(val)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class pp_s:
|
||||||
|
def __init__(self, val):
|
||||||
|
self.val = val
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
a = self.val["a"]
|
||||||
|
b = self.val["b"]
|
||||||
|
if a.address != b:
|
||||||
|
raise Exception("&a(%s) != b(%s)" % (str(a.address), str(b)))
|
||||||
|
return "a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
|
||||||
|
|
||||||
|
|
||||||
|
class pp_ss:
|
||||||
|
def __init__(self, val):
|
||||||
|
self.val = val
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return "a=<" + str(self.val["a"]) + "> b=<" + str(self.val["b"]) + ">"
|
||||||
|
|
||||||
|
|
||||||
|
def build_pretty_printer():
|
||||||
|
pp = gdb.printing.RegexpCollectionPrettyPrinter("pp-test")
|
||||||
|
|
||||||
|
pp.add_printer('struct s', '^struct s$', pp_s)
|
||||||
|
pp.add_printer('s', '^s$', pp_s)
|
||||||
|
|
||||||
|
# Use a lambda this time to exercise doing things this way.
|
||||||
|
pp.add_printer('struct ss', '^struct ss$', lambda val: pp_ss(val))
|
||||||
|
pp.add_printer('ss', '^ss$', lambda val: pp_ss(val))
|
||||||
|
|
||||||
|
return pp
|
||||||
|
|
||||||
|
|
||||||
|
gdb.printing.register_pretty_printer(gdb, lookup_function_lookup_test)
|
||||||
|
gdb.printing.register_pretty_printer(gdb, build_pretty_printer())
|
Loading…
Add table
Add a link
Reference in a new issue