mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-21 01:12:26 +08:00
634 lines
21 KiB
Python
634 lines
21 KiB
Python
|
# Pretty printers for the NPTL lock types.
|
||
|
#
|
||
|
# Copyright (C) 2016 Free Software Foundation, Inc.
|
||
|
# This file is part of the GNU C Library.
|
||
|
#
|
||
|
# The GNU C Library is free software; you can redistribute it and/or
|
||
|
# modify it under the terms of the GNU Lesser General Public
|
||
|
# License as published by the Free Software Foundation; either
|
||
|
# version 2.1 of the License, or (at your option) any later version.
|
||
|
#
|
||
|
# The GNU C Library is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
# Lesser General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU Lesser General Public
|
||
|
# License along with the GNU C Library; if not, see
|
||
|
# <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
"""This file contains the gdb pretty printers for the following types:
|
||
|
|
||
|
* pthread_mutex_t
|
||
|
* pthread_mutexattr_t
|
||
|
* pthread_cond_t
|
||
|
* pthread_condattr_t
|
||
|
* pthread_rwlock_t
|
||
|
* pthread_rwlockattr_t
|
||
|
|
||
|
You can check which printers are registered and enabled by issuing the
|
||
|
'info pretty-printer' gdb command. Printers should trigger automatically when
|
||
|
trying to print a variable of one of the types mentioned above.
|
||
|
"""
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
import gdb
|
||
|
import gdb.printing
|
||
|
from nptl_lock_constants import *
|
||
|
|
||
|
MUTEX_TYPES = {
|
||
|
PTHREAD_MUTEX_NORMAL: ('Type', 'Normal'),
|
||
|
PTHREAD_MUTEX_RECURSIVE: ('Type', 'Recursive'),
|
||
|
PTHREAD_MUTEX_ERRORCHECK: ('Type', 'Error check'),
|
||
|
PTHREAD_MUTEX_ADAPTIVE_NP: ('Type', 'Adaptive')
|
||
|
}
|
||
|
|
||
|
class MutexPrinter(object):
|
||
|
"""Pretty printer for pthread_mutex_t."""
|
||
|
|
||
|
def __init__(self, mutex):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
mutex: A gdb.value representing a pthread_mutex_t.
|
||
|
"""
|
||
|
|
||
|
data = mutex['__data']
|
||
|
self.lock = data['__lock']
|
||
|
self.count = data['__count']
|
||
|
self.owner = data['__owner']
|
||
|
self.kind = data['__kind']
|
||
|
self.values = []
|
||
|
self.read_values()
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_mutex_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_mutex_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_mutex_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the mutex's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
self.read_type()
|
||
|
self.read_status()
|
||
|
self.read_attributes()
|
||
|
self.read_misc_info()
|
||
|
|
||
|
def read_type(self):
|
||
|
"""Read the mutex's type."""
|
||
|
|
||
|
mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
|
||
|
|
||
|
# mutex_type must be casted to int because it's a gdb.Value
|
||
|
self.values.append(MUTEX_TYPES[int(mutex_type)])
|
||
|
|
||
|
def read_status(self):
|
||
|
"""Read the mutex's status.
|
||
|
|
||
|
For architectures which support lock elision, this method reads
|
||
|
whether the mutex appears as locked in memory (i.e. it may show it as
|
||
|
unlocked even after calling pthread_mutex_lock).
|
||
|
"""
|
||
|
|
||
|
if self.kind == PTHREAD_MUTEX_DESTROYED:
|
||
|
self.values.append(('Status', 'Destroyed'))
|
||
|
elif self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
|
||
|
self.read_status_robust()
|
||
|
else:
|
||
|
self.read_status_no_robust()
|
||
|
|
||
|
def read_status_robust(self):
|
||
|
"""Read the status of a robust mutex.
|
||
|
|
||
|
In glibc robust mutexes are implemented in a very different way than
|
||
|
non-robust ones. This method reads their locking status,
|
||
|
whether it may have waiters, their registered owner (if any),
|
||
|
whether the owner is alive or not, and the status of the state
|
||
|
they're protecting.
|
||
|
"""
|
||
|
|
||
|
if self.lock == PTHREAD_MUTEX_UNLOCKED:
|
||
|
self.values.append(('Status', 'Unlocked'))
|
||
|
else:
|
||
|
if self.lock & FUTEX_WAITERS:
|
||
|
self.values.append(('Status', 'Locked, possibly with waiters'))
|
||
|
else:
|
||
|
self.values.append(('Status',
|
||
|
'Locked, possibly with no waiters'))
|
||
|
|
||
|
if self.lock & FUTEX_OWNER_DIED:
|
||
|
self.values.append(('Owner ID', '%d (dead)' % self.owner))
|
||
|
else:
|
||
|
self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
|
||
|
|
||
|
if self.owner == PTHREAD_MUTEX_INCONSISTENT:
|
||
|
self.values.append(('State protected by this mutex',
|
||
|
'Inconsistent'))
|
||
|
elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
|
||
|
self.values.append(('State protected by this mutex',
|
||
|
'Not recoverable'))
|
||
|
|
||
|
def read_status_no_robust(self):
|
||
|
"""Read the status of a non-robust mutex.
|
||
|
|
||
|
Read info on whether the mutex is locked, if it may have waiters
|
||
|
and its owner (if any).
|
||
|
"""
|
||
|
|
||
|
lock_value = self.lock
|
||
|
|
||
|
if self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
|
||
|
lock_value &= ~(PTHREAD_MUTEX_PRIO_CEILING_MASK)
|
||
|
|
||
|
if lock_value == PTHREAD_MUTEX_UNLOCKED:
|
||
|
self.values.append(('Status', 'Unlocked'))
|
||
|
else:
|
||
|
if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
|
||
|
waiters = self.lock & FUTEX_WAITERS
|
||
|
owner = self.lock & FUTEX_TID_MASK
|
||
|
else:
|
||
|
# Mutex protocol is PP or none
|
||
|
waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
|
||
|
owner = self.owner
|
||
|
|
||
|
if waiters:
|
||
|
self.values.append(('Status', 'Locked, possibly with waiters'))
|
||
|
else:
|
||
|
self.values.append(('Status',
|
||
|
'Locked, possibly with no waiters'))
|
||
|
|
||
|
self.values.append(('Owner ID', owner))
|
||
|
|
||
|
def read_attributes(self):
|
||
|
"""Read the mutex's attributes."""
|
||
|
|
||
|
if self.kind != PTHREAD_MUTEX_DESTROYED:
|
||
|
if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
|
||
|
self.values.append(('Robust', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Robust', 'No'))
|
||
|
|
||
|
# In glibc, robust mutexes always have their pshared flag set to
|
||
|
# 'shared' regardless of what the pshared flag of their
|
||
|
# mutexattr was. Therefore a robust mutex will act as shared
|
||
|
# even if it was initialized with a 'private' mutexattr.
|
||
|
if self.kind & PTHREAD_MUTEX_PSHARED_BIT:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
|
||
|
self.values.append(('Protocol', 'Priority inherit'))
|
||
|
elif self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
|
||
|
prio_ceiling = ((self.lock & PTHREAD_MUTEX_PRIO_CEILING_MASK)
|
||
|
>> PTHREAD_MUTEX_PRIO_CEILING_SHIFT)
|
||
|
|
||
|
self.values.append(('Protocol', 'Priority protect'))
|
||
|
self.values.append(('Priority ceiling', prio_ceiling))
|
||
|
else:
|
||
|
# PTHREAD_PRIO_NONE
|
||
|
self.values.append(('Protocol', 'None'))
|
||
|
|
||
|
def read_misc_info(self):
|
||
|
"""Read miscellaneous info on the mutex.
|
||
|
|
||
|
For now this reads the number of times a recursive mutex was locked
|
||
|
by the same thread.
|
||
|
"""
|
||
|
|
||
|
mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
|
||
|
|
||
|
if mutex_type == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
|
||
|
self.values.append(('Times locked recursively', self.count))
|
||
|
|
||
|
class MutexAttributesPrinter(object):
|
||
|
"""Pretty printer for pthread_mutexattr_t.
|
||
|
|
||
|
In the NPTL this is a type that's always casted to struct pthread_mutexattr
|
||
|
which has a single 'mutexkind' field containing the actual attributes.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, mutexattr):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
mutexattr: A gdb.value representing a pthread_mutexattr_t.
|
||
|
"""
|
||
|
|
||
|
self.values = []
|
||
|
|
||
|
try:
|
||
|
mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
|
||
|
self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
|
||
|
self.read_values()
|
||
|
except gdb.error:
|
||
|
# libpthread doesn't have debug symbols, thus we can't find the
|
||
|
# real struct type. Just print the union members.
|
||
|
self.values.append(('__size', mutexattr['__size']))
|
||
|
self.values.append(('__align', mutexattr['__align']))
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_mutexattr_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_mutexattr_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_mutexattr_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the mutexattr's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
mutexattr_type = (self.mutexattr
|
||
|
& ~PTHREAD_MUTEXATTR_FLAG_BITS
|
||
|
& ~PTHREAD_MUTEX_NO_ELISION_NP)
|
||
|
|
||
|
# mutexattr_type must be casted to int because it's a gdb.Value
|
||
|
self.values.append(MUTEX_TYPES[int(mutexattr_type)])
|
||
|
|
||
|
if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
|
||
|
self.values.append(('Robust', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Robust', 'No'))
|
||
|
|
||
|
if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
|
||
|
PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
|
||
|
|
||
|
if protocol == PTHREAD_PRIO_NONE:
|
||
|
self.values.append(('Protocol', 'None'))
|
||
|
elif protocol == PTHREAD_PRIO_INHERIT:
|
||
|
self.values.append(('Protocol', 'Priority inherit'))
|
||
|
elif protocol == PTHREAD_PRIO_PROTECT:
|
||
|
self.values.append(('Protocol', 'Priority protect'))
|
||
|
|
||
|
CLOCK_IDS = {
|
||
|
CLOCK_REALTIME: 'CLOCK_REALTIME',
|
||
|
CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
|
||
|
CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
|
||
|
CLOCK_THREAD_CPUTIME_ID: 'CLOCK_THREAD_CPUTIME_ID',
|
||
|
CLOCK_MONOTONIC_RAW: 'CLOCK_MONOTONIC_RAW',
|
||
|
CLOCK_REALTIME_COARSE: 'CLOCK_REALTIME_COARSE',
|
||
|
CLOCK_MONOTONIC_COARSE: 'CLOCK_MONOTONIC_COARSE'
|
||
|
}
|
||
|
|
||
|
class ConditionVariablePrinter(object):
|
||
|
"""Pretty printer for pthread_cond_t."""
|
||
|
|
||
|
def __init__(self, cond):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
cond: A gdb.value representing a pthread_cond_t.
|
||
|
"""
|
||
|
|
||
|
# Since PTHREAD_COND_SHARED is an integer, we need to cast it to void *
|
||
|
# to be able to compare it to the condvar's __data.__mutex member.
|
||
|
#
|
||
|
# While it looks like self.shared_value should be a class variable,
|
||
|
# that would result in it having an incorrect size if we're loading
|
||
|
# these printers through .gdbinit for a 64-bit objfile in AMD64.
|
||
|
# This is because gdb initially assumes the pointer size to be 4 bytes,
|
||
|
# and only sets it to 8 after loading the 64-bit objfiles. Since
|
||
|
# .gdbinit runs before any objfiles are loaded, this would effectively
|
||
|
# make self.shared_value have a size of 4, thus breaking later
|
||
|
# comparisons with pointers whose types are looked up at runtime.
|
||
|
void_ptr_type = gdb.lookup_type('void').pointer()
|
||
|
self.shared_value = gdb.Value(PTHREAD_COND_SHARED).cast(void_ptr_type)
|
||
|
|
||
|
data = cond['__data']
|
||
|
self.total_seq = data['__total_seq']
|
||
|
self.mutex = data['__mutex']
|
||
|
self.nwaiters = data['__nwaiters']
|
||
|
self.values = []
|
||
|
|
||
|
self.read_values()
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_cond_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_cond_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_cond_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the condvar's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
self.read_status()
|
||
|
self.read_attributes()
|
||
|
self.read_mutex_info()
|
||
|
|
||
|
def read_status(self):
|
||
|
"""Read the status of the condvar.
|
||
|
|
||
|
This method reads whether the condvar is destroyed and how many threads
|
||
|
are waiting for it.
|
||
|
"""
|
||
|
|
||
|
if self.total_seq == PTHREAD_COND_DESTROYED:
|
||
|
self.values.append(('Status', 'Destroyed'))
|
||
|
|
||
|
self.values.append(('Threads waiting for this condvar',
|
||
|
self.nwaiters >> COND_NWAITERS_SHIFT))
|
||
|
|
||
|
def read_attributes(self):
|
||
|
"""Read the condvar's attributes."""
|
||
|
|
||
|
clock_id = self.nwaiters & ((1 << COND_NWAITERS_SHIFT) - 1)
|
||
|
|
||
|
# clock_id must be casted to int because it's a gdb.Value
|
||
|
self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
|
||
|
|
||
|
shared = (self.mutex == self.shared_value)
|
||
|
|
||
|
if shared:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
def read_mutex_info(self):
|
||
|
"""Read the data of the mutex this condvar is bound to.
|
||
|
|
||
|
A pthread_cond_t's __data.__mutex member is a void * which
|
||
|
must be casted to pthread_mutex_t *. For shared condvars, this
|
||
|
member isn't recorded and has a special value instead.
|
||
|
"""
|
||
|
|
||
|
if self.mutex and self.mutex != self.shared_value:
|
||
|
mutex_type = gdb.lookup_type('pthread_mutex_t')
|
||
|
mutex = self.mutex.cast(mutex_type.pointer()).dereference()
|
||
|
|
||
|
self.values.append(('Mutex', mutex))
|
||
|
|
||
|
class ConditionVariableAttributesPrinter(object):
|
||
|
"""Pretty printer for pthread_condattr_t.
|
||
|
|
||
|
In the NPTL this is a type that's always casted to struct pthread_condattr,
|
||
|
which has a single 'value' field containing the actual attributes.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, condattr):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
condattr: A gdb.value representing a pthread_condattr_t.
|
||
|
"""
|
||
|
|
||
|
self.values = []
|
||
|
|
||
|
try:
|
||
|
condattr_struct = gdb.lookup_type('struct pthread_condattr')
|
||
|
self.condattr = condattr.cast(condattr_struct)['value']
|
||
|
self.read_values()
|
||
|
except gdb.error:
|
||
|
# libpthread doesn't have debug symbols, thus we can't find the
|
||
|
# real struct type. Just print the union members.
|
||
|
self.values.append(('__size', condattr['__size']))
|
||
|
self.values.append(('__align', condattr['__align']))
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_condattr_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_condattr_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_condattr_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the condattr's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
clock_id = self.condattr & ((1 << COND_NWAITERS_SHIFT) - 1)
|
||
|
|
||
|
# clock_id must be casted to int because it's a gdb.Value
|
||
|
self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
|
||
|
|
||
|
if self.condattr & 1:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
class RWLockPrinter(object):
|
||
|
"""Pretty printer for pthread_rwlock_t."""
|
||
|
|
||
|
def __init__(self, rwlock):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
rwlock: A gdb.value representing a pthread_rwlock_t.
|
||
|
"""
|
||
|
|
||
|
data = rwlock['__data']
|
||
|
self.readers = data['__nr_readers']
|
||
|
self.queued_readers = data['__nr_readers_queued']
|
||
|
self.queued_writers = data['__nr_writers_queued']
|
||
|
self.writer_id = data['__writer']
|
||
|
self.shared = data['__shared']
|
||
|
self.prefers_writers = data['__flags']
|
||
|
self.values = []
|
||
|
self.read_values()
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_rwlock_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_rwlock_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_rwlock_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the rwlock's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
self.read_status()
|
||
|
self.read_attributes()
|
||
|
|
||
|
def read_status(self):
|
||
|
"""Read the status of the rwlock."""
|
||
|
|
||
|
# Right now pthread_rwlock_destroy doesn't do anything, so there's no
|
||
|
# way to check if an rwlock is destroyed.
|
||
|
|
||
|
if self.writer_id:
|
||
|
self.values.append(('Status', 'Locked (Write)'))
|
||
|
self.values.append(('Writer ID', self.writer_id))
|
||
|
elif self.readers:
|
||
|
self.values.append(('Status', 'Locked (Read)'))
|
||
|
self.values.append(('Readers', self.readers))
|
||
|
else:
|
||
|
self.values.append(('Status', 'Unlocked'))
|
||
|
|
||
|
self.values.append(('Queued readers', self.queued_readers))
|
||
|
self.values.append(('Queued writers', self.queued_writers))
|
||
|
|
||
|
def read_attributes(self):
|
||
|
"""Read the attributes of the rwlock."""
|
||
|
|
||
|
if self.shared:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
if self.prefers_writers:
|
||
|
self.values.append(('Prefers', 'Writers'))
|
||
|
else:
|
||
|
self.values.append(('Prefers', 'Readers'))
|
||
|
|
||
|
class RWLockAttributesPrinter(object):
|
||
|
"""Pretty printer for pthread_rwlockattr_t.
|
||
|
|
||
|
In the NPTL this is a type that's always casted to
|
||
|
struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
|
||
|
containing the actual attributes.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, rwlockattr):
|
||
|
"""Initialize the printer's internal data structures.
|
||
|
|
||
|
Args:
|
||
|
rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
|
||
|
"""
|
||
|
|
||
|
self.values = []
|
||
|
|
||
|
try:
|
||
|
rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
|
||
|
self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
|
||
|
self.read_values()
|
||
|
except gdb.error:
|
||
|
# libpthread doesn't have debug symbols, thus we can't find the
|
||
|
# real struct type. Just print the union members.
|
||
|
self.values.append(('__size', rwlockattr['__size']))
|
||
|
self.values.append(('__align', rwlockattr['__align']))
|
||
|
|
||
|
def to_string(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_rwlockattr_t.
|
||
|
"""
|
||
|
|
||
|
return 'pthread_rwlockattr_t'
|
||
|
|
||
|
def children(self):
|
||
|
"""gdb API function.
|
||
|
|
||
|
This is called from gdb when we try to print a pthread_rwlockattr_t.
|
||
|
"""
|
||
|
|
||
|
return self.values
|
||
|
|
||
|
def read_values(self):
|
||
|
"""Read the rwlockattr's info and store it in self.values.
|
||
|
|
||
|
The data contained in self.values will be returned by the Iterator
|
||
|
created in self.children.
|
||
|
"""
|
||
|
|
||
|
rwlock_type = self.rwlockattr['lockkind']
|
||
|
shared = self.rwlockattr['pshared']
|
||
|
|
||
|
if shared == PTHREAD_PROCESS_SHARED:
|
||
|
self.values.append(('Shared', 'Yes'))
|
||
|
else:
|
||
|
# PTHREAD_PROCESS_PRIVATE
|
||
|
self.values.append(('Shared', 'No'))
|
||
|
|
||
|
if (rwlock_type == PTHREAD_RWLOCK_PREFER_READER_NP or
|
||
|
rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NP):
|
||
|
# This is a known bug. Using PTHREAD_RWLOCK_PREFER_WRITER_NP will
|
||
|
# still make the rwlock prefer readers.
|
||
|
self.values.append(('Prefers', 'Readers'))
|
||
|
elif rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
|
||
|
self.values.append(('Prefers', 'Writers'))
|
||
|
|
||
|
def register(objfile):
|
||
|
"""Register the pretty printers within the given objfile."""
|
||
|
|
||
|
printer = gdb.printing.RegexpCollectionPrettyPrinter('glibc-pthread-locks')
|
||
|
|
||
|
printer.add_printer('pthread_mutex_t', r'^pthread_mutex_t$',
|
||
|
MutexPrinter)
|
||
|
printer.add_printer('pthread_mutexattr_t', r'^pthread_mutexattr_t$',
|
||
|
MutexAttributesPrinter)
|
||
|
printer.add_printer('pthread_cond_t', r'^pthread_cond_t$',
|
||
|
ConditionVariablePrinter)
|
||
|
printer.add_printer('pthread_condattr_t', r'^pthread_condattr_t$',
|
||
|
ConditionVariableAttributesPrinter)
|
||
|
printer.add_printer('pthread_rwlock_t', r'^pthread_rwlock_t$',
|
||
|
RWLockPrinter)
|
||
|
printer.add_printer('pthread_rwlockattr_t', r'^pthread_rwlockattr_t$',
|
||
|
RWLockAttributesPrinter)
|
||
|
|
||
|
if objfile == None:
|
||
|
objfile = gdb
|
||
|
|
||
|
gdb.printing.register_pretty_printer(objfile, printer)
|
||
|
|
||
|
register(gdb.current_objfile())
|