1# Pretty printers for the NPTL lock types.
2#
3# Copyright (C) 2016-2022 Free Software Foundation, Inc.
4# This file is part of the GNU C Library.
5#
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.
10#
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.
15#
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/>.
19
20"""This file contains the gdb pretty printers for the following types:
21
22    * pthread_mutex_t
23    * pthread_mutexattr_t
24    * pthread_cond_t
25    * pthread_condattr_t
26    * pthread_rwlock_t
27    * pthread_rwlockattr_t
28
29You can check which printers are registered and enabled by issuing the
30'info pretty-printer' gdb command.  Printers should trigger automatically when
31trying to print a variable of one of the types mentioned above.
32"""
33
34from __future__ import print_function
35
36import gdb
37import gdb.printing
38from nptl_lock_constants import *
39
40MUTEX_TYPES = {
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')
45}
46
47class MutexPrinter(object):
48    """Pretty printer for pthread_mutex_t."""
49
50    def __init__(self, mutex):
51        """Initialize the printer's internal data structures.
52
53        Args:
54            mutex: A gdb.value representing a pthread_mutex_t.
55        """
56
57        data = mutex['__data']
58        self.lock = data['__lock']
59        self.count = data['__count']
60        self.owner = data['__owner']
61        self.kind = data['__kind']
62        self.values = []
63        self.read_values()
64
65    def to_string(self):
66        """gdb API function.
67
68        This is called from gdb when we try to print a pthread_mutex_t.
69        """
70
71        return 'pthread_mutex_t'
72
73    def children(self):
74        """gdb API function.
75
76        This is called from gdb when we try to print a pthread_mutex_t.
77        """
78
79        return self.values
80
81    def read_values(self):
82        """Read the mutex's info and store it in self.values.
83
84        The data contained in self.values will be returned by the Iterator
85        created in self.children.
86        """
87
88        self.read_type()
89        self.read_status()
90        self.read_attributes()
91        self.read_misc_info()
92
93    def read_type(self):
94        """Read the mutex's type."""
95
96        mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
97
98        # mutex_type must be casted to int because it's a gdb.Value
99        self.values.append(MUTEX_TYPES[int(mutex_type)])
100
101    def read_status(self):
102        """Read the mutex's status.
103
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
106        "Unknown".
107        """
108
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()
113        else:
114            self.read_status_no_robust()
115
116    def read_status_robust(self):
117        """Read the status of a robust mutex.
118
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
123        they're protecting.
124        """
125
126        if self.lock == PTHREAD_MUTEX_UNLOCKED:
127            self.values.append(('Status', 'Not acquired'))
128        else:
129            if self.lock & FUTEX_WAITERS:
130                self.values.append(('Status',
131                                    'Acquired, possibly with waiters'))
132            else:
133                self.values.append(('Status',
134                                    'Acquired, possibly with no waiters'))
135
136            if self.lock & FUTEX_OWNER_DIED:
137                self.values.append(('Owner ID', '%d (dead)' % self.owner))
138            else:
139                self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
140
141        if self.owner == PTHREAD_MUTEX_INCONSISTENT:
142            self.values.append(('State protected by this mutex',
143                                'Inconsistent'))
144        elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
145            self.values.append(('State protected by this mutex',
146                                'Not recoverable'))
147
148    def read_status_no_robust(self):
149        """Read the status of a non-robust mutex.
150
151        Read info on whether the mutex is acquired, if it may have waiters
152        and its owner (if any).
153        """
154
155        lock_value = self.lock
156
157        if self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
158            lock_value &= 0xffffffff & ~(PTHREAD_MUTEX_PRIO_CEILING_MASK)
159
160        if lock_value == PTHREAD_MUTEX_UNLOCKED:
161            self.values.append(('Status', 'Not acquired'))
162        else:
163            if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
164                waiters = self.lock & FUTEX_WAITERS
165                owner = self.lock & FUTEX_TID_MASK
166            else:
167                # Mutex protocol is PP or none
168                waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
169                owner = self.owner
170
171            if waiters:
172                self.values.append(('Status',
173                                    'Acquired, possibly with waiters'))
174            else:
175                self.values.append(('Status',
176                                    'Acquired, possibly with no waiters'))
177
178            if self.owner != 0:
179                self.values.append(('Owner ID', owner))
180            else:
181                # Owner isn't recorded, probably because lock elision
182                # is enabled.
183                self.values.append(('Owner ID', 'Unknown'))
184
185    def read_attributes(self):
186        """Read the mutex's attributes."""
187
188        if self.kind != PTHREAD_MUTEX_DESTROYED:
189            if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
190                self.values.append(('Robust', 'Yes'))
191            else:
192                self.values.append(('Robust', 'No'))
193
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'))
200            else:
201                self.values.append(('Shared', 'No'))
202
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)
208
209                self.values.append(('Protocol', 'Priority protect'))
210                self.values.append(('Priority ceiling', prio_ceiling))
211            else:
212                # PTHREAD_PRIO_NONE
213                self.values.append(('Protocol', 'None'))
214
215    def read_misc_info(self):
216        """Read miscellaneous info on the mutex.
217
218        For now this reads the number of times a recursive mutex was acquired
219        by the same thread.
220        """
221
222        mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
223
224        if mutex_type == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
225            self.values.append(('Times acquired by the owner', self.count))
226
227class MutexAttributesPrinter(object):
228    """Pretty printer for pthread_mutexattr_t.
229
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.
232    """
233
234    def __init__(self, mutexattr):
235        """Initialize the printer's internal data structures.
236
237        Args:
238            mutexattr: A gdb.value representing a pthread_mutexattr_t.
239        """
240
241        self.values = []
242
243        try:
244            mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
245            self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
246            self.read_values()
247        except gdb.error:
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']))
252
253    def to_string(self):
254        """gdb API function.
255
256        This is called from gdb when we try to print a pthread_mutexattr_t.
257        """
258
259        return 'pthread_mutexattr_t'
260
261    def children(self):
262        """gdb API function.
263
264        This is called from gdb when we try to print a pthread_mutexattr_t.
265        """
266
267        return self.values
268
269    def read_values(self):
270        """Read the mutexattr's info and store it in self.values.
271
272        The data contained in self.values will be returned by the Iterator
273        created in self.children.
274        """
275
276        mutexattr_type = (self.mutexattr
277                          & 0xffffffff
278                          & ~PTHREAD_MUTEXATTR_FLAG_BITS
279                          & ~PTHREAD_MUTEX_NO_ELISION_NP)
280
281        # mutexattr_type must be casted to int because it's a gdb.Value
282        self.values.append(MUTEX_TYPES[int(mutexattr_type)])
283
284        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
285            self.values.append(('Robust', 'Yes'))
286        else:
287            self.values.append(('Robust', 'No'))
288
289        if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
290            self.values.append(('Shared', 'Yes'))
291        else:
292            self.values.append(('Shared', 'No'))
293
294        protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
295                    PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
296
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'))
303
304class ConditionVariablePrinter(object):
305    """Pretty printer for pthread_cond_t."""
306
307    def __init__(self, cond):
308        """Initialize the printer's internal data structures.
309
310        Args:
311            cond: A gdb.value representing a pthread_cond_t.
312        """
313
314        data = cond['__data']
315        self.wrefs = data['__wrefs']
316        self.values = []
317
318        self.read_values()
319
320    def to_string(self):
321        """gdb API function.
322
323        This is called from gdb when we try to print a pthread_cond_t.
324        """
325
326        return 'pthread_cond_t'
327
328    def children(self):
329        """gdb API function.
330
331        This is called from gdb when we try to print a pthread_cond_t.
332        """
333
334        return self.values
335
336    def read_values(self):
337        """Read the condvar's info and store it in self.values.
338
339        The data contained in self.values will be returned by the Iterator
340        created in self.children.
341        """
342
343        self.read_status()
344        self.read_attributes()
345
346    def read_status(self):
347        """Read the status of the condvar.
348
349        This method reads whether the condvar is destroyed and how many threads
350        are waiting for it.
351        """
352
353        self.values.append(('Threads known to still execute a wait function',
354                            self.wrefs >> PTHREAD_COND_WREFS_SHIFT))
355
356    def read_attributes(self):
357        """Read the condvar's attributes."""
358
359        if (self.wrefs & PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0:
360            self.values.append(('Clock ID', 'CLOCK_MONOTONIC'))
361        else:
362            self.values.append(('Clock ID', 'CLOCK_REALTIME'))
363
364        if (self.wrefs & PTHREAD_COND_SHARED_MASK) != 0:
365            self.values.append(('Shared', 'Yes'))
366        else:
367            self.values.append(('Shared', 'No'))
368
369class ConditionVariableAttributesPrinter(object):
370    """Pretty printer for pthread_condattr_t.
371
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.
374    """
375
376    def __init__(self, condattr):
377        """Initialize the printer's internal data structures.
378
379        Args:
380            condattr: A gdb.value representing a pthread_condattr_t.
381        """
382
383        self.values = []
384
385        try:
386            condattr_struct = gdb.lookup_type('struct pthread_condattr')
387            self.condattr = condattr.cast(condattr_struct)['value']
388            self.read_values()
389        except gdb.error:
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']))
394
395    def to_string(self):
396        """gdb API function.
397
398        This is called from gdb when we try to print a pthread_condattr_t.
399        """
400
401        return 'pthread_condattr_t'
402
403    def children(self):
404        """gdb API function.
405
406        This is called from gdb when we try to print a pthread_condattr_t.
407        """
408
409        return self.values
410
411    def read_values(self):
412        """Read the condattr's info and store it in self.values.
413
414        The data contained in self.values will be returned by the Iterator
415        created in self.children.
416        """
417
418        clock_id = (self.condattr >> 1) & ((1 << COND_CLOCK_BITS) - 1)
419
420        if clock_id != 0:
421            self.values.append(('Clock ID', 'CLOCK_MONOTONIC'))
422        else:
423            self.values.append(('Clock ID', 'CLOCK_REALTIME'))
424
425        if self.condattr & 1:
426            self.values.append(('Shared', 'Yes'))
427        else:
428            self.values.append(('Shared', 'No'))
429
430class RWLockPrinter(object):
431    """Pretty printer for pthread_rwlock_t."""
432
433    def __init__(self, rwlock):
434        """Initialize the printer's internal data structures.
435
436        Args:
437            rwlock: A gdb.value representing a pthread_rwlock_t.
438        """
439
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']
445        self.values = []
446        self.read_values()
447
448    def to_string(self):
449        """gdb API function.
450
451        This is called from gdb when we try to print a pthread_rwlock_t.
452        """
453
454        return 'pthread_rwlock_t'
455
456    def children(self):
457        """gdb API function.
458
459        This is called from gdb when we try to print a pthread_rwlock_t.
460        """
461
462        return self.values
463
464    def read_values(self):
465        """Read the rwlock's info and store it in self.values.
466
467        The data contained in self.values will be returned by the Iterator
468        created in self.children.
469        """
470
471        self.read_status()
472        self.read_attributes()
473
474    def read_status(self):
475        """Read the status of the rwlock."""
476
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))
481            else:
482                self.values.append(('Status', 'Not acquired'))
483        else:
484            r = self.readers >> PTHREAD_RWLOCK_READER_SHIFT
485            if r > 0:
486                self.values.append(('Status', 'Acquired (Read)'))
487                self.values.append(('Readers', r))
488            else:
489                self.values.append(('Status', 'Not acquired'))
490
491    def read_attributes(self):
492        """Read the attributes of the rwlock."""
493
494        if self.shared:
495            self.values.append(('Shared', 'Yes'))
496        else:
497            self.values.append(('Shared', 'No'))
498
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'))
503        else:
504            self.values.append(('Prefers', 'Writers no recursive readers'))
505
506class RWLockAttributesPrinter(object):
507    """Pretty printer for pthread_rwlockattr_t.
508
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.
512    """
513
514    def __init__(self, rwlockattr):
515        """Initialize the printer's internal data structures.
516
517        Args:
518            rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
519        """
520
521        self.values = []
522
523        try:
524            rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
525            self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
526            self.read_values()
527        except gdb.error:
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']))
532
533    def to_string(self):
534        """gdb API function.
535
536        This is called from gdb when we try to print a pthread_rwlockattr_t.
537        """
538
539        return 'pthread_rwlockattr_t'
540
541    def children(self):
542        """gdb API function.
543
544        This is called from gdb when we try to print a pthread_rwlockattr_t.
545        """
546
547        return self.values
548
549    def read_values(self):
550        """Read the rwlockattr's info and store it in self.values.
551
552        The data contained in self.values will be returned by the Iterator
553        created in self.children.
554        """
555
556        rwlock_type = self.rwlockattr['lockkind']
557        shared = self.rwlockattr['pshared']
558
559        if shared == PTHREAD_PROCESS_SHARED:
560            self.values.append(('Shared', 'Yes'))
561        else:
562            # PTHREAD_PROCESS_PRIVATE
563            self.values.append(('Shared', 'No'))
564
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'))
569        else:
570            self.values.append(('Prefers', 'Writers no recursive readers'))
571
572def register(objfile):
573    """Register the pretty printers within the given objfile."""
574
575    printer = gdb.printing.RegexpCollectionPrettyPrinter('glibc-pthread-locks')
576
577    printer.add_printer('pthread_mutex_t', r'^pthread_mutex_t$',
578                        MutexPrinter)
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$',
586                        RWLockPrinter)
587    printer.add_printer('pthread_rwlockattr_t', r'^pthread_rwlockattr_t$',
588                        RWLockAttributesPrinter)
589
590    if objfile == None:
591        objfile = gdb
592
593    gdb.printing.register_pretty_printer(objfile, printer)
594
595register(gdb.current_objfile())
596