]> git.ipfire.org Git - thirdparty/glibc.git/blob - nptl/nptl-printers.py
Add pretty printers for the NPTL lock types
[thirdparty/glibc.git] / nptl / nptl-printers.py
1 # Pretty printers for the NPTL lock types.
2 #
3 # Copyright (C) 2016 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 # <http://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 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 For architectures which support lock elision, this method reads
105 whether the mutex appears as locked in memory (i.e. it may show it as
106 unlocked even after calling pthread_mutex_lock).
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', 'Unlocked'))
128 else:
129 if self.lock & FUTEX_WAITERS:
130 self.values.append(('Status', 'Locked, possibly with waiters'))
131 else:
132 self.values.append(('Status',
133 'Locked, possibly with no waiters'))
134
135 if self.lock & FUTEX_OWNER_DIED:
136 self.values.append(('Owner ID', '%d (dead)' % self.owner))
137 else:
138 self.values.append(('Owner ID', self.lock & FUTEX_TID_MASK))
139
140 if self.owner == PTHREAD_MUTEX_INCONSISTENT:
141 self.values.append(('State protected by this mutex',
142 'Inconsistent'))
143 elif self.owner == PTHREAD_MUTEX_NOTRECOVERABLE:
144 self.values.append(('State protected by this mutex',
145 'Not recoverable'))
146
147 def read_status_no_robust(self):
148 """Read the status of a non-robust mutex.
149
150 Read info on whether the mutex is locked, if it may have waiters
151 and its owner (if any).
152 """
153
154 lock_value = self.lock
155
156 if self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
157 lock_value &= ~(PTHREAD_MUTEX_PRIO_CEILING_MASK)
158
159 if lock_value == PTHREAD_MUTEX_UNLOCKED:
160 self.values.append(('Status', 'Unlocked'))
161 else:
162 if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
163 waiters = self.lock & FUTEX_WAITERS
164 owner = self.lock & FUTEX_TID_MASK
165 else:
166 # Mutex protocol is PP or none
167 waiters = (self.lock != PTHREAD_MUTEX_LOCKED_NO_WAITERS)
168 owner = self.owner
169
170 if waiters:
171 self.values.append(('Status', 'Locked, possibly with waiters'))
172 else:
173 self.values.append(('Status',
174 'Locked, possibly with no waiters'))
175
176 self.values.append(('Owner ID', owner))
177
178 def read_attributes(self):
179 """Read the mutex's attributes."""
180
181 if self.kind != PTHREAD_MUTEX_DESTROYED:
182 if self.kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP:
183 self.values.append(('Robust', 'Yes'))
184 else:
185 self.values.append(('Robust', 'No'))
186
187 # In glibc, robust mutexes always have their pshared flag set to
188 # 'shared' regardless of what the pshared flag of their
189 # mutexattr was. Therefore a robust mutex will act as shared
190 # even if it was initialized with a 'private' mutexattr.
191 if self.kind & PTHREAD_MUTEX_PSHARED_BIT:
192 self.values.append(('Shared', 'Yes'))
193 else:
194 self.values.append(('Shared', 'No'))
195
196 if self.kind & PTHREAD_MUTEX_PRIO_INHERIT_NP:
197 self.values.append(('Protocol', 'Priority inherit'))
198 elif self.kind & PTHREAD_MUTEX_PRIO_PROTECT_NP:
199 prio_ceiling = ((self.lock & PTHREAD_MUTEX_PRIO_CEILING_MASK)
200 >> PTHREAD_MUTEX_PRIO_CEILING_SHIFT)
201
202 self.values.append(('Protocol', 'Priority protect'))
203 self.values.append(('Priority ceiling', prio_ceiling))
204 else:
205 # PTHREAD_PRIO_NONE
206 self.values.append(('Protocol', 'None'))
207
208 def read_misc_info(self):
209 """Read miscellaneous info on the mutex.
210
211 For now this reads the number of times a recursive mutex was locked
212 by the same thread.
213 """
214
215 mutex_type = self.kind & PTHREAD_MUTEX_KIND_MASK
216
217 if mutex_type == PTHREAD_MUTEX_RECURSIVE and self.count > 1:
218 self.values.append(('Times locked recursively', self.count))
219
220 class MutexAttributesPrinter(object):
221 """Pretty printer for pthread_mutexattr_t.
222
223 In the NPTL this is a type that's always casted to struct pthread_mutexattr
224 which has a single 'mutexkind' field containing the actual attributes.
225 """
226
227 def __init__(self, mutexattr):
228 """Initialize the printer's internal data structures.
229
230 Args:
231 mutexattr: A gdb.value representing a pthread_mutexattr_t.
232 """
233
234 self.values = []
235
236 try:
237 mutexattr_struct = gdb.lookup_type('struct pthread_mutexattr')
238 self.mutexattr = mutexattr.cast(mutexattr_struct)['mutexkind']
239 self.read_values()
240 except gdb.error:
241 # libpthread doesn't have debug symbols, thus we can't find the
242 # real struct type. Just print the union members.
243 self.values.append(('__size', mutexattr['__size']))
244 self.values.append(('__align', mutexattr['__align']))
245
246 def to_string(self):
247 """gdb API function.
248
249 This is called from gdb when we try to print a pthread_mutexattr_t.
250 """
251
252 return 'pthread_mutexattr_t'
253
254 def children(self):
255 """gdb API function.
256
257 This is called from gdb when we try to print a pthread_mutexattr_t.
258 """
259
260 return self.values
261
262 def read_values(self):
263 """Read the mutexattr's info and store it in self.values.
264
265 The data contained in self.values will be returned by the Iterator
266 created in self.children.
267 """
268
269 mutexattr_type = (self.mutexattr
270 & ~PTHREAD_MUTEXATTR_FLAG_BITS
271 & ~PTHREAD_MUTEX_NO_ELISION_NP)
272
273 # mutexattr_type must be casted to int because it's a gdb.Value
274 self.values.append(MUTEX_TYPES[int(mutexattr_type)])
275
276 if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_ROBUST:
277 self.values.append(('Robust', 'Yes'))
278 else:
279 self.values.append(('Robust', 'No'))
280
281 if self.mutexattr & PTHREAD_MUTEXATTR_FLAG_PSHARED:
282 self.values.append(('Shared', 'Yes'))
283 else:
284 self.values.append(('Shared', 'No'))
285
286 protocol = ((self.mutexattr & PTHREAD_MUTEXATTR_PROTOCOL_MASK) >>
287 PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)
288
289 if protocol == PTHREAD_PRIO_NONE:
290 self.values.append(('Protocol', 'None'))
291 elif protocol == PTHREAD_PRIO_INHERIT:
292 self.values.append(('Protocol', 'Priority inherit'))
293 elif protocol == PTHREAD_PRIO_PROTECT:
294 self.values.append(('Protocol', 'Priority protect'))
295
296 CLOCK_IDS = {
297 CLOCK_REALTIME: 'CLOCK_REALTIME',
298 CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
299 CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
300 CLOCK_THREAD_CPUTIME_ID: 'CLOCK_THREAD_CPUTIME_ID',
301 CLOCK_MONOTONIC_RAW: 'CLOCK_MONOTONIC_RAW',
302 CLOCK_REALTIME_COARSE: 'CLOCK_REALTIME_COARSE',
303 CLOCK_MONOTONIC_COARSE: 'CLOCK_MONOTONIC_COARSE'
304 }
305
306 class ConditionVariablePrinter(object):
307 """Pretty printer for pthread_cond_t."""
308
309 def __init__(self, cond):
310 """Initialize the printer's internal data structures.
311
312 Args:
313 cond: A gdb.value representing a pthread_cond_t.
314 """
315
316 # Since PTHREAD_COND_SHARED is an integer, we need to cast it to void *
317 # to be able to compare it to the condvar's __data.__mutex member.
318 #
319 # While it looks like self.shared_value should be a class variable,
320 # that would result in it having an incorrect size if we're loading
321 # these printers through .gdbinit for a 64-bit objfile in AMD64.
322 # This is because gdb initially assumes the pointer size to be 4 bytes,
323 # and only sets it to 8 after loading the 64-bit objfiles. Since
324 # .gdbinit runs before any objfiles are loaded, this would effectively
325 # make self.shared_value have a size of 4, thus breaking later
326 # comparisons with pointers whose types are looked up at runtime.
327 void_ptr_type = gdb.lookup_type('void').pointer()
328 self.shared_value = gdb.Value(PTHREAD_COND_SHARED).cast(void_ptr_type)
329
330 data = cond['__data']
331 self.total_seq = data['__total_seq']
332 self.mutex = data['__mutex']
333 self.nwaiters = data['__nwaiters']
334 self.values = []
335
336 self.read_values()
337
338 def to_string(self):
339 """gdb API function.
340
341 This is called from gdb when we try to print a pthread_cond_t.
342 """
343
344 return 'pthread_cond_t'
345
346 def children(self):
347 """gdb API function.
348
349 This is called from gdb when we try to print a pthread_cond_t.
350 """
351
352 return self.values
353
354 def read_values(self):
355 """Read the condvar's info and store it in self.values.
356
357 The data contained in self.values will be returned by the Iterator
358 created in self.children.
359 """
360
361 self.read_status()
362 self.read_attributes()
363 self.read_mutex_info()
364
365 def read_status(self):
366 """Read the status of the condvar.
367
368 This method reads whether the condvar is destroyed and how many threads
369 are waiting for it.
370 """
371
372 if self.total_seq == PTHREAD_COND_DESTROYED:
373 self.values.append(('Status', 'Destroyed'))
374
375 self.values.append(('Threads waiting for this condvar',
376 self.nwaiters >> COND_NWAITERS_SHIFT))
377
378 def read_attributes(self):
379 """Read the condvar's attributes."""
380
381 clock_id = self.nwaiters & ((1 << COND_NWAITERS_SHIFT) - 1)
382
383 # clock_id must be casted to int because it's a gdb.Value
384 self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
385
386 shared = (self.mutex == self.shared_value)
387
388 if shared:
389 self.values.append(('Shared', 'Yes'))
390 else:
391 self.values.append(('Shared', 'No'))
392
393 def read_mutex_info(self):
394 """Read the data of the mutex this condvar is bound to.
395
396 A pthread_cond_t's __data.__mutex member is a void * which
397 must be casted to pthread_mutex_t *. For shared condvars, this
398 member isn't recorded and has a special value instead.
399 """
400
401 if self.mutex and self.mutex != self.shared_value:
402 mutex_type = gdb.lookup_type('pthread_mutex_t')
403 mutex = self.mutex.cast(mutex_type.pointer()).dereference()
404
405 self.values.append(('Mutex', mutex))
406
407 class ConditionVariableAttributesPrinter(object):
408 """Pretty printer for pthread_condattr_t.
409
410 In the NPTL this is a type that's always casted to struct pthread_condattr,
411 which has a single 'value' field containing the actual attributes.
412 """
413
414 def __init__(self, condattr):
415 """Initialize the printer's internal data structures.
416
417 Args:
418 condattr: A gdb.value representing a pthread_condattr_t.
419 """
420
421 self.values = []
422
423 try:
424 condattr_struct = gdb.lookup_type('struct pthread_condattr')
425 self.condattr = condattr.cast(condattr_struct)['value']
426 self.read_values()
427 except gdb.error:
428 # libpthread doesn't have debug symbols, thus we can't find the
429 # real struct type. Just print the union members.
430 self.values.append(('__size', condattr['__size']))
431 self.values.append(('__align', condattr['__align']))
432
433 def to_string(self):
434 """gdb API function.
435
436 This is called from gdb when we try to print a pthread_condattr_t.
437 """
438
439 return 'pthread_condattr_t'
440
441 def children(self):
442 """gdb API function.
443
444 This is called from gdb when we try to print a pthread_condattr_t.
445 """
446
447 return self.values
448
449 def read_values(self):
450 """Read the condattr's info and store it in self.values.
451
452 The data contained in self.values will be returned by the Iterator
453 created in self.children.
454 """
455
456 clock_id = self.condattr & ((1 << COND_NWAITERS_SHIFT) - 1)
457
458 # clock_id must be casted to int because it's a gdb.Value
459 self.values.append(('Clock ID', CLOCK_IDS[int(clock_id)]))
460
461 if self.condattr & 1:
462 self.values.append(('Shared', 'Yes'))
463 else:
464 self.values.append(('Shared', 'No'))
465
466 class RWLockPrinter(object):
467 """Pretty printer for pthread_rwlock_t."""
468
469 def __init__(self, rwlock):
470 """Initialize the printer's internal data structures.
471
472 Args:
473 rwlock: A gdb.value representing a pthread_rwlock_t.
474 """
475
476 data = rwlock['__data']
477 self.readers = data['__nr_readers']
478 self.queued_readers = data['__nr_readers_queued']
479 self.queued_writers = data['__nr_writers_queued']
480 self.writer_id = data['__writer']
481 self.shared = data['__shared']
482 self.prefers_writers = data['__flags']
483 self.values = []
484 self.read_values()
485
486 def to_string(self):
487 """gdb API function.
488
489 This is called from gdb when we try to print a pthread_rwlock_t.
490 """
491
492 return 'pthread_rwlock_t'
493
494 def children(self):
495 """gdb API function.
496
497 This is called from gdb when we try to print a pthread_rwlock_t.
498 """
499
500 return self.values
501
502 def read_values(self):
503 """Read the rwlock's info and store it in self.values.
504
505 The data contained in self.values will be returned by the Iterator
506 created in self.children.
507 """
508
509 self.read_status()
510 self.read_attributes()
511
512 def read_status(self):
513 """Read the status of the rwlock."""
514
515 # Right now pthread_rwlock_destroy doesn't do anything, so there's no
516 # way to check if an rwlock is destroyed.
517
518 if self.writer_id:
519 self.values.append(('Status', 'Locked (Write)'))
520 self.values.append(('Writer ID', self.writer_id))
521 elif self.readers:
522 self.values.append(('Status', 'Locked (Read)'))
523 self.values.append(('Readers', self.readers))
524 else:
525 self.values.append(('Status', 'Unlocked'))
526
527 self.values.append(('Queued readers', self.queued_readers))
528 self.values.append(('Queued writers', self.queued_writers))
529
530 def read_attributes(self):
531 """Read the attributes of the rwlock."""
532
533 if self.shared:
534 self.values.append(('Shared', 'Yes'))
535 else:
536 self.values.append(('Shared', 'No'))
537
538 if self.prefers_writers:
539 self.values.append(('Prefers', 'Writers'))
540 else:
541 self.values.append(('Prefers', 'Readers'))
542
543 class RWLockAttributesPrinter(object):
544 """Pretty printer for pthread_rwlockattr_t.
545
546 In the NPTL this is a type that's always casted to
547 struct pthread_rwlockattr, which has two fields ('lockkind' and 'pshared')
548 containing the actual attributes.
549 """
550
551 def __init__(self, rwlockattr):
552 """Initialize the printer's internal data structures.
553
554 Args:
555 rwlockattr: A gdb.value representing a pthread_rwlockattr_t.
556 """
557
558 self.values = []
559
560 try:
561 rwlockattr_struct = gdb.lookup_type('struct pthread_rwlockattr')
562 self.rwlockattr = rwlockattr.cast(rwlockattr_struct)
563 self.read_values()
564 except gdb.error:
565 # libpthread doesn't have debug symbols, thus we can't find the
566 # real struct type. Just print the union members.
567 self.values.append(('__size', rwlockattr['__size']))
568 self.values.append(('__align', rwlockattr['__align']))
569
570 def to_string(self):
571 """gdb API function.
572
573 This is called from gdb when we try to print a pthread_rwlockattr_t.
574 """
575
576 return 'pthread_rwlockattr_t'
577
578 def children(self):
579 """gdb API function.
580
581 This is called from gdb when we try to print a pthread_rwlockattr_t.
582 """
583
584 return self.values
585
586 def read_values(self):
587 """Read the rwlockattr's info and store it in self.values.
588
589 The data contained in self.values will be returned by the Iterator
590 created in self.children.
591 """
592
593 rwlock_type = self.rwlockattr['lockkind']
594 shared = self.rwlockattr['pshared']
595
596 if shared == PTHREAD_PROCESS_SHARED:
597 self.values.append(('Shared', 'Yes'))
598 else:
599 # PTHREAD_PROCESS_PRIVATE
600 self.values.append(('Shared', 'No'))
601
602 if (rwlock_type == PTHREAD_RWLOCK_PREFER_READER_NP or
603 rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NP):
604 # This is a known bug. Using PTHREAD_RWLOCK_PREFER_WRITER_NP will
605 # still make the rwlock prefer readers.
606 self.values.append(('Prefers', 'Readers'))
607 elif rwlock_type == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:
608 self.values.append(('Prefers', 'Writers'))
609
610 def register(objfile):
611 """Register the pretty printers within the given objfile."""
612
613 printer = gdb.printing.RegexpCollectionPrettyPrinter('glibc-pthread-locks')
614
615 printer.add_printer('pthread_mutex_t', r'^pthread_mutex_t$',
616 MutexPrinter)
617 printer.add_printer('pthread_mutexattr_t', r'^pthread_mutexattr_t$',
618 MutexAttributesPrinter)
619 printer.add_printer('pthread_cond_t', r'^pthread_cond_t$',
620 ConditionVariablePrinter)
621 printer.add_printer('pthread_condattr_t', r'^pthread_condattr_t$',
622 ConditionVariableAttributesPrinter)
623 printer.add_printer('pthread_rwlock_t', r'^pthread_rwlock_t$',
624 RWLockPrinter)
625 printer.add_printer('pthread_rwlockattr_t', r'^pthread_rwlockattr_t$',
626 RWLockAttributesPrinter)
627
628 if objfile == None:
629 objfile = gdb
630
631 gdb.printing.register_pretty_printer(objfile, printer)
632
633 register(gdb.current_objfile())