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