(root)/
glibc-2.38/
nptl/
nptl-printers.py
       1  # Pretty printers for the NPTL lock types.
       2  #
       3  # Copyright (C) 2016-2023 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  
      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.
      32  """
      33  
      34  from __future__ import print_function
      35  
      36  import gdb
      37  import gdb.printing
      38  from nptl_lock_constants import *
      39  
      40  MUTEX_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  
      47  class ESC[4;38;5;81mMutexPrinter(ESC[4;38;5;149mobject):
      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  
     227  class ESC[4;38;5;81mMutexAttributesPrinter(ESC[4;38;5;149mobject):
     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  
     304  class ESC[4;38;5;81mConditionVariablePrinter(ESC[4;38;5;149mobject):
     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  
     369  class ESC[4;38;5;81mConditionVariableAttributesPrinter(ESC[4;38;5;149mobject):
     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  
     430  class ESC[4;38;5;81mRWLockPrinter(ESC[4;38;5;149mobject):
     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  
     506  class ESC[4;38;5;81mRWLockAttributesPrinter(ESC[4;38;5;149mobject):
     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  
     572  def 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  
     595  register(gdb.current_objfile())