summaryrefslogtreecommitdiffstats
path: root/import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch
diff options
context:
space:
mode:
Diffstat (limited to 'import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch')
-rw-r--r--import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch3197
1 files changed, 3197 insertions, 0 deletions
diff --git a/import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch b/import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch
new file mode 100644
index 000000000..9eb635d71
--- /dev/null
+++ b/import-layers/yocto-poky/meta/recipes-core/glibc/glibc/0003-Add-pretty-printers-for-the-NPTL-lock-types.patch
@@ -0,0 +1,3197 @@
+From 246fee86fc90c57738ee282a061039f82832f4ea Mon Sep 17 00:00:00 2001
+From: Catalin Enache <catalin.enache@windriver.com>
+Date: Fri, 30 Jun 2017 13:42:04 +0300
+Subject: [PATCH 3/6] Add pretty printers for the NPTL lock types
+
+This patch adds pretty printers for the following NPTL types:
+
+- pthread_mutex_t
+- pthread_mutexattr_t
+- pthread_cond_t
+- pthread_condattr_t
+- pthread_rwlock_t
+- pthread_rwlockattr_t
+
+To load the pretty printers into your gdb session, do the following:
+
+python
+import sys
+sys.path.insert(0, '/path/to/glibc/build/nptl/pretty-printers')
+end
+
+source /path/to/glibc/source/pretty-printers/nptl-printers.py
+
+You can check which printers are registered and enabled by issuing the
+'info pretty-printer' gdb command. Printers should trigger automatically when
+trying to print a variable of one of the types mentioned above.
+
+The printers are architecture-independent, and were tested on an AMD64 running
+Ubuntu 14.04 and an x86 VM running Fedora 24.
+
+In order to work, the printers need to know the values of various flags that
+are scattered throughout pthread.h and pthreadP.h as enums and #defines. Since
+replicating these constants in the printers file itself would create a
+maintenance burden, I wrote a script called gen-py-const.awk that Makerules uses
+to extract the constants. This script is pretty much the same as gen-as-const.awk,
+except it doesn't cast the constant values to 'long' and is thorougly documented.
+The constants need only to be enumerated in a .pysym file, which is then referenced
+by a Make variable called gen-py-const-headers.
+
+As for the install directory, I discussed this with Mike Frysinger and Siddhesh
+Poyarekar, and we agreed that it can be handled in a separate patch, and shouldn't
+block merging of this one.
+
+In addition, I've written a series of test cases for the pretty printers.
+Each lock type (mutex, condvar and rwlock) has two test programs, one for itself
+and other for its related 'attributes' object. Each test program in turn has a
+PExpect-based Python script that drives gdb and compares its output to the
+expected printer's. The tests run on the glibc host, which is assumed to have
+both gdb and PExpect; if either is absent the tests will fail with code 77
+(UNSUPPORTED). For cross-testing you should use cross-test-ssh.sh as test-wrapper.
+I've tested the printers on both native builds and a cross build using a Beaglebone
+Black running Debian, with the build system's filesystem shared with the board
+through NFS.
+
+Finally, I've written a README that explains all this and more.
+
+ * 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.
+
+Upstream-Status: Backport
+
+Author: Martin Galvan <martin.galvan@tallertechnologies.com>
+Signed-off-by: Catalin Enache <catalin.enache@windriver.com>
+---
+ ChangeLog | 45 +++
+ INSTALL | 27 ++
+ Makeconfig | 76 ++++-
+ Makerules | 46 +++
+ NEWS | 6 +
+ README.pretty-printers | 169 ++++++++++
+ Rules | 44 ++-
+ manual/install.texi | 30 ++
+ nptl/Makefile | 18 +
+ nptl/nptl-printers.py | 633 ++++++++++++++++++++++++++++++++++++
+ nptl/nptl_lock_constants.pysym | 75 +++++
+ nptl/test-cond-printers.c | 57 ++++
+ nptl/test-cond-printers.py | 50 +++
+ nptl/test-condattr-printers.c | 94 ++++++
+ nptl/test-condattr-printers.py | 71 ++++
+ nptl/test-mutex-printers.c | 151 +++++++++
+ nptl/test-mutex-printers.py | 97 ++++++
+ nptl/test-mutexattr-printers.c | 144 ++++++++
+ nptl/test-mutexattr-printers.py | 101 ++++++
+ nptl/test-rwlock-printers.c | 78 +++++
+ nptl/test-rwlock-printers.py | 64 ++++
+ nptl/test-rwlockattr-printers.c | 98 ++++++
+ nptl/test-rwlockattr-printers.py | 73 +++++
+ scripts/gen-py-const.awk | 118 +++++++
+ scripts/test_printers_common.py | 364 +++++++++++++++++++++
+ scripts/test_printers_exceptions.py | 61 ++++
+ 26 files changed, 2770 insertions(+), 20 deletions(-)
+ create mode 100644 README.pretty-printers
+ create mode 100644 nptl/nptl-printers.py
+ create mode 100644 nptl/nptl_lock_constants.pysym
+ create mode 100644 nptl/test-cond-printers.c
+ create mode 100644 nptl/test-cond-printers.py
+ create mode 100644 nptl/test-condattr-printers.c
+ create mode 100644 nptl/test-condattr-printers.py
+ create mode 100644 nptl/test-mutex-printers.c
+ create mode 100644 nptl/test-mutex-printers.py
+ create mode 100644 nptl/test-mutexattr-printers.c
+ create mode 100644 nptl/test-mutexattr-printers.py
+ create mode 100644 nptl/test-rwlock-printers.c
+ create mode 100644 nptl/test-rwlock-printers.py
+ create mode 100644 nptl/test-rwlockattr-printers.c
+ create mode 100644 nptl/test-rwlockattr-printers.py
+ create mode 100644 scripts/gen-py-const.awk
+ create mode 100644 scripts/test_printers_common.py
+ create mode 100644 scripts/test_printers_exceptions.py
+
+diff --git a/ChangeLog b/ChangeLog
+index 96b6da2..8036c1e 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-08-09 Torvald Riegel <triegel@redhat.com>
+
+ * include/atomic.h (atomic_fetch_and_relaxed,
+diff --git a/INSTALL b/INSTALL
+index ec3445f..dd62c86 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 03fd89c..2d92d94 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 be3c11b..b7e0f59 100644
+--- a/Makerules
++++ b/Makerules
+@@ -214,6 +214,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 b0447e7..3002773 100644
+--- a/NEWS
++++ b/NEWS
+@@ -5,6 +5,12 @@ See the end for copying conditions.
+ Please send GNU C library bug reports via <http://sourceware.org/bugzilla/>
+ using `glibc' in the "product" field.
+
++
++* 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.
++
+ Version 2.24
+
+ * The minimum Linux kernel version that this version of the GNU C Library
+diff --git a/README.pretty-printers b/README.pretty-printers
+new file mode 100644
+index 0000000..8662900
+--- /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 8306d36..10a6479 100644
+--- a/Rules
++++ b/Rules
+@@ -85,16 +85,27 @@ common-generated += dummy.o dummy.c
+
+ .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),$(tests) $(test-srcs)) $(tests-special)
++tests: $(addprefix $(objpfx),$(tests) $(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
+
+@@ -102,7 +113,8 @@ tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
+ xtests-special-notdir = $(patsubst $(objpfx)%, %, $(xtests-special))
+ tests:
+ $(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
+- $(sort $(tests) $(tests-special-notdir:.out=)) \
++ $(sort $(tests) $(tests-special-notdir:.out=) \
++ $(tests-printers)) \
+ > $(objpfx)subdir-tests.sum
+ xtests:
+ $(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
+@@ -212,6 +224,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 79ee45f..468479e 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 7dec4ed..49f6ba6 100644
+--- a/nptl/Makefile
++++ b/nptl/Makefile
+@@ -308,6 +308,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 0000000..e402f23
+--- /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 0000000..303ec61
+--- /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 0000000..0f2a5f4
+--- /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 0000000..af0e12e
+--- /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 0000000..4db4098
+--- /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 0000000..7ea01db
+--- /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 0000000..b973e82
+--- /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 0000000..7f542ad
+--- /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 0000000..9ecfff7
+--- /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 0000000..4464723
+--- /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 0000000..dbbe9b8
+--- /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 0000000..b972fa6
+--- /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 0000000..d12facf
+--- /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 0000000..1ca2dc6
+--- /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 0000000..4586f59
+--- /dev/null
++++ b/scripts/gen-py-const.awk
+@@ -0,0 +1,118 @@
++# Script to generate constants for Python pretty printers.
++#
++# Copyright (C) 2016 Free Software Foundation, Inc.
++# This file is part of the GNU C Library.
++#
++# The GNU C Library is free software; you can redistribute it and/or
++# modify it under the terms of the GNU Lesser General Public
++# License as published by the Free Software Foundation; either
++# version 2.1 of the License, or (at your option) any later version.
++#
++# The GNU C Library is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# Lesser General Public License for more details.
++#
++# You should have received a copy of the GNU Lesser General Public
++# License along with the GNU C Library; if not, see
++# <http://www.gnu.org/licenses/>.
++
++# This script is a smaller version of the clever gen-asm-const.awk hack used to
++# generate ASM constants from .sym files. We'll use this to generate constants
++# for Python pretty printers.
++#
++# The input to this script are .pysym files that look like:
++# #C_Preprocessor_Directive...
++# NAME1
++# NAME2 expression...
++#
++# A line giving just a name implies an expression consisting of just that name.
++# Comments start with '--'.
++#
++# The output of this script is a 'dummy' function containing 'asm' declarations
++# for each non-preprocessor line in the .pysym file. The expression values
++# will appear as input operands to the 'asm' declaration. For example, if we
++# have:
++#
++# /* header.h */
++# #define MACRO 42
++#
++# struct S {
++# char c1;
++# char c2;
++# char c3;
++# };
++#
++# enum E {
++# ZERO,
++# ONE
++# };
++#
++# /* symbols.pysym */
++# #include <stddef.h>
++# #include "header.h"
++# -- This is a comment
++# MACRO
++# C3_OFFSET offsetof(struct S, c3)
++# E_ONE ONE
++#
++# the output will be:
++#
++# #include <stddef.h>
++# #include "header.h"
++# void dummy(void)
++# {
++# asm ("@name@MACRO@value@%0@" : : "i" (MACRO));
++# asm ("@name@C3_OFFSET@value@%0@" : : "i" (offsetof(struct S, c3)));
++# asm ("@name@E_ONE@value@%0@" : : "i" (ONE));
++# }
++#
++# We'll later feed this output to gcc -S. Since '-S' tells gcc to compile but
++# not assemble, gcc will output something like:
++#
++# dummy:
++# ...
++# @name@MACRO@value@$42@
++# @name@C3_OFFSET@value@$2@
++# @name@E_ONE@value@$1@
++#
++# Finally, we can process that output to extract the constant values.
++# Notice gcc may prepend a special character such as '$' to each value.
++
++# found_symbol indicates whether we found a non-comment, non-preprocessor line.
++BEGIN { found_symbol = 0 }
++
++# C preprocessor directives go straight through.
++/^#/ { print; next; }
++
++# Skip comments.
++/--/ { next; }
++
++# Trim leading whitespace.
++{ sub(/^[[:blank:]]*/, ""); }
++
++# If we found a non-comment, non-preprocessor line, print the 'dummy' function
++# header.
++NF > 0 && !found_symbol {
++ print "void dummy(void)\n{";
++ found_symbol = 1;
++}
++
++# If the line contains just a name, duplicate it so we can use that name
++# as the value of the expression.
++NF == 1 { sub(/^.*$/, "& &"); }
++
++# If a line contains a name and an expression...
++NF > 1 {
++ name = $1;
++
++ # Remove any characters before the second field.
++ sub(/^[^[:blank:]]+[[:blank:]]+/, "");
++
++ # '$0' ends up being everything that appeared after the first field
++ # separator.
++ printf " asm (\"@name@%s@value@%0@\" : : \"i\" (%s));\n", name, $0;
++}
++
++# Close the 'dummy' function.
++END { if (found_symbol) print "}"; }
+diff --git a/scripts/test_printers_common.py b/scripts/test_printers_common.py
+new file mode 100644
+index 0000000..c79d7e3
+--- /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 0000000..17034b5
+--- /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))
+--
+2.10.2
+
OpenPOWER on IntegriCloud