diff options
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.patch | 3197 |
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 + |