|
Packit |
6c4009 |
README for the glibc Python pretty printers
|
|
Packit |
6c4009 |
===========================================
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Pretty printers are gdb extensions that allow it to print useful, human-readable
|
|
Packit |
6c4009 |
information about a program's variables. For example, for a pthread_mutex_t
|
|
Packit |
6c4009 |
gdb would usually output something like this:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
(gdb) print mutex
|
|
Packit |
6c4009 |
$1 = {
|
|
Packit |
6c4009 |
__data = {
|
|
Packit |
6c4009 |
__lock = 22020096,
|
|
Packit |
6c4009 |
__count = 0,
|
|
Packit |
6c4009 |
__owner = 0,
|
|
Packit |
6c4009 |
__nusers = 0,
|
|
Packit |
6c4009 |
__kind = 576,
|
|
Packit |
6c4009 |
__spins = 0,
|
|
Packit |
6c4009 |
__elision = 0,
|
|
Packit |
6c4009 |
__list = {
|
|
Packit |
6c4009 |
__prev = 0x0,
|
|
Packit |
6c4009 |
__next = 0x0
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
},
|
|
Packit |
6c4009 |
__size = "\000\000P\001", '\000' <repeats 12 times>, "@\002", '\000' <repeats 21 times>,
|
|
Packit |
6c4009 |
__align = 22020096
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
However, with a pretty printer gdb will output something like this:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
(gdb) print mutex
|
|
Packit |
6c4009 |
$1 = pthread_mutex_t = {
|
|
Packit |
6c4009 |
Type = Normal,
|
|
Packit |
6c4009 |
Status = Not acquired,
|
|
Packit |
6c4009 |
Robust = No,
|
|
Packit |
6c4009 |
Shared = No,
|
|
Packit |
6c4009 |
Protocol = Priority protect,
|
|
Packit |
6c4009 |
Priority ceiling = 42
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Before printing a value, gdb will first check if there's a pretty printer
|
|
Packit |
6c4009 |
registered for it. If there is, it'll use it, otherwise it'll print the value
|
|
Packit |
6c4009 |
as usual. Pretty printers can be registered in various ways; for our purposes
|
|
Packit |
6c4009 |
we register them for the current objfile by calling
|
|
Packit |
6c4009 |
gdb.printing.register_pretty_printer().
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Currently our printers are based on gdb.RegexpCollectionPrettyPrinter, which
|
|
Packit |
6c4009 |
means they'll be triggered if the type of the variable we're printing matches
|
|
Packit |
6c4009 |
a given regular expression. For example, MutexPrinter will be triggered if
|
|
Packit |
6c4009 |
our variable's type matches the regexp '^pthread_mutex_t$'.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Besides the printers themselves, each module may have a constants file which the
|
|
Packit |
6c4009 |
printers will import. These constants are generated from C headers during the
|
|
Packit |
6c4009 |
build process, and need to be in the Python search path when loading the
|
|
Packit |
6c4009 |
printers.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Installing and loading
|
|
Packit |
6c4009 |
----------------------
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The pretty printers and their constant files may be installed in different paths
|
|
Packit |
6c4009 |
for each distro, though gdb should be able to automatically load them by itself.
|
|
Packit |
6c4009 |
When in doubt, you can use the 'info pretty-printer' gdb command to list the
|
|
Packit |
6c4009 |
loaded pretty printers.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
If the printers aren't automatically loaded for some reason, you should add the
|
|
Packit |
6c4009 |
following to your .gdbinit:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
python
|
|
Packit |
6c4009 |
import sys
|
|
Packit |
6c4009 |
sys.path.insert(0, '/path/to/constants/file/directory')
|
|
Packit |
6c4009 |
end
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
source /path/to/printers.py
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
If you're building glibc manually, '/path/to/constants/file/directory' should be
|
|
Packit |
6c4009 |
'/path/to/glibc-build/submodule', where 'submodule' is e.g. nptl.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Testing
|
|
Packit |
6c4009 |
-------
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The pretty printers come with a small test suite based on PExpect, which is a
|
|
Packit |
6c4009 |
Python module with Expect-like features for spawning and controlling interactive
|
|
Packit |
6c4009 |
programs. Each printer has a corresponding C program and a Python script
|
|
Packit |
6c4009 |
that uses PExpect to drive gdb through the program and compare its output to
|
|
Packit |
6c4009 |
the expected printer's.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The tests run on the glibc host, which is assumed to have both gdb and PExpect;
|
|
Packit |
6c4009 |
if any of those is absent the tests will fail with code 77 (UNSUPPORTED).
|
|
Packit |
6c4009 |
Native builds can be tested simply by doing 'make check'; cross builds must use
|
|
Packit |
6c4009 |
cross-test-ssh.sh as test-wrapper, like this:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
make test-wrapper='/path/to/scripts/cross-test-ssh.sh user@host' check
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
(Remember to share the build system's filesystem with the glibc host's through
|
|
Packit |
6c4009 |
NFS or something similar).
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Running 'make check' on a cross build will only compile the test programs,
|
|
Packit |
6c4009 |
without running the scripts.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Adding new pretty printers
|
|
Packit |
6c4009 |
--------------------------
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Adding new pretty printers to glibc requires following these steps:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
1. Identify which constants must be generated from C headers, and write the
|
|
Packit |
6c4009 |
corresponding .pysym file. See scripts/gen-py-const.awk for more information
|
|
Packit |
6c4009 |
on how this works. The name of the .pysym file must be added to the
|
|
Packit |
6c4009 |
'gen-py-const-headers' variable in your submodule's Makefile (without the .pysym
|
|
Packit |
6c4009 |
extension).
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
2. Write the pretty printer code itself. For this you can follow the gdb
|
|
Packit |
6c4009 |
Python API documentation, and use the existing printers as examples. The printer
|
|
Packit |
6c4009 |
code must import the generated constants file (which will have the same name
|
|
Packit |
6c4009 |
as your .pysym file). The names of the pretty printer files must be added
|
|
Packit |
6c4009 |
to the 'pretty-printers' variable in your submodule's Makefile (without the .py
|
|
Packit |
6c4009 |
extension).
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
3. Write the unit tests for your pretty printers. The build system calls each
|
|
Packit |
6c4009 |
test script passing it the paths to the test program source, the test program
|
|
Packit |
6c4009 |
binary, and the printer files you added to 'pretty-printers' in the previous
|
|
Packit |
6c4009 |
step. The test scripts, in turn, must import scripts/test_printers_common
|
|
Packit |
6c4009 |
and call the init_test function passing it, among other things, the name of the
|
|
Packit |
6c4009 |
set of pretty printers to enable (as seen by running 'info pretty-printer').
|
|
Packit |
6c4009 |
You can use the existing unit tests as examples.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
4. Add the names of the pretty printer tests to the 'tests-printers' variable
|
|
Packit |
6c4009 |
in your submodule's Makefile (without extensions). In addition, for each test
|
|
Packit |
6c4009 |
program you must define a corresponding CFLAGS-* and CPPFLAGS-* variable and
|
|
Packit |
6c4009 |
set it to $(CFLAGS-printers-tests) to ensure they're compiled correctly. For
|
|
Packit |
6c4009 |
example, test-foo-printer.c requires the following:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
CFLAGS-test-foo-printer.c := $(CFLAGS-printers-tests)
|
|
Packit |
6c4009 |
CPPFLAGS-test-foo-printer.c := $(CFLAGS-printers-tests)
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Finally, if your programs need to be linked with a specific library, you can add
|
|
Packit |
6c4009 |
its name to the 'tests-printers-libs' variable in your submodule's Makefile.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Known issues
|
|
Packit |
6c4009 |
------------
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
* Pretty printers are inherently coupled to the code they're targetting, thus
|
|
Packit |
6c4009 |
any changes to the target code must also update the corresponding printers.
|
|
Packit |
6c4009 |
On the plus side, the printer code itself may serve as a kind of documentation
|
|
Packit |
6c4009 |
for the target code.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
* There's no guarantee that the information the pretty printers provide is
|
|
Packit |
6c4009 |
complete, i.e. some details might be left off. For example, the pthread_mutex_t
|
|
Packit |
6c4009 |
printers won't report whether a thread is spin-waiting in an attempt to acquire
|
|
Packit |
6c4009 |
the mutex.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
* Older versions of the gdb Python API have a bug where
|
|
Packit |
6c4009 |
gdb.RegexpCollectionPrettyPrinter would not be able to get a value's real type
|
|
Packit |
6c4009 |
if it was typedef'd. This would cause gdb to ignore the pretty printers for
|
|
Packit |
6c4009 |
types like pthread_mutex_t, which is defined as:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
typedef union
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
...
|
|
Packit |
6c4009 |
} pthread_mutex_t;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
This was fixed in commit 1b588015839caafc608a6944a78aea170f5fb2f6, and released
|
|
Packit |
6c4009 |
as part of gdb 7.8. However, typedef'ing an already typedef'd type may cause
|
|
Packit |
6c4009 |
a similar issue, e.g.:
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
typedef pthread_mutex_t mutex;
|
|
Packit |
6c4009 |
mutex a_mutex;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
Here, trying to print a_mutex won't trigger the pthread_mutex_t printer.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
* The test programs must be compiled without optimizations. This is necessary
|
|
Packit |
6c4009 |
because the test scripts rely on the C code structure being preserved when
|
|
Packit |
6c4009 |
stepping through the programs. Things like aggressive instruction reordering
|
|
Packit |
6c4009 |
or optimizing variables out may make this kind of testing impossible.
|