glibc/pretty-printers
Martin Galvan 62ce266b0b Add pretty printers for the NPTL lock types
This patch adds pretty printers for the following NPTL types:

- pthread_mutex_t
- pthread_mutexattr_t
- pthread_cond_t
- pthread_condattr_t
- pthread_rwlock_t
- pthread_rwlockattr_t

To load the pretty printers into your gdb session, do the following:

python
import sys
sys.path.insert(0, '/path/to/glibc/build/nptl/pretty-printers')
end

source /path/to/glibc/source/pretty-printers/nptl-printers.py

You can check which printers are registered and enabled by issuing the
'info pretty-printer' gdb command. Printers should trigger automatically when
trying to print a variable of one of the types mentioned above.

The printers are architecture-independent, and were manually tested on both
the gdb CLI and Eclipse CDT.

In order to work, the printers need to know the values of various flags that
are scattered throughout pthread.h and pthreadP.h as enums and #defines. Since
replicating these constants in the printers file itself would create a
maintenance burden, I wrote a script called gen-py-const.awk that Makerules uses
to extract the constants. This script is pretty much the same as gen-as-const.awk,
except it doesn't cast the constant values to 'long' and is thorougly documented.
The constants need only to be enumerated in a .pysym file, which is then referenced
by a Make variable called gen-py-const-headers.

As for the install directory, I discussed this with Mike Frysinger and Siddhesh
Poyarekar, and we agreed that it can be handled in a separate patch, and it shouldn't
block merging of this one.

In addition, I've written a series of test cases for the pretty printers.
Each lock type (mutex, condvar and rwlock) has two test programs, one for itself
and other for its related 'attributes' object. Each test program in turn has a
PExpect-based Python script that drives gdb and compares its output to the
expected printer's. The tests run on the glibc host, which is assumed to have
both gdb and PExpect; if either is absent the tests will fail with code 77
(UNSUPPORTED). For cross-testing you should use cross-test-ssh.sh as test-wrapper.
I've tested the printers on both a native build and a cross build using a Beaglebone
Black, with the build system's filesystem shared with the board through NFS.

Finally, I've written a README that explains all this and more.

Hopefully this should be good to go in now. Thanks.

ChangeLog:

2016-07-04  Martin Galvan  <martin.galvan@tallertechnologies.com>

	* Makeconfig (build-hardcoded-path-in-tests): Set to 'yes' for shared builds
	if tests-need-hardcoded-path is defined.
	(all-subdirs): Add pretty-printers.
	* Makerules ($(py-const)): New rule.
	* Rules (others): Add $(py-const), if defined.
	* nptl/Makefile (gen-py-const-headers): Define.
	* nptl/nptl-printers.py: New file.
	* nptl/nptl_lock_constants.pysym: Likewise.
	* pretty-printers/Makefile: Likewise.
	* pretty-printers/README: Likewise.
	* pretty-printers/test-condvar-attributes.c: Likewise.
	* pretty-printers/test-condvar-attributes.p: Likewise.
	* pretty-printers/test-condvar-printer.c: Likewise.
	* pretty-printers/test-condvar-printer.py: Likewise.
	* pretty-printers/test-mutex-attributes.c: Likewise.
	* pretty-printers/test-mutex-attributes.py: Likewise.
	* pretty-printers/test-mutex-printer.c: Likewise.
	* pretty-printers/test-mutex-printer.py: Likewise.
	* pretty-printers/test-rwlock-attributes.c: Likewise.
	* pretty-printers/test-rwlock-attributes.py: Likewise.
	* pretty-printers/test-rwlock-printer.c: Likewise.
	* pretty-printers/test-rwlock-printer.py: Likewise.
	* pretty-printers/test_common.py: Likewise.
	* scripts/gen-py-const.awk: Likewise.
2016-07-08 20:03:05 +05:30
..
Makefile
README
test_common.py
test-condvar-attributes.c
test-condvar-attributes.py
test-condvar-printer.c
test-condvar-printer.py
test-mutex-attributes.c
test-mutex-attributes.py
test-mutex-printer.c
test-mutex-printer.py
test-rwlock-attributes.c
test-rwlock-attributes.py
test-rwlock-printer.c
test-rwlock-printer.py

README for the glibc Python pretty printers
-------------------------------------------

Pretty printers are gdb extensions that allow it to print useful, human-readable
information about a program's variables.  For example, for a pthread_mutex_t
gdb would usually output something like this:

(gdb) print mutex
$1 = {
  __data = {
    __lock = 22020096,
    __count = 0,
    __owner = 0,
    __nusers = 0,
    __kind = 576,
    __spins = 0,
    __elision = 0,
    __list = {
      __prev = 0x0,
      __next = 0x0
    }
  },
  __size = "\000\000P\001", '\000' <repeats 12 times>, "@\002", '\000' <repeats 21 times>,
  __align = 22020096
}

However, with a pretty printer gdb will output something like this:

(gdb) print mutex
$1 = pthread_mutex_t = {
  Type = Normal,
  Status = Unlocked,
  Robust = No,
  Shared = No,
  Protocol = Priority protect,
  Priority ceiling = 42
}

Before printing a value, gdb will first check if there's a pretty printer
registered for it.  If there is, it'll use it, otherwise it'll print the value
as usual.  Pretty printers can be registered in various ways; for our purposes
we register them for the current objfile by calling
gdb.printing.register_pretty_printer().

Currently our printers are based on gdb.RegexpCollectionPrettyPrinter, which
means they'll be triggered if the type of the variable we're printing matches
a given regular expression.  For example, MutexPrinter will be triggered if
our variable's type matches the regexp '^pthread_mutex_t$'.

Besides the printers themselves, each module may have a constants file which the
printers will import.  These constants are generated from C headers during the
build process, and need to be in the Python search path when loading the
printers.


Installing and loading
----------------------

The pretty printers and their constant files may be installed in different paths
for each distro, though gdb should be able to automatically load them by itself.
When in doubt, you can use the 'info pretty printer' gdb command to list the
loaded pretty printers.

If the printers aren't automatically loaded for some reason, you should add the
following to your .gdbinit:

python
import sys
sys.path.insert(0, '/path/to/constants/file/directory')
end

source /path/to/printers.py

If you're building glibc manually, '/path/to/constants/file/directory' should be
'/path/to/glibc-build/submodule', where 'submodule' is e.g. nptl.


Testing
-------

The pretty printers come with a small test suite based on PExpect, which is a
Python module with Expect-like features for spawning and controlling interactive
programs.  Each printer has a corresponding C program and a Python script
that uses PExpect to drive gdb through the program and compare its output to
the expected printer's.

The tests run on the glibc host, which is assumed to have both gdb and PExpect;
if any of those is absent the tests will fail with code 77 (UNSUPPORTED).
Native builds can be tested simply by doing 'make check'; cross builds must use
cross-test-ssh.sh as test-wrapper, like this:

make test-wrapper='/path/to/scripts/cross-test-ssh.sh user@host' check

(Remember to share the build system's filesystem with the glibc host's through
NFS or something similar).

Running 'make check' on a cross build will only compile the test programs,
without running the scripts.


Known issues
------------

* Pretty printers are inherently coupled to the code they're targetting, thus
any changes to the target code must also update the corresponding printers.
On the plus side, the printer code itself may serve as a kind of documentation
for the target code.

* Older versions of the gdb Python API have a bug where
gdb.RegexpCollectionPrettyPrinter would not be able to get a value's real type
if it was typedef'd.  This would cause gdb to ignore the pretty printers for
types like pthread_mutex_t, which is defined as:

typedef union
{
  ...
} pthread_mutex_t;

This was fixed in commit 1b588015839caafc608a6944a78aea170f5fb2f6.  However,
typedef'ing an already typedef'd type may cause a similar issue, e.g.:

typedef pthread_mutex_t mutex;
mutex a_mutex;

Here, trying to print a_mutex won't trigger the pthread_mutex_t printer.

* The test programs must be compiled without optimizations.  This is necessary
because the test scripts rely on the C code structure being preserved when
stepping through the programs.  Things like aggressive instruction reordering
or optimizing variables out may make this kind of testing impossible.