summaryrefslogtreecommitdiff
path: root/nptl/nptl-printers.py
diff options
context:
space:
mode:
Diffstat (limited to 'nptl/nptl-printers.py')
-rw-r--r--nptl/nptl-printers.py593
1 files changed, 593 insertions, 0 deletions
diff --git a/nptl/nptl-printers.py b/nptl/nptl-printers.py
new file mode 100644
index 0000000000..f64216fb57
--- /dev/null
+++ b/nptl/nptl-printers.py
@@ -0,0 +1,593 @@
+# 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())