mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
Revert "Add pretty printers for the NPTL lock types"
This reverts commit 62ce266b0b
.
The change is not mature enough because it needs the following fixes:
1. Redirect test output to a file like other tests
2. Eliminate the need to use a .gdbinit because distributions will
break without it. I should have caught that but I was in too much
of a hurry to get the patch in :/
3. Feature checking during configure to determine things like minimum
required gdb version, python-pexpect version, etc. to make sure
that tests work correctly.
This commit is contained in:
parent
26c2910ac6
commit
c10f90dcef
27
ChangeLog
27
ChangeLog
@ -34,33 +34,6 @@
|
||||
* sysdeps/unix/sysv/linux/pwritev64.c
|
||||
[__WORDSIZE != 64 || __ASSUME_OFF_DIFF_OFF64] (pwritev64): Likewise.
|
||||
|
||||
2016-07-08 Martin Galvan <martin.galvan@tallertechnologies.com>
|
||||
|
||||
* Makeconfig (build-hardcoded-path-in-tests): Set to 'yes'
|
||||
for shared builds if tests-need-hardcoded-path is defined.
|
||||
(all-subdirs): Add pretty-printers.
|
||||
* Makerules ($(py-const)): New rule.
|
||||
* Rules (others): Add $(py-const), if defined.
|
||||
* nptl/Makefile (gen-py-const-headers): Define.
|
||||
* nptl/nptl-printers.py: New file.
|
||||
* nptl/nptl_lock_constants.pysym: Likewise.
|
||||
* pretty-printers/Makefile: Likewise.
|
||||
* pretty-printers/README: Likewise.
|
||||
* pretty-printers/test-condvar-attributes.c: Likewise.
|
||||
* pretty-printers/test-condvar-attributes.p: Likewise.
|
||||
* pretty-printers/test-condvar-printer.c: Likewise.
|
||||
* pretty-printers/test-condvar-printer.py: Likewise.
|
||||
* pretty-printers/test-mutex-attributes.c: Likewise.
|
||||
* pretty-printers/test-mutex-attributes.py: Likewise.
|
||||
* pretty-printers/test-mutex-printer.c: Likewise.
|
||||
* pretty-printers/test-mutex-printer.py: Likewise.
|
||||
* pretty-printers/test-rwlock-attributes.c: Likewise.
|
||||
* pretty-printers/test-rwlock-attributes.py: Likewise.
|
||||
* pretty-printers/test-rwlock-printer.c: Likewise.
|
||||
* pretty-printers/test-rwlock-printer.py: Likewise.
|
||||
* pretty-printers/test_common.py: Likewise.
|
||||
* scripts/gen-py-const.awk: Likewise.
|
||||
|
||||
2016-07-08 Chris Metcalf <cmetcalf@mellanox.com>
|
||||
|
||||
* sysdeps/unix/sysv/linux/tile/kernel-features.h
|
||||
|
14
Makeconfig
14
Makeconfig
@ -472,18 +472,6 @@ else
|
||||
+link-tests = $(+link-static-tests)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Some modules may have test programs that must always link against the newly
|
||||
# built libraries instead of the installed ones. Such modules must define
|
||||
# tests-need-hardcoded-path in their Makefile before including Makerules.
|
||||
# This will cause the test programs to be linked with -rpath instead of
|
||||
# -rpath-link, and their dynamic linker will be set to the built ld.so.
|
||||
ifeq (yes,$(build-shared))
|
||||
ifdef tests-need-hardcoded-path
|
||||
build-hardcoded-path-in-tests := yes
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (yes,$(build-shared))
|
||||
ifndef rtld-LDFLAGS
|
||||
rtld-LDFLAGS = -Wl,-dynamic-linker=$(rtlddir)/$(rtld-installed-name)
|
||||
@ -1109,7 +1097,7 @@ all-subdirs = csu assert ctype locale intl catgets math setjmp signal \
|
||||
grp pwd posix io termios resource misc socket sysvipc gmon \
|
||||
gnulib iconv iconvdata wctype manual shadow gshadow po argp \
|
||||
crypt localedata timezone rt conform debug mathvec \
|
||||
$(add-on-subdirs) dlfcn elf pretty-printers
|
||||
$(add-on-subdirs) dlfcn elf
|
||||
|
||||
ifndef avoid-generated
|
||||
# sysd-sorted itself will contain rules making the sysd-sorted target
|
||||
|
46
Makerules
46
Makerules
@ -214,52 +214,6 @@ sed-remove-dotdot := -e 's@ *\([^ \/$$][^ \]*\)@ $$(..)\1@g' \
|
||||
-e 's@^\([^ \/$$][^ \]*\)@$$(..)\1@g'
|
||||
endif
|
||||
|
||||
ifdef gen-py-const-headers
|
||||
# We'll use a static pattern rule to match .pysym files with their
|
||||
# corresponding generated .py files.
|
||||
# The generated .py files go in the submodule's dir in the glibc source dir.
|
||||
py-const-files := $(patsubst %.pysym,%.py,$(gen-py-const-headers))
|
||||
py-const-dir := $(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.
|
||||
|
3
NEWS
3
NEWS
@ -47,9 +47,6 @@ Version 2.24
|
||||
direction of negative infinity. These are currently enabled as GNU
|
||||
extensions.
|
||||
|
||||
* Initial support is added for pretty printing of pthread variables in gdb.
|
||||
See pretty-printers/README for details on how to use it.
|
||||
|
||||
Security related changes:
|
||||
|
||||
* An unnecessary stack copy in _nss_dns_getnetbyname_r was removed. It
|
||||
|
5
Rules
5
Rules
@ -98,11 +98,6 @@ tests: $(tests:%=$(objpfx)%.out) $(tests-special)
|
||||
xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
|
||||
endif
|
||||
|
||||
# Generate constant files for Python pretty printers if required.
|
||||
ifdef py-const
|
||||
others: $(py-const)
|
||||
endif
|
||||
|
||||
tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
|
||||
xtests-special-notdir = $(patsubst $(objpfx)%, %, $(xtests-special))
|
||||
tests:
|
||||
|
@ -307,7 +307,6 @@ gen-as-const-headers = pthread-errnos.sym \
|
||||
unwindbuf.sym \
|
||||
lowlevelrobustlock.sym pthread-pi-defines.sym
|
||||
|
||||
gen-py-const-headers = nptl_lock_constants.pysym
|
||||
|
||||
LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
|
||||
|
||||
|
@ -1,593 +0,0 @@
|
||||
# Pretty printers for the NPTL lock types.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""This file contains the gdb pretty printers for the following types:
|
||||
|
||||
* pthread_mutex_t
|
||||
* pthread_mutexattr_t
|
||||
* pthread_cond_t
|
||||
* pthread_condattr_t
|
||||
* pthread_rwlock_t
|
||||
* pthread_rwlockattr_t
|
||||
|
||||
You can check which printers are registered and enabled by issuing the
|
||||
'info pretty-printer' gdb command. Printers should trigger automatically when
|
||||
trying to print a variable of one of the types mentioned above.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import gdb
|
||||
from nptl_lock_constants import *
|
||||
|
||||
MUTEX_TYPES = {
|
||||
PTHREAD_MUTEX_NORMAL: ('Type', 'Normal'),
|
||||
PTHREAD_MUTEX_RECURSIVE: ('Type', 'Recursive'),
|
||||
PTHREAD_MUTEX_ERRORCHECK: ('Type', 'Error check'),
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP: ('Type', 'Adaptive')
|
||||
}
|
||||
|
||||
class MutexPrinter(object):
|
||||
"""Pretty printer for pthread_mutex_t."""
|
||||
|
||||
def __init__(self, mutex):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
mutex: A gdb.value representing a pthread_mutex_t.
|
||||
"""
|
||||
|
||||
data = mutex['__data']
|
||||
self.lock = data['__lock']
|
||||
self.count = data['__count']
|
||||
self.owner = data['__owner']
|
||||
self.kind = data['__kind']
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_mutex_t.
|
||||
"""
|
||||
|
||||
return 'pthread_mutex_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_mutex_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the mutex's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
self.read_type()
|
||||
self.read_status()
|
||||
self.read_attributes()
|
||||
self.read_misc_info()
|
||||
|
||||
def read_type(self):
|
||||
"""Read the mutex's type."""
|
||||
|
||||
mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
|
||||
|
||||
# mutex_type must be casted to int because it's a gdb.Value
|
||||
self.values.append(MUTEX_TYPES[int(mutex_type)])
|
||||
|
||||
def read_status(self):
|
||||
"""Read the mutex's status.
|
||||
|
||||
For architectures which support lock elision, this method reads
|
||||
whether the mutex appears as locked in memory (i.e. it may show it as
|
||||
unlocked even after calling pthread_mutex_lock).
|
||||
"""
|
||||
|
||||
if self.kind == PTHREAD_MUTEX_DESTROYED:
|
||||
self.values.append(('Status', 'Destroyed'))
|
||||
elif self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
|
||||
self.read_status_robust()
|
||||
else:
|
||||
self.read_status_no_robust()
|
||||
|
||||
def read_status_robust(self):
|
||||
"""Read the status of a robust mutex.
|
||||
|
||||
In glibc robust mutexes are implemented in a very different way than
|
||||
non-robust ones. This method reads their locking status,
|
||||
whether it may have waiters, their registered owner (if any),
|
||||
whether the owner is alive or not, and the status of the state
|
||||
they're protecting.
|
||||
"""
|
||||
|
||||
if self.lock == PTHREAD_MUTEX_UNLOCKED:
|
||||
self.values.append(('Status', 'Unlocked'))
|
||||
else:
|
||||
if self.lock & FUTEX_WAITERS:
|
||||
self.values.append(('Status', 'Locked, possibly with waiters'))
|
||||
else:
|
||||
self.values.append(('Status',
|
||||
'Locked, possibly with no waiters'))
|
||||
|
||||
if self.lock & FUTEX_OWNER_DIED:
|
||||
self.values.append(('Owner ID', '%d (dead)' % self.owner))
|
||||
else:
|
||||
self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
|
||||
|
||||
if self.owner == PTHREAD_MUTEX_INCONSISTENT:
|
||||
self.values.append(('State protected by this mutex',
|
||||
'Inconsistent'))
|
||||
elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
|
||||
self.values.append(('State protected by this mutex',
|
||||
'Not recoverable'))
|
||||
|
||||
def read_status_no_robust(self):
|
||||
"""Read the status of a non-robust mutex.
|
||||
|
||||
Read info on whether the mutex is locked, if it may have waiters
|
||||
and its owner (if any).
|
||||
"""
|
||||
|
||||
lock_value = self.lock
|
||||
|
||||
if self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
|
||||
lock_value &= ~(PTHREAD_MUTEX_PRIO_CEILING_MASK)
|
||||
|
||||
if lock_value == PTHREAD_MUTEX_UNLOCKED:
|
||||
self.values.append(('Status', 'Unlocked'))
|
||||
else:
|
||||
if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
|
||||
waiters = self.lock & FUTEX_WAITERS
|
||||
owner = self.lock & FUTEX_TID_MASK
|
||||
else:
|
||||
# Mutex protocol is PP or none
|
||||
waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
|
||||
owner = self.owner
|
||||
|
||||
if waiters:
|
||||
self.values.append(('Status', 'Locked, possibly with waiters'))
|
||||
else:
|
||||
self.values.append(('Status',
|
||||
'Locked, possibly with no waiters'))
|
||||
|
||||
self.values.append(('Owner ID', owner))
|
||||
|
||||
def read_attributes(self):
|
||||
"""Read the mutex's attributes."""
|
||||
|
||||
if self.kind != PTHREAD_MUTEX_DESTROYED:
|
||||
if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
|
||||
self.values.append(('Robust', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Robust', 'No'))
|
||||
|
||||
# In glibc, robust mutexes always have their pshared flag set to
|
||||
# 'shared' regardless of what the pshared flag of their
|
||||
# mutexattr was. Therefore a robust mutex will act as shared
|
||||
# even if it was initialized with a 'private' mutexattr.
|
||||
if self.kind & PTHREAD_MUTEX_PSHARED_BIT:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
|
||||
self.values.append(('Protocol', 'Priority inherit'))
|
||||
elif self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
|
||||
prio_ceiling = ((self.lock & PTHREAD_MUTEX_PRIO_CEILING_MASK)
|
||||
>> PTHREAD_MUTEX_PRIO_CEILING_SHIFT)
|
||||
|
||||
self.values.append(('Protocol', 'Priority protect'))
|
||||
self.values.append(('Priority ceiling', prio_ceiling))
|
||||
else:
|
||||
# PTHREAD_PRIO_NONE
|
||||
self.values.append(('Protocol', 'None'))
|
||||
|
||||
def read_misc_info(self):
|
||||
"""Read miscellaneous info on the mutex.
|
||||
|
||||
For now this reads the number of times a recursive mutex was locked
|
||||
by the same thread.
|
||||
"""
|
||||
|
||||
mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
|
||||
|
||||
if mutex_type == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
|
||||
self.values.append(('Times locked recursively', self.count))
|
||||
|
||||
class MutexAttributesPrinter(object):
|
||||
"""Pretty printer for pthread_mutexattr_t.
|
||||
|
||||
In the NPTL this is a type that's always casted to struct pthread_mutexattr
|
||||
which has a single 'mutexkind' field containing the actual attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, mutexattr):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
mutexattr: A gdb.value representing a pthread_mutexattr_t.
|
||||
"""
|
||||
|
||||
mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
|
||||
self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_mutexattr_t.
|
||||
"""
|
||||
|
||||
return 'pthread_mutexattr_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_mutexattr_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the mutexattr's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
mutexattr_type = (self.mutexattr
|
||||
& ~PTHREAD_MUTEXATTR_FLAG_BITS
|
||||
& ~PTHREAD_MUTEX_NO_ELISION_NP)
|
||||
|
||||
# mutexattr_type must be casted to int because it's a gdb.Value
|
||||
self.values.append(MUTEX_TYPES[int(mutexattr_type)])
|
||||
|
||||
if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
|
||||
self.values.append(('Robust', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Robust', 'No'))
|
||||
|
||||
if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
|
||||
PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
|
||||
|
||||
if protocol == PTHREAD_PRIO_NONE:
|
||||
self.values.append(('Protocol', 'None'))
|
||||
elif protocol == PTHREAD_PRIO_INHERIT:
|
||||
self.values.append(('Protocol', 'Priority inherit'))
|
||||
elif protocol == PTHREAD_PRIO_PROTECT:
|
||||
self.values.append(('Protocol', 'Priority protect'))
|
||||
|
||||
CLOCK_IDS = {
|
||||
CLOCK_REALTIME: 'CLOCK_REALTIME',
|
||||
CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
|
||||
CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
|
||||
CLOCK_THREAD_CPUTIME_ID: 'CLOCK_THREAD_CPUTIME_ID',
|
||||
CLOCK_MONOTONIC_RAW: 'CLOCK_MONOTONIC_RAW',
|
||||
CLOCK_REALTIME_COARSE: 'CLOCK_REALTIME_COARSE',
|
||||
CLOCK_MONOTONIC_COARSE: 'CLOCK_MONOTONIC_COARSE'
|
||||
}
|
||||
|
||||
class ConditionVariablePrinter(object):
|
||||
"""Pretty printer for pthread_cond_t."""
|
||||
|
||||
def __init__(self, cond):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
cond: A gdb.value representing a pthread_cond_t.
|
||||
"""
|
||||
|
||||
data = cond['__data']
|
||||
self.total_seq = data['__total_seq']
|
||||
self.mutex = data['__mutex']
|
||||
self.nwaiters = data['__nwaiters']
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_cond_t.
|
||||
"""
|
||||
|
||||
return 'pthread_cond_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_cond_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the condvar's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
self.read_status()
|
||||
self.read_attributes()
|
||||
self.read_mutex_info()
|
||||
|
||||
def read_status(self):
|
||||
"""Read the status of the condvar.
|
||||
|
||||
This method reads whether the condvar is destroyed and how many threads
|
||||
are waiting for it.
|
||||
"""
|
||||
|
||||
if self.total_seq == PTHREAD_COND_DESTROYED:
|
||||
self.values.append(('Status', 'Destroyed'))
|
||||
|
||||
self.values.append(('Threads waiting for this condvar',
|
||||
self.nwaiters >> COND_NWAITERS_SHIFT))
|
||||
|
||||
def read_attributes(self):
|
||||
"""Read the condvar's attributes."""
|
||||
|
||||
clock_id = self.nwaiters & ((1 << COND_NWAITERS_SHIFT) - 1)
|
||||
|
||||
# clock_id must be casted to int because it's a gdb.Value
|
||||
self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
|
||||
|
||||
shared = (self.mutex == PTHREAD_COND_SHARED)
|
||||
|
||||
if shared:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
def read_mutex_info(self):
|
||||
"""Read the data of the mutex this condvar is bound to.
|
||||
|
||||
A pthread_cond_t's __data.__mutex member is a void * which
|
||||
must be casted to pthread_mutex_t *. For shared condvars, this
|
||||
member isn't recorded and has a value of ~0l instead.
|
||||
"""
|
||||
|
||||
if self.mutex and self.mutex != PTHREAD_COND_SHARED:
|
||||
mutex_type = gdb.lookup_type('pthread_mutex_t')
|
||||
mutex = self.mutex.cast(mutex_type.pointer()).dereference()
|
||||
|
||||
self.values.append(('Mutex', mutex))
|
||||
|
||||
class ConditionVariableAttributesPrinter(object):
|
||||
"""Pretty printer for pthread_condattr_t.
|
||||
|
||||
In the NPTL this is a type that's always casted to struct pthread_condattr,
|
||||
which has a single 'value' field containing the actual attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, condattr):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
condattr: A gdb.value representing a pthread_condattr_t.
|
||||
"""
|
||||
|
||||
condattr_struct = gdb.lookup_type('struct pthread_condattr')
|
||||
self.condattr = condattr.cast(condattr_struct)['value']
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_condattr_t.
|
||||
"""
|
||||
|
||||
return 'pthread_condattr_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_condattr_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the condattr's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
clock_id = self.condattr & ((1 << COND_NWAITERS_SHIFT) - 1)
|
||||
|
||||
# clock_id must be casted to int because it's a gdb.Value
|
||||
self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
|
||||
|
||||
if self.condattr & 1:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
class RWLockPrinter(object):
|
||||
"""Pretty printer for pthread_rwlock_t."""
|
||||
|
||||
def __init__(self, rwlock):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
rwlock: A gdb.value representing a pthread_rwlock_t.
|
||||
"""
|
||||
|
||||
data = rwlock['__data']
|
||||
self.readers = data['__nr_readers']
|
||||
self.queued_readers = data['__nr_readers_queued']
|
||||
self.queued_writers = data['__nr_writers_queued']
|
||||
self.writer_id = data['__writer']
|
||||
self.shared = data['__shared']
|
||||
self.prefers_writers = data['__flags']
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_rwlock_t.
|
||||
"""
|
||||
|
||||
return 'pthread_rwlock_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_rwlock_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the rwlock's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
self.read_status()
|
||||
self.read_attributes()
|
||||
|
||||
def read_status(self):
|
||||
"""Read the status of the rwlock."""
|
||||
|
||||
# Right now pthread_rwlock_destroy doesn't do anything, so there's no
|
||||
# way to check if an rwlock is destroyed.
|
||||
|
||||
if self.writer_id:
|
||||
self.values.append(('Status', 'Locked (Write)'))
|
||||
self.values.append(('Writer ID', self.writer_id))
|
||||
elif self.readers:
|
||||
self.values.append(('Status', 'Locked (Read)'))
|
||||
self.values.append(('Readers', self.readers))
|
||||
else:
|
||||
self.values.append(('Status', 'Unlocked'))
|
||||
|
||||
self.values.append(('Queued readers', self.queued_readers))
|
||||
self.values.append(('Queued writers', self.queued_writers))
|
||||
|
||||
def read_attributes(self):
|
||||
"""Read the attributes of the rwlock."""
|
||||
|
||||
if self.shared:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
if self.prefers_writers:
|
||||
self.values.append(('Prefers', 'Writers'))
|
||||
else:
|
||||
self.values.append(('Prefers', 'Readers'))
|
||||
|
||||
class RWLockAttributesPrinter(object):
|
||||
"""Pretty printer for pthread_rwlockattr_t.
|
||||
|
||||
In the NPTL this is a type that's always casted to
|
||||
struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
|
||||
containing the actual attributes.
|
||||
"""
|
||||
|
||||
def __init__(self, rwlockattr):
|
||||
"""Initialize the printer's internal data structures.
|
||||
|
||||
Args:
|
||||
rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
|
||||
"""
|
||||
|
||||
rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
|
||||
self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
|
||||
self.values = []
|
||||
self.read_values()
|
||||
|
||||
def to_string(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_rwlockattr_t.
|
||||
"""
|
||||
|
||||
return 'pthread_rwlockattr_t'
|
||||
|
||||
def children(self):
|
||||
"""gdb API function.
|
||||
|
||||
This is called from gdb when we try to print a pthread_rwlockattr_t.
|
||||
"""
|
||||
|
||||
return self.values
|
||||
|
||||
def read_values(self):
|
||||
"""Read the rwlockattr's info and store it in self.values.
|
||||
|
||||
The data contained in self.values will be returned by the Iterator
|
||||
created in self.children.
|
||||
"""
|
||||
|
||||
rwlock_type = self.rwlockattr['lockkind']
|
||||
shared = self.rwlockattr['pshared']
|
||||
|
||||
if shared == PTHREAD_PROCESS_SHARED:
|
||||
self.values.append(('Shared', 'Yes'))
|
||||
else:
|
||||
# PTHREAD_PROCESS_PRIVATE
|
||||
self.values.append(('Shared', 'No'))
|
||||
|
||||
if (rwlock_type == PTHREAD_RWLOCK_PREFER_READER_NP or
|
||||
rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NP):
|
||||
# This is a known bug. Using PTHREAD_RWLOCK_PREFER_WRITER_NP will
|
||||
# still make the rwlock prefer readers.
|
||||
self.values.append(('Prefers', 'Readers'))
|
||||
elif rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
|
||||
self.values.append(('Prefers', 'Writers'))
|
||||
|
||||
def register(objfile):
|
||||
"""Register the pretty printers within the given objfile."""
|
||||
|
||||
printer = gdb.printing.RegexpCollectionPrettyPrinter('glibc pthread locks')
|
||||
|
||||
printer.add_printer('pthread_mutex_t', r'^pthread_mutex_t$',
|
||||
MutexPrinter)
|
||||
printer.add_printer('pthread_mutexattr_t', r'^pthread_mutexattr_t$',
|
||||
MutexAttributesPrinter)
|
||||
printer.add_printer('pthread_cond_t', r'^pthread_cond_t$',
|
||||
ConditionVariablePrinter)
|
||||
printer.add_printer('pthread_condattr_t', r'^pthread_condattr_t$',
|
||||
ConditionVariableAttributesPrinter)
|
||||
printer.add_printer('pthread_rwlock_t', r'^pthread_rwlock_t$',
|
||||
RWLockPrinter)
|
||||
printer.add_printer('pthread_rwlockattr_t', r'^pthread_rwlockattr_t$',
|
||||
RWLockAttributesPrinter)
|
||||
|
||||
gdb.printing.register_pretty_printer(objfile, printer)
|
||||
|
||||
register(gdb.current_objfile())
|
@ -1,75 +0,0 @@
|
||||
#include <pthreadP.h>
|
||||
|
||||
-- Mutex types
|
||||
PTHREAD_MUTEX_KIND_MASK PTHREAD_MUTEX_KIND_MASK_NP
|
||||
PTHREAD_MUTEX_NORMAL
|
||||
PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
|
||||
PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP
|
||||
PTHREAD_MUTEX_ADAPTIVE_NP
|
||||
|
||||
-- Mutex status
|
||||
-- These are hardcoded all over the code; there are no enums/macros for them.
|
||||
PTHREAD_MUTEX_DESTROYED -1
|
||||
PTHREAD_MUTEX_UNLOCKED 0
|
||||
PTHREAD_MUTEX_LOCKED_NO_WAITERS 1
|
||||
|
||||
-- For robust mutexes
|
||||
PTHREAD_MUTEX_INCONSISTENT
|
||||
PTHREAD_MUTEX_NOTRECOVERABLE
|
||||
FUTEX_OWNER_DIED
|
||||
|
||||
-- For robust and PI mutexes
|
||||
FUTEX_WAITERS
|
||||
FUTEX_TID_MASK
|
||||
|
||||
-- Mutex attributes
|
||||
PTHREAD_MUTEX_ROBUST_NORMAL_NP
|
||||
PTHREAD_MUTEX_PRIO_INHERIT_NP
|
||||
PTHREAD_MUTEX_PRIO_PROTECT_NP
|
||||
PTHREAD_MUTEX_PSHARED_BIT
|
||||
PTHREAD_MUTEX_PRIO_CEILING_SHIFT
|
||||
PTHREAD_MUTEX_PRIO_CEILING_MASK
|
||||
|
||||
-- Mutex attribute flags
|
||||
PTHREAD_MUTEXATTR_PROTOCOL_SHIFT
|
||||
PTHREAD_MUTEXATTR_PROTOCOL_MASK
|
||||
PTHREAD_MUTEXATTR_PRIO_CEILING_MASK
|
||||
PTHREAD_MUTEXATTR_FLAG_ROBUST
|
||||
PTHREAD_MUTEXATTR_FLAG_PSHARED
|
||||
PTHREAD_MUTEXATTR_FLAG_BITS
|
||||
PTHREAD_MUTEX_NO_ELISION_NP
|
||||
|
||||
-- Priority protocols
|
||||
PTHREAD_PRIO_NONE
|
||||
PTHREAD_PRIO_INHERIT
|
||||
PTHREAD_PRIO_PROTECT
|
||||
|
||||
-- These values are hardcoded as well:
|
||||
-- Value of __mutex for shared condvars.
|
||||
PTHREAD_COND_SHARED ~0l
|
||||
|
||||
-- Value of __total_seq for destroyed condvars.
|
||||
PTHREAD_COND_DESTROYED -1ull
|
||||
|
||||
-- __nwaiters encodes the number of threads waiting on a condvar
|
||||
-- and the clock ID.
|
||||
-- __nwaiters >> COND_NWAITERS_SHIFT gives us the number of waiters.
|
||||
COND_NWAITERS_SHIFT
|
||||
|
||||
-- Condvar clock IDs
|
||||
CLOCK_REALTIME
|
||||
CLOCK_MONOTONIC
|
||||
CLOCK_PROCESS_CPUTIME_ID
|
||||
CLOCK_THREAD_CPUTIME_ID
|
||||
CLOCK_MONOTONIC_RAW
|
||||
CLOCK_REALTIME_COARSE
|
||||
CLOCK_MONOTONIC_COARSE
|
||||
|
||||
-- Rwlock attributes
|
||||
PTHREAD_RWLOCK_PREFER_READER_NP
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NP
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
|
||||
|
||||
-- 'Shared' attribute values
|
||||
PTHREAD_PROCESS_PRIVATE
|
||||
PTHREAD_PROCESS_SHARED
|
@ -1,82 +0,0 @@
|
||||
# Makefile for the Python pretty printers.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This contains rules for building and running the pretty printer tests.
|
||||
|
||||
subdir := pretty-printers
|
||||
tests-need-hardcoded-path := yes
|
||||
|
||||
include ../Makeconfig
|
||||
|
||||
PYTHON := python
|
||||
|
||||
tests-pretty-printers := test-mutex-attributes test-mutex-printer \
|
||||
test-condvar-attributes test-condvar-printer \
|
||||
test-rwlock-attributes test-rwlock-printer
|
||||
|
||||
# Add the test programs to test-srcs so that they'll be compiled regardless
|
||||
# of whether we should actually run them.
|
||||
test-srcs := $(tests-pretty-printers)
|
||||
|
||||
ifeq ($(build-shared),yes)
|
||||
nptl-tests-libs := $(shared-thread-library)
|
||||
else
|
||||
nptl-tests-libs := $(static-thread-library)
|
||||
endif
|
||||
|
||||
# The test programs need to be compiled without optimizations so they won't
|
||||
# confuse gdb. We could use either the 'GCC optimize' pragma or the 'optimize'
|
||||
# function attribute to achieve this; however, at least on ARM, gcc always
|
||||
# produces different debugging symbols when invoked with a -O greater than 0
|
||||
# than when invoked with -O0, regardless of anything else we're using
|
||||
# to suppress optimizations. Therefore, we need to explicitly pass -O0 to it
|
||||
# through CFLAGS.
|
||||
# Additionally, the build system will try to -include $(common-objpfx)/config.h
|
||||
# when compiling the tests, which will throw an error if some special macros
|
||||
# (such as __OPTIMIZE__ and IS_IN_BUILD) aren't defined. To avoid this, we
|
||||
# tell gcc to define IS_IN_build.
|
||||
CFLAGS-test-mutex-attributes.c := -O0 -ggdb3 -DIS_IN_build
|
||||
CFLAGS-test-mutex-printer.c := -O0 -ggdb3 -DIS_IN_build
|
||||
CFLAGS-test-condvar-attributes.c := -O0 -ggdb3 -DIS_IN_build
|
||||
CFLAGS-test-condvar-printer.c := -O0 -ggdb3 -DIS_IN_build
|
||||
CFLAGS-test-rwlock-attributes.c := -O0 -ggdb3 -DIS_IN_build
|
||||
CFLAGS-test-rwlock-printer.c := -O0 -ggdb3 -DIS_IN_build
|
||||
|
||||
tests-pretty-printers-dest := $(addprefix $(objpfx),$(tests-pretty-printers))
|
||||
tests-pretty-printers-pp := $(addsuffix -pp,$(tests-pretty-printers-dest))
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += $(tests-pretty-printers-pp)
|
||||
endif
|
||||
|
||||
include ../Rules
|
||||
|
||||
# Add the thread libraries to the prerequisites of the NPTL test programs.
|
||||
$(tests-pretty-printers-dest): $(nptl-tests-libs)
|
||||
|
||||
# We won't actually create any *-pp files, so we mark this target as PHONY
|
||||
# to ensure it always runs when required.
|
||||
.PHONY: $(tests-pretty-printers-pp)
|
||||
|
||||
# Static pattern rule that matches the test-* targets to their .c and .py
|
||||
# prerequisites. It'll run the corresponding test script for each test program
|
||||
# we compiled. test_common.py must be present for all.
|
||||
$(tests-pretty-printers-pp): $(objpfx)%-pp: $(objpfx)% %.py test_common.py
|
||||
$(test-wrapper-env) $(PYTHON) $*.py $*.c $(objpfx)$*; \
|
||||
$(evaluate-test)
|
@ -1,130 +0,0 @@
|
||||
README for the glibc Python pretty printers
|
||||
-------------------------------------------
|
||||
|
||||
Pretty printers are gdb extensions that allow it to print useful, human-readable
|
||||
information about a program's variables. For example, for a pthread_mutex_t
|
||||
gdb would usually output something like this:
|
||||
|
||||
(gdb) print mutex
|
||||
$1 = {
|
||||
__data = {
|
||||
__lock = 22020096,
|
||||
__count = 0,
|
||||
__owner = 0,
|
||||
__nusers = 0,
|
||||
__kind = 576,
|
||||
__spins = 0,
|
||||
__elision = 0,
|
||||
__list = {
|
||||
__prev = 0x0,
|
||||
__next = 0x0
|
||||
}
|
||||
},
|
||||
__size = "\000\000P\001", '\000' <repeats 12 times>, "@\002", '\000' <repeats 21 times>,
|
||||
__align = 22020096
|
||||
}
|
||||
|
||||
However, with a pretty printer gdb will output something like this:
|
||||
|
||||
(gdb) print mutex
|
||||
$1 = pthread_mutex_t = {
|
||||
Type = Normal,
|
||||
Status = Unlocked,
|
||||
Robust = No,
|
||||
Shared = No,
|
||||
Protocol = Priority protect,
|
||||
Priority ceiling = 42
|
||||
}
|
||||
|
||||
Before printing a value, gdb will first check if there's a pretty printer
|
||||
registered for it. If there is, it'll use it, otherwise it'll print the value
|
||||
as usual. Pretty printers can be registered in various ways; for our purposes
|
||||
we register them for the current objfile by calling
|
||||
gdb.printing.register_pretty_printer().
|
||||
|
||||
Currently our printers are based on gdb.RegexpCollectionPrettyPrinter, which
|
||||
means they'll be triggered if the type of the variable we're printing matches
|
||||
a given regular expression. For example, MutexPrinter will be triggered if
|
||||
our variable's type matches the regexp '^pthread_mutex_t$'.
|
||||
|
||||
Besides the printers themselves, each module may have a constants file which the
|
||||
printers will import. These constants are generated from C headers during the
|
||||
build process, and need to be in the Python search path when loading the
|
||||
printers.
|
||||
|
||||
|
||||
Installing and loading
|
||||
----------------------
|
||||
|
||||
The pretty printers and their constant files may be installed in different paths
|
||||
for each distro, though gdb should be able to automatically load them by itself.
|
||||
When in doubt, you can use the 'info pretty printer' gdb command to list the
|
||||
loaded pretty printers.
|
||||
|
||||
If the printers aren't automatically loaded for some reason, you should add the
|
||||
following to your .gdbinit:
|
||||
|
||||
python
|
||||
import sys
|
||||
sys.path.insert(0, '/path/to/constants/file/directory')
|
||||
end
|
||||
|
||||
source /path/to/printers.py
|
||||
|
||||
If you're building glibc manually, '/path/to/constants/file/directory' should be
|
||||
'/path/to/glibc-build/submodule', where 'submodule' is e.g. nptl.
|
||||
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
The pretty printers come with a small test suite based on PExpect, which is a
|
||||
Python module with Expect-like features for spawning and controlling interactive
|
||||
programs. Each printer has a corresponding C program and a Python script
|
||||
that uses PExpect to drive gdb through the program and compare its output to
|
||||
the expected printer's.
|
||||
|
||||
The tests run on the glibc host, which is assumed to have both gdb and PExpect;
|
||||
if any of those is absent the tests will fail with code 77 (UNSUPPORTED).
|
||||
Native builds can be tested simply by doing 'make check'; cross builds must use
|
||||
cross-test-ssh.sh as test-wrapper, like this:
|
||||
|
||||
make test-wrapper='/path/to/scripts/cross-test-ssh.sh user@host' check
|
||||
|
||||
(Remember to share the build system's filesystem with the glibc host's through
|
||||
NFS or something similar).
|
||||
|
||||
Running 'make check' on a cross build will only compile the test programs,
|
||||
without running the scripts.
|
||||
|
||||
|
||||
Known issues
|
||||
------------
|
||||
|
||||
* Pretty printers are inherently coupled to the code they're targetting, thus
|
||||
any changes to the target code must also update the corresponding printers.
|
||||
On the plus side, the printer code itself may serve as a kind of documentation
|
||||
for the target code.
|
||||
|
||||
* Older versions of the gdb Python API have a bug where
|
||||
gdb.RegexpCollectionPrettyPrinter would not be able to get a value's real type
|
||||
if it was typedef'd. This would cause gdb to ignore the pretty printers for
|
||||
types like pthread_mutex_t, which is defined as:
|
||||
|
||||
typedef union
|
||||
{
|
||||
...
|
||||
} pthread_mutex_t;
|
||||
|
||||
This was fixed in commit 1b588015839caafc608a6944a78aea170f5fb2f6. However,
|
||||
typedef'ing an already typedef'd type may cause a similar issue, e.g.:
|
||||
|
||||
typedef pthread_mutex_t mutex;
|
||||
mutex a_mutex;
|
||||
|
||||
Here, trying to print a_mutex won't trigger the pthread_mutex_t printer.
|
||||
|
||||
* The test programs must be compiled without optimizations. This is necessary
|
||||
because the test scripts rely on the C code structure being preserved when
|
||||
stepping through the programs. Things like aggressive instruction reordering
|
||||
or optimizing variables out may make this kind of testing impossible.
|
@ -1,94 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
# Common tests for the ConditionVariablePrinter and
|
||||
# ConditionVariableAttributesPrinter classes.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
condvar_var = 'condvar'
|
||||
condvar_to_string = 'pthread_cond_t'
|
||||
|
||||
attr_var = 'attr'
|
||||
attr_to_string = 'pthread_condattr_t'
|
||||
|
||||
break_at(test_source, 'Set clock')
|
||||
continue_cmd() # Go to test_setclock
|
||||
next_cmd(2)
|
||||
test_printer(condvar_var, condvar_to_string, {'Clock ID': 'CLOCK_REALTIME'})
|
||||
test_printer(attr_var, attr_to_string, {'Clock ID': 'CLOCK_REALTIME'})
|
||||
|
||||
break_at(test_source, 'Set shared')
|
||||
continue_cmd() # Go to test_setpshared
|
||||
next_cmd(2)
|
||||
test_printer(condvar_var, condvar_to_string, {'Shared': 'Yes'})
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
|
||||
next_cmd(2)
|
||||
test_printer(condvar_var, condvar_to_string, {'Shared': 'No'})
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'No'})
|
||||
|
||||
continue_cmd() # Exit
|
||||
|
||||
except (NoLineError, pexpect.TIMEOUT) as exception:
|
||||
print('Error: {0}'.format(exception))
|
||||
result = FAIL
|
||||
|
||||
exit(result)
|
@ -1,57 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
# Common tests for the ConditionVariablePrinter class.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
var = 'condvar'
|
||||
to_string = 'pthread_cond_t'
|
||||
|
||||
break_at(test_source, 'Test status (destroyed)')
|
||||
continue_cmd() # Go to test_status_destroyed
|
||||
test_printer(var, to_string, {'Status': 'Destroyed'})
|
||||
|
||||
continue_cmd() # Exit
|
||||
|
||||
except (NoLineError, pexpect.TIMEOUT) as exception:
|
||||
print('Error: {0}'.format(exception))
|
||||
result = FAIL
|
||||
|
||||
exit(result)
|
@ -1,144 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
# Common tests for the MutexPrinter and MutexAttributesPrinter classes.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
PRIOCEILING = 42
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
mutex_var = 'mutex'
|
||||
mutex_to_string = 'pthread_mutex_t'
|
||||
|
||||
attr_var = 'attr'
|
||||
attr_to_string = 'pthread_mutexattr_t'
|
||||
|
||||
break_at(test_source, 'Set type')
|
||||
continue_cmd() # Go to test_settype
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Type': 'Error check'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Type': 'Error check'})
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Type': 'Recursive'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Type': 'Recursive'})
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Type': 'Normal'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Type': 'Normal'})
|
||||
|
||||
break_at(test_source, 'Set robust')
|
||||
continue_cmd() # Go to test_setrobust
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Robust': 'Yes'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Robust': 'Yes'})
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Robust': 'No'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Robust': 'No'})
|
||||
|
||||
break_at(test_source, 'Set shared')
|
||||
continue_cmd() # Go to test_setpshared
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Shared': 'Yes'})
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'No'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Shared': 'No'})
|
||||
|
||||
break_at(test_source, 'Set protocol')
|
||||
continue_cmd() # Go to test_setprotocol
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Protocol': 'Priority inherit'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Protocol': 'Priority inherit'})
|
||||
next_cmd(2)
|
||||
test_printer(attr_var, attr_to_string, {'Protocol': 'Priority protect'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Protocol': 'Priority protect'})
|
||||
next_cmd(2)
|
||||
test_printer(mutex_var, mutex_to_string, {'Priority ceiling':
|
||||
str(PRIOCEILING)})
|
||||
next_cmd()
|
||||
test_printer(attr_var, attr_to_string, {'Protocol': 'None'})
|
||||
test_printer(mutex_var, mutex_to_string, {'Protocol': 'None'})
|
||||
|
||||
continue_cmd() # Exit
|
||||
|
||||
except (NoLineError, pexpect.TIMEOUT) as exception:
|
||||
print('Error: {0}'.format(exception))
|
||||
result = FAIL
|
||||
|
||||
exit(result)
|
@ -1,151 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
# Tests for the MutexPrinter class.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
var = 'mutex'
|
||||
to_string = 'pthread_mutex_t'
|
||||
|
||||
break_at(test_source, 'Test status (destroyed)')
|
||||
continue_cmd() # Go to test_status_destroyed
|
||||
test_printer(var, to_string, {'Status': 'Destroyed'})
|
||||
|
||||
break_at(test_source, 'Test status (non-robust)')
|
||||
continue_cmd() # Go to test_status_no_robust
|
||||
test_printer(var, to_string, {'Status': 'Unlocked'})
|
||||
next_cmd()
|
||||
thread_id = get_current_thread_lwpid()
|
||||
test_printer(var, to_string, {'Status': 'Locked, possibly with no waiters',
|
||||
'Owner ID': thread_id})
|
||||
|
||||
break_at(test_source, 'Test status (robust)')
|
||||
continue_cmd() # Go to test_status_robust
|
||||
test_printer(var, to_string, {'Status': 'Unlocked'})
|
||||
|
||||
# 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
|
||||
|
||||
exit(result)
|
@ -1,98 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
# Common tests for the RWLockPrinter and RWLockAttributesPrinter classes.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
rwlock_var = 'rwlock'
|
||||
rwlock_to_string = 'pthread_rwlock_t'
|
||||
|
||||
attr_var = 'attr'
|
||||
attr_to_string = 'pthread_rwlockattr_t'
|
||||
|
||||
break_at(test_source, 'Set kind')
|
||||
continue_cmd() # Go to test_setkind_np
|
||||
next_cmd(2)
|
||||
test_printer(rwlock_var, rwlock_to_string, {'Prefers': 'Readers'})
|
||||
test_printer(attr_var, attr_to_string, {'Prefers': 'Readers'})
|
||||
next_cmd(2)
|
||||
test_printer(rwlock_var, rwlock_to_string, {'Prefers': 'Writers'})
|
||||
test_printer(attr_var, attr_to_string, {'Prefers': 'Writers'})
|
||||
|
||||
break_at(test_source, 'Set shared')
|
||||
continue_cmd() # Go to test_setpshared
|
||||
next_cmd(2)
|
||||
test_printer(rwlock_var, rwlock_to_string, {'Shared': 'Yes'})
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'Yes'})
|
||||
next_cmd(2)
|
||||
test_printer(rwlock_var, rwlock_to_string, {'Shared': 'No'})
|
||||
test_printer(attr_var, attr_to_string, {'Shared': 'No'})
|
||||
|
||||
continue_cmd() # Exit
|
||||
|
||||
except (NoLineError, pexpect.TIMEOUT) as exception:
|
||||
print('Error: {0}'.format(exception))
|
||||
result = FAIL
|
||||
|
||||
exit(result)
|
@ -1,78 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
# Common tests for the RWLockPrinter class.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from test_common import *
|
||||
|
||||
test_source = sys.argv[1]
|
||||
test_bin = sys.argv[2]
|
||||
result = PASS
|
||||
|
||||
try:
|
||||
init_test(test_bin)
|
||||
go_to_main()
|
||||
|
||||
var = 'rwlock'
|
||||
to_string = 'pthread_rwlock_t'
|
||||
|
||||
break_at(test_source, 'Test locking (reader)')
|
||||
continue_cmd() # Go to test_locking_reader
|
||||
test_printer(var, to_string, {'Status': 'Unlocked'})
|
||||
next_cmd()
|
||||
test_printer(var, to_string, {'Status': r'Locked \(Read\)', 'Readers': '1'})
|
||||
next_cmd()
|
||||
test_printer(var, to_string, {'Readers': '2'})
|
||||
next_cmd()
|
||||
test_printer(var, to_string, {'Readers': '3'})
|
||||
|
||||
break_at(test_source, 'Test locking (writer)')
|
||||
continue_cmd() # Go to test_locking_writer
|
||||
test_printer(var, to_string, {'Status': 'Unlocked'})
|
||||
next_cmd()
|
||||
thread_id = get_current_thread_lwpid()
|
||||
test_printer(var, to_string, {'Status': r'Locked \(Write\)',
|
||||
'Writer ID': thread_id})
|
||||
|
||||
continue_cmd() # Exit
|
||||
|
||||
except (NoLineError, pexpect.TIMEOUT) as exception:
|
||||
print('Error: {0}'.format(exception))
|
||||
result = FAIL
|
||||
|
||||
exit(result)
|
@ -1,315 +0,0 @@
|
||||
# Common functions and variables for testing the Python pretty printers.
|
||||
#
|
||||
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""These tests require PExpect.
|
||||
|
||||
Attributes:
|
||||
PASS, FAIL, UNSUPPORTED (int): Test exit codes, as per evaluate-test.sh.
|
||||
GDB (string): A string with the name of the gdb binary.
|
||||
gdb (pexpect.spawn): The gdb process, as handled by PExpect.
|
||||
gdb_prompt (raw string): A pattern for matching the gdb prompt.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
PASS = 0
|
||||
FAIL = 1
|
||||
UNSUPPORTED = 77
|
||||
GDB = 'gdb'
|
||||
|
||||
try:
|
||||
import pexpect
|
||||
except ImportError:
|
||||
print('PExpect must be installed in order to test the pretty printers.')
|
||||
exit(UNSUPPORTED)
|
||||
|
||||
if not pexpect.which(GDB):
|
||||
print('gdb must be installed in order to test the pretty printers.')
|
||||
exit(UNSUPPORTED)
|
||||
|
||||
class NoLineError(Exception):
|
||||
"""Custom exception which indicates that a test file doesn't contain
|
||||
the requested string.
|
||||
"""
|
||||
|
||||
def __init__(self, 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))
|
||||
|
||||
timeout = 1
|
||||
TIMEOUTFACTOR = os.environ.get('TIMEOUTFACTOR')
|
||||
|
||||
if TIMEOUTFACTOR:
|
||||
timeout = int(TIMEOUTFACTOR)
|
||||
|
||||
gdb = pexpect.spawn(GDB, echo=False, timeout=timeout)
|
||||
|
||||
# Set the gdb prompt to a custom one, so that user-defined prompts won't
|
||||
# interfere. We assume the user won't have his prompt set to this.
|
||||
gdb_prompt = r'gdb-test% '
|
||||
gdb.sendline('set prompt {0}'.format(gdb_prompt))
|
||||
gdb.expect(gdb_prompt)
|
||||
|
||||
def test(command, pattern):
|
||||
"""Sends 'command' to gdb and expects the given 'pattern'.
|
||||
|
||||
If 'pattern' is None, simply consumes everything up to and including
|
||||
the gdb prompt.
|
||||
|
||||
Args:
|
||||
command (string): The command we'll send to gdb.
|
||||
pattern (raw string): A pattern the gdb output should match.
|
||||
|
||||
Returns:
|
||||
string: The string that matched 'pattern', or an empty string if
|
||||
'pattern' was None.
|
||||
"""
|
||||
|
||||
match = ''
|
||||
|
||||
gdb.sendline(command)
|
||||
|
||||
if pattern:
|
||||
# PExpect does a non-greedy match for '+' and '*'. Since it can't look
|
||||
# ahead on the gdb output stream, if 'pattern' ends with a '+' or a '*'
|
||||
# we may end up matching only part of the required output.
|
||||
# To avoid this, we'll consume 'pattern' and anything that follows it
|
||||
# up to and including the gdb prompt, then extract 'pattern' later.
|
||||
index = gdb.expect([r'{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):
|
||||
"""Loads the test binary file to gdb.
|
||||
|
||||
Args:
|
||||
test_bin (string): The name of the test binary file.
|
||||
"""
|
||||
|
||||
test('file {0}'.format(test_bin), None)
|
||||
|
||||
def go_to_main():
|
||||
"""Executes a gdb 'start' command, which takes us to main."""
|
||||
|
||||
test('start', r'main')
|
||||
|
||||
def get_line_number(file_name, string):
|
||||
"""Returns the number of the line in which 'string' appears within a file.
|
||||
|
||||
Args:
|
||||
file_name (string): The name of the file we'll search through.
|
||||
string (string): The string we'll look for.
|
||||
|
||||
Returns:
|
||||
int: The number of the line in which 'string' appears, starting from 1.
|
||||
"""
|
||||
number = -1
|
||||
|
||||
with open(file_name) as src_file:
|
||||
for i, line in enumerate(src_file):
|
||||
if string in line:
|
||||
number = i + 1
|
||||
break
|
||||
|
||||
if number == -1:
|
||||
raise NoLineError(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, None)
|
||||
|
||||
def next_cmd(count=1, thread=None):
|
||||
"""Executes a gdb 'next' command.
|
||||
|
||||
If 'thread' isn't None, the command will be applied to all the threads.
|
||||
Otherwise, it'll be applied only to 'thread'.
|
||||
|
||||
Args:
|
||||
count (int): The 'count' argument of the 'next' command.
|
||||
thread (int): The number of the thread we'll apply the command to,
|
||||
as seen by gdb. If specified, it should be greater than zero.
|
||||
"""
|
||||
|
||||
if not thread:
|
||||
command = 'next'
|
||||
else:
|
||||
command = 'thread apply {0} next'
|
||||
|
||||
test('{0} {1}'.format(command, count), None)
|
||||
|
||||
def select_thread(thread):
|
||||
"""Selects the thread indicated by 'thread'.
|
||||
|
||||
Args:
|
||||
thread (int): The number of the thread we'll switch to, as seen by gdb.
|
||||
This should be greater than zero.
|
||||
"""
|
||||
|
||||
if thread > 0:
|
||||
test('thread {0}'.format(thread), None)
|
||||
|
||||
def get_current_thread_lwpid():
|
||||
"""Gets the current thread's Lightweight Process ID.
|
||||
|
||||
Returns:
|
||||
string: The current thread's LWP ID.
|
||||
"""
|
||||
|
||||
# It's easier to get the LWP ID through the Python API than the gdb CLI.
|
||||
command = 'python print(gdb.selected_thread().ptid[1])'
|
||||
|
||||
return test(command, r'[0-9]+')
|
||||
|
||||
def set_scheduler_locking(mode):
|
||||
"""Executes the gdb 'set scheduler-locking' command.
|
||||
|
||||
Args:
|
||||
mode (bool): Whether the scheduler locking mode should be 'on'.
|
||||
"""
|
||||
modes = {
|
||||
True: 'on',
|
||||
False: 'off'
|
||||
}
|
||||
|
||||
test('set scheduler-locking {0}'.format(modes[mode]), None)
|
||||
|
||||
def test_printer(var, to_string, children=None, is_ptr=True):
|
||||
""" Tests the output of a pretty printer.
|
||||
|
||||
For a variable called 'var', this tests whether its associated printer
|
||||
outputs the expected 'to_string' and children (if any).
|
||||
|
||||
Args:
|
||||
var (string): The name of the variable we'll print.
|
||||
to_string (raw string): The expected output of the printer's 'to_string'
|
||||
method.
|
||||
children (map {raw string->raw string}): A map with the expected output
|
||||
of the printer's children' method.
|
||||
is_ptr (bool): Whether 'var' is a pointer, and thus should be
|
||||
dereferenced.
|
||||
"""
|
||||
|
||||
if is_ptr:
|
||||
var = '*{0}'.format(var)
|
||||
|
||||
test('print {0}'.format(var), to_string)
|
||||
|
||||
if children:
|
||||
for name, value in children.items():
|
||||
# Children are shown as 'name = value'.
|
||||
test('print {0}'.format(var), r'{0} = {1}'.format(name, value))
|
@ -1,118 +0,0 @@
|
||||
# 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 "}"; }
|
Loading…
Reference in New Issue
Block a user