diff mbox

[v8] Add pretty printers for the NPTL lock types

Message ID 20160608153304.12910-1-martin.galvan@tallertechnologies.com
State New
Headers show

Commit Message

Martin Galvan June 8, 2016, 3:33 p.m. UTC
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-06-08  Martin Galvan  <martin.galvan@tallertechnologies.com>

	* Makeconfig (all-subdirs): Add pretty-printers.
	* Makerules ($(py-const)): New rule.
	* Rules (others): Add $(py-const), if defined.
	* nptl/Makefile (gen-py-const-headers): New variable.
	* nptl/nptl-printers.py: New file.
	* nptl/nptl_lock_constants.py: Likewise.
	* 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.
---
 Makeconfig                                 |   2 +-
 Makerules                                  |  44 +++
 Rules                                      |   5 +
 nptl/Makefile                              |   1 +
 nptl/nptl-printers.py                      | 593 +++++++++++++++++++++++++++++
 nptl/nptl_lock_constants.py                |  49 +++
 nptl/nptl_lock_constants.pysym             |  75 ++++
 pretty-printers/Makefile                   |  81 ++++
 pretty-printers/README                     | 130 +++++++
 pretty-printers/test-condvar-attributes.c  |  94 +++++
 pretty-printers/test-condvar-attributes.py |  59 +++
 pretty-printers/test-condvar-printer.c     |  57 +++
 pretty-printers/test-condvar-printer.py    |  44 +++
 pretty-printers/test-mutex-attributes.c    | 144 +++++++
 pretty-printers/test-mutex-attributes.py   |  89 +++++
 pretty-printers/test-mutex-printer.c       | 151 ++++++++
 pretty-printers/test-mutex-printer.py      |  87 +++++
 pretty-printers/test-rwlock-attributes.c   |  98 +++++
 pretty-printers/test-rwlock-attributes.py  |  61 +++
 pretty-printers/test-rwlock-printer.c      |  78 ++++
 pretty-printers/test-rwlock-printer.py     |  58 +++
 pretty-printers/test_common.py             | 301 +++++++++++++++
 scripts/gen-py-const.awk                   | 118 ++++++
 23 files changed, 2418 insertions(+), 1 deletion(-)
 create mode 100644 nptl/nptl-printers.py
 create mode 100644 nptl/nptl_lock_constants.py
 create mode 100644 nptl/nptl_lock_constants.pysym
 create mode 100644 pretty-printers/Makefile
 create mode 100644 pretty-printers/README
 create mode 100644 pretty-printers/test-condvar-attributes.c
 create mode 100644 pretty-printers/test-condvar-attributes.py
 create mode 100644 pretty-printers/test-condvar-printer.c
 create mode 100644 pretty-printers/test-condvar-printer.py
 create mode 100644 pretty-printers/test-mutex-attributes.c
 create mode 100644 pretty-printers/test-mutex-attributes.py
 create mode 100644 pretty-printers/test-mutex-printer.c
 create mode 100644 pretty-printers/test-mutex-printer.py
 create mode 100644 pretty-printers/test-rwlock-attributes.c
 create mode 100644 pretty-printers/test-rwlock-attributes.py
 create mode 100644 pretty-printers/test-rwlock-printer.c
 create mode 100644 pretty-printers/test-rwlock-printer.py
 create mode 100644 pretty-printers/test_common.py
 create mode 100644 scripts/gen-py-const.awk

Comments

Siddhesh Poyarekar June 8, 2016, 3:39 p.m. UTC | #1
On Wed, Jun 08, 2016 at 12:33:04PM -0300, Martin Galvan wrote:
> 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-06-08  Martin Galvan  <martin.galvan@tallertechnologies.com>
> 
> 	* Makeconfig (all-subdirs): Add pretty-printers.
> 	* Makerules ($(py-const)): New rule.
> 	* Rules (others): Add $(py-const), if defined.
> 	* nptl/Makefile (gen-py-const-headers): New variable.
> 	* nptl/nptl-printers.py: New file.
> 	* nptl/nptl_lock_constants.py: Likewise.
> 	* 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.

Thanks, looks good to me now on visual inspection.  I'll test it out
and give it a spin this weekend and also give a week to anybody else
who may want to review the patch.  So if everything goes well, I'll
push this patch in about a week.

Siddhesh
Martin Galvan June 8, 2016, 3:42 p.m. UTC | #2
On Wed, Jun 8, 2016 at 12:39 PM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> Thanks, looks good to me now on visual inspection.  I'll test it out
> and give it a spin this weekend and also give a week to anybody else
> who may want to review the patch.  So if everything goes well, I'll
> push this patch in about a week.

Great. Don't forget to set your .gdbinit to load the printers if
you're running the testsuite, and use cross-test-ssh.sh if you're
gonna cross-test.

Thanks a lot!
Martin Galvan June 15, 2016, 3:14 p.m. UTC | #3
On Wed, Jun 8, 2016 at 12:39 PM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> Thanks, looks good to me now on visual inspection.  I'll test it out
> and give it a spin this weekend and also give a week to anybody else
> who may want to review the patch.  So if everything goes well, I'll
> push this patch in about a week.

Hi Sid, have you got any news on this? Could you test it?
Siddhesh Poyarekar June 15, 2016, 3:33 p.m. UTC | #4
On Wed, Jun 15, 2016 at 12:14:30PM -0300, Martin Galvan wrote:
> Hi Sid, have you got any news on this? Could you test it?

I couldn't unfortunately.  I hope to find some time this weekend and
if it works, I'll push it in.

Siddhesh
Siddhesh Poyarekar June 19, 2016, 4:44 p.m. UTC | #5
On Wed, Jun 15, 2016 at 12:14:30PM -0300, Martin Galvan wrote:
> Hi Sid, have you got any news on this? Could you test it?

Sorry this has taken so long, especially because I have not committed
the patch yet because I ran into a few issues while testing the patch.

When you run the binary under gdb for testing, you're not loading the
libc.so and friends that you just built, you're loading them from the
system.  This is because our testsuite builds the binaries using
rpath-link and not rpath by default and one needs to take extra effort
to make sure that the correct libraries are picked during execution,
like using the testrun.sh or invoking the built ld.so directly.

Neither of those are feasible for running under gdb and doing a clean
enough debug session.  The best option hence is to use rpath instead,
which guarantees that the desired libraries will be built.

However, when you actually do set everything up correctly (i.e. use
--enable-hardcoded-path-in-tests during configure), test-mutex-printer
still fails for me.  I initially thought that it could be due to
thread debugging not being available and added an auto-load safe path
to the nptl_db directory, but that is probably not it because it does
finish successfully if I do something as trivial as printing
gdb.after.  There is probably a race in that test that I haven't
looked at deeply enough to understand so that's something for you to
look at.

That brings me to the final point: debugging of tests when they fail.
There needs to be some more information printed on stdout so that one
may easily find out which part of the test failed by looking at the
${testname}.test-result file.

Now back to the main issue of rpath vs rpath-link.  It is clear that
the pretty printer tests will always need rpath, so I would suggest
fixing that in the makefile instead of bailing out by documenting that
requirement (of enabling hardcoded path in tests during configure) in
the README.

Also, please revert to generating the constants file in objpfx and not
in the source tree in line with Joseph's clarification of practices in
glibc.

Siddhesh
Martin Galvan June 20, 2016, 3:18 a.m. UTC | #6
On Sun, Jun 19, 2016 at 1:44 PM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> Neither of those are feasible for running under gdb and doing a clean
> enough debug session.  The best option hence is to use rpath instead,
> which guarantees that the desired libraries will be built.

Thanks for the answer. Can you specify what exactly is it that I'd
need to do to get this running properly? That is, please tell me,
step-by-step, exactly what I need to do in gdb/the Makefiles. I know
very little of this, and won't sink countless more hours dwelling
within the build system just to get it wrong again.

> However, when you actually do set everything up correctly (i.e. use
> --enable-hardcoded-path-in-tests during configure), test-mutex-printer
> still fails for me.

Odd. Will take a look at it. I doubt it's a race condition since I
specifically wrote it so that there wouldn't be any, but you never
know.

> That brings me to the final point: debugging of tests when they fail.
> There needs to be some more information printed on stdout so that one
> may easily find out which part of the test failed by looking at the
> ${testname}.test-result file.

I don't know if Python's print() will end up in .test-result, but
yeah, that's definitely something necessary. At least if I can't
reproduce the failure I can send the new tests to you and we'll be
able to figure it out from the output.

> Now back to the main issue of rpath vs rpath-link.  It is clear that
> the pretty printer tests will always need rpath, so I would suggest
> fixing that in the makefile instead of bailing out by documenting that
> requirement (of enabling hardcoded path in tests during configure) in
> the README.

See the first answer.

> Also, please revert to generating the constants file in objpfx and not
> in the source tree in line with Joseph's clarification of practices in
> glibc.

Sure. Hopefully nobody else will jump in and tell me to revert any
additional changes at the last minute :)
Siddhesh Poyarekar June 20, 2016, 4:12 a.m. UTC | #7
On Mon, Jun 20, 2016 at 12:18:37AM -0300, Martin Galvan wrote:
> Thanks for the answer. Can you specify what exactly is it that I'd
> need to do to get this running properly? That is, please tell me,
> step-by-step, exactly what I need to do in gdb/the Makefiles. I know
> very little of this, and won't sink countless more hours dwelling
> within the build system just to get it wrong again.

In Makeconfig there is a conditional that decides whether to use rpath
or rpath-link.  Additionally it also decides whether to use the built
dynamic linker or the system dynamic linker.  You'll have to enhance
the conditional so that it selects rpath and the built dynamic linker
when in the pretty-printers subdir.

> I don't know if Python's print() will end up in .test-result, but
> yeah, that's definitely something necessary. At least if I can't
> reproduce the failure I can send the new tests to you and we'll be
> able to figure it out from the output.

Yes, it is not a bad idea to print out diagnostic information even in
case of success.  The hang (rather, timeout) happens on line 66
('Owner ID': r'{0} \(dead\)') for me.

> Sure. Hopefully nobody else will jump in and tell me to revert any
> additional changes at the last minute :)

Hazards of collaborative community development :)

Siddhesh
Martin Galvan June 21, 2016, 9:22 p.m. UTC | #8
On Mon, Jun 20, 2016 at 1:12 AM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> In Makeconfig there is a conditional that decides whether to use rpath
> or rpath-link.  Additionally it also decides whether to use the built
> dynamic linker or the system dynamic linker.  You'll have to enhance
> the conditional so that it selects rpath and the built dynamic linker
> when in the pretty-printers subdir.

Great, how would I go about doing this? That is, just exactly what
should I write? I'm thinking of adding something like

old-link-rpath := link-libc-tests-rpath-link
link-libc-tests-rpath-link = -Wl,-rpath=$(rpath-link)

at the beginning of pretty-printers/Makefile, and then

link-libc-tests-rpath-link = old-link-rpath

at the end.

In any case, how can I know gdb isn't loading the recently built
libraries? info share shows the following for test-mutex-printer:

(gdb) info share
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddbad0  0x00007ffff7df5960  Yes
/home/martin/glibc/install/lib/ld-linux-x86-64.so.2
                                        No          linux-vdso.so.1
0x00007ffff7bc3a90  0x00007ffff7bd03f1  Yes
/home/martin/glibc/install/lib/libpthread.so.0
0x00007ffff78489a0  0x00007ffff796ad53  Yes
/home/martin/glibc/install/lib/libc.so.6

Looks to me like it's finding the built library alright.

Just in case, I'm using gdb 7.7.1. However, gdb 7.11.50.20160531-git
also seems to be loading fine:

(gdb) info share
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddbad0  0x00007ffff7df5960  Yes
/home/martin/glibc/install/lib/ld-linux-x86-64.so.2
0x00007ffff7bc3a90  0x00007ffff7bd03f1  Yes
/home/martin/glibc/install/lib/libpthread.so.0
0x00007ffff78489a0  0x00007ffff796ad53  Yes
/home/martin/glibc/install/lib/libc.so.6

> Yes, it is not a bad idea to print out diagnostic information even in
> case of success.  The hang (rather, timeout) happens on line 66
> ('Owner ID': r'{0} \(dead\)') for me.

Now, here's the interesting thing: it seems that set scheduler-locking
on (which is supposed to prevent the child thread from running) isn't
working correctly in gdb 7.11, yet it does in gdb 7.7. I'll look
further into this and get back at you. In the meanwhile, it would be
useful if you told me which version are you using.

Thanks.
Martin Galvan June 22, 2016, 9:44 p.m. UTC | #9
I've confirmed the test is failing because of a gdb bug:

https://sourceware.org/bugzilla/show_bug.cgi?id=20291

I'll be working on fixing that bug, but I don't think that should
block the merging of this patch. Right now I'm using set
scheduler-locking on to grab the child thread's ID for more rigurous
testing. We can either leave it as it is, knowing it's a gdb bug, or
remove the check for the child thread's ID (for now at least).

I'm still curious about info share showing the libraries were loaded
correctly (to me at least). Can you please tell me what are you
seeing?

(Also, I meant to type link-libc-tests-rpath-link = $(old-link-rpath)
in my last e-mail. Sorry about that).

On Tue, Jun 21, 2016 at 6:22 PM, Martin Galvan
<martin.galvan@tallertechnologies.com> wrote:
> On Mon, Jun 20, 2016 at 1:12 AM, Siddhesh Poyarekar
> <siddhesh@sourceware.org> wrote:
>> In Makeconfig there is a conditional that decides whether to use rpath
>> or rpath-link.  Additionally it also decides whether to use the built
>> dynamic linker or the system dynamic linker.  You'll have to enhance
>> the conditional so that it selects rpath and the built dynamic linker
>> when in the pretty-printers subdir.
>
> Great, how would I go about doing this? That is, just exactly what
> should I write? I'm thinking of adding something like
>
> old-link-rpath := link-libc-tests-rpath-link
> link-libc-tests-rpath-link = -Wl,-rpath=$(rpath-link)
>
> at the beginning of pretty-printers/Makefile, and then
>
> link-libc-tests-rpath-link = old-link-rpath
>
> at the end.
>
> In any case, how can I know gdb isn't loading the recently built
> libraries? info share shows the following for test-mutex-printer:
>
> (gdb) info share
> From                To                  Syms Read   Shared Object Library
> 0x00007ffff7ddbad0  0x00007ffff7df5960  Yes
> /home/martin/glibc/install/lib/ld-linux-x86-64.so.2
>                                         No          linux-vdso.so.1
> 0x00007ffff7bc3a90  0x00007ffff7bd03f1  Yes
> /home/martin/glibc/install/lib/libpthread.so.0
> 0x00007ffff78489a0  0x00007ffff796ad53  Yes
> /home/martin/glibc/install/lib/libc.so.6
>
> Looks to me like it's finding the built library alright.
>
> Just in case, I'm using gdb 7.7.1. However, gdb 7.11.50.20160531-git
> also seems to be loading fine:
>
> (gdb) info share
> From                To                  Syms Read   Shared Object Library
> 0x00007ffff7ddbad0  0x00007ffff7df5960  Yes
> /home/martin/glibc/install/lib/ld-linux-x86-64.so.2
> 0x00007ffff7bc3a90  0x00007ffff7bd03f1  Yes
> /home/martin/glibc/install/lib/libpthread.so.0
> 0x00007ffff78489a0  0x00007ffff796ad53  Yes
> /home/martin/glibc/install/lib/libc.so.6
>
>> Yes, it is not a bad idea to print out diagnostic information even in
>> case of success.  The hang (rather, timeout) happens on line 66
>> ('Owner ID': r'{0} \(dead\)') for me.
>
> Now, here's the interesting thing: it seems that set scheduler-locking
> on (which is supposed to prevent the child thread from running) isn't
> working correctly in gdb 7.11, yet it does in gdb 7.7. I'll look
> further into this and get back at you. In the meanwhile, it would be
> useful if you told me which version are you using.
>
> Thanks.
Pedro Alves June 23, 2016, 9:47 a.m. UTC | #10
On 06/22/2016 10:44 PM, Martin Galvan wrote:
> I've confirmed the test is failing because of a gdb bug:
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=20291

(The difference is that nowadays gdb doesn't use libthread_db.so
thread create event breakpoints, which were masking the issue.)

> 
> I'll be working on fixing that bug, but I don't think that should
> block the merging of this patch. Right now I'm using set
> scheduler-locking on to grab the child thread's ID for more rigurous
> testing. We can either leave it as it is, knowing it's a gdb bug, or
> remove the check for the child thread's ID (for now at least).

- Workaround:

  Can you work around it by setting a breakpoint that the child
  trips on?  Once the breakpoint triggers, you can switch back to
  the parent, and continue/step it, and the child won't run further,
  due to "set scheduler-locking on".

- Hang stepping over pthread_create?

  Note that if there's synchronization (mutex/futex/whatnot) between
  the parent/child threads inside glibc's pthread_create, then making gdb's
  "set scheduler-locking on" lock the child thread _immediately_, such
  that it is never ever scheduled -- that is, never let it run as soon as
  we collect the initial PTRACE_EVENT_CLONE-injected SIGSTOP -- 
  then trying to "step"/"next"/"continue to breakpoint" over pthread_create
  will hang, with the parent waiting forever for the child to
  unlock the mutex/futex/whatnot.

- Why it works with older GDBs, probably:

  Since libthread_db thread create event breakpoints force synchronization
  inside (see nptl/pthread_create.c:__pthread_create_2_1), it seems to me
  that with older GDBs you don't see that hang by chance, because
  the child still runs a bit before the thread create event breakpoint
  is hit by the parent, probably enough to have the child unlock the
  synchronization lock.

Thanks,
Pedro Alves
Siddhesh Poyarekar June 23, 2016, 4:40 p.m. UTC | #11
On Tue, Jun 21, 2016 at 06:22:27PM -0300, Martin Galvan wrote:
> Great, how would I go about doing this? That is, just exactly what
> should I write? I'm thinking of adding something like
> 
> old-link-rpath := link-libc-tests-rpath-link
> link-libc-tests-rpath-link = -Wl,-rpath=$(rpath-link)
> 
> at the beginning of pretty-printers/Makefile, and then
> 
> link-libc-tests-rpath-link = old-link-rpath
> 
> at the end.

I was actually thinking of something even simpler, like setting
build-hardcoded-path-in-tests in Makeconfig if the subdir is
pretty-printers.  You could over-engineer it by adding a new
test-need-hardcoded-path variable that each makefile sets and then
test that in Makeconfig, but I think we can leave that for when any
other subdir always needs a hardcoded path in tests.

> 
> In any case, how can I know gdb isn't loading the recently built
> libraries? info share shows the following for test-mutex-printer:
> 
> (gdb) info share
> From                To                  Syms Read   Shared Object Library
> 0x00007ffff7ddbad0  0x00007ffff7df5960  Yes
> /home/martin/glibc/install/lib/ld-linux-x86-64.so.2
>                                         No          linux-vdso.so.1
> 0x00007ffff7bc3a90  0x00007ffff7bd03f1  Yes
> /home/martin/glibc/install/lib/libpthread.so.0
> 0x00007ffff78489a0  0x00007ffff796ad53  Yes
> /home/martin/glibc/install/lib/libc.so.6
> 
> Looks to me like it's finding the built library alright.

I had checked /proc/PID/maps of the inferior.  I can run it again over
the weekend and check what 'info shared' says if you want, but if
there's a difference, I'd say gdb is wrong and not the proc file.

Siddhesh
Martin Galvan June 24, 2016, 4:14 p.m. UTC | #12
Update: Pedro's suggestion worked fine, so now the unit tests should
work with newer versions of gdb as well. I've also added diagnostic
messages for when the unit tests fail, including the command that was
issued to gdb, its response, and the expected pattern. Hopefully I'll
have a new version ready before/through the weekend.

Sid, could you please tell me which command are you using to check the
libraries, so I'm sure we're on the same page?
Siddhesh Poyarekar June 24, 2016, 4:51 p.m. UTC | #13
On Fri, Jun 24, 2016 at 01:14:45PM -0300, Martin Galvan wrote:
> Sid, could you please tell me which command are you using to check the
> libraries, so I'm sure we're on the same page?

ps aux | grep <test case name> to give me the pid ($PID) for the
inferior and then `cat /proc/$PID/maps`.

Siddhesh
Martin Galvan June 24, 2016, 6:10 p.m. UTC | #14
That's odd, I'm still seeing the built libraries, both with gdb 7.7
and the mainline:

$ cat /proc/${PID}/maps | tr -s ' ' | cut -d' ' -f6 | uniq | awk NF
/home/martin/glibc/build/pretty-printers/test-mutex-printer
/home/martin/glibc/install/lib/libc-2.23.90.so
/home/martin/glibc/install/lib/libpthread-2.23.90.so
/home/martin/glibc/install/lib/ld-2.23.90.so
[vdso]
/home/martin/glibc/install/lib/ld-2.23.90.so
[stack]
[vsyscall]

Just in case, do you have anything special in your .gdbinit that may
be causing the issue?

On Fri, Jun 24, 2016 at 1:51 PM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> On Fri, Jun 24, 2016 at 01:14:45PM -0300, Martin Galvan wrote:
>> Sid, could you please tell me which command are you using to check the
>> libraries, so I'm sure we're on the same page?
>
> ps aux | grep <test case name> to give me the pid ($PID) for the
> inferior and then `cat /proc/$PID/maps`.
>
> Siddhesh
Siddhesh Poyarekar June 24, 2016, 8:32 p.m. UTC | #15
On Fri, Jun 24, 2016 at 03:10:14PM -0300, Martin Galvan wrote:
> That's odd, I'm still seeing the built libraries, both with gdb 7.7
> and the mainline:
> 
> $ cat /proc/${PID}/maps | tr -s ' ' | cut -d' ' -f6 | uniq | awk NF
> /home/martin/glibc/build/pretty-printers/test-mutex-printer
> /home/martin/glibc/install/lib/libc-2.23.90.so
> /home/martin/glibc/install/lib/libpthread-2.23.90.so
> /home/martin/glibc/install/lib/ld-2.23.90.so
> [vdso]
> /home/martin/glibc/install/lib/ld-2.23.90.so
> [stack]
> [vsyscall]
> 
> Just in case, do you have anything special in your .gdbinit that may
> be causing the issue?

Nope, my .gdbinit only has the lines for the pretty-printers.  Are you
sure you don't have anything in your environment that may be picking
up the built glibc?  Because it is not supposed to work with just
rpath-link.  Also check with ldd to verify what libraries get picked
up.  If that also picks up the built libraries then either you are
building with rpath (somehow) or your environment is set to pick up
libraries from that install path.

Siddhesh
Martin Galvan June 24, 2016, 8:39 p.m. UTC | #16
Ldd seems to show the system libraries, not the built ones:

ldd test-mutex-printer
    linux-vdso.so.1 =>  (0x00007fff611fe000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0
(0x00007fc72fe5e000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc72fa99000)
    /home/martin/glibc/install/lib/ld-linux-x86-64.so.2 =>
/lib64/ld-linux-x86-64.so.2 (0x00007fc7300a3000)

That's really weird, and I'm starting to think it's something on my
side. What does 'info share' show in your case?
Martin Galvan June 24, 2016, 8:46 p.m. UTC | #17
I added an endless loop to the test code, so that I could examine its
proc mappings without running it through gdb, and I'm still seeing the
built libraries. And yeah, you're right in that it's being compiled
using rpath-link:

gcc -nostdlib -nostartfiles -o
/home/martin/glibc/build/pretty-printers/test-mutex-printer
-Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both
/home/martin/glibc/build/csu/crt1.o
/home/martin/glibc/build/csu/crti.o `gcc
--print-file-name=crtbegin.o`
/home/martin/glibc/build/pretty-printers/test-mutex-printer.o
/home/martin/glibc/build/nptl/libpthread_nonshared.a
/home/martin/glibc/build/nptl/libpthread.so
-Wl,-dynamic-linker=/home/martin/glibc/install/lib/ld-linux-x86-64.so.2
-Wl,-rpath-link=/home/martin/glibc/build:/home/martin/glibc/build/math:/home/martin/glibc/build/elf:/home/martin/glibc/build/dlfcn:/home/martin/glibc/build/nss:/home/martin/glibc/build/nis:/home/martin/glibc/build/rt:/home/martin/glibc/build/resolv:/home/martin/glibc/build/crypt:/home/martin/glibc/build/mathvec:/home/martin/glibc/build/nptl
/home/martin/glibc/build/libc.so.6
/home/martin/glibc/build/libc_nonshared.a -Wl,--as-needed
/home/martin/glibc/build/elf/ld.so -Wl,--no-as-needed -lgcc
-Wl,--as-needed -lgcc_s  -Wl,--no-as-needed `gcc
--print-file-name=crtend.o` /home/martin/glibc/build/csu/crtn.o
Martin Galvan June 24, 2016, 9:03 p.m. UTC | #18
I just tried running the test program on a separate laptop and it
failed to work because it couldn't find the libraries. Ldd still
showed:

linux-vdso.so.1 =>  (0x00007fff611fe000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc72fe5e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc72fa99000)
/home/martin/glibc/install/lib/ld-linux-x86-64.so.2 =>
/lib64/ld-linux-x86-64.so.2 (0x00007fc7300a3000)

I wonder how that last path got hardcoded there?

On Fri, Jun 24, 2016 at 5:46 PM, Martin Galvan
<martin.galvan@tallertechnologies.com> wrote:
> I added an endless loop to the test code, so that I could examine its
> proc mappings without running it through gdb, and I'm still seeing the
> built libraries. And yeah, you're right in that it's being compiled
> using rpath-link:
>
> gcc -nostdlib -nostartfiles -o
> /home/martin/glibc/build/pretty-printers/test-mutex-printer
> -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both
> /home/martin/glibc/build/csu/crt1.o
> /home/martin/glibc/build/csu/crti.o `gcc
> --print-file-name=crtbegin.o`
> /home/martin/glibc/build/pretty-printers/test-mutex-printer.o
> /home/martin/glibc/build/nptl/libpthread_nonshared.a
> /home/martin/glibc/build/nptl/libpthread.so
> -Wl,-dynamic-linker=/home/martin/glibc/install/lib/ld-linux-x86-64.so.2
> -Wl,-rpath-link=/home/martin/glibc/build:/home/martin/glibc/build/math:/home/martin/glibc/build/elf:/home/martin/glibc/build/dlfcn:/home/martin/glibc/build/nss:/home/martin/glibc/build/nis:/home/martin/glibc/build/rt:/home/martin/glibc/build/resolv:/home/martin/glibc/build/crypt:/home/martin/glibc/build/mathvec:/home/martin/glibc/build/nptl
> /home/martin/glibc/build/libc.so.6
> /home/martin/glibc/build/libc_nonshared.a -Wl,--as-needed
> /home/martin/glibc/build/elf/ld.so -Wl,--no-as-needed -lgcc
> -Wl,--as-needed -lgcc_s  -Wl,--no-as-needed `gcc
> --print-file-name=crtend.o` /home/martin/glibc/build/csu/crtn.o
Pedro Alves June 24, 2016, 9:52 p.m. UTC | #19
On 06/24/2016 07:10 PM, Martin Galvan wrote:
> That's odd, I'm still seeing the built libraries, both with gdb 7.7
> and the mainline:
> 
> $ cat /proc/${PID}/maps | tr -s ' ' | cut -d' ' -f6 | uniq | awk NF
> /home/martin/glibc/build/pretty-printers/test-mutex-printer
> /home/martin/glibc/install/lib/libc-2.23.90.so
> /home/martin/glibc/install/lib/libpthread-2.23.90.so
> /home/martin/glibc/install/lib/ld-2.23.90.so
> [vdso]
> /home/martin/glibc/install/lib/ld-2.23.90.so
> [stack]
> [vsyscall]
> 
> Just in case, do you have anything special in your .gdbinit that may
> be causing the issue?

Why not start gdb with "gdb -nx" in order to avoid .gdbinit
ever being an issue?

Thanks,
Pedro Alves
Pedro Alves June 24, 2016, 10:31 p.m. UTC | #20
On 06/24/2016 11:04 PM, Martin Galvan wrote:
> Pedro: In our case we need gdb to automatically load the pretty printers
> before doing any testing, which is why I suggested placing a few lines
> in .gdbinit to do so. There's probably a better way to do it (perhaps
> manually copying the printers to some special path gdb can auto-load
> from?), but I thought it was good enough for testing.

You want to test the printers in the source tree, not the system
one's right?  At least by default.

How about:

- add whatever lines those are to a gdb script that lives along
  side the tests.

- have the harness start gdb with:

   gdb -nx -ix /path/to/that/script

No need to manually tweak ~/.gdbinit that way.

Thanks,
Pedro Alves
Siddhesh Poyarekar June 25, 2016, 4:33 a.m. UTC | #21
On Fri, Jun 24, 2016 at 07:04:19PM -0300, Martin Galvan wrote:
> Sid: I did a bit more testing, and it seems like the magic comes from the
> -dynamic-linker flag that's being passed to ld. Without it, info share
> shows that the system libraries are being used:

Yes, that is the interesting bit: your build seems to end up using the
built dynamic linker instead of the one from the system.  This might
be due to the way you're configuring your build.  I do just this:

    ../configure --prefix=/usr --enable-multi-arch

> (gdb) info share
> From                To                  Syms Read   Shared Object Library
> 0x00007ffff7ddaae0  0x00007ffff7df5490  Yes
> /lib64/ld-linux-x86-64.so.2
> 0x00007ffff7bc19f0  0x00007ffff7bce471  Yes
> /lib/x86_64-linux-gnu/libpthread.so.0
> 0x00007ffff7816520  0x00007ffff795b183  Yes
> /lib/x86_64-linux-gnu/libc.so.6
> 
> If I'm correct, then this shouldn't be an issue since glibc's newly built
> ld.so should take care of finding the correct libraries. Don't ask me how
> it works internally though, I'm learning as I go along :)

My theory is that it might be because of the difference in
configuration.  I'll play with it a bit tonight and let you know.

Siddhesh
Siddhesh Poyarekar June 25, 2016, 4:44 a.m. UTC | #22
(To elaborate on my last response, since I realized I did not explain
the mechanics of how this works)

On Fri, Jun 24, 2016 at 05:46:23PM -0300, Martin Galvan wrote:
> I added an endless loop to the test code, so that I could examine its
> proc mappings without running it through gdb, and I'm still seeing the
> built libraries. And yeah, you're right in that it's being compiled
> using rpath-link:

Your default dynamic linker is being set as the built one instead of
the system, perhaps because of the way your source is configured.
That alone does not however determine why your program picks up the
built libraries because you've only specified rpath-link and not rpath
and hence the binary itself does not have the paths to search from.

So the question is how the binary ends up picking the built libraries
and the answer might be in why your build system picks up the built
dynamic linker.  Check out your rtldir and slibdir in the generated
config.make and that might tell you why.  I think there is some system
configuration on your computer that ends up in picking the built
libraries, either LD_LIBRARY_PATH set somewhere or something you
specified in configure, although the latter seems unlikely.

I'll run tests tonight but you might want to try your entire exercise
on a different computer and with a very basic configure command:

    ../configure --prefix=/usr

Siddhesh
Martin Galvan June 25, 2016, 3:06 p.m. UTC | #23
On Fri, Jun 24, 2016 at 7:31 PM, Pedro Alves <palves@redhat.com> wrote:
> How about:
>
> - add whatever lines those are to a gdb script that lives along
>   side the tests.
>
> - have the harness start gdb with:
>
>    gdb -nx -ix /path/to/that/script
>
> No need to manually tweak ~/.gdbinit that way.

Sounds good! I'll see about adding that when I send the install patch
though, as I'd like to wrap this one up first.

On Sat, Jun 25, 2016 at 1:44 AM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> (To elaborate on my last response, since I realized I did not explain
> the mechanics of how this works)

Thanks for the thorough explanation.

> Your default dynamic linker is being set as the built one instead of
> the system, perhaps because of the way your source is configured.

Funny, I actually thought it was supposed to do that :)

> That alone does not however determine why your program picks up the
> built libraries because you've only specified rpath-link and not rpath
> and hence the binary itself does not have the paths to search from.

Maybe the built ld.so somehow knows where to find the built libraries,
as if my custom install dir was some sort of sysroot?

> So the question is how the binary ends up picking the built libraries
> and the answer might be in why your build system picks up the built
> dynamic linker.  Check out your rtldir and slibdir in the generated
> config.make and that might tell you why.  I think there is some system
> configuration on your computer that ends up in picking the built
> libraries, either LD_LIBRARY_PATH set somewhere or something you
> specified in configure, although the latter seems unlikely.

I checked rtldir and slibdir, and both are empty. So are
LD_LIBRARY_PATH and LD_RUN_PATH. I never used anything weird for my
configure, just --prefix=/home/martin/glibc/install as always.

It would be great if you could tell me what flags are being passed to
gcc when you compile the tests. Your gdb version, as well as the
output of info share, might also give us a clue.
Siddhesh Poyarekar June 25, 2016, 5:22 p.m. UTC | #24
On Sat, Jun 25, 2016 at 12:06:03PM -0300, Martin Galvan wrote:
> > Your default dynamic linker is being set as the built one instead of
> > the system, perhaps because of the way your source is configured.
> 
> Funny, I actually thought it was supposed to do that :)

A correction is in order here: The dynamic linker set in your binaries
is not the built one, it is the *installed* one, i.e. in
/home/martin/glibc/install and not /home/martin/glibc/build (or
whatever the build directory is).  And with this correction I can now
explain in detail what is going on.

The TL;DR; version is to stick to doing ../configure --prefix=/usr to
test what all distributions are doing and then take it from there.

When you configure with --prefix=<some-nonstandard-prefix>, the source
tree is built with that prefix hardcoded in.  That is, for the dynamic
linker built with these settings, the default search path is in
<some-nonstandard-prefix>/lib:<some-nonstandard-prefix>/usr/lib.  This
is why your tests succeed; they find the libraries in your install
tree and run correctly.  If there was no install tree, the tests would
fail.  In other words, the tests are incapable of running from just
within the build tree.

The /usr prefix is special in that it retains the behaviour in the
dynamic linker of picking libraries from the system directories
because that is what we eventually want when we install the library.
This is also why I see the system libraries being picked up and not
the build tree libraries.  This configuration is very important for
testing because most major distributions configure with --prefix=/usr
and testsuite runs within their build trees need to run correctly.

To get the tests running correctly with the --prefix=/usr setting, you
need to provide some kind of hint inside the binaries so that the
correct libraries are picked and also set the dynamic linker to the
one that you want to test.  Enter option
--enable-hardcoded-path-in-tests.  It sets the dynamic linker header
entry and adds DT_RPATH to the binaries so that they're invoked using
our built dynamic linker and picks up our built libraries.

What we need to do is emulate the --enable-hardcoded-path-in-tests
option all the time for the pretty-printers so that they libraries are
picked correctly.

Siddhesh
Martin Galvan June 27, 2016, 7:59 p.m. UTC | #25
On Sat, Jun 25, 2016 at 2:22 PM, Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
> What we need to do is emulate the --enable-hardcoded-path-in-tests
> option all the time for the pretty-printers so that they libraries are
> picked correctly.

Oh, I see. This is all very interesting stuff, is it documented
somewhere? I know the manual mentions enable-hardcoded-path-in-tests,
but I don't remember anything that mentions testing with /usr as
prefix, nor the role the dynamic linker plays here. Of course, I take
it that I should *not* do 'make install' afterwards, or else my
system's libraries would get overwritten.

I'll touch up Makeconfig and send v9. Thanks.
Martin Galvan June 27, 2016, 8:24 p.m. UTC | #26
All this got me thinking, though. Pretty-printer testing aside, if we
always want to run the testsuite without an install tree, shouldn't
rpath be the default for all the tests instead of rpath-link? How do
all the other tests work?
Siddhesh Poyarekar June 28, 2016, 6:43 a.m. UTC | #27
On Mon, Jun 27, 2016 at 04:59:38PM -0300, Martin Galvan wrote:
> Oh, I see. This is all very interesting stuff, is it documented
> somewhere? I know the manual mentions enable-hardcoded-path-in-tests,

It isn't AFAIK, unless you can find it on the wiki.  You're welcome to
write it up if it isn't :)

> but I don't remember anything that mentions testing with /usr as
> prefix, nor the role the dynamic linker plays here. Of course, I take
> it that I should *not* do 'make install' afterwards, or else my
> system's libraries would get overwritten.

You can do 'make install', but you need to use the DESTDIR to use a
different base.  That is what distributions do for packaging.

BTW, if a make install successfully overwrites your system libraries
then you're doing something wrong ;)

> All this got me thinking, though. Pretty-printer testing aside, if we
> always want to run the testsuite without an install tree, shouldn't
> rpath be the default for all the tests instead of rpath-link? How do
> all the other tests work?

That was my thought as well but somebody had mentioned use cases for
not including DT_RPATH years ago.  I can't remember what it was
though; maybe a search through the mailing list archives might help
answer that question.

Siddhesh
diff mbox

Patch

diff --git a/Makeconfig b/Makeconfig
index 901e253..4eb9f23 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -1096,7 +1096,7 @@  all-subdirs = csu assert ctype locale intl catgets math setjmp signal	    \
 	      grp pwd posix io termios resource misc socket sysvipc gmon    \
 	      gnulib iconv iconvdata wctype manual shadow gshadow po argp   \
 	      crypt localedata timezone rt conform debug mathvec	    \
-	      $(add-on-subdirs) dlfcn elf
+	      $(add-on-subdirs) dlfcn elf pretty-printers
 
 ifndef avoid-generated
 # sysd-sorted itself will contain rules making the sysd-sorted target
diff --git a/Makerules b/Makerules
index 53eabfa..56c9a07 100644
--- a/Makerules
+++ b/Makerules
@@ -190,6 +190,50 @@  sed-remove-dotdot := -e 's@  *\([^ 	\/$$][^ 	\]*\)@ $$(..)\1@g' \
 		     -e 's@^\([^ 	\/$$][^ 	\]*\)@$$(..)\1@g'
 endif
 
+ifdef gen-py-const-headers
+# We'll use a static pattern rule to match .pysym files with their
+# corresponding generated .py files.
+# The generated .py files go in the submodule's dir in the glibc source dir.
+py-const-files := $(patsubst %.pysym,%.py,$(gen-py-const-headers))
+py-const-dir := $(subdir)
+py-const := $(py-const-files)
+py-const-script := $(..)scripts/gen-py-const.awk
+
+# This is a hack we use to generate .py files with constants for Python
+# pretty printers.  It works the same way as gen-as-const.
+# See scripts/gen-py-const.awk for details on how the awk | gcc mechanism
+# works.
+#
+# $@.tmp and $@.tmp2 are temporary files we use to store the partial contents
+# of the target file.  We do this instead of just writing on $@ because, if the
+# build process terminates prematurely, re-running Make wouldn't run this rule
+# since Make would see that the target file already exists (despite it being
+# incomplete).
+#
+# The sed line replaces "@name@SOME_NAME@value@SOME_VALUE@" strings from the
+# output of 'gcc -S' with "SOME_NAME = SOME_VALUE" strings.
+# The '-n' option, combined with the '/p' command, makes sed output only the
+# modified lines instead of the whole input file.  The output is redirected
+# to a .py file; we'll import it in the pretty printers file to read
+# the constants generated by gen-py-const.awk.
+# The regex has two capturing groups, for SOME_NAME and SOME_VALUE
+# respectively.  Notice SOME_VALUE may be prepended by a special character,
+# depending on the assembly syntax (e.g. immediates are prefixed by a '$'
+# in AT&T x86, and by a '#' in ARM).  We discard it using a complemented set
+# before the second capturing group.
+$(py-const): %.py: %.pysym $(py-const-script) $(common-before-compile)
+	$(AWK) -f $(py-const-script) $< \
+		| $(CC) -S -o $@.tmp $(CFLAGS) $(CPPFLAGS) -x c -
+	echo '# GENERATED FILE\n' > $@.tmp2
+	echo '# Constant definitions for pretty printers.' >> $@.tmp2
+	echo '# See gen-py-const.awk for details.\n' >> $@.tmp2
+	sed -n -r 's/^.*@name@([^@]+)@value@[^[:xdigit:]Xx-]*([[:xdigit:]Xx-]+)@.*/\1 = \2/p' \
+		  $@.tmp >> $@.tmp2
+	mv -f $@.tmp2 $@
+	rm -f $@.tmp
+
+generated += $(py-const)
+endif  # gen-py-const-headers
 
 ifdef gen-as-const-headers
 # Generating headers for assembly constants.
diff --git a/Rules b/Rules
index 8306d36..cd3e764 100644
--- a/Rules
+++ b/Rules
@@ -98,6 +98,11 @@  tests: $(tests:%=$(objpfx)%.out) $(tests-special)
 xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
 endif
 
+# Generate constant files for Python pretty printers if required.
+ifdef py-const
+others: $(py-const)
+endif
+
 tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
 xtests-special-notdir = $(patsubst $(objpfx)%, %, $(xtests-special))
 tests:
diff --git a/nptl/Makefile b/nptl/Makefile
index eaa6f7f..82ad67f 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -306,6 +306,7 @@  gen-as-const-headers = pthread-errnos.sym \
 		       unwindbuf.sym \
 		       lowlevelrobustlock.sym pthread-pi-defines.sym
 
+gen-py-const-headers = nptl_lock_constants.pysym
 
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
 
diff --git a/nptl/nptl-printers.py b/nptl/nptl-printers.py
new file mode 100644
index 0000000..f64216f
--- /dev/null
+++ b/nptl/nptl-printers.py
@@ -0,0 +1,593 @@ 
+# Pretty printers for the NPTL lock types.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""This file contains the gdb pretty printers for the following types:
+
+    * pthread_mutex_t
+    * pthread_mutexattr_t
+    * pthread_cond_t
+    * pthread_condattr_t
+    * pthread_rwlock_t
+    * pthread_rwlockattr_t
+
+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.
+"""
+
+from __future__ import print_function
+
+import gdb
+from nptl_lock_constants import *
+
+MUTEX_TYPES = {
+    PTHREAD_MUTEX_NORMAL: ('Type', 'Normal'),
+    PTHREAD_MUTEX_RECURSIVE: ('Type', 'Recursive'),
+    PTHREAD_MUTEX_ERRORCHECK: ('Type', 'Error check'),
+    PTHREAD_MUTEX_ADAPTIVE_NP: ('Type', 'Adaptive')
+}
+
+class MutexPrinter(object):
+    """Pretty printer for pthread_mutex_t."""
+
+    def __init__(self, mutex):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            mutex: A gdb.value representing a pthread_mutex_t.
+        """
+
+        data = mutex['__data']
+        self.lock = data['__lock']
+        self.count = data['__count']
+        self.owner = data['__owner']
+        self.kind = data['__kind']
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutex_t.
+        """
+
+        return 'pthread_mutex_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutex_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the mutex's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.read_type()
+        self.read_status()
+        self.read_attributes()
+        self.read_misc_info()
+
+    def read_type(self):
+        """Read the mutex's type."""
+
+        mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
+
+        # mutex_type must be casted to int because it's a gdb.Value
+        self.values.append(MUTEX_TYPES[int(mutex_type)])
+
+    def read_status(self):
+        """Read the mutex's status.
+
+        For architectures which support lock elision, this method reads
+        whether the mutex appears as locked in memory (i.e. it may show it as
+        unlocked even after calling pthread_mutex_lock).
+        """
+
+        if self.kind == PTHREAD_MUTEX_DESTROYED:
+            self.values.append(('Status', 'Destroyed'))
+        elif self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
+            self.read_status_robust()
+        else:
+            self.read_status_no_robust()
+
+    def read_status_robust(self):
+        """Read the status of a robust mutex.
+
+        In glibc robust mutexes are implemented in a very different way than
+        non-robust ones.  This method reads their locking status,
+        whether it may have waiters, their registered owner (if any),
+        whether the owner is alive or not, and the status of the state
+        they're protecting.
+        """
+
+        if self.lock == PTHREAD_MUTEX_UNLOCKED:
+            self.values.append(('Status', 'Unlocked'))
+        else:
+            if self.lock & FUTEX_WAITERS:
+                self.values.append(('Status', 'Locked, possibly with waiters'))
+            else:
+                self.values.append(('Status',
+                                    'Locked, possibly with no waiters'))
+
+            if self.lock & FUTEX_OWNER_DIED:
+                self.values.append(('Owner ID', '%d (dead)' % self.owner))
+            else:
+                self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
+
+        if self.owner == PTHREAD_MUTEX_INCONSISTENT:
+            self.values.append(('State protected by this mutex',
+                                'Inconsistent'))
+        elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
+            self.values.append(('State protected by this mutex',
+                                'Not recoverable'))
+
+    def read_status_no_robust(self):
+        """Read the status of a non-robust mutex.
+
+        Read info on whether the mutex is locked, if it may have waiters
+        and its owner (if any).
+        """
+
+        lock_value = self.lock
+
+        if self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
+            lock_value &= ~(PTHREAD_MUTEX_PRIO_CEILING_MASK)
+
+        if lock_value == PTHREAD_MUTEX_UNLOCKED:
+            self.values.append(('Status', 'Unlocked'))
+        else:
+            if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
+                waiters = self.lock & FUTEX_WAITERS
+                owner = self.lock & FUTEX_TID_MASK
+            else:
+                # Mutex protocol is PP or none
+                waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
+                owner = self.owner
+
+            if waiters:
+                self.values.append(('Status', 'Locked, possibly with waiters'))
+            else:
+                self.values.append(('Status',
+                                    'Locked, possibly with no waiters'))
+
+            self.values.append(('Owner ID', owner))
+
+    def read_attributes(self):
+        """Read the mutex's attributes."""
+
+        if self.kind != PTHREAD_MUTEX_DESTROYED:
+            if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
+                self.values.append(('Robust', 'Yes'))
+            else:
+                self.values.append(('Robust', 'No'))
+
+            # In glibc, robust mutexes always have their pshared flag set to
+            # 'shared' regardless of what the pshared flag of their
+            # mutexattr was.  Therefore a robust mutex will act as shared
+            # even if it was initialized with a 'private' mutexattr.
+            if self.kind & PTHREAD_MUTEX_PSHARED_BIT:
+                self.values.append(('Shared', 'Yes'))
+            else:
+                self.values.append(('Shared', 'No'))
+
+            if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
+                self.values.append(('Protocol', 'Priority inherit'))
+            elif self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
+                prio_ceiling = ((self.lock & PTHREAD_MUTEX_PRIO_CEILING_MASK)
+                                >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT)
+
+                self.values.append(('Protocol', 'Priority protect'))
+                self.values.append(('Priority ceiling', prio_ceiling))
+            else:
+                # PTHREAD_PRIO_NONE
+                self.values.append(('Protocol', 'None'))
+
+    def read_misc_info(self):
+        """Read miscellaneous info on the mutex.
+
+        For now this reads the number of times a recursive mutex was locked
+        by the same thread.
+        """
+
+        mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
+
+        if mutex_type == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
+            self.values.append(('Times locked recursively', self.count))
+
+class MutexAttributesPrinter(object):
+    """Pretty printer for pthread_mutexattr_t.
+
+    In the NPTL this is a type that's always casted to struct pthread_mutexattr
+    which has a single 'mutexkind' field containing the actual attributes.
+    """
+
+    def __init__(self, mutexattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            mutexattr: A gdb.value representing a pthread_mutexattr_t.
+        """
+
+        mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
+        self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutexattr_t.
+        """
+
+        return 'pthread_mutexattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_mutexattr_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the mutexattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        mutexattr_type = (self.mutexattr
+                          & ~PTHREAD_MUTEXATTR_FLAG_BITS
+                          & ~PTHREAD_MUTEX_NO_ELISION_NP)
+
+        # mutexattr_type must be casted to int because it's a gdb.Value
+        self.values.append(MUTEX_TYPES[int(mutexattr_type)])
+
+        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
+            self.values.append(('Robust', 'Yes'))
+        else:
+            self.values.append(('Robust', 'No'))
+
+        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
+                    PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
+
+        if protocol == PTHREAD_PRIO_NONE:
+            self.values.append(('Protocol', 'None'))
+        elif protocol == PTHREAD_PRIO_INHERIT:
+            self.values.append(('Protocol', 'Priority inherit'))
+        elif protocol == PTHREAD_PRIO_PROTECT:
+            self.values.append(('Protocol', 'Priority protect'))
+
+CLOCK_IDS = {
+    CLOCK_REALTIME: 'CLOCK_REALTIME',
+    CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
+    CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
+    CLOCK_THREAD_CPUTIME_ID: 'CLOCK_THREAD_CPUTIME_ID',
+    CLOCK_MONOTONIC_RAW: 'CLOCK_MONOTONIC_RAW',
+    CLOCK_REALTIME_COARSE: 'CLOCK_REALTIME_COARSE',
+    CLOCK_MONOTONIC_COARSE: 'CLOCK_MONOTONIC_COARSE'
+}
+
+class ConditionVariablePrinter(object):
+    """Pretty printer for pthread_cond_t."""
+
+    def __init__(self, cond):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            cond: A gdb.value representing a pthread_cond_t.
+        """
+
+        data = cond['__data']
+        self.total_seq = data['__total_seq']
+        self.mutex = data['__mutex']
+        self.nwaiters = data['__nwaiters']
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_cond_t.
+        """
+
+        return 'pthread_cond_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_cond_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the condvar's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.read_status()
+        self.read_attributes()
+        self.read_mutex_info()
+
+    def read_status(self):
+        """Read the status of the condvar.
+
+        This method reads whether the condvar is destroyed and how many threads
+        are waiting for it.
+        """
+
+        if self.total_seq == PTHREAD_COND_DESTROYED:
+            self.values.append(('Status', 'Destroyed'))
+
+        self.values.append(('Threads waiting for this condvar',
+                            self.nwaiters >> COND_NWAITERS_SHIFT))
+
+    def read_attributes(self):
+        """Read the condvar's attributes."""
+
+        clock_id = self.nwaiters & ((1 << COND_NWAITERS_SHIFT) - 1)
+
+        # clock_id must be casted to int because it's a gdb.Value
+        self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
+
+        shared = (self.mutex == PTHREAD_COND_SHARED)
+
+        if shared:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+    def read_mutex_info(self):
+        """Read the data of the mutex this condvar is bound to.
+
+        A pthread_cond_t's __data.__mutex member is a void * which
+        must be casted to pthread_mutex_t *.  For shared condvars, this
+        member isn't recorded and has a value of ~0l instead.
+        """
+
+        if self.mutex and self.mutex != PTHREAD_COND_SHARED:
+            mutex_type = gdb.lookup_type('pthread_mutex_t')
+            mutex = self.mutex.cast(mutex_type.pointer()).dereference()
+
+            self.values.append(('Mutex', mutex))
+
+class ConditionVariableAttributesPrinter(object):
+    """Pretty printer for pthread_condattr_t.
+
+    In the NPTL this is a type that's always casted to struct pthread_condattr,
+    which has a single 'value' field containing the actual attributes.
+    """
+
+    def __init__(self, condattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            condattr: A gdb.value representing a pthread_condattr_t.
+        """
+
+        condattr_struct = gdb.lookup_type('struct pthread_condattr')
+        self.condattr = condattr.cast(condattr_struct)['value']
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_condattr_t.
+        """
+
+        return 'pthread_condattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_condattr_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the condattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        clock_id = self.condattr & ((1 << COND_NWAITERS_SHIFT) - 1)
+
+        # clock_id must be casted to int because it's a gdb.Value
+        self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
+
+        if self.condattr & 1:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+class RWLockPrinter(object):
+    """Pretty printer for pthread_rwlock_t."""
+
+    def __init__(self, rwlock):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            rwlock: A gdb.value representing a pthread_rwlock_t.
+        """
+
+        data = rwlock['__data']
+        self.readers = data['__nr_readers']
+        self.queued_readers = data['__nr_readers_queued']
+        self.queued_writers = data['__nr_writers_queued']
+        self.writer_id = data['__writer']
+        self.shared = data['__shared']
+        self.prefers_writers = data['__flags']
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlock_t.
+        """
+
+        return 'pthread_rwlock_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlock_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the rwlock's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        self.read_status()
+        self.read_attributes()
+
+    def read_status(self):
+        """Read the status of the rwlock."""
+
+        # Right now pthread_rwlock_destroy doesn't do anything, so there's no
+        # way to check if an rwlock is destroyed.
+
+        if self.writer_id:
+            self.values.append(('Status', 'Locked (Write)'))
+            self.values.append(('Writer ID', self.writer_id))
+        elif self.readers:
+            self.values.append(('Status', 'Locked (Read)'))
+            self.values.append(('Readers', self.readers))
+        else:
+            self.values.append(('Status', 'Unlocked'))
+
+        self.values.append(('Queued readers', self.queued_readers))
+        self.values.append(('Queued writers', self.queued_writers))
+
+    def read_attributes(self):
+        """Read the attributes of the rwlock."""
+
+        if self.shared:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            self.values.append(('Shared', 'No'))
+
+        if self.prefers_writers:
+            self.values.append(('Prefers', 'Writers'))
+        else:
+            self.values.append(('Prefers', 'Readers'))
+
+class RWLockAttributesPrinter(object):
+    """Pretty printer for pthread_rwlockattr_t.
+
+    In the NPTL this is a type that's always casted to
+    struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
+    containing the actual attributes.
+    """
+
+    def __init__(self, rwlockattr):
+        """Initialize the printer's internal data structures.
+
+        Args:
+            rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
+        """
+
+        rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
+        self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
+        self.values = []
+        self.read_values()
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlockattr_t.
+        """
+
+        return 'pthread_rwlockattr_t'
+
+    def children(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print a pthread_rwlockattr_t.
+        """
+
+        return self.values
+
+    def read_values(self):
+        """Read the rwlockattr's info and store it in self.values.
+
+        The data contained in self.values will be returned by the Iterator
+        created in self.children.
+        """
+
+        rwlock_type = self.rwlockattr['lockkind']
+        shared = self.rwlockattr['pshared']
+
+        if shared == PTHREAD_PROCESS_SHARED:
+            self.values.append(('Shared', 'Yes'))
+        else:
+            # PTHREAD_PROCESS_PRIVATE
+            self.values.append(('Shared', 'No'))
+
+        if (rwlock_type == PTHREAD_RWLOCK_PREFER_READER_NP or
+            rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NP):
+            # This is a known bug.  Using PTHREAD_RWLOCK_PREFER_WRITER_NP will
+            # still make the rwlock prefer readers.
+            self.values.append(('Prefers', 'Readers'))
+        elif rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
+            self.values.append(('Prefers', 'Writers'))
+
+def register(objfile):
+    """Register the pretty printers within the given objfile."""
+
+    printer = gdb.printing.RegexpCollectionPrettyPrinter('glibc pthread locks')
+
+    printer.add_printer('pthread_mutex_t', r'^pthread_mutex_t$',
+                        MutexPrinter)
+    printer.add_printer('pthread_mutexattr_t', r'^pthread_mutexattr_t$',
+                        MutexAttributesPrinter)
+    printer.add_printer('pthread_cond_t', r'^pthread_cond_t$',
+                        ConditionVariablePrinter)
+    printer.add_printer('pthread_condattr_t', r'^pthread_condattr_t$',
+                        ConditionVariableAttributesPrinter)
+    printer.add_printer('pthread_rwlock_t', r'^pthread_rwlock_t$',
+                        RWLockPrinter)
+    printer.add_printer('pthread_rwlockattr_t', r'^pthread_rwlockattr_t$',
+                        RWLockAttributesPrinter)
+
+    gdb.printing.register_pretty_printer(objfile, printer)
+
+register(gdb.current_objfile())
diff --git a/nptl/nptl_lock_constants.py b/nptl/nptl_lock_constants.py
new file mode 100644
index 0000000..29857b6
--- /dev/null
+++ b/nptl/nptl_lock_constants.py
@@ -0,0 +1,49 @@ 
+# GENERATED FILE
+
+# Constant definitions for pretty printers.
+# See gen-py-const.awk for details.
+
+PTHREAD_MUTEX_KIND_MASK = 3
+PTHREAD_MUTEX_NORMAL = 0
+PTHREAD_MUTEX_RECURSIVE = 1
+PTHREAD_MUTEX_ERRORCHECK = 2
+PTHREAD_MUTEX_ADAPTIVE_NP = 3
+PTHREAD_MUTEX_DESTROYED = -1
+PTHREAD_MUTEX_UNLOCKED = 0
+PTHREAD_MUTEX_LOCKED_NO_WAITERS = 1
+PTHREAD_MUTEX_INCONSISTENT = 2147483647
+PTHREAD_MUTEX_NOTRECOVERABLE = 2147483646
+FUTEX_OWNER_DIED = 1073741824
+FUTEX_WAITERS = -2147483648
+FUTEX_TID_MASK = 1073741823
+PTHREAD_MUTEX_ROBUST_NORMAL_NP = 16
+PTHREAD_MUTEX_PRIO_INHERIT_NP = 32
+PTHREAD_MUTEX_PRIO_PROTECT_NP = 64
+PTHREAD_MUTEX_PSHARED_BIT = 128
+PTHREAD_MUTEX_PRIO_CEILING_SHIFT = 19
+PTHREAD_MUTEX_PRIO_CEILING_MASK = -524288
+PTHREAD_MUTEXATTR_PROTOCOL_SHIFT = 28
+PTHREAD_MUTEXATTR_PROTOCOL_MASK = 805306368
+PTHREAD_MUTEXATTR_PRIO_CEILING_MASK = 16773120
+PTHREAD_MUTEXATTR_FLAG_ROBUST = 1073741824
+PTHREAD_MUTEXATTR_FLAG_PSHARED = -2147483648
+PTHREAD_MUTEXATTR_FLAG_BITS = -251662336
+PTHREAD_MUTEX_NO_ELISION_NP = 512
+PTHREAD_PRIO_NONE = 0
+PTHREAD_PRIO_INHERIT = 1
+PTHREAD_PRIO_PROTECT = 2
+PTHREAD_COND_SHARED = -1
+PTHREAD_COND_DESTROYED = -1
+COND_NWAITERS_SHIFT = 1
+CLOCK_REALTIME = 0
+CLOCK_MONOTONIC = 1
+CLOCK_PROCESS_CPUTIME_ID = 2
+CLOCK_THREAD_CPUTIME_ID = 3
+CLOCK_MONOTONIC_RAW = 4
+CLOCK_REALTIME_COARSE = 5
+CLOCK_MONOTONIC_COARSE = 6
+PTHREAD_RWLOCK_PREFER_READER_NP = 0
+PTHREAD_RWLOCK_PREFER_WRITER_NP = 1
+PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP = 2
+PTHREAD_PROCESS_PRIVATE = 0
+PTHREAD_PROCESS_SHARED = 1
diff --git a/nptl/nptl_lock_constants.pysym b/nptl/nptl_lock_constants.pysym
new file mode 100644
index 0000000..e6ac427
--- /dev/null
+++ b/nptl/nptl_lock_constants.pysym
@@ -0,0 +1,75 @@ 
+#include <pthreadP.h>
+
+-- Mutex types
+PTHREAD_MUTEX_KIND_MASK          PTHREAD_MUTEX_KIND_MASK_NP
+PTHREAD_MUTEX_NORMAL
+PTHREAD_MUTEX_RECURSIVE          PTHREAD_MUTEX_RECURSIVE_NP
+PTHREAD_MUTEX_ERRORCHECK         PTHREAD_MUTEX_ERRORCHECK_NP
+PTHREAD_MUTEX_ADAPTIVE_NP
+
+-- Mutex status
+-- These are hardcoded all over the code; there are no enums/macros for them.
+PTHREAD_MUTEX_DESTROYED         -1
+PTHREAD_MUTEX_UNLOCKED           0
+PTHREAD_MUTEX_LOCKED_NO_WAITERS  1
+
+-- For robust mutexes
+PTHREAD_MUTEX_INCONSISTENT
+PTHREAD_MUTEX_NOTRECOVERABLE
+FUTEX_OWNER_DIED
+
+-- For robust and PI mutexes
+FUTEX_WAITERS
+FUTEX_TID_MASK
+
+-- Mutex attributes
+PTHREAD_MUTEX_ROBUST_NORMAL_NP
+PTHREAD_MUTEX_PRIO_INHERIT_NP
+PTHREAD_MUTEX_PRIO_PROTECT_NP
+PTHREAD_MUTEX_PSHARED_BIT
+PTHREAD_MUTEX_PRIO_CEILING_SHIFT
+PTHREAD_MUTEX_PRIO_CEILING_MASK
+
+-- Mutex attribute flags
+PTHREAD_MUTEXATTR_PROTOCOL_SHIFT
+PTHREAD_MUTEXATTR_PROTOCOL_MASK
+PTHREAD_MUTEXATTR_PRIO_CEILING_MASK
+PTHREAD_MUTEXATTR_FLAG_ROBUST
+PTHREAD_MUTEXATTR_FLAG_PSHARED
+PTHREAD_MUTEXATTR_FLAG_BITS
+PTHREAD_MUTEX_NO_ELISION_NP
+
+-- Priority protocols
+PTHREAD_PRIO_NONE
+PTHREAD_PRIO_INHERIT
+PTHREAD_PRIO_PROTECT
+
+-- These values are hardcoded as well:
+-- Value of __mutex for shared condvars.
+PTHREAD_COND_SHARED             ~0l
+
+-- Value of __total_seq for destroyed condvars.
+PTHREAD_COND_DESTROYED          -1ull
+
+-- __nwaiters encodes the number of threads waiting on a condvar
+-- and the clock ID.
+-- __nwaiters >> COND_NWAITERS_SHIFT gives us the number of waiters.
+COND_NWAITERS_SHIFT
+
+-- Condvar clock IDs
+CLOCK_REALTIME
+CLOCK_MONOTONIC
+CLOCK_PROCESS_CPUTIME_ID
+CLOCK_THREAD_CPUTIME_ID
+CLOCK_MONOTONIC_RAW
+CLOCK_REALTIME_COARSE
+CLOCK_MONOTONIC_COARSE
+
+-- Rwlock attributes
+PTHREAD_RWLOCK_PREFER_READER_NP
+PTHREAD_RWLOCK_PREFER_WRITER_NP
+PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
+
+-- 'Shared' attribute values
+PTHREAD_PROCESS_PRIVATE
+PTHREAD_PROCESS_SHARED
diff --git a/pretty-printers/Makefile b/pretty-printers/Makefile
new file mode 100644
index 0000000..ff04108
--- /dev/null
+++ b/pretty-printers/Makefile
@@ -0,0 +1,81 @@ 
+# Makefile for the Python pretty printers.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# This contains rules for building and running the pretty printer tests.
+
+subdir := pretty-printers
+
+include ../Makeconfig
+
+PYTHON := python
+
+tests-pretty-printers := test-mutex-attributes test-mutex-printer \
+			 test-condvar-attributes test-condvar-printer \
+			 test-rwlock-attributes test-rwlock-printer
+
+# Add the test programs to test-srcs so that they'll be compiled regardless
+# of whether we should actually run them.
+test-srcs := $(tests-pretty-printers)
+
+ifeq ($(build-shared),yes)
+nptl-tests-libs := $(shared-thread-library)
+else
+nptl-tests-libs := $(static-thread-library)
+endif
+
+# The test programs need to be compiled without optimizations so they won't
+# confuse gdb.  We could use either the 'GCC optimize' pragma or the 'optimize'
+# function attribute to achieve this; however, at least on ARM, gcc always
+# produces different debugging symbols when invoked with a -O greater than 0
+# than when invoked with -O0, regardless of anything else we're using
+# to suppress optimizations.  Therefore, we need to explicitly pass -O0 to it
+# through CFLAGS.
+# Additionally, the build system will try to -include $(common-objpfx)/config.h
+# when compiling the tests, which will throw an error if some special macros
+# (such as __OPTIMIZE__ and IS_IN_BUILD) aren't defined.  To avoid this, we
+# tell gcc to define IS_IN_build.
+CFLAGS-test-mutex-attributes.c := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-test-mutex-printer.c := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-test-condvar-attributes.c := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-test-condvar-printer.c := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-test-rwlock-attributes.c := -O0 -ggdb3 -DIS_IN_build
+CFLAGS-test-rwlock-printer.c := -O0 -ggdb3 -DIS_IN_build
+
+tests-pretty-printers-dest := $(addprefix $(objpfx),$(tests-pretty-printers))
+tests-pretty-printers-pp := $(addsuffix -pp,$(tests-pretty-printers-dest))
+
+ifeq ($(run-built-tests),yes)
+tests-special += $(tests-pretty-printers-pp)
+endif
+
+include ../Rules
+
+# Add the thread libraries to the prerequisites of the NPTL test programs.
+$(tests-pretty-printers-dest): $(nptl-tests-libs)
+
+# We won't actually create any *-pp files, so we mark this target as PHONY
+# to ensure it always runs when required.
+.PHONY: $(tests-pretty-printers-pp)
+
+# Static pattern rule that matches the test-* targets to their .c and .py
+# prerequisites.  It'll run the corresponding test script for each test program
+# we compiled.  test_common.py must be present for all.
+$(tests-pretty-printers-pp): $(objpfx)%-pp: $(objpfx)% %.py test_common.py
+	$(test-wrapper-env) $(PYTHON) $*.py $*.c $(objpfx)$*; \
+	$(evaluate-test)
diff --git a/pretty-printers/README b/pretty-printers/README
new file mode 100644
index 0000000..0dc26dd
--- /dev/null
+++ b/pretty-printers/README
@@ -0,0 +1,130 @@ 
+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-source/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.
diff --git a/pretty-printers/test-condvar-attributes.c b/pretty-printers/test-condvar-attributes.c
new file mode 100644
index 0000000..4db4098
--- /dev/null
+++ b/pretty-printers/test-condvar-attributes.c
@@ -0,0 +1,94 @@ 
+/* Helper program for testing the pthread_cond_t and pthread_condattr_t
+   pretty printers.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <time.h>
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+
+static int condvar_reinit (pthread_cond_t *condvar,
+			   const pthread_condattr_t *attr);
+static int test_setclock (pthread_cond_t *condvar, pthread_condattr_t *attr);
+static int test_setpshared (pthread_cond_t *condvar, pthread_condattr_t *attr);
+
+/* Need these so we don't have lines longer than 79 chars.  */
+#define SET_SHARED(attr, shared) pthread_condattr_setpshared (attr, shared)
+
+int
+main (void)
+{
+  pthread_cond_t condvar;
+  pthread_condattr_t attr;
+  int result = FAIL;
+
+  if (pthread_condattr_init (&attr) == 0
+      && pthread_cond_init (&condvar, NULL) == 0
+      && test_setclock (&condvar, &attr) == PASS
+      && test_setpshared (&condvar, &attr) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_cond* functions failed.  */
+
+  return result;
+}
+
+/* Destroys CONDVAR and re-initializes it using ATTR.  */
+static int
+condvar_reinit (pthread_cond_t *condvar, const pthread_condattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_cond_destroy (condvar) == 0
+      && pthread_cond_init (condvar, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting the clock ID attribute.  */
+static int
+test_setclock (pthread_cond_t *condvar, pthread_condattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_condattr_setclock (attr, CLOCK_REALTIME) == 0 /* Set clock.  */
+      && condvar_reinit (condvar, attr) == PASS)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting whether the condvar can be shared between processes.  */
+static int
+test_setpshared (pthread_cond_t *condvar, pthread_condattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_SHARED (attr, PTHREAD_PROCESS_SHARED) == 0 /* Set shared.  */
+      && condvar_reinit (condvar, attr) == PASS
+      && SET_SHARED (attr, PTHREAD_PROCESS_PRIVATE) == 0
+      && condvar_reinit (condvar, attr) == PASS)
+    result = PASS;
+
+  return result;
+}
diff --git a/pretty-printers/test-condvar-attributes.py b/pretty-printers/test-condvar-attributes.py
new file mode 100644
index 0000000..7a16b6f
--- /dev/null
+++ b/pretty-printers/test-condvar-attributes.py
@@ -0,0 +1,59 @@ 
+# Common tests for the ConditionVariablePrinter and
+# ConditionVariableAttributesPrinter classes.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    condvar_var = 'condvar'
+    condvar_to_string = 'pthread_cond_t'
+
+    attr_var = 'attr'
+    attr_to_string = 'pthread_condattr_t'
+
+    break_at(test_source, 'Set clock')
+    continue_cmd() # Go to test_setclock
+    next_cmd(2)
+    test_printer(condvar_var, condvar_to_string, {'Clock ID': 'CLOCK_REALTIME'})
+    test_printer(attr_var, attr_to_string, {'Clock ID': 'CLOCK_REALTIME'})
+
+    break_at(test_source, 'Set shared')
+    continue_cmd() # Go to test_setpshared
+    next_cmd(2)
+    test_printer(condvar_var, condvar_to_string, {'Shared': 'Yes'})
+    test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
+    next_cmd(2)
+    test_printer(condvar_var, condvar_to_string, {'Shared': 'No'})
+    test_printer(attr_var, attr_to_string, {'Shared': 'No'})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test-condvar-printer.c b/pretty-printers/test-condvar-printer.c
new file mode 100644
index 0000000..0f2a5f4
--- /dev/null
+++ b/pretty-printers/test-condvar-printer.c
@@ -0,0 +1,57 @@ 
+/* Helper program for testing the pthread_cond_t pretty printer.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <time.h>
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+
+static int test_status_destroyed (pthread_cond_t *condvar);
+
+int
+main (void)
+{
+  pthread_cond_t condvar;
+  pthread_condattr_t attr;
+  int result = FAIL;
+
+  if (pthread_condattr_init (&attr) == 0
+      && test_status_destroyed (&condvar) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_cond* functions failed.  */
+
+  return result;
+}
+
+/* Initializes CONDVAR, then destroys it.  */
+static int
+test_status_destroyed (pthread_cond_t *condvar)
+{
+  int result = FAIL;
+
+  if (pthread_cond_init (condvar, NULL) == 0
+      && pthread_cond_destroy (condvar) == 0)
+    result = PASS; /* Test status (destroyed).  */
+
+  return result;
+}
diff --git a/pretty-printers/test-condvar-printer.py b/pretty-printers/test-condvar-printer.py
new file mode 100644
index 0000000..ad4fc61
--- /dev/null
+++ b/pretty-printers/test-condvar-printer.py
@@ -0,0 +1,44 @@ 
+# Common tests for the ConditionVariablePrinter class.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    var = 'condvar'
+    to_string = 'pthread_cond_t'
+
+    break_at(test_source, 'Test status (destroyed)')
+    continue_cmd() # Go to test_status_destroyed
+    test_printer(var, to_string, {'Status': 'Destroyed'})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test-mutex-attributes.c b/pretty-printers/test-mutex-attributes.c
new file mode 100644
index 0000000..9ecfff7
--- /dev/null
+++ b/pretty-printers/test-mutex-attributes.c
@@ -0,0 +1,144 @@ 
+/* Helper program for testing the pthread_mutex_t and pthread_mutexattr_t
+   pretty printers.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+#define PRIOCEILING 42
+
+/* Need these so we don't have lines longer than 79 chars.  */
+#define SET_TYPE(attr, type) pthread_mutexattr_settype (attr, type)
+#define SET_ROBUST(attr, robust) pthread_mutexattr_setrobust (attr, robust)
+#define SET_SHARED(attr, shared) pthread_mutexattr_setpshared (attr, shared)
+#define SET_PROTOCOL(attr, protocol) \
+	pthread_mutexattr_setprotocol (attr, protocol)
+#define SET_PRIOCEILING(mutex, prioceiling, old_ceiling) \
+	pthread_mutex_setprioceiling (mutex, prioceiling, old_ceiling)
+
+static int mutex_reinit (pthread_mutex_t *mutex,
+			 const pthread_mutexattr_t *attr);
+static int test_settype (pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
+static int test_setrobust (pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
+static int test_setpshared (pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
+static int test_setprotocol (pthread_mutex_t *mutex,
+			     pthread_mutexattr_t *attr);
+
+int
+main (void)
+{
+  pthread_mutex_t mutex;
+  pthread_mutexattr_t attr;
+  int result = FAIL;
+
+  if (pthread_mutexattr_init (&attr) == 0
+      && pthread_mutex_init (&mutex, NULL) == 0
+      && test_settype (&mutex, &attr) == PASS
+      && test_setrobust (&mutex, &attr) == PASS
+      && test_setpshared (&mutex, &attr) == PASS
+      && test_setprotocol (&mutex, &attr) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_mutex* functions failed.  */
+
+  return result;
+}
+
+/* Destroys MUTEX and re-initializes it using ATTR.  */
+static int
+mutex_reinit (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_mutex_destroy (mutex) == 0
+      && pthread_mutex_init (mutex, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting the mutex type.  */
+static int
+test_settype (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_TYPE (attr, PTHREAD_MUTEX_ERRORCHECK) == 0 /* Set type.  */
+      && mutex_reinit (mutex, attr) == 0
+      && SET_TYPE (attr, PTHREAD_MUTEX_RECURSIVE) == 0
+      && mutex_reinit (mutex, attr) == 0
+      && SET_TYPE (attr, PTHREAD_MUTEX_NORMAL) == 0
+      && mutex_reinit (mutex, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting whether the mutex is robust.  */
+static int
+test_setrobust (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_ROBUST (attr, PTHREAD_MUTEX_ROBUST) == 0 /* Set robust.  */
+      && mutex_reinit (mutex, attr) == 0
+      && SET_ROBUST (attr, PTHREAD_MUTEX_STALLED) == 0
+      && mutex_reinit (mutex, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting whether the mutex can be shared between processes.  */
+static int
+test_setpshared (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_SHARED (attr, PTHREAD_PROCESS_SHARED) == 0 /* Set shared.  */
+      && mutex_reinit (mutex, attr) == 0
+      && SET_SHARED (attr, PTHREAD_PROCESS_PRIVATE) == 0
+      && mutex_reinit (mutex, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting the mutex protocol and, for Priority Protect, the Priority
+   Ceiling.  */
+static int
+test_setprotocol (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+  int old_prioceiling;
+
+  if (SET_PROTOCOL (attr, PTHREAD_PRIO_INHERIT) == 0 /* Set protocol.  */
+      && mutex_reinit (mutex, attr) == 0
+      && SET_PROTOCOL (attr, PTHREAD_PRIO_PROTECT) == 0
+      && mutex_reinit (mutex, attr) == 0
+      && SET_PRIOCEILING(mutex, PRIOCEILING, &old_prioceiling) == 0
+      && SET_PROTOCOL (attr, PTHREAD_PRIO_NONE) == 0
+      && mutex_reinit (mutex, attr) == 0)
+    result = PASS;
+
+  return result;
+}
diff --git a/pretty-printers/test-mutex-attributes.py b/pretty-printers/test-mutex-attributes.py
new file mode 100644
index 0000000..9d84841
--- /dev/null
+++ b/pretty-printers/test-mutex-attributes.py
@@ -0,0 +1,89 @@ 
+# Common tests for the MutexPrinter and MutexAttributesPrinter classes.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+PRIOCEILING = 42
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    mutex_var = 'mutex'
+    mutex_to_string = 'pthread_mutex_t'
+
+    attr_var = 'attr'
+    attr_to_string = 'pthread_mutexattr_t'
+
+    break_at(test_source, 'Set type')
+    continue_cmd() # Go to test_settype
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Type': 'Error check'})
+    test_printer(mutex_var, mutex_to_string, {'Type': 'Error check'})
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Type': 'Recursive'})
+    test_printer(mutex_var, mutex_to_string, {'Type': 'Recursive'})
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Type': 'Normal'})
+    test_printer(mutex_var, mutex_to_string, {'Type': 'Normal'})
+
+    break_at(test_source, 'Set robust')
+    continue_cmd() # Go to test_setrobust
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Robust': 'Yes'})
+    test_printer(mutex_var, mutex_to_string, {'Robust': 'Yes'})
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Robust': 'No'})
+    test_printer(mutex_var, mutex_to_string, {'Robust': 'No'})
+
+    break_at(test_source, 'Set shared')
+    continue_cmd() # Go to test_setpshared
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
+    test_printer(mutex_var, mutex_to_string, {'Shared': 'Yes'})
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Shared': 'No'})
+    test_printer(mutex_var, mutex_to_string, {'Shared': 'No'})
+
+    break_at(test_source, 'Set protocol')
+    continue_cmd() # Go to test_setprotocol
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Protocol': 'Priority inherit'})
+    test_printer(mutex_var, mutex_to_string, {'Protocol': 'Priority inherit'})
+    next_cmd(2)
+    test_printer(attr_var, attr_to_string, {'Protocol': 'Priority protect'})
+    test_printer(mutex_var, mutex_to_string, {'Protocol': 'Priority protect'})
+    next_cmd(2)
+    test_printer(mutex_var, mutex_to_string, {'Priority ceiling':
+                                              str(PRIOCEILING)})
+    next_cmd()
+    test_printer(attr_var, attr_to_string, {'Protocol': 'None'})
+    test_printer(mutex_var, mutex_to_string, {'Protocol': 'None'})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test-mutex-printer.c b/pretty-printers/test-mutex-printer.c
new file mode 100644
index 0000000..d072177
--- /dev/null
+++ b/pretty-printers/test-mutex-printer.c
@@ -0,0 +1,151 @@ 
+/* Helper program for testing the pthread_mutex_t pretty printer.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+
+static int test_status_destroyed (pthread_mutex_t *mutex);
+static int test_status_no_robust (pthread_mutex_t *mutex,
+				  pthread_mutexattr_t *attr);
+static int test_status_robust (pthread_mutex_t *mutex,
+			       pthread_mutexattr_t *attr);
+static int test_locking_state_robust (pthread_mutex_t *mutex);
+static void *thread_function (void *arg);
+static int test_recursive_locks (pthread_mutex_t *mutex,
+				 pthread_mutexattr_t *attr);
+
+int
+main (void)
+{
+  pthread_mutex_t mutex;
+  pthread_mutexattr_t attr;
+  int result = FAIL;
+
+  if (pthread_mutexattr_init (&attr) == 0
+      && test_status_destroyed (&mutex) == PASS
+      && test_status_no_robust (&mutex, &attr) == PASS
+      && test_status_robust (&mutex, &attr) == PASS
+      && test_recursive_locks (&mutex, &attr) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_mutex* functions failed.  */
+
+  return result;
+}
+
+/* Initializes MUTEX, then destroys it.  */
+static int
+test_status_destroyed (pthread_mutex_t *mutex)
+{
+  int result = FAIL;
+
+  if (pthread_mutex_init (mutex, NULL) == 0
+      && pthread_mutex_destroy (mutex) == 0)
+    result = PASS; /* Test status (destroyed).  */
+
+  return result;
+}
+
+/* Tests locking of non-robust mutexes.  */
+static int
+test_status_no_robust (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_mutexattr_setrobust (attr, PTHREAD_MUTEX_STALLED) == 0
+      && pthread_mutex_init (mutex, attr) == 0
+      && pthread_mutex_lock (mutex) == 0 /* Test status (non-robust).  */
+      && pthread_mutex_unlock (mutex) == 0
+      && pthread_mutex_destroy (mutex) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests locking of robust mutexes.  */
+static int
+test_status_robust (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_mutexattr_setrobust (attr, PTHREAD_MUTEX_ROBUST) == 0
+      && pthread_mutex_init (mutex, attr) == 0
+      && test_locking_state_robust (mutex) == PASS /* Test status (robust).  */
+      && pthread_mutex_destroy (mutex) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests locking and state corruption of robust mutexes.  We'll mark it as
+   inconsistent, then not recoverable.  */
+static int
+test_locking_state_robust (pthread_mutex_t *mutex)
+{
+  int result = FAIL;
+  pthread_t thread; /* Test locking and state (robust).  */
+
+  if (pthread_create (&thread, NULL, thread_function, mutex) == 0
+      && pthread_join (thread, NULL) == 0
+      && pthread_mutex_lock (mutex) == EOWNERDEAD
+      && pthread_mutex_unlock (mutex) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Function to be called by the child thread when testing robust mutexes.  */
+static void *
+thread_function (void *arg)
+{
+  pthread_mutex_t *mutex = (pthread_mutex_t *)arg;
+
+  if (pthread_mutex_lock (mutex) != 0)
+    exit (FAIL);
+
+  /* Thread terminates without unlocking the mutex, thus marking it as
+     inconsistent.  */
+  return NULL;
+}
+
+/* Tests locking the mutex multiple times in a row.  */
+static int
+test_recursive_locks (pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_mutexattr_settype (attr, PTHREAD_MUTEX_RECURSIVE) == 0
+      && pthread_mutex_init (mutex, attr) == 0
+      && pthread_mutex_lock (mutex) == 0
+      && pthread_mutex_lock (mutex) == 0
+      && pthread_mutex_lock (mutex) == 0 /* Test recursive locks.  */
+      && pthread_mutex_unlock (mutex) == 0
+      && pthread_mutex_unlock (mutex) == 0
+      && pthread_mutex_unlock (mutex) == 0
+      && pthread_mutex_destroy (mutex) == 0)
+    result = PASS;
+
+  return result;
+}
diff --git a/pretty-printers/test-mutex-printer.py b/pretty-printers/test-mutex-printer.py
new file mode 100644
index 0000000..5af8135
--- /dev/null
+++ b/pretty-printers/test-mutex-printer.py
@@ -0,0 +1,87 @@ 
+# Tests for the MutexPrinter class.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    var = 'mutex'
+    to_string = 'pthread_mutex_t'
+
+    break_at(test_source, 'Test status (destroyed)')
+    continue_cmd() # Go to test_status_destroyed
+    test_printer(var, to_string, {'Status': 'Destroyed'})
+
+    break_at(test_source, 'Test status (non-robust)')
+    continue_cmd() # Go to test_status_no_robust
+    test_printer(var, to_string, {'Status': 'Unlocked'})
+    next_cmd()
+    thread_id = get_current_thread_lwpid()
+    test_printer(var, to_string, {'Status': 'Locked, possibly with no waiters',
+                                  'Owner ID': thread_id})
+
+    break_at(test_source, 'Test status (robust)')
+    continue_cmd() # Go to test_status_robust
+    test_printer(var, to_string, {'Status': 'Unlocked'})
+    break_at(test_source, 'Test locking and state (robust)')
+    continue_cmd() # Go to test_locking_state_robust
+
+    # We'll create a new thread now.  Notice the previous breakpoint will make
+    # us stop *after* the line at which we're inserting it.
+    set_scheduler_locking(True)
+    next_cmd()
+    parent = 1
+    child = 2
+    select_thread(child)
+    child_id = get_current_thread_lwpid()
+    select_thread(parent)
+    # Make the new thread finish its function while we wait.
+    continue_cmd(thread=child)
+    # The new thread should be dead by now, and the mutex state is inconsistent.
+    next_cmd()
+    test_printer(var, to_string, {'Owner ID': r'{0} \(dead\)'.format(child_id)})
+    # Try to lock and unlock the mutex.
+    next_cmd()
+    test_printer(var, to_string, {'Owner ID': thread_id,
+                            'State protected by this mutex': 'Inconsistent'})
+    next_cmd()
+    test_printer(var, to_string, {'Status': 'Unlocked',
+                            'State protected by this mutex': 'Not recoverable'})
+    set_scheduler_locking(False)
+
+    break_at(test_source, 'Test recursive locks')
+    continue_cmd() # Go to test_recursive_locks
+    test_printer(var, to_string, {'Times locked recursively': '2'})
+    next_cmd()
+    test_printer(var, to_string, {'Times locked recursively': '3'})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test-rwlock-attributes.c b/pretty-printers/test-rwlock-attributes.c
new file mode 100644
index 0000000..d12facf
--- /dev/null
+++ b/pretty-printers/test-rwlock-attributes.c
@@ -0,0 +1,98 @@ 
+/* Helper program for testing the pthread_rwlock_t and pthread_rwlockattr_t
+   pretty printers.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+
+/* Need these so we don't have lines longer than 79 chars.  */
+#define SET_KIND(attr, kind) pthread_rwlockattr_setkind_np (attr, kind)
+#define SET_SHARED(attr, shared) pthread_rwlockattr_setpshared (attr, shared)
+
+static int rwlock_reinit (pthread_rwlock_t *rwlock,
+			  const pthread_rwlockattr_t *attr);
+static int test_setkind_np (pthread_rwlock_t *rwlock,
+			    pthread_rwlockattr_t *attr);
+static int test_setpshared (pthread_rwlock_t *rwlock,
+			    pthread_rwlockattr_t *attr);
+
+int
+main (void)
+{
+  pthread_rwlock_t rwlock;
+  pthread_rwlockattr_t attr;
+  int result = FAIL;
+
+  if (pthread_rwlockattr_init (&attr) == 0
+      && pthread_rwlock_init (&rwlock, NULL) == 0
+      && test_setkind_np (&rwlock, &attr) == PASS
+      && test_setpshared (&rwlock, &attr) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_rwlock* functions failed.  */
+
+  return result;
+}
+
+/* Destroys RWLOCK and re-initializes it using ATTR.  */
+static int
+rwlock_reinit (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
+{
+  int result = FAIL;
+
+  if (pthread_rwlock_destroy (rwlock) == 0
+      && pthread_rwlock_init (rwlock, attr) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting whether the rwlock prefers readers or writers.  */
+static int
+test_setkind_np (pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_KIND (attr, PTHREAD_RWLOCK_PREFER_READER_NP) == 0 /* Set kind.  */
+      && rwlock_reinit (rwlock, attr) == PASS
+      && SET_KIND (attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0
+      && rwlock_reinit (rwlock, attr) == PASS)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests setting whether the rwlock can be shared between processes.  */
+static int
+test_setpshared (pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr)
+{
+  int result = FAIL;
+
+  if (SET_SHARED (attr, PTHREAD_PROCESS_SHARED) == 0 /* Set shared.  */
+      && rwlock_reinit (rwlock, attr) == PASS
+      && SET_SHARED (attr, PTHREAD_PROCESS_PRIVATE) == 0
+      && rwlock_reinit (rwlock, attr) == PASS)
+    result = PASS;
+
+  return result;
+}
diff --git a/pretty-printers/test-rwlock-attributes.py b/pretty-printers/test-rwlock-attributes.py
new file mode 100644
index 0000000..72b3c28
--- /dev/null
+++ b/pretty-printers/test-rwlock-attributes.py
@@ -0,0 +1,61 @@ 
+# Common tests for the RWLockPrinter and RWLockAttributesPrinter classes.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    rwlock_var = 'rwlock'
+    rwlock_to_string = 'pthread_rwlock_t'
+
+    attr_var = 'attr'
+    attr_to_string = 'pthread_rwlockattr_t'
+
+    break_at(test_source, 'Set kind')
+    continue_cmd() # Go to test_setkind_np
+    next_cmd(2)
+    test_printer(rwlock_var, rwlock_to_string, {'Prefers': 'Readers'})
+    test_printer(attr_var, attr_to_string, {'Prefers': 'Readers'})
+    next_cmd(2)
+    test_printer(rwlock_var, rwlock_to_string, {'Prefers': 'Writers'})
+    test_printer(attr_var, attr_to_string, {'Prefers': 'Writers'})
+
+    break_at(test_source, 'Set shared')
+    continue_cmd() # Go to test_setpshared
+    next_cmd(2)
+    test_printer(rwlock_var, rwlock_to_string, {'Shared': 'Yes'})
+    test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
+    next_cmd(2)
+    test_printer(rwlock_var, rwlock_to_string, {'Shared': 'No'})
+    test_printer(attr_var, attr_to_string, {'Shared': 'No'})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test-rwlock-printer.c b/pretty-printers/test-rwlock-printer.c
new file mode 100644
index 0000000..dbbe9b8
--- /dev/null
+++ b/pretty-printers/test-rwlock-printer.c
@@ -0,0 +1,78 @@ 
+/* Helper program for testing the pthread_rwlock_t pretty printer.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Keep the calls to the pthread_* functions on separate lines to make it easy
+   to advance through the program using the gdb 'next' command.  */
+
+#include <pthread.h>
+
+#define PASS 0
+#define FAIL 1
+
+static int test_locking_reader (pthread_rwlock_t *rwlock);
+static int test_locking_writer (pthread_rwlock_t *rwlock);
+
+int
+main (void)
+{
+  pthread_rwlock_t rwlock;
+
+  int result = FAIL;
+
+  if (test_locking_reader (&rwlock) == PASS
+      && test_locking_writer (&rwlock) == PASS)
+    result = PASS;
+  /* Else, one of the pthread_rwlock* functions failed.  */
+
+  return result;
+}
+
+/* Tests locking the rwlock multiple times as a reader.  */
+static int
+test_locking_reader (pthread_rwlock_t *rwlock)
+{
+  int result = FAIL;
+
+  if (pthread_rwlock_init (rwlock, NULL) == 0
+      && pthread_rwlock_rdlock (rwlock) == 0 /* Test locking (reader).  */
+      && pthread_rwlock_rdlock (rwlock) == 0
+      && pthread_rwlock_rdlock (rwlock) == 0
+      && pthread_rwlock_unlock (rwlock) == 0
+      && pthread_rwlock_unlock (rwlock) == 0
+      && pthread_rwlock_unlock (rwlock) == 0
+      && pthread_rwlock_destroy (rwlock) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Tests locking the rwlock as a writer.  */
+static int
+test_locking_writer (pthread_rwlock_t *rwlock)
+{
+  int result = FAIL;
+
+  if (pthread_rwlock_init (rwlock, NULL) == 0
+      && pthread_rwlock_wrlock (rwlock) == 0 /* Test locking (writer).  */
+      && pthread_rwlock_unlock (rwlock) == 0
+      && pthread_rwlock_destroy (rwlock) == 0)
+    result = PASS;
+
+  return result;
+}
diff --git a/pretty-printers/test-rwlock-printer.py b/pretty-printers/test-rwlock-printer.py
new file mode 100644
index 0000000..a7c2f53
--- /dev/null
+++ b/pretty-printers/test-rwlock-printer.py
@@ -0,0 +1,58 @@ 
+# Common tests for the RWLockPrinter class.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+result = PASS
+
+try:
+    init_test(test_bin)
+    go_to_main()
+
+    var = 'rwlock'
+    to_string = 'pthread_rwlock_t'
+
+    break_at(test_source, 'Test locking (reader)')
+    continue_cmd() # Go to test_locking_reader
+    test_printer(var, to_string, {'Status': 'Unlocked'})
+    next_cmd()
+    test_printer(var, to_string, {'Status': r'Locked \(Read\)', 'Readers': '1'})
+    next_cmd()
+    test_printer(var, to_string, {'Readers': '2'})
+    next_cmd()
+    test_printer(var, to_string, {'Readers': '3'})
+
+    break_at(test_source, 'Test locking (writer)')
+    continue_cmd() # Go to test_locking_writer
+    test_printer(var, to_string, {'Status': 'Unlocked'})
+    next_cmd()
+    thread_id = get_current_thread_lwpid()
+    test_printer(var, to_string, {'Status': r'Locked \(Write\)',
+                                  'Writer ID': thread_id})
+
+    continue_cmd() # Exit
+
+except pexpect.TIMEOUT:
+    result = FAIL
+
+exit(result)
diff --git a/pretty-printers/test_common.py b/pretty-printers/test_common.py
new file mode 100644
index 0000000..3d75766
--- /dev/null
+++ b/pretty-printers/test_common.py
@@ -0,0 +1,301 @@ 
+# Common functions and variables for testing the Python pretty printers.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""These tests require PExpect.
+
+Attributes:
+    PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
+    GDB (string): A string with the name of the gdb binary.
+    gdb (pexpect.spawn): The gdb process, as handled by PExpect.
+    gdb_prompt (raw string): A pattern for matching the gdb prompt.
+"""
+
+import os
+import re
+
+PASS = 0
+FAIL = 1
+UNSUPPORTED = 77
+GDB = 'gdb'
+
+try:
+    import pexpect
+except ImportError:
+    print('PExpect must be installed in order to test the pretty printers.')
+    exit(UNSUPPORTED)
+
+if not pexpect.which(GDB):
+    print('gdb must be installed in order to test the pretty printers.')
+    exit(UNSUPPORTED)
+
+class NoLineError(Exception):
+    """Custom exception which indicates that a test file doesn't contain
+    the requested string.
+    """
+
+    def __init__(self, string):
+        """Constructor.
+
+        Args:
+            string (string): The string that was requested.
+        """
+
+        super(NoLineError, self).__init__()
+        self.string = string
+
+    def __str__(self):
+        """Shows a readable representation of the exception."""
+
+        return 'No line containing the following text: {0}'.format(self.string)
+
+timeout = 1
+TIMEOUTFACTOR = os.environ.get('TIMEOUTFACTOR')
+
+if TIMEOUTFACTOR:
+    timeout = int(TIMEOUTFACTOR)
+
+gdb = pexpect.spawn(GDB, echo=False, timeout=timeout)
+
+# Set the gdb prompt to a custom one, so that user-defined prompts won't
+# interfere.  We assume the user won't have his prompt set to this.
+gdb_prompt = r'gdb-test% '
+gdb.sendline('set prompt {0}'.format(gdb_prompt))
+gdb.expect(gdb_prompt)
+
+def test(command, pattern):
+    """Sends 'command' to gdb and expects the given 'pattern'.
+
+    If 'pattern' is None, simply consumes everything up to and including
+    the gdb prompt.
+
+    Args:
+        command (string): The command we'll send to gdb.
+        pattern (raw string): A pattern the gdb output should match.
+
+    Returns:
+        string: The string that matched 'pattern', or an empty string if
+            'pattern' was None.
+    """
+
+    match = ''
+
+    gdb.sendline(command)
+
+    if pattern:
+        # PExpect does a non-greedy match for '+' and '*'.  Since it can't look
+        # ahead on the gdb output stream, if 'pattern' ends with a '+' or a '*'
+        # we may end up matching only part of the required output.
+        # To avoid this, we'll consume 'pattern' and anything that follows it
+        # up to and including the gdb prompt, then extract 'pattern' later.
+        gdb.expect(r'{0}.+{1}'.format(pattern, gdb_prompt))
+
+        # gdb.after now contains the whole match.  Extract the text that matches
+        # 'pattern'.
+        match = re.match(pattern, gdb.after, re.DOTALL).group()
+    else:
+        # Consume just the the gdb prompt.
+        gdb.expect(gdb_prompt)
+
+    return match
+
+def init_test(test_bin):
+    """Loads the test binary file to gdb.
+
+    Args:
+        test_bin (string): The name of the test binary file.
+    """
+
+    test('file {0}'.format(test_bin), None)
+
+def go_to_main():
+    """Executes a gdb 'start' command, which takes us to main."""
+
+    test('start', r'main')
+
+def get_line_number(file_name, string):
+    """Returns the number of the line in which 'string' appears within a file.
+
+    Args:
+        file_name (string): The name of the file we'll search through.
+        string (string): The string we'll look for.
+
+    Returns:
+        int: The number of the line in which 'string' appears, starting from 1.
+    """
+    number = -1
+
+    with open(file_name) as src_file:
+        for i, line in enumerate(src_file):
+            if string in line:
+                number = i + 1
+                break
+
+    if number == -1:
+        raise NoLineError(string)
+
+    return number
+
+def break_at(file_name, string, temporary=True, thread=None):
+    """Places a breakpoint on the first line in 'file_name' containing 'string'.
+
+    'string' is usually a comment like "Stop here".  Notice this may fail unless
+    the comment is placed inline next to actual code, e.g.:
+
+        ...
+        /* Stop here */
+        ...
+
+    may fail, while:
+
+        ...
+        some_func(); /* Stop here */
+        ...
+
+    will succeed.
+
+    If 'thread' isn't None, the breakpoint will be set for all the threads.
+    Otherwise, it'll be set only for 'thread'.
+
+    Args:
+        file_name (string): The name of the file we'll place the breakpoint in.
+        string (string): A string we'll look for inside the file.
+            We'll place a breakpoint on the line which contains it.
+        temporary (bool): Whether the breakpoint should be automatically deleted
+            after we reach it.
+        thread (int): The number of the thread we'll place the breakpoint for,
+            as seen by gdb.  If specified, it should be greater than zero.
+    """
+
+    if not thread:
+        thread_str = ''
+    else:
+        thread_str = 'thread {0}'.format(thread)
+
+    if temporary:
+        command = 'tbreak'
+        break_type = 'Temporary breakpoint'
+    else:
+        command = 'break'
+        break_type = 'Breakpoint'
+
+    line_number = str(get_line_number(file_name, string))
+
+    test('{0} {1}:{2} {3}'.format(command, file_name, line_number, thread_str),
+         r'{0} [0-9]+ at 0x[a-f0-9]+: file {1}, line {2}\.'.format(break_type,
+                                                                   file_name,
+                                                                   line_number))
+
+def continue_cmd(thread=None):
+    """Executes a gdb 'continue' command.
+
+    If 'thread' isn't None, the command will be applied to all the threads.
+    Otherwise, it'll be applied only to 'thread'.
+
+    Args:
+        thread (int): The number of the thread we'll apply the command to,
+            as seen by gdb.  If specified, it should be greater than zero.
+    """
+
+    if not thread:
+        command = 'continue'
+    else:
+        command = 'thread apply {0} continue'.format(thread)
+
+    test(command, None)
+
+def next_cmd(count=1, thread=None):
+    """Executes a gdb 'next' command.
+
+    If 'thread' isn't None, the command will be applied to all the threads.
+    Otherwise, it'll be applied only to 'thread'.
+
+    Args:
+        count (int): The 'count' argument of the 'next' command.
+        thread (int): The number of the thread we'll apply the command to,
+            as seen by gdb.  If specified, it should be greater than zero.
+    """
+
+    if not thread:
+        command = 'next'
+    else:
+        command = 'thread apply {0} next'
+
+    test('{0} {1}'.format(command, count), None)
+
+def select_thread(thread):
+    """Selects the thread indicated by 'thread'.
+
+    Args:
+        thread (int): The number of the thread we'll switch to, as seen by gdb.
+            This should be greater than zero.
+    """
+
+    if thread > 0:
+        test('thread {0}'.format(thread), None)
+
+def get_current_thread_lwpid():
+    """Gets the current thread's Lightweight Process ID.
+
+    Returns:
+        string: The current thread's LWP ID.
+    """
+
+    # It's easier to get the LWP ID through the Python API than the gdb CLI.
+    command = 'python print(gdb.selected_thread().ptid[1])'
+
+    return test(command, r'[0-9]+')
+
+def set_scheduler_locking(mode):
+    """Executes the gdb 'set scheduler-locking' command.
+
+    Args:
+        mode (bool): Whether the scheduler locking mode should be 'on'.
+    """
+    modes = {
+        True: 'on',
+        False: 'off'
+    }
+
+    test('set scheduler-locking {0}'.format(modes[mode]), None)
+
+def test_printer(var, to_string, children=None, is_ptr=True):
+    """ Tests the output of a pretty printer.
+
+    For a variable called 'var', this tests whether its associated printer
+    outputs the expected 'to_string' and children (if any).
+
+    Args:
+        var (string): The name of the variable we'll print.
+        to_string (raw string): The expected output of the printer's 'to_string'
+            method.
+        children (map {raw string->raw string}): A map with the expected output
+            of the printer's children' method.
+        is_ptr (bool): Whether 'var' is a pointer, and thus should be
+            dereferenced.
+    """
+
+    if is_ptr:
+        var = '*{0}'.format(var)
+
+    test('print {0}'.format(var), to_string)
+
+    if children:
+        for name, value in children.items():
+            # Children are shown as 'name = value'.
+            test('print {0}'.format(var), r'{0} = {1}'.format(name, value))
diff --git a/scripts/gen-py-const.awk b/scripts/gen-py-const.awk
new file mode 100644
index 0000000..4586f59
--- /dev/null
+++ b/scripts/gen-py-const.awk
@@ -0,0 +1,118 @@ 
+# Script to generate constants for Python pretty printers.
+#
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# This script is a smaller version of the clever gen-asm-const.awk hack used to
+# generate ASM constants from .sym files.  We'll use this to generate constants
+# for Python pretty printers.
+#
+# The input to this script are .pysym files that look like:
+# #C_Preprocessor_Directive...
+# NAME1
+# NAME2 expression...
+#
+# A line giving just a name implies an expression consisting of just that name.
+# Comments start with '--'.
+#
+# The output of this script is a 'dummy' function containing 'asm' declarations
+# for each non-preprocessor line in the .pysym file.  The expression values
+# will appear as input operands to the 'asm' declaration.  For example, if we
+# have:
+#
+# /* header.h */
+# #define MACRO 42
+#
+# struct S {
+#     char c1;
+#     char c2;
+#     char c3;
+# };
+#
+# enum E {
+#     ZERO,
+#     ONE
+# };
+#
+# /* symbols.pysym */
+# #include <stddef.h>
+# #include "header.h"
+# -- This is a comment
+# MACRO
+# C3_OFFSET offsetof(struct S, c3)
+# E_ONE ONE
+#
+# the output will be:
+#
+# #include <stddef.h>
+# #include "header.h"
+# void dummy(void)
+# {
+#   asm ("@name@MACRO@value@%0@" : : "i" (MACRO));
+#   asm ("@name@C3_OFFSET@value@%0@" : : "i" (offsetof(struct S, c3)));
+#   asm ("@name@E_ONE@value@%0@" : : "i" (ONE));
+# }
+#
+# We'll later feed this output to gcc -S.  Since '-S' tells gcc to compile but
+# not assemble, gcc will output something like:
+#
+# dummy:
+# 	...
+# 	@name@MACRO@value@$42@
+# 	@name@C3_OFFSET@value@$2@
+# 	@name@E_ONE@value@$1@
+#
+# Finally, we can process that output to extract the constant values.
+# Notice gcc may prepend a special character such as '$' to each value.
+
+# found_symbol indicates whether we found a non-comment, non-preprocessor line.
+BEGIN { found_symbol = 0 }
+
+# C preprocessor directives go straight through.
+/^#/ { print; next; }
+
+# Skip comments.
+/--/ { next; }
+
+# Trim leading whitespace.
+{ sub(/^[[:blank:]]*/, ""); }
+
+# If we found a non-comment, non-preprocessor line, print the 'dummy' function
+# header.
+NF > 0 && !found_symbol {
+    print "void dummy(void)\n{";
+    found_symbol = 1;
+}
+
+# If the line contains just a name, duplicate it so we can use that name
+# as the value of the expression.
+NF == 1 { sub(/^.*$/, "& &"); }
+
+# If a line contains a name and an expression...
+NF > 1 {
+    name = $1;
+
+    # Remove any characters before the second field.
+    sub(/^[^[:blank:]]+[[:blank:]]+/, "");
+
+    # '$0' ends up being everything that appeared after the first field
+    # separator.
+    printf "  asm (\"@name@%s@value@%0@\" : : \"i\" (%s));\n", name, $0;
+}
+
+# Close the 'dummy' function.
+END { if (found_symbol) print "}"; }