about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog45
-rw-r--r--INSTALL27
-rw-r--r--Makeconfig76
-rw-r--r--Makerules46
-rw-r--r--NEWS5
-rw-r--r--README.pretty-printers169
-rw-r--r--Rules44
-rw-r--r--manual/install.texi30
-rw-r--r--nptl/Makefile18
-rw-r--r--nptl/nptl-printers.py633
-rw-r--r--nptl/nptl_lock_constants.pysym75
-rw-r--r--nptl/test-cond-printers.c57
-rw-r--r--nptl/test-cond-printers.py50
-rw-r--r--nptl/test-condattr-printers.c94
-rw-r--r--nptl/test-condattr-printers.py71
-rw-r--r--nptl/test-mutex-printers.c151
-rw-r--r--nptl/test-mutex-printers.py97
-rw-r--r--nptl/test-mutexattr-printers.c144
-rw-r--r--nptl/test-mutexattr-printers.py101
-rw-r--r--nptl/test-rwlock-printers.c78
-rw-r--r--nptl/test-rwlock-printers.py64
-rw-r--r--nptl/test-rwlockattr-printers.c98
-rw-r--r--nptl/test-rwlockattr-printers.py73
-rw-r--r--scripts/gen-py-const.awk118
-rw-r--r--scripts/test_printers_common.py364
-rw-r--r--scripts/test_printers_exceptions.py61
26 files changed, 2769 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 57ce74d446..d5ef99ce8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+2016-12-08  Martin Galvan  <martin.galvan@tallertechnologies.com>
+
+	* INSTALL: Regenerated.
+	* Makeconfig: Add comments and whitespace to make the control flow
+	clearer.
+	(+link-printers-tests, +link-pie-printers-tests,
+	CFLAGS-printers-tests, installed-rtld-LDFLAGS,
+	built-rtld-LDFLAGS, link-libc-rpath,
+	link-libc-tests-after-rpath-link,
+	link-libc-printers-tests): New.
+	(rtld-LDFLAGS, rtld-tests-LDFLAGS, link-libc-tests-rpath-link,
+	link-libc-tests): Use the new variables as required.
+	* Makerules ($(py-const)): New rule.
+	generated: Add $(py-const).
+	* README.pretty-printers: New file.
+	* Rules (tests-printers-programs, tests-printers-out, py-env): New.
+	(others): Depend on $(py-const).
+	(tests): Depend on $(tests-printers-programs) or
+	$(tests-printers-out),
+	as required.  Pass $(tests-printers) to merge-test-results.sh.
+	* manual/install.texi: Add requirements for testing the pretty
+	printers.
+	* nptl/Makefile (gen-py-const-headers, pretty-printers,
+	tests-printers, CFLAGS-test-mutexattr-printers.c
+	CFLAGS-test-mutex-printers.c, CFLAGS-test-condattr-printers.c,
+	CFLAGS-test-cond-printers.c, CFLAGS-test-rwlockattr-printers.c
+	CFLAGS-test-rwlock-printers.c, tests-printers-libs): Define.
+	* nptl/nptl-printers.py: New file.
+	* nptl/nptl_lock_constants.pysym: Likewise.
+	* nptl/test-cond-printers.c: Likewise.
+	* nptl/test-cond-printers.py: Likewise.
+	* nptl/test-condattr-printers.c: Likewise.
+	* nptl/test-condattr-printers.py: Likewise.
+	* nptl/test-mutex-printers.c: Likewise.
+	* nptl/test-mutex-printers.py: Likewise.
+	* nptl/test-mutexattr-printers.c: Likewise.
+	* nptl/test-mutexattr-printers.py: Likewise.
+	* nptl/test-rwlock-printers.c: Likewise.
+	* nptl/test-rwlock-printers.py: Likewise.
+	* nptl/test-rwlockattr-printers.c: Likewise.
+	* nptl/test-rwlockattr-printers.py: Likewise.
+	* scripts/gen-py-const.awk: Likewise.
+	* scripts/test_printers_common.py: Likewise.
+	* scripts/test_printers_exceptions.py: Likewise.
+
 2016-12-07  Joseph Myers  <joseph@codesourcery.com>
 
 	* scripts/build-many-glibcs.py (Context.__init__): Take strip
diff --git a/INSTALL b/INSTALL
index b5acedcc96..acb622a102 100644
--- a/INSTALL
+++ b/INSTALL
@@ -224,6 +224,33 @@ You can specify 'stop-on-test-failure=y' when running 'make check' to
 make the test run stop and exit with an error status immediately when a
 failure occurs.
 
+   The GNU C Library pretty printers come with their own set of scripts
+for testing, which run together with the rest of the testsuite through
+'make check'.  These scripts require the following tools to run
+successfully:
+
+   * Python 2.7.6/3.4.3 or later
+
+     Python is required for running the printers' test scripts.
+
+   * PExpect 4.0
+
+     The printer tests drive GDB through test programs and compare its
+     output to the printers'.  PExpect is used to capture the output of
+     GDB, and should be compatible with the Python version in your
+     system.
+
+   * GDB 7.8 or later with support for Python 2.7.6/3.4.3 or later
+
+     GDB itself needs to be configured with Python support in order to
+     use the pretty printers.  Notice that your system having Python
+     available doesn't imply that GDB supports it, nor that your
+     system's Python and GDB's have the same version.
+
+If these tools are absent, the printer tests will report themselves as
+'UNSUPPORTED'.  Notice that some of the printer tests require the GNU C
+Library to be compiled with debugging symbols.
+
    To format the 'GNU C Library Reference Manual' for printing, type
 'make dvi'.  You need a working TeX installation to do this.  The
 distribution builds the on-line formatted version of the manual, as Info
diff --git a/Makeconfig b/Makeconfig
index a7858607b0..e9d8da9d60 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -416,6 +416,11 @@ $(+link-pie-before-libc) $(rtld-tests-LDFLAGS) $(link-libc-tests) \
 			 $(+link-pie-after-libc)
 $(call after-link,$@)
 endef
+define +link-pie-printers-tests
+$(+link-pie-before-libc) $(built-rtld-LDFLAGS) $(link-libc-printers-tests) \
+			 $(+link-pie-after-libc)
+$(call after-link,$@)
+endef
 endif
 # Command for statically linking programs with the C library.
 ifndef +link-static
@@ -445,7 +450,8 @@ ifeq (yes,$(build-pie-default))
 no-pie-ldflag = -no-pie
 +link = $(+link-pie)
 +link-tests = $(+link-pie-tests)
-else
++link-printers-tests = $(+link-pie-printers-tests)
+else  # not build-pie-default
 +link-before-libc = $(CC) -nostdlib -nostartfiles -o $@ \
 	      $(sysdep-LDFLAGS) $(LDFLAGS) $(LDFLAGS-$(@F)) \
 	      $(combreloc-LDFLAGS) $(relro-LDFLAGS) $(hashstyle-LDFLAGS) \
@@ -466,51 +472,87 @@ $(+link-before-libc) $(rtld-tests-LDFLAGS) $(link-libc-tests) \
 		     $(+link-after-libc)
 $(call after-link,$@)
 endef
-endif
-else
+define +link-printers-tests
+$(+link-before-libc) $(built-rtld-LDFLAGS) $(link-libc-printers-tests) \
+		     $(+link-after-libc)
+$(call after-link,$@)
+endef
+endif  # build-pie-default
+else  # build-static
 +link = $(+link-static)
 +link-tests = $(+link-static-tests)
-endif
-endif
++link-printers-tests = $(+link-static-tests)
+endif  # build-shared
+endif  # +link
+
+# The pretty printer 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-printers-tests := -O0 -ggdb3 -DIS_IN_build
+
 ifeq (yes,$(build-shared))
+# These indicate whether to link using the built ld.so or the installed one.
+installed-rtld-LDFLAGS = -Wl,-dynamic-linker=$(rtlddir)/$(rtld-installed-name)
+built-rtld-LDFLAGS = -Wl,-dynamic-linker=$(elf-objpfx)ld.so
+
 ifndef rtld-LDFLAGS
-rtld-LDFLAGS = -Wl,-dynamic-linker=$(rtlddir)/$(rtld-installed-name)
+rtld-LDFLAGS = $(installed-rtld-LDFLAGS)
 endif
+
 ifndef rtld-tests-LDFLAGS
 ifeq (yes,$(build-hardcoded-path-in-tests))
-rtld-tests-LDFLAGS = -Wl,-dynamic-linker=$(elf-objpfx)ld.so
+rtld-tests-LDFLAGS = $(built-rtld-LDFLAGS)
 else
-rtld-tests-LDFLAGS = $(rtld-LDFLAGS)
-endif
-endif
-endif
+rtld-tests-LDFLAGS = $(installed-rtld-LDFLAGS)
+endif  # build-hardcoded-path-in-tests
+endif  # rtld-tests-LDFLAGS
+
+endif  # build-shared
+
 ifndef link-libc
 ifeq (yes,$(build-shared))
 # We need the versioned name of libc.so in the deps of $(others) et al
 # so that the symlink to libc.so is created before anything tries to
 # run the linked programs.
+link-libc-rpath = -Wl,-rpath=$(rpath-link)
 link-libc-rpath-link = -Wl,-rpath-link=$(rpath-link)
+
 ifeq (yes,$(build-hardcoded-path-in-tests))
-link-libc-tests-rpath-link = -Wl,-rpath=$(rpath-link)
+link-libc-tests-rpath-link = $(link-libc-rpath)
 else
 link-libc-tests-rpath-link = $(link-libc-rpath-link)
-endif
+endif  # build-hardcoded-path-in-tests
+
 link-libc-before-gnulib = $(common-objpfx)libc.so$(libc.so-version) \
 			  $(common-objpfx)$(patsubst %,$(libtype.oS),c) \
 			  $(as-needed) $(elf-objpfx)ld.so \
 			  $(no-as-needed)
 link-libc = $(link-libc-rpath-link) $(link-libc-before-gnulib) $(gnulib)
+
+link-libc-tests-after-rpath-link = $(link-libc-before-gnulib) $(gnulib-tests)
 link-libc-tests = $(link-libc-tests-rpath-link) \
-		  $(link-libc-before-gnulib) $(gnulib-tests)
+		  $(link-libc-tests-after-rpath-link)
+# Pretty printer test programs always require rpath instead of rpath-link.
+link-libc-printers-tests = $(link-libc-rpath) \
+			   $(link-libc-tests-after-rpath-link)
+
 # This is how to find at build-time things that will be installed there.
 rpath-dirs = math elf dlfcn nss nis rt resolv crypt mathvec
 rpath-link = \
 $(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
-else
+else  # build-static
 link-libc = $(common-objpfx)libc.a $(otherlibs) $(gnulib) $(common-objpfx)libc.a $(gnulib)
 link-libc-tests = $(common-objpfx)libc.a $(otherlibs) $(gnulib-tests) $(common-objpfx)libc.a $(gnulib-tests)
-endif
-endif
+endif  # build-shared
+endif  # link-libc
 
 # Differences in the linkers on the various platforms.
 LDFLAGS-rpath-ORIGIN = -Wl,-rpath,'$$ORIGIN'
diff --git a/Makerules b/Makerules
index e865782b43..7214b2b43c 100644
--- a/Makerules
+++ b/Makerules
@@ -210,6 +210,52 @@ 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 build dir.
+py-const-files := $(patsubst %.pysym,%.py,$(gen-py-const-headers))
+py-const-dir := $(objpfx)
+py-const := $(addprefix $(py-const-dir),$(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-const-dir)%.py: %.pysym $(py-const-script) \
+	     $(common-before-compile)
+	$(make-target-directory)
+	$(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/NEWS b/NEWS
index c2e973d183..922a5004ff 100644
--- a/NEWS
+++ b/NEWS
@@ -141,6 +141,11 @@ Version 2.25
   variable for a particular architecture in the GCC source file
   'gcc/config.gcc'.
 
+* GDB pretty printers have been added for mutex and condition variable
+  structures in POSIX Threads. When installed and loaded in gdb these pretty
+  printers show various pthread variables in human-readable form when read
+  using the 'print' or 'display' commands in gdb.
+
 Security related changes:
 
   On ARM EABI (32-bit), generating a backtrace for execution contexts which
diff --git a/README.pretty-printers b/README.pretty-printers
new file mode 100644
index 0000000000..86629001e6
--- /dev/null
+++ b/README.pretty-printers
@@ -0,0 +1,169 @@
+README for the glibc Python pretty printers
+===========================================
+
+Pretty printers are gdb extensions that allow it to print useful, human-readable
+information about a program's variables.  For example, for a pthread_mutex_t
+gdb would usually output something like this:
+
+(gdb) print mutex
+$1 = {
+  __data = {
+    __lock = 22020096,
+    __count = 0,
+    __owner = 0,
+    __nusers = 0,
+    __kind = 576,
+    __spins = 0,
+    __elision = 0,
+    __list = {
+      __prev = 0x0,
+      __next = 0x0
+    }
+  },
+  __size = "\000\000P\001", '\000' <repeats 12 times>, "@\002", '\000' <repeats 21 times>,
+  __align = 22020096
+}
+
+However, with a pretty printer gdb will output something like this:
+
+(gdb) print mutex
+$1 = pthread_mutex_t = {
+  Type = Normal,
+  Status = Unlocked,
+  Robust = No,
+  Shared = No,
+  Protocol = Priority protect,
+  Priority ceiling = 42
+}
+
+Before printing a value, gdb will first check if there's a pretty printer
+registered for it.  If there is, it'll use it, otherwise it'll print the value
+as usual.  Pretty printers can be registered in various ways; for our purposes
+we register them for the current objfile by calling
+gdb.printing.register_pretty_printer().
+
+Currently our printers are based on gdb.RegexpCollectionPrettyPrinter, which
+means they'll be triggered if the type of the variable we're printing matches
+a given regular expression.  For example, MutexPrinter will be triggered if
+our variable's type matches the regexp '^pthread_mutex_t$'.
+
+Besides the printers themselves, each module may have a constants file which the
+printers will import.  These constants are generated from C headers during the
+build process, and need to be in the Python search path when loading the
+printers.
+
+
+Installing and loading
+----------------------
+
+The pretty printers and their constant files may be installed in different paths
+for each distro, though gdb should be able to automatically load them by itself.
+When in doubt, you can use the 'info pretty-printer' gdb command to list the
+loaded pretty printers.
+
+If the printers aren't automatically loaded for some reason, you should add the
+following to your .gdbinit:
+
+python
+import sys
+sys.path.insert(0, '/path/to/constants/file/directory')
+end
+
+source /path/to/printers.py
+
+If you're building glibc manually, '/path/to/constants/file/directory' should be
+'/path/to/glibc-build/submodule', where 'submodule' is e.g. nptl.
+
+
+Testing
+-------
+
+The pretty printers come with a small test suite based on PExpect, which is a
+Python module with Expect-like features for spawning and controlling interactive
+programs.  Each printer has a corresponding C program and a Python script
+that uses PExpect to drive gdb through the program and compare its output to
+the expected printer's.
+
+The tests run on the glibc host, which is assumed to have both gdb and PExpect;
+if any of those is absent the tests will fail with code 77 (UNSUPPORTED).
+Native builds can be tested simply by doing 'make check'; cross builds must use
+cross-test-ssh.sh as test-wrapper, like this:
+
+make test-wrapper='/path/to/scripts/cross-test-ssh.sh user@host' check
+
+(Remember to share the build system's filesystem with the glibc host's through
+NFS or something similar).
+
+Running 'make check' on a cross build will only compile the test programs,
+without running the scripts.
+
+
+Adding new pretty printers
+--------------------------
+
+Adding new pretty printers to glibc requires following these steps:
+
+1. Identify which constants must be generated from C headers, and write the
+corresponding .pysym file.  See scripts/gen-py-const.awk for more information
+on how this works.  The name of the .pysym file must be added to the
+'gen-py-const-headers' variable in your submodule's Makefile (without the .pysym
+extension).
+
+2. Write the pretty printer code itself.  For this you can follow the gdb
+Python API documentation, and use the existing printers as examples.  The printer
+code must import the generated constants file (which will have the same name
+as your .pysym file).  The names of the pretty printer files must be added
+to the 'pretty-printers' variable in your submodule's Makefile (without the .py
+extension).
+
+3. Write the unit tests for your pretty printers.  The build system calls each
+test script passing it the paths to the test program source, the test program
+binary, and the printer files you added to 'pretty-printers' in the previous
+step.  The test scripts, in turn, must import scripts/test_printers_common
+and call the init_test function passing it, among other things, the name of the
+set of pretty printers to enable (as seen by running 'info pretty-printer').
+You can use the existing unit tests as examples.
+
+4. Add the names of the pretty printer tests to the 'tests-printers' variable
+in your submodule's Makefile (without extensions).  In addition, for each test
+program you must define a corresponding CFLAGS-* variable and set it to
+$(CFLAGS-printers-tests) to ensure they're compiled correctly.  For example,
+test-foo-printer.c requires the following:
+
+CFLAGS-test-foo-printer.c := $(CFLAGS-printers-tests)
+
+Finally, if your programs need to be linked with a specific library, you can add
+its name to the 'tests-printers-libs' variable in your submodule's Makefile.
+
+
+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, and released
+as part of gdb 7.8.  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/Rules b/Rules
index 466db076f0..714e917de4 100644
--- a/Rules
+++ b/Rules
@@ -108,17 +108,28 @@ endif
 
 .PHONY: others tests bench bench-build
 
+# Test programs for the pretty printers.
+tests-printers-programs := $(addprefix $(objpfx),$(tests-printers))
+
+# .out files with the output of running the pretty printer tests.
+tests-printers-out := $(patsubst %,$(objpfx)%.out,$(tests-printers))
+
 ifeq ($(build-programs),yes)
 others: $(addprefix $(objpfx),$(others) $(sysdep-others) $(extra-objs))
 else
 others: $(addprefix $(objpfx),$(extra-objs))
 endif
+
+# Generate constant files for Python pretty printers if required.
+others: $(py-const)
+
 ifeq ($(run-built-tests),no)
 tests: $(addprefix $(objpfx),$(filter-out $(tests-unsupported),$(tests)) \
-			     $(test-srcs)) $(tests-special)
+			     $(test-srcs)) $(tests-special) \
+       $(tests-printers-programs)
 xtests: tests $(xtests-special)
 else
-tests: $(tests:%=$(objpfx)%.out) $(tests-special)
+tests: $(tests:%=$(objpfx)%.out) $(tests-special) $(tests-printers-out)
 xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
 endif
 
@@ -131,7 +142,8 @@ tests-expected = $(tests)
 endif
 tests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
-	  $(sort $(tests-expected) $(tests-special-notdir:.out=)) \
+	  $(sort $(tests-expected) $(tests-special-notdir:.out=) \
+	  $(tests-printers)) \
 	  > $(objpfx)subdir-tests.sum
 xtests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
@@ -241,6 +253,32 @@ endif
 
 endif	# tests
 
+ifneq "$(strip $(tests-printers))" ""
+# We're defining this here for now; later it'll be defined at configure time
+# inside Makeconfig.
+PYTHON := python
+
+# Static pattern rule for building the test programs for the pretty printers.
+$(tests-printers-programs): %: %.o $(tests-printers-libs) \
+  $(sort $(filter $(common-objpfx)lib%,$(link-libc-static-tests))) \
+  $(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
+	$(+link-printers-tests)
+
+# Add the paths to the generated constants file and test_common_printers.py
+# to PYTHONPATH so the test scripts can find them.
+py-env := PYTHONPATH=$(py-const-dir):$(..)scripts:$${PYTHONPATH}
+
+# 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 and place its output in the corresponding .out file.
+# The pretty printer files and test_common_printers.py must be present for all.
+$(tests-printers-out): $(objpfx)%.out: $(objpfx)% %.py %.c $(pretty-printers) \
+		       $(..)scripts/test_printers_common.py
+	$(test-wrapper-env) $(py-env) \
+	    $(PYTHON) $*.py $*.c $(objpfx)$* $(pretty-printers) > $@; \
+	$(evaluate-test)
+endif
+
 
 .PHONY: distclean realclean subdir_distclean subdir_realclean \
 	subdir_clean subdir_mostlyclean subdir_testclean
diff --git a/manual/install.texi b/manual/install.texi
index de1c2030d3..35e02ada58 100644
--- a/manual/install.texi
+++ b/manual/install.texi
@@ -256,6 +256,36 @@ occurred.  You can specify @samp{stop-on-test-failure=y} when running
 @code{make check} to make the test run stop and exit with an error
 status immediately when a failure occurs.
 
+The @glibcadj{} pretty printers come with their own set of scripts for testing,
+which run together with the rest of the testsuite through @code{make check}.
+These scripts require the following tools to run successfully:
+
+@itemize @bullet
+@item
+Python 2.7.6/3.4.3 or later
+
+Python is required for running the printers' test scripts.
+
+@item PExpect 4.0
+
+The printer tests drive GDB through test programs and compare its output
+to the printers'.  PExpect is used to capture the output of GDB, and should be
+compatible with the Python version in your system.
+
+@item
+GDB 7.8 or later with support for Python 2.7.6/3.4.3 or later
+
+GDB itself needs to be configured with Python support in order to use the
+pretty printers.  Notice that your system having Python available doesn't imply
+that GDB supports it, nor that your system's Python and GDB's have the same
+version.
+@end itemize
+
+@noindent
+If these tools are absent, the printer tests will report themselves as
+@code{UNSUPPORTED}.  Notice that some of the printer tests require @theglibc{}
+to be compiled with debugging symbols.
+
 To format the @cite{GNU C Library Reference Manual} for printing, type
 @w{@code{make dvi}}.  You need a working @TeX{} installation to do
 this.  The distribution builds the on-line formatted version of the
diff --git a/nptl/Makefile b/nptl/Makefile
index 11588fe996..7ac9196975 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -312,6 +312,24 @@ gen-as-const-headers = pthread-errnos.sym \
 		       unwindbuf.sym \
 		       lowlevelrobustlock.sym pthread-pi-defines.sym
 
+gen-py-const-headers := nptl_lock_constants.pysym
+pretty-printers := nptl-printers.py
+tests-printers := test-mutexattr-printers test-mutex-printers \
+		  test-condattr-printers test-cond-printers \
+		  test-rwlockattr-printers test-rwlock-printers
+
+CFLAGS-test-mutexattr-printers.c := $(CFLAGS-printers-tests)
+CFLAGS-test-mutex-printers.c := $(CFLAGS-printers-tests)
+CFLAGS-test-condattr-printers.c := $(CFLAGS-printers-tests)
+CFLAGS-test-cond-printers.c := $(CFLAGS-printers-tests)
+CFLAGS-test-rwlockattr-printers.c := $(CFLAGS-printers-tests)
+CFLAGS-test-rwlock-printers.c := $(CFLAGS-printers-tests)
+
+ifeq ($(build-shared),yes)
+tests-printers-libs := $(shared-thread-library)
+else
+tests-printers-libs := $(static-thread-library)
+endif
 
 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 0000000000..e402f232c7
--- /dev/null
+++ b/nptl/nptl-printers.py
@@ -0,0 +1,633 @@
+# 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
+import gdb.printing
+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.
+        """
+
+        self.values = []
+
+        try:
+            mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
+            self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
+            self.read_values()
+        except gdb.error:
+            # libpthread doesn't have debug symbols, thus we can't find the
+            # real struct type.  Just print the union members.
+            self.values.append(('__size', mutexattr['__size']))
+            self.values.append(('__align', mutexattr['__align']))
+
+    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.
+        """
+
+        # Since PTHREAD_COND_SHARED is an integer, we need to cast it to void *
+        # to be able to compare it to the condvar's __data.__mutex member.
+        #
+        # While it looks like self.shared_value should be a class variable,
+        # that would result in it having an incorrect size if we're loading
+        # these printers through .gdbinit for a 64-bit objfile in AMD64.
+        # This is because gdb initially assumes the pointer size to be 4 bytes,
+        # and only sets it to 8 after loading the 64-bit objfiles.  Since
+        # .gdbinit runs before any objfiles are loaded, this would effectively
+        # make self.shared_value have a size of 4, thus breaking later
+        # comparisons with pointers whose types are looked up at runtime.
+        void_ptr_type = gdb.lookup_type('void').pointer()
+        self.shared_value = gdb.Value(PTHREAD_COND_SHARED).cast(void_ptr_type)
+
+        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 == self.shared_value)
+
+        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 special value instead.
+        """
+
+        if self.mutex and self.mutex != self.shared_value:
+            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.
+        """
+
+        self.values = []
+
+        try:
+            condattr_struct = gdb.lookup_type('struct pthread_condattr')
+            self.condattr = condattr.cast(condattr_struct)['value']
+            self.read_values()
+        except gdb.error:
+            # libpthread doesn't have debug symbols, thus we can't find the
+            # real struct type.  Just print the union members.
+            self.values.append(('__size', condattr['__size']))
+            self.values.append(('__align', condattr['__align']))
+
+    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.
+        """
+
+        self.values = []
+
+        try:
+            rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
+            self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
+            self.read_values()
+        except gdb.error:
+            # libpthread doesn't have debug symbols, thus we can't find the
+            # real struct type.  Just print the union members.
+            self.values.append(('__size', rwlockattr['__size']))
+            self.values.append(('__align', rwlockattr['__align']))
+
+    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)
+
+    if objfile == None:
+        objfile = gdb
+
+    gdb.printing.register_pretty_printer(objfile, printer)
+
+register(gdb.current_objfile())
diff --git a/nptl/nptl_lock_constants.pysym b/nptl/nptl_lock_constants.pysym
new file mode 100644
index 0000000000..303ec61213
--- /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             (void *)~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/nptl/test-cond-printers.c b/nptl/test-cond-printers.c
new file mode 100644
index 0000000000..0f2a5f4827
--- /dev/null
+++ b/nptl/test-cond-printers.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/nptl/test-cond-printers.py b/nptl/test-cond-printers.py
new file mode 100644
index 0000000000..af0e12eb97
--- /dev/null
+++ b/nptl/test-cond-printers.py
@@ -0,0 +1,50 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/nptl/test-condattr-printers.c b/nptl/test-condattr-printers.c
new file mode 100644
index 0000000000..4db4098827
--- /dev/null
+++ b/nptl/test-condattr-printers.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/nptl/test-condattr-printers.py b/nptl/test-condattr-printers.py
new file mode 100644
index 0000000000..7ea01db158
--- /dev/null
+++ b/nptl/test-condattr-printers.py
@@ -0,0 +1,71 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    go_to_main()
+
+    check_debug_symbol('struct pthread_condattr')
+
+    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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+except DebugError as exception:
+    print(exception)
+    result = UNSUPPORTED
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/nptl/test-mutex-printers.c b/nptl/test-mutex-printers.c
new file mode 100644
index 0000000000..b973e82840
--- /dev/null
+++ b/nptl/test-mutex-printers.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_func (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;
+
+  if (pthread_create (&thread, NULL, thread_func, mutex) == 0 /* Create.  */
+      && pthread_join (thread, NULL) == 0
+      && pthread_mutex_lock (mutex) == EOWNERDEAD /* Test locking (robust).  */
+      && pthread_mutex_unlock (mutex) == 0)
+    result = PASS;
+
+  return result;
+}
+
+/* Function to be called by the child thread when testing robust mutexes.  */
+static void *
+thread_func (void *arg)
+{
+  pthread_mutex_t *mutex = (pthread_mutex_t *)arg;
+
+  if (pthread_mutex_lock (mutex) != 0) /* Thread function.  */
+    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/nptl/test-mutex-printers.py b/nptl/test-mutex-printers.py
new file mode 100644
index 0000000000..7f542adcd7
--- /dev/null
+++ b/nptl/test-mutex-printers.py
@@ -0,0 +1,97 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    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'})
+
+    # We'll now test the robust mutex locking states.  We'll create a new
+    # thread that will lock a robust mutex and exit without unlocking it.
+    break_at(test_source, 'Create')
+    continue_cmd() # Go to test_locking_state_robust
+    # Set a breakpoint for the new thread to hit.
+    break_at(test_source, 'Thread function')
+    continue_cmd()
+    # By now the new thread is created and has hit its breakpoint.
+    set_scheduler_locking(True)
+    parent = 1
+    child = 2
+    select_thread(child)
+    child_id = get_current_thread_lwpid()
+    # We've got the new thread's ID.
+    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.
+    break_at(test_source, 'Test locking (robust)')
+    continue_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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/nptl/test-mutexattr-printers.c b/nptl/test-mutexattr-printers.c
new file mode 100644
index 0000000000..9ecfff76c3
--- /dev/null
+++ b/nptl/test-mutexattr-printers.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/nptl/test-mutexattr-printers.py b/nptl/test-mutexattr-printers.py
new file mode 100644
index 0000000000..4464723fbe
--- /dev/null
+++ b/nptl/test-mutexattr-printers.py
@@ -0,0 +1,101 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+PRIOCEILING = 42
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    go_to_main()
+
+    check_debug_symbol('struct pthread_mutexattr')
+
+    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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+except DebugError as exception:
+    print(exception)
+    result = UNSUPPORTED
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/nptl/test-rwlock-printers.c b/nptl/test-rwlock-printers.c
new file mode 100644
index 0000000000..dbbe9b80d6
--- /dev/null
+++ b/nptl/test-rwlock-printers.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/nptl/test-rwlock-printers.py b/nptl/test-rwlock-printers.py
new file mode 100644
index 0000000000..b972fa60d0
--- /dev/null
+++ b/nptl/test-rwlock-printers.py
@@ -0,0 +1,64 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/nptl/test-rwlockattr-printers.c b/nptl/test-rwlockattr-printers.c
new file mode 100644
index 0000000000..d12facf41c
--- /dev/null
+++ b/nptl/test-rwlockattr-printers.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/nptl/test-rwlockattr-printers.py b/nptl/test-rwlockattr-printers.py
new file mode 100644
index 0000000000..1ca2dc6c33
--- /dev/null
+++ b/nptl/test-rwlockattr-printers.py
@@ -0,0 +1,73 @@
+# 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_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-pthread-locks']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    go_to_main()
+
+    check_debug_symbol('struct pthread_rwlockattr')
+
+    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 (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+except DebugError as exception:
+    print(exception)
+    result = UNSUPPORTED
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/scripts/gen-py-const.awk b/scripts/gen-py-const.awk
new file mode 100644
index 0000000000..4586f59d89
--- /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 "}"; }
diff --git a/scripts/test_printers_common.py b/scripts/test_printers_common.py
new file mode 100644
index 0000000000..c79d7e3be2
--- /dev/null
+++ b/scripts/test_printers_common.py
@@ -0,0 +1,364 @@
+# 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 4.0 or newer.
+
+Exported constants:
+    PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
+"""
+
+import os
+import re
+from test_printers_exceptions import *
+
+PASS = 0
+FAIL = 1
+UNSUPPORTED = 77
+
+gdb_bin = 'gdb'
+gdb_options = '-q -nx'
+gdb_invocation = '{0} {1}'.format(gdb_bin, gdb_options)
+pexpect_min_version = 4
+gdb_min_version = (7, 8)
+encoding = 'utf-8'
+
+try:
+    import pexpect
+except ImportError:
+    print('PExpect 4.0 or newer must be installed to test the pretty printers.')
+    exit(UNSUPPORTED)
+
+pexpect_version = pexpect.__version__.split('.')[0]
+
+if int(pexpect_version) < pexpect_min_version:
+    print('PExpect 4.0 or newer must be installed to test the pretty printers.')
+    exit(UNSUPPORTED)
+
+if not pexpect.which(gdb_bin):
+    print('gdb 7.8 or newer must be installed to test the pretty printers.')
+    exit(UNSUPPORTED)
+
+timeout = 5
+TIMEOUTFACTOR = os.environ.get('TIMEOUTFACTOR')
+
+if TIMEOUTFACTOR:
+    timeout = int(TIMEOUTFACTOR)
+
+try:
+    # Check the gdb version.
+    version_cmd = '{0} --version'.format(gdb_invocation, timeout=timeout)
+    gdb_version_out = pexpect.run(version_cmd, encoding=encoding)
+
+    # The gdb version string is "GNU gdb <PKGVERSION><version>", where
+    # PKGVERSION can be any text.  We assume that there'll always be a space
+    # between PKGVERSION and the version number for the sake of the regexp.
+    version_match = re.search(r'GNU gdb .* ([1-9]+)\.([0-9]+)', gdb_version_out)
+
+    if not version_match:
+        print('The gdb version string (gdb -v) is incorrectly formatted.')
+        exit(UNSUPPORTED)
+
+    gdb_version = (int(version_match.group(1)), int(version_match.group(2)))
+
+    if gdb_version < gdb_min_version:
+        print('gdb 7.8 or newer must be installed to test the pretty printers.')
+        exit(UNSUPPORTED)
+
+    # Check if gdb supports Python.
+    gdb_python_cmd = '{0} -ex "python import os" -batch'.format(gdb_invocation,
+                                                                timeout=timeout)
+    gdb_python_error = pexpect.run(gdb_python_cmd, encoding=encoding)
+
+    if gdb_python_error:
+        print('gdb must have python support to test the pretty printers.')
+        exit(UNSUPPORTED)
+
+    # If everything's ok, spawn the gdb process we'll use for testing.
+    gdb = pexpect.spawn(gdb_invocation, echo=False, timeout=timeout,
+                        encoding=encoding)
+    gdb_prompt = u'\(gdb\)'
+    gdb.expect(gdb_prompt)
+
+except pexpect.ExceptionPexpect as exception:
+    print('Error: {0}'.format(exception))
+    exit(FAIL)
+
+def test(command, pattern=None):
+    """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.
+        index = gdb.expect([u'{0}.+{1}'.format(pattern, gdb_prompt),
+                            pexpect.TIMEOUT])
+
+        if index == 0:
+            # gdb.after now contains the whole match.  Extract the text that
+            # matches 'pattern'.
+            match = re.match(pattern, gdb.after, re.DOTALL).group()
+        elif index == 1:
+            # We got a timeout exception.  Print information on what caused it
+            # and bail out.
+            error = ('Response does not match the expected pattern.\n'
+                     'Command: {0}\n'
+                     'Expected pattern: {1}\n'
+                     'Response: {2}'.format(command, pattern, gdb.before))
+
+            raise pexpect.TIMEOUT(error)
+    else:
+        # Consume just the the gdb prompt.
+        gdb.expect(gdb_prompt)
+
+    return match
+
+def init_test(test_bin, printer_files, printer_names):
+    """Loads the test binary file and the required pretty printers to gdb.
+
+    Args:
+        test_bin (string): The name of the test binary file.
+        pretty_printers (list of strings): A list with the names of the pretty
+            printer files.
+    """
+
+    # Load all the pretty printer files.  We're assuming these are safe.
+    for printer_file in printer_files:
+        test('source {0}'.format(printer_file))
+
+    # Disable all the pretty printers.
+    test('disable pretty-printer', r'0 of [0-9]+ printers enabled')
+
+    # Enable only the required printers.
+    for printer in printer_names:
+        test('enable pretty-printer {0}'.format(printer),
+             r'[1-9][0-9]* of [1-9]+ printers enabled')
+
+    # Finally, load the test binary.
+    test('file {0}'.format(test_bin))
+
+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(file_name, 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)
+
+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))
+
+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))
+
+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]))
+
+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))
+
+def check_debug_symbol(symbol):
+    """ Tests whether a given debugging symbol exists.
+
+    If the symbol doesn't exist, raises a DebugError.
+
+    Args:
+        symbol (string): The symbol we're going to check for.
+    """
+
+    try:
+        test('ptype {0}'.format(symbol), r'type = {0}'.format(symbol))
+
+    except pexpect.TIMEOUT:
+        # The symbol doesn't exist.
+        raise DebugError(symbol)
diff --git a/scripts/test_printers_exceptions.py b/scripts/test_printers_exceptions.py
new file mode 100644
index 0000000000..17034b544e
--- /dev/null
+++ b/scripts/test_printers_exceptions.py
@@ -0,0 +1,61 @@
+# Exception classes used when 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/>.
+
+class NoLineError(Exception):
+    """Custom exception to indicate that a test file doesn't contain
+    the requested string.
+    """
+
+    def __init__(self, file_name, string):
+        """Constructor.
+
+        Args:
+            file_name (string): The name of the test file.
+            string (string): The string that was requested.
+        """
+
+        super(NoLineError, self).__init__()
+        self.file_name = file_name
+        self.string = string
+
+    def __str__(self):
+        """Shows a readable representation of the exception."""
+
+        return ('File {0} has no line containing the following string: {1}'
+                .format(self.file_name, self.string))
+
+class DebugError(Exception):
+    """Custom exception to indicate that a required debugging symbol is missing.
+    """
+
+    def __init__(self, symbol):
+        """Constructor.
+
+        Args:
+            symbol (string): The name of the entity whose debug info is missing.
+        """
+
+        super(DebugError, self).__init__()
+        self.symbol = symbol
+
+    def __str__(self):
+        """Shows a readable representation of the exception."""
+
+        return ('The required debugging information for {0} is missing.'
+                .format(self.symbol))