1 # Pretty printers for the NPTL lock types.
3 # Copyright (C) 2016-2023 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
6 # The GNU C Library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # The GNU C Library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with the GNU C Library; if not, see
18 # <https://www.gnu.org/licenses/>.
20 """This file contains the gdb pretty printers for the following types:
27 * pthread_rwlockattr_t
29 You can check which printers are registered and enabled by issuing the
30 'info pretty-printer' gdb command. Printers should trigger automatically when
31 trying to print a variable of one of the types mentioned above.
34 from __future__
import print_function
38 from nptl_lock_constants
import *
41 PTHREAD_MUTEX_NORMAL
: ('Type', 'Normal'),
42 PTHREAD_MUTEX_RECURSIVE
: ('Type', 'Recursive'),
43 PTHREAD_MUTEX_ERRORCHECK
: ('Type', 'Error check'),
44 PTHREAD_MUTEX_ADAPTIVE_NP
: ('Type', 'Adaptive')
47 class MutexPrinter(object):
48 """Pretty printer for pthread_mutex_t."""
50 def __init__(self
, mutex
):
51 """Initialize the printer's internal data structures.
54 mutex: A gdb.value representing a pthread_mutex_t.
57 data
= mutex
['__data']
58 self
.lock
= data
['__lock']
59 self
.count
= data
['__count']
60 self
.owner
= data
['__owner']
61 self
.kind
= data
['__kind']
68 This is called from gdb when we try to print a pthread_mutex_t.
71 return 'pthread_mutex_t'
76 This is called from gdb when we try to print a pthread_mutex_t.
81 def read_values(self
):
82 """Read the mutex's info and store it in self.values.
84 The data contained in self.values will be returned by the Iterator
85 created in self.children.
90 self
.read_attributes()
94 """Read the mutex's type."""
96 mutex_type
= self
.kind
& PTHREAD_MUTEX_KIND_MASK
98 # mutex_type must be casted to int because it's a gdb.Value
99 self
.values
.append(MUTEX_TYPES
[int(mutex_type
)])
101 def read_status(self
):
102 """Read the mutex's status.
104 Architectures that support lock elision might not record the mutex owner
105 ID in the __owner field. In that case, the owner will be reported as
109 if self
.kind
== PTHREAD_MUTEX_DESTROYED
:
110 self
.values
.append(('Status', 'Destroyed'))
111 elif self
.kind
& PTHREAD_MUTEX_ROBUST_NORMAL_NP
:
112 self
.read_status_robust()
114 self
.read_status_no_robust()
116 def read_status_robust(self
):
117 """Read the status of a robust mutex.
119 In glibc robust mutexes are implemented in a very different way than
120 non-robust ones. This method reads their locking status,
121 whether it may have waiters, their registered owner (if any),
122 whether the owner is alive or not, and the status of the state
126 if self
.lock
== PTHREAD_MUTEX_UNLOCKED
:
127 self
.values
.append(('Status', 'Not acquired'))
129 if self
.lock
& FUTEX_WAITERS
:
130 self
.values
.append(('Status',
131 'Acquired, possibly with waiters'))
133 self
.values
.append(('Status',
134 'Acquired, possibly with no waiters'))
136 if self
.lock
& FUTEX_OWNER_DIED
:
137 self
.values
.append(('Owner ID', '%d (dead)' % self
.owner
))
139 self
.values
.append(('Owner ID', self
.lock
& FUTEX_TID_MASK
))
141 if self
.owner
== PTHREAD_MUTEX_INCONSISTENT
:
142 self
.values
.append(('State protected by this mutex',
144 elif self
.owner
== PTHREAD_MUTEX_NOTRECOVERABLE
:
145 self
.values
.append(('State protected by this mutex',
148 def read_status_no_robust(self
):
149 """Read the status of a non-robust mutex.
151 Read info on whether the mutex is acquired, if it may have waiters
152 and its owner (if any).
155 lock_value
= self
.lock
157 if self
.kind
& PTHREAD_MUTEX_PRIO_PROTECT_NP
:
158 lock_value
&= 0xffffffff & ~
(PTHREAD_MUTEX_PRIO_CEILING_MASK
)
160 if lock_value
== PTHREAD_MUTEX_UNLOCKED
:
161 self
.values
.append(('Status', 'Not acquired'))
163 if self
.kind
& PTHREAD_MUTEX_PRIO_INHERIT_NP
:
164 waiters
= self
.lock
& FUTEX_WAITERS
165 owner
= self
.lock
& FUTEX_TID_MASK
167 # Mutex protocol is PP or none
168 waiters
= (self
.lock
!= PTHREAD_MUTEX_LOCKED_NO_WAITERS
)
172 self
.values
.append(('Status',
173 'Acquired, possibly with waiters'))
175 self
.values
.append(('Status',
176 'Acquired, possibly with no waiters'))
179 self
.values
.append(('Owner ID', owner
))
181 # Owner isn't recorded, probably because lock elision
183 self
.values
.append(('Owner ID', 'Unknown'))
185 def read_attributes(self
):
186 """Read the mutex's attributes."""
188 if self
.kind
!= PTHREAD_MUTEX_DESTROYED
:
189 if self
.kind
& PTHREAD_MUTEX_ROBUST_NORMAL_NP
:
190 self
.values
.append(('Robust', 'Yes'))
192 self
.values
.append(('Robust', 'No'))
194 # In glibc, robust mutexes always have their pshared flag set to
195 # 'shared' regardless of what the pshared flag of their
196 # mutexattr was. Therefore a robust mutex will act as shared
197 # even if it was initialized with a 'private' mutexattr.
198 if self
.kind
& PTHREAD_MUTEX_PSHARED_BIT
:
199 self
.values
.append(('Shared', 'Yes'))
201 self
.values
.append(('Shared', 'No'))
203 if self
.kind
& PTHREAD_MUTEX_PRIO_INHERIT_NP
:
204 self
.values
.append(('Protocol', 'Priority inherit'))
205 elif self
.kind
& PTHREAD_MUTEX_PRIO_PROTECT_NP
:
206 prio_ceiling
= ((self
.lock
& PTHREAD_MUTEX_PRIO_CEILING_MASK
)
207 >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT
)
209 self
.values
.append(('Protocol', 'Priority protect'))
210 self
.values
.append(('Priority ceiling', prio_ceiling
))
213 self
.values
.append(('Protocol', 'None'))
215 def read_misc_info(self
):
216 """Read miscellaneous info on the mutex.
218 For now this reads the number of times a recursive mutex was acquired
222 mutex_type
= self
.kind
& PTHREAD_MUTEX_KIND_MASK
224 if mutex_type
== PTHREAD_MUTEX_RECURSIVE
and self
.count
> 1:
225 self
.values
.append(('Times acquired by the owner', self
.count
))
227 class MutexAttributesPrinter(object):
228 """Pretty printer for pthread_mutexattr_t.
230 In the NPTL this is a type that's always casted to struct pthread_mutexattr
231 which has a single 'mutexkind' field containing the actual attributes.
234 def __init__(self
, mutexattr
):
235 """Initialize the printer's internal data structures.
238 mutexattr: A gdb.value representing a pthread_mutexattr_t.
244 mutexattr_struct
= gdb
.lookup_type('struct pthread_mutexattr')
245 self
.mutexattr
= mutexattr
.cast(mutexattr_struct
)['mutexkind']
248 # libpthread doesn't have debug symbols, thus we can't find the
249 # real struct type. Just print the union members.
250 self
.values
.append(('__size', mutexattr
['__size']))
251 self
.values
.append(('__align', mutexattr
['__align']))
256 This is called from gdb when we try to print a pthread_mutexattr_t.
259 return 'pthread_mutexattr_t'
264 This is called from gdb when we try to print a pthread_mutexattr_t.
269 def read_values(self
):
270 """Read the mutexattr's info and store it in self.values.
272 The data contained in self.values will be returned by the Iterator
273 created in self.children.
276 mutexattr_type
= (self
.mutexattr
278 & ~PTHREAD_MUTEXATTR_FLAG_BITS
279 & ~PTHREAD_MUTEX_NO_ELISION_NP
)
281 # mutexattr_type must be casted to int because it's a gdb.Value
282 self
.values
.append(MUTEX_TYPES
[int(mutexattr_type
)])
284 if self
.mutexattr
& PTHREAD_MUTEXATTR_FLAG_ROBUST
:
285 self
.values
.append(('Robust', 'Yes'))
287 self
.values
.append(('Robust', 'No'))
289 if self
.mutexattr
& PTHREAD_MUTEXATTR_FLAG_PSHARED
:
290 self
.values
.append(('Shared', 'Yes'))
292 self
.values
.append(('Shared', 'No'))
294 protocol
= ((self
.mutexattr
& PTHREAD_MUTEXATTR_PROTOCOL_MASK
) >>
295 PTHREAD_MUTEXATTR_PROTOCOL_SHIFT
)
297 if protocol
== PTHREAD_PRIO_NONE
:
298 self
.values
.append(('Protocol', 'None'))
299 elif protocol
== PTHREAD_PRIO_INHERIT
:
300 self
.values
.append(('Protocol', 'Priority inherit'))
301 elif protocol
== PTHREAD_PRIO_PROTECT
:
302 self
.values
.append(('Protocol', 'Priority protect'))
304 class ConditionVariablePrinter(object):
305 """Pretty printer for pthread_cond_t."""
307 def __init__(self
, cond
):
308 """Initialize the printer's internal data structures.
311 cond: A gdb.value representing a pthread_cond_t.
314 data
= cond
['__data']
315 self
.wrefs
= data
['__wrefs']
323 This is called from gdb when we try to print a pthread_cond_t.
326 return 'pthread_cond_t'
331 This is called from gdb when we try to print a pthread_cond_t.
336 def read_values(self
):
337 """Read the condvar's info and store it in self.values.
339 The data contained in self.values will be returned by the Iterator
340 created in self.children.
344 self
.read_attributes()
346 def read_status(self
):
347 """Read the status of the condvar.
349 This method reads whether the condvar is destroyed and how many threads
353 self
.values
.append(('Threads known to still execute a wait function',
354 self
.wrefs
>> PTHREAD_COND_WREFS_SHIFT
))
356 def read_attributes(self
):
357 """Read the condvar's attributes."""
359 if (self
.wrefs
& PTHREAD_COND_CLOCK_MONOTONIC_MASK
) != 0:
360 self
.values
.append(('Clock ID', 'CLOCK_MONOTONIC'))
362 self
.values
.append(('Clock ID', 'CLOCK_REALTIME'))
364 if (self
.wrefs
& PTHREAD_COND_SHARED_MASK
) != 0:
365 self
.values
.append(('Shared', 'Yes'))
367 self
.values
.append(('Shared', 'No'))
369 class ConditionVariableAttributesPrinter(object):
370 """Pretty printer for pthread_condattr_t.
372 In the NPTL this is a type that's always casted to struct pthread_condattr,
373 which has a single 'value' field containing the actual attributes.
376 def __init__(self
, condattr
):
377 """Initialize the printer's internal data structures.
380 condattr: A gdb.value representing a pthread_condattr_t.
386 condattr_struct
= gdb
.lookup_type('struct pthread_condattr')
387 self
.condattr
= condattr
.cast(condattr_struct
)['value']
390 # libpthread doesn't have debug symbols, thus we can't find the
391 # real struct type. Just print the union members.
392 self
.values
.append(('__size', condattr
['__size']))
393 self
.values
.append(('__align', condattr
['__align']))
398 This is called from gdb when we try to print a pthread_condattr_t.
401 return 'pthread_condattr_t'
406 This is called from gdb when we try to print a pthread_condattr_t.
411 def read_values(self
):
412 """Read the condattr's info and store it in self.values.
414 The data contained in self.values will be returned by the Iterator
415 created in self.children.
418 clock_id
= (self
.condattr
>> 1) & ((1 << COND_CLOCK_BITS
) - 1)
421 self
.values
.append(('Clock ID', 'CLOCK_MONOTONIC'))
423 self
.values
.append(('Clock ID', 'CLOCK_REALTIME'))
425 if self
.condattr
& 1:
426 self
.values
.append(('Shared', 'Yes'))
428 self
.values
.append(('Shared', 'No'))
430 class RWLockPrinter(object):
431 """Pretty printer for pthread_rwlock_t."""
433 def __init__(self
, rwlock
):
434 """Initialize the printer's internal data structures.
437 rwlock: A gdb.value representing a pthread_rwlock_t.
440 data
= rwlock
['__data']
441 self
.readers
= data
['__readers']
442 self
.cur_writer
= data
['__cur_writer']
443 self
.shared
= data
['__shared']
444 self
.flags
= data
['__flags']
451 This is called from gdb when we try to print a pthread_rwlock_t.
454 return 'pthread_rwlock_t'
459 This is called from gdb when we try to print a pthread_rwlock_t.
464 def read_values(self
):
465 """Read the rwlock's info and store it in self.values.
467 The data contained in self.values will be returned by the Iterator
468 created in self.children.
472 self
.read_attributes()
474 def read_status(self
):
475 """Read the status of the rwlock."""
477 if self
.readers
& PTHREAD_RWLOCK_WRPHASE
:
478 if self
.readers
& PTHREAD_RWLOCK_WRLOCKED
:
479 self
.values
.append(('Status', 'Acquired (Write)'))
480 self
.values
.append(('Writer ID', self
.cur_writer
))
482 self
.values
.append(('Status', 'Not acquired'))
484 r
= self
.readers
>> PTHREAD_RWLOCK_READER_SHIFT
486 self
.values
.append(('Status', 'Acquired (Read)'))
487 self
.values
.append(('Readers', r
))
489 self
.values
.append(('Status', 'Not acquired'))
491 def read_attributes(self
):
492 """Read the attributes of the rwlock."""
495 self
.values
.append(('Shared', 'Yes'))
497 self
.values
.append(('Shared', 'No'))
499 if self
.flags
== PTHREAD_RWLOCK_PREFER_READER_NP
:
500 self
.values
.append(('Prefers', 'Readers'))
501 elif self
.flags
== PTHREAD_RWLOCK_PREFER_WRITER_NP
:
502 self
.values
.append(('Prefers', 'Writers'))
504 self
.values
.append(('Prefers', 'Writers no recursive readers'))
506 class RWLockAttributesPrinter(object):
507 """Pretty printer for pthread_rwlockattr_t.
509 In the NPTL this is a type that's always casted to
510 struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
511 containing the actual attributes.
514 def __init__(self
, rwlockattr
):
515 """Initialize the printer's internal data structures.
518 rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
524 rwlockattr_struct
= gdb
.lookup_type('struct pthread_rwlockattr')
525 self
.rwlockattr
= rwlockattr
.cast(rwlockattr_struct
)
528 # libpthread doesn't have debug symbols, thus we can't find the
529 # real struct type. Just print the union members.
530 self
.values
.append(('__size', rwlockattr
['__size']))
531 self
.values
.append(('__align', rwlockattr
['__align']))
536 This is called from gdb when we try to print a pthread_rwlockattr_t.
539 return 'pthread_rwlockattr_t'
544 This is called from gdb when we try to print a pthread_rwlockattr_t.
549 def read_values(self
):
550 """Read the rwlockattr's info and store it in self.values.
552 The data contained in self.values will be returned by the Iterator
553 created in self.children.
556 rwlock_type
= self
.rwlockattr
['lockkind']
557 shared
= self
.rwlockattr
['pshared']
559 if shared
== PTHREAD_PROCESS_SHARED
:
560 self
.values
.append(('Shared', 'Yes'))
562 # PTHREAD_PROCESS_PRIVATE
563 self
.values
.append(('Shared', 'No'))
565 if rwlock_type
== PTHREAD_RWLOCK_PREFER_READER_NP
:
566 self
.values
.append(('Prefers', 'Readers'))
567 elif rwlock_type
== PTHREAD_RWLOCK_PREFER_WRITER_NP
:
568 self
.values
.append(('Prefers', 'Writers'))
570 self
.values
.append(('Prefers', 'Writers no recursive readers'))
572 def register(objfile
):
573 """Register the pretty printers within the given objfile."""
575 printer
= gdb
.printing
.RegexpCollectionPrettyPrinter('glibc-pthread-locks')
577 printer
.add_printer('pthread_mutex_t', r
'^pthread_mutex_t$',
579 printer
.add_printer('pthread_mutexattr_t', r
'^pthread_mutexattr_t$',
580 MutexAttributesPrinter
)
581 printer
.add_printer('pthread_cond_t', r
'^pthread_cond_t$',
582 ConditionVariablePrinter
)
583 printer
.add_printer('pthread_condattr_t', r
'^pthread_condattr_t$',
584 ConditionVariableAttributesPrinter
)
585 printer
.add_printer('pthread_rwlock_t', r
'^pthread_rwlock_t$',
587 printer
.add_printer('pthread_rwlockattr_t', r
'^pthread_rwlockattr_t$',
588 RWLockAttributesPrinter
)
593 gdb
.printing
.register_pretty_printer(objfile
, printer
)
595 register(gdb
.current_objfile())