File indexing completed on 2024-04-14 05:35:05
0001 # Copyright 2014 Alex Merry <alex.merry@kde.org> 0002 # 0003 # Permission to use, copy, modify, and distribute this software 0004 # and its documentation for any purpose and without fee is hereby 0005 # granted, provided that the above copyright notice appear in all 0006 # copies and that both that the copyright notice and this 0007 # permission notice and warranty disclaimer appear in supporting 0008 # documentation, and that the name of the author not be used in 0009 # advertising or publicity pertaining to distribution of the 0010 # software without specific, written prior permission. 0011 # 0012 # The author disclaims all warranties with regard to this 0013 # software, including all implied warranties of merchantability 0014 # and fitness. In no event shall the author be liable for any 0015 # special, indirect or consequential damages or any damages 0016 # whatsoever resulting from loss of use, data or profits, whether 0017 # in an action of contract, negligence or other tortious action, 0018 # arising out of or in connection with the use or performance of 0019 # this software. 0020 0021 import gdb.printing 0022 import itertools 0023 from qt5printers import typeinfo 0024 try: 0025 import urlparse 0026 except ImportError: 0027 # Python 3 0028 import urllib.parse as urlparse 0029 0030 """Qt5Core pretty printer for GDB.""" 0031 0032 # NB: no QPair printer: the default should be fine 0033 0034 def _format_jd(jd): 0035 """Format a Julian Day in YYYY-MM-DD format.""" 0036 # maths from https://www.tondering.dk/claus/cal/julperiod.php 0037 a = jd + 32044 0038 b = (4 * a + 3) // 146097 0039 c = a - ( (146097 * b) // 4 ) 0040 d = (4 * c + 3) // 1461 0041 e = c - ( (1461 * d) // 4 ) 0042 m = (5 * e + 2) // 153 0043 day = e - ( (153 * m + 2) // 5 ) + 1 0044 month = m + 3 - 12 * ( m // 10 ) 0045 year = 100 * b + d - 4800 + ( m // 10 ) 0046 return '{:0=4}-{:0=2}-{:0=2}'.format(year, month, day) 0047 0048 def _jd_is_valid(jd): 0049 """Return whether QDate would consider a given Julian Day valid.""" 0050 return jd >= -784350574879 and jd <= 784354017364 0051 0052 def _format_time_ms(msecs): 0053 """Format a number of milliseconds since midnight in HH:MM:SS.ssss format.""" 0054 secs = msecs // 1000 0055 mins = secs // 60 0056 hours = mins // 60 0057 return '{:0=2}:{:0=2}:{:0=2}.{:0=3}'.format( 0058 hours % 24, mins % 60, secs % 60, msecs % 1000) 0059 0060 def _ms_is_valid(msecs): 0061 """Return whether QTime would consider a ms since midnight valid.""" 0062 return msecs >= 0 and msecs <= 86400000 0063 0064 class ArrayIter: 0065 """Iterates over a fixed-size array.""" 0066 def __init__(self, array, size): 0067 self.array = array 0068 self.i = -1 0069 self.size = size 0070 0071 def __iter__(self): 0072 return self 0073 0074 def __next__(self): 0075 if self.i + 1 >= self.size: 0076 raise StopIteration 0077 self.i += 1 0078 return ('[%d]' % self.i, self.array[self.i]) 0079 0080 def next(self): 0081 return self.__next__() 0082 0083 class StructReader: 0084 """Reads entries from a struct.""" 0085 def __init__(self, data): 0086 self.data = data.reinterpret_cast(gdb.lookup_type('char').pointer()) 0087 self.ptr_t = gdb.lookup_type('void').pointer() 0088 0089 def next_aligned_val(self, typ): 0090 ptr_val = int(str(self.data.reinterpret_cast(self.ptr_t)), 16) 0091 misalignment = ptr_val % self.ptr_t.sizeof 0092 if misalignment > 0: 0093 self.data += self.ptr_t.sizeof - misalignment 0094 val = self.data.reinterpret_cast(typ.pointer()) 0095 self.data += typ.sizeof 0096 return val.referenced_value() 0097 0098 def next_val(self, typ): 0099 val = self.data.reinterpret_cast(typ.pointer()) 0100 self.data += typ.sizeof 0101 return val.referenced_value() 0102 0103 class QBitArrayPrinter: 0104 """Print a Qt5 QBitArray""" 0105 0106 class Iter: 0107 def __init__(self, data, size): 0108 self.data = data 0109 self.i = -1 0110 self.size = size 0111 0112 def __iter__(self): 0113 return self 0114 0115 def __next__(self): 0116 if self.i + 1 >= self.size: 0117 raise StopIteration 0118 self.i += 1 0119 if self.data[1 + (self.i >> 3)] & (1 << (self.i&7)): 0120 return (str(self.i), 1) 0121 else: 0122 return (str(self.i), 0) 0123 0124 def next(self): 0125 return self.__next__() 0126 0127 def __init__(self, val): 0128 self.val = val 0129 0130 def children(self): 0131 d = self.val['d']['d'] 0132 data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0133 size = (int(d['size']) << 3) - int(data[0]) 0134 0135 return self.Iter(data, size) 0136 0137 def to_string(self): 0138 d = self.val['d']['d'] 0139 data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0140 size = (int(d['size']) << 3) - int(data[0]) 0141 if size == 0: 0142 return '<empty>' 0143 return None 0144 0145 def display_hint(self): 0146 return 'array' 0147 0148 class QByteArrayPrinter: 0149 """Print a Qt5 QByteArray""" 0150 0151 def __init__(self, val): 0152 self.val = val 0153 0154 def children(self): 0155 d = self.val['d'] 0156 data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0157 return ArrayIter(data, d['size']) 0158 0159 def to_string(self): 0160 d = self.val['d'] 0161 data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0162 return data.string('', 'replace', d['size']) 0163 0164 def display_hint(self): 0165 return 'string' 0166 0167 class QCharPrinter: 0168 """Print a Qt5 QChar""" 0169 0170 def __init__(self, val): 0171 self.val = val 0172 0173 def to_string(self): 0174 ucs = self.val['ucs'] 0175 data = ucs.address.reinterpret_cast(gdb.lookup_type('char').pointer()) 0176 unicode_str = data.string('utf-16', 'replace', 2) 0177 uch = unicode_str[0] 0178 if uch == unichr(0x27): 0179 return "'\\''" 0180 # this actually gives us Python escapes, but they should all be 0181 # valid C escapes as well 0182 return "'" + uch.encode('unicode_escape') + "'" 0183 0184 def display_hint(self): 0185 # this is not recognized by gdb, hence the manual escaping and quoting 0186 # we do above 0187 return 'char' 0188 0189 class QDatePrinter: 0190 """Print a Qt5 QDate""" 0191 0192 def __init__(self, val): 0193 self.val = val 0194 0195 def to_string(self): 0196 jd = int(self.val['jd']) 0197 if not _jd_is_valid(jd): 0198 return '<invalid>' 0199 return _format_jd(jd) 0200 0201 def display_hint(self): 0202 return 'date' 0203 0204 class QDateTimePrinter: 0205 """Print a Qt5 QDateTime""" 0206 0207 def __init__(self, val): 0208 self.val = val 0209 0210 _unix_epoch_jd = 2440588 0211 _ms_per_day = 86400000 0212 0213 # status field 0214 _validDate = 0x04 0215 _validTime = 0x08 0216 _validDateTime = 0x10 0217 _timeZoneCached = 0x20 0218 0219 # time spec 0220 _localTime = 0 0221 _UTC = 1 0222 _offsetFromUTC = 2 0223 _timeZone = 3 0224 0225 def to_string(self): 0226 d = self.val['d']['d'] 0227 if not d: 0228 return '<invalid>' 0229 0230 try: 0231 qshareddata_t = gdb.lookup_type('QSharedData') 0232 except gdb.error: 0233 try: 0234 # well, it only has a QAtomicInt in it 0235 qshareddata_t = gdb.lookup_type('QAtomicInt') 0236 except gdb.error: 0237 # let's hope it's the same size as an int 0238 qshareddata_t = gdb.lookup_type('int') 0239 try: 0240 timespec_t = gdb.lookup_type('Qt::TimeSpec') 0241 except gdb.error: 0242 # probably an int 0243 timespec_t = gdb.lookup_type('int') 0244 0245 reader = StructReader(d) 0246 reader.next_val(qshareddata_t) 0247 m_msecs = reader.next_aligned_val(gdb.lookup_type('qint64')) 0248 spec = int(reader.next_val(timespec_t)) 0249 m_offsetFromUtc = reader.next_val(gdb.lookup_type('int')) 0250 m_timeZone = reader.next_val(gdb.lookup_type('QTimeZone')) 0251 status = int(reader.next_val(gdb.lookup_type('int'))) 0252 0253 if spec == self._timeZone: 0254 timeZoneStr = QTimeZonePrinter(m_timeZone).to_string() 0255 if timeZoneStr == '': 0256 return '<invalid>' 0257 0258 if spec == self._localTime or (spec == self._timeZone and 0259 not status & self._timeZoneCached): 0260 # Because QDateTime delays timezone calculations as far as 0261 # possible, the ValidDateTime flag may not be set even if 0262 # it is a valid DateTime. 0263 if not status & self._validDate or not status & self._validTime: 0264 return '<invalid>' 0265 elif not (status & self._validDateTime): 0266 return '<invalid>' 0267 0268 # actually fetch: 0269 m_msecs = int(m_msecs) 0270 0271 jd = self._unix_epoch_jd # UNIX epoch 0272 jd += m_msecs // self._ms_per_day 0273 msecs = m_msecs % self._ms_per_day 0274 if msecs < 0: 0275 # need to adjust back to the previous day 0276 jd -= 1 0277 msecs += self._ms_per_day 0278 0279 result = _format_jd(jd) + ' ' + _format_time_ms(msecs) 0280 0281 if spec == self._localTime: 0282 result += ' (Local)' 0283 elif spec == self._UTC: 0284 result += ' (UTC)' 0285 elif spec == self._offsetFromUTC: 0286 offset = int(m_offsetFromUtc) 0287 if offset == 0: 0288 diffstr = '' 0289 else: 0290 hours = abs(offset // 3600) 0291 mins = abs((offset % 3600) // 60) 0292 secs = abs(offset % 60) 0293 sign = '+' if offset > 0 else '-' 0294 diffstr = '{:}{:0=2d}:{:0=2d}'.format(sign, hours, mins) 0295 if secs > 0: 0296 diffstr += ':{:0=2d}'.format(secs) 0297 result += ' (UTC{:})'.format(diffstr) 0298 elif spec == self._timeZone: 0299 result += ' ({:})'.format(timeZoneStr) 0300 0301 return result 0302 0303 def display_hint(self): 0304 return 'datetime' 0305 0306 class QHashPrinter: 0307 """Print a Qt5 QHash""" 0308 0309 class Iter: 0310 def __init__(self, d, e): 0311 self.buckets_left = d['numBuckets'] 0312 self.node_type = e.type 0313 # set us up at the end of a "dummy bucket" 0314 self.current_bucket = d['buckets'] - 1 0315 self.current_node = None 0316 self.i = -1 0317 self.waiting_for_value = False 0318 0319 def __iter__(self): 0320 return self 0321 0322 def __next__(self): 0323 if self.waiting_for_value: 0324 self.waiting_for_value = False 0325 node = self.current_node.reinterpret_cast(self.node_type) 0326 return ('value' + str(self.i), node['value']) 0327 0328 if self.current_node: 0329 self.current_node = self.current_node['next'] 0330 0331 # the dummy node that terminates a bucket is distinguishable 0332 # by not having its 'next' value set 0333 if not self.current_node or not self.current_node['next']: 0334 while self.buckets_left: 0335 self.current_bucket += 1 0336 self.buckets_left -= 1 0337 self.current_node = self.current_bucket.referenced_value() 0338 if self.current_node['next']: 0339 break 0340 else: 0341 raise StopIteration 0342 0343 self.i += 1 0344 self.waiting_for_value = True 0345 node = self.current_node.reinterpret_cast(self.node_type) 0346 return ('key' + str(self.i), node['key']) 0347 0348 def next(self): 0349 return self.__next__() 0350 0351 def __init__(self, val): 0352 self.val = val 0353 0354 def children(self): 0355 d = self.val['d'] 0356 0357 if d['size'] == 0: 0358 return [] 0359 0360 return self.Iter(d, self.val['e']) 0361 0362 def to_string(self): 0363 # if we return an empty list from children, gdb doesn't print anything 0364 if self.val['d']['size'] == 0: 0365 return '<empty>' 0366 return None 0367 0368 def display_hint(self): 0369 return 'map' 0370 0371 class QLatin1StringPrinter: 0372 """Print a Qt5 QLatin1String""" 0373 0374 def __init__(self, val): 0375 self.val = val 0376 0377 def to_string(self): 0378 return self.val['m_data'].string('', 'replace', self.val['m_size']) 0379 0380 def display_hint(self): 0381 return 'string' 0382 0383 class QLinkedListPrinter: 0384 """Print a Qt5 QLinkedList""" 0385 0386 class Iter: 0387 def __init__(self, tail, size): 0388 self.current = tail 0389 self.i = -1 0390 self.size = size 0391 0392 def __iter__(self): 0393 return self 0394 0395 def __next__(self): 0396 if self.i + 1 >= self.size: 0397 raise StopIteration 0398 self.i += 1 0399 self.current = self.current['n'] 0400 return (str(self.i), self.current['t']) 0401 0402 def next(self): 0403 return self.__next__() 0404 0405 def __init__(self, val): 0406 self.val = val 0407 0408 def children(self): 0409 size = int(self.val['d']['size']) 0410 0411 if size == 0: 0412 return [] 0413 0414 return self.Iter(self.val['e'], size) 0415 0416 def to_string(self): 0417 # if we return an empty list from children, gdb doesn't print anything 0418 if self.val['d']['size'] == 0: 0419 return '<empty>' 0420 return None 0421 0422 def display_hint(self): 0423 return 'array' 0424 0425 class QListPrinter: 0426 """Print a Qt5 QList""" 0427 0428 class Iter: 0429 def __init__(self, array, begin, end, typ): 0430 self.array = array 0431 self.end = end 0432 self.begin = begin 0433 self.offset = 0 0434 if typ.name == 'QStringList': 0435 self.el_type = gdb.lookup_type('QString') 0436 else: 0437 self.el_type = typ.template_argument(0) 0438 0439 if ((self.el_type.sizeof > gdb.lookup_type('void').pointer().sizeof) 0440 or typeinfo.type_is_known_static(self.el_type)): 0441 self.is_pointer = True 0442 elif (typeinfo.type_is_known_movable(self.el_type) or 0443 typeinfo.type_is_known_primitive(self.el_type)): 0444 self.is_pointer = False 0445 else: 0446 raise ValueError("Could not determine whether QList stores " + 0447 self.el_type.name + " directly or as a pointer: to fix " + 0448 "this, add it to one of the variables in the "+ 0449 "qt5printers.typeinfo module") 0450 self.node_type = gdb.lookup_type(typ.name + '::Node').pointer() 0451 0452 def __iter__(self): 0453 return self 0454 0455 def __next__(self): 0456 if self.begin + self.offset >= self.end: 0457 raise StopIteration 0458 node = self.array[self.begin + self.offset].reinterpret_cast(self.node_type) 0459 if self.is_pointer: 0460 p = node['v'] 0461 else: 0462 p = node 0463 self.offset += 1 0464 return ((str(self.offset), p.cast(self.el_type))) 0465 0466 def next(self): 0467 return self.__next__() 0468 0469 def __init__(self, val): 0470 self.val = val 0471 0472 def children(self): 0473 d = self.val['d'] 0474 begin = int(d['begin']) 0475 end = int(d['end']) 0476 0477 if begin == end: 0478 return [] 0479 0480 return self.Iter(d['array'], begin, end, self.val.type.strip_typedefs()) 0481 0482 def to_string(self): 0483 # if we return an empty list from children, gdb doesn't print anything 0484 if self.val['d']['begin'] == self.val['d']['end']: 0485 return '<empty>' 0486 return None 0487 0488 def display_hint(self): 0489 return 'array' 0490 0491 class QMapPrinter: 0492 """Print a Qt5 QMap""" 0493 0494 class Iter: 0495 def __init__(self, root, node_p_type): 0496 self.root = root 0497 self.current = None 0498 self.node_p_type = node_p_type 0499 self.next_is_key = True 0500 self.i = -1 0501 # we store the path here to avoid keeping re-fetching 0502 # values from the inferior (also, skips the pointer 0503 # arithmetic involved in using the parent pointer) 0504 self.path = [] 0505 0506 def __iter__(self): 0507 return self 0508 0509 def moveToNextNode(self): 0510 if self.current is None: 0511 # find the leftmost node 0512 if not self.root['left']: 0513 return False 0514 self.current = self.root 0515 while self.current['left']: 0516 self.path.append(self.current) 0517 self.current = self.current['left'] 0518 elif self.current['right']: 0519 self.path.append(self.current) 0520 self.current = self.current['right'] 0521 while self.current['left']: 0522 self.path.append(self.current) 0523 self.current = self.current['left'] 0524 else: 0525 last = self.current 0526 self.current = self.path.pop() 0527 while self.current['right'] == last: 0528 last = self.current 0529 self.current = self.path.pop() 0530 # if there are no more parents, we are at the root 0531 if len(self.path) == 0: 0532 return False 0533 return True 0534 0535 def __next__(self): 0536 if self.next_is_key: 0537 if not self.moveToNextNode(): 0538 raise StopIteration 0539 self.current_typed = self.current.reinterpret_cast(self.node_p_type) 0540 self.next_is_key = False 0541 self.i += 1 0542 return ('key' + str(self.i), self.current_typed['key']) 0543 else: 0544 self.next_is_key = True 0545 return ('value' + str(self.i), self.current_typed['value']) 0546 0547 def next(self): 0548 return self.__next__() 0549 0550 def __init__(self, val): 0551 self.val = val 0552 0553 def children(self): 0554 d = self.val['d'] 0555 size = int(d['size']) 0556 0557 if size == 0: 0558 return [] 0559 0560 realtype = self.val.type.strip_typedefs() 0561 keytype = realtype.template_argument(0) 0562 valtype = realtype.template_argument(1) 0563 node_type = gdb.lookup_type('QMapData<' + keytype.name + ',' + valtype.name + '>::Node') 0564 0565 return self.Iter(d['header'], node_type.pointer()) 0566 0567 def to_string(self): 0568 # if we return an empty list from children, gdb doesn't print anything 0569 if self.val['d']['size'] == 0: 0570 return '<empty>' 0571 return None 0572 0573 def display_hint(self): 0574 return 'map' 0575 0576 class QSetPrinter: 0577 """Print a Qt5 QSet""" 0578 0579 def __init__(self, val): 0580 self.val = val 0581 0582 def children(self): 0583 hashPrinter = QHashPrinter(self.val['q_hash']) 0584 # the keys of the hash are the elements of the set, so select 0585 # every other item (starting with the first) 0586 return itertools.islice(hashPrinter.children(), 0, None, 2) 0587 0588 def to_string(self): 0589 # if we return an empty list from children, gdb doesn't print anything 0590 if self.val['q_hash']['d']['size'] == 0: 0591 return '<empty>' 0592 return None 0593 0594 def display_hint(self): 0595 return 'array' 0596 0597 class QStringPrinter: 0598 """Print a Qt5 QString""" 0599 0600 def __init__(self, val): 0601 self.val = val 0602 0603 def to_string(self): 0604 d = self.val['d'] 0605 data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0606 data_len = d['size'] * gdb.lookup_type('unsigned short').sizeof 0607 return data.string('utf-16', 'replace', data_len) 0608 0609 def display_hint(self): 0610 return 'string' 0611 0612 class QTimePrinter: 0613 """Print a Qt5 QTime""" 0614 0615 def __init__(self, val): 0616 self.val = val 0617 0618 def to_string(self): 0619 msecs = int(self.val['mds']) 0620 if not _ms_is_valid(msecs): 0621 return '<invalid>' 0622 return _format_time_ms(msecs) 0623 0624 def display_hint(self): 0625 return 'time' 0626 0627 class QTimeZonePrinter: 0628 """Print a Qt5 QTimeZone""" 0629 0630 def __init__(self, val): 0631 self.val = val 0632 0633 def to_string(self): 0634 d = self.val['d']['d'] 0635 if not d: 0636 return '' 0637 0638 try: 0639 # Accessing the private data is error-prone, 0640 # so try just calling the id() method. 0641 # This should be reasonably safe, as all it will 0642 # do is create a QByteArray that references the 0643 # same internal data as the stored one. However, 0644 # it will only work with an attached process. 0645 m_id = gdb.parse_and_eval('((QTimeZone*){:})->id()'.format(self.val.address)) 0646 except: 0647 ptr_size = gdb.lookup_type('void').pointer().sizeof 0648 try: 0649 qshareddata_t = gdb.lookup_type('QSharedData') 0650 except gdb.error: 0651 try: 0652 # well, it only has a QAtomicInt in it 0653 qshareddata_t = gdb.lookup_type('QAtomicInt') 0654 except gdb.error: 0655 # let's hope it's the same size as an int 0656 qshareddata_t = gdb.lookup_type('int') 0657 0658 reader = StructReader(d) 0659 reader.next_val(gdb.lookup_type('void').pointer()) # vtable 0660 reader.next_val(qshareddata_t) 0661 m_id = reader.next_aligned_val(gdb.lookup_type('QByteArray')) 0662 0663 return QByteArrayPrinter(m_id).to_string() 0664 0665 def display_hint(self): 0666 return 'string' 0667 0668 class QVariantPrinter: 0669 """Print a Qt5 QVariant""" 0670 0671 _varmap = { 0672 'char': 'c', 0673 'uchar': 'uc', 0674 'short': 's', 0675 'signed char': 'sc', 0676 'ushort': 'us', 0677 'int': 'i', 0678 'uint': 'u', 0679 'long': 'l', 0680 'ulong': 'ul', 0681 'bool': 'b', 0682 'double': 'd', 0683 'float': 'f', 0684 'qreal': 'real', 0685 'qlonglong': 'll', 0686 'qulonglong': 'ull', 0687 'QObject*': 'o', 0688 'void*': 'ptr' 0689 } 0690 0691 def __init__(self, val): 0692 self.val = val 0693 0694 def children(self): 0695 d = self.val['d'] 0696 typ = int(d['type']) 0697 if typ == typeinfo.meta_type_unknown: 0698 return [('type', 'invalid')] 0699 0700 data = d['data'] 0701 0702 if typ in typeinfo.meta_type_names: 0703 typename = typeinfo.meta_type_names[typ] 0704 if typename in self._varmap: 0705 field = self._varmap[typename] 0706 return [('type', typename), ('data', data[field])] 0707 0708 try: 0709 if typename.endswith('*'): 0710 gdb_type = gdb.lookup_type(typename[0:-1]).pointer() 0711 else: 0712 gdb_type = gdb.lookup_type(typename) 0713 except gdb.error: 0714 # couldn't find any type information 0715 return [('type', typename), ('data', data)] 0716 0717 if gdb_type.sizeof > data.type.sizeof: 0718 is_pointer = True 0719 elif (typeinfo.type_is_known_movable(gdb_type) or 0720 typeinfo.type_is_known_primitive(gdb_type)): 0721 is_pointer = False 0722 elif gdb_type.tag == 'enum': 0723 is_pointer = False 0724 else: 0725 # couldn't figure out how the type is stored 0726 return [('type', typename), ('data', data)] 0727 0728 if is_pointer: 0729 value = data['shared']['ptr'].reinterpret_cast(gdb_type.pointer()) 0730 else: 0731 void_star = gdb.lookup_type('void').pointer() 0732 data_void = data['c'].address.reinterpret_cast(void_star) 0733 value = data_void.reinterpret_cast(gdb_type.pointer()) 0734 0735 return [('type', typename), ('data', value.referenced_value())] 0736 else: 0737 # custom type? 0738 return [('type', typ), ('data', data)] 0739 0740 def to_string(self): 0741 return None 0742 0743 class QVarLengthArrayPrinter: 0744 """Print a Qt5 QVarLengthArray""" 0745 0746 def __init__(self, val): 0747 self.val = val 0748 0749 def children(self): 0750 size = int(self.val['s']) 0751 0752 if size == 0: 0753 return [] 0754 0755 return ArrayIter(self.val['ptr'], size) 0756 0757 def to_string(self): 0758 # if we return an empty list from children, gdb doesn't print anything 0759 if self.val['s'] == 0: 0760 return '<empty>' 0761 return None 0762 0763 def display_hint(self): 0764 return 'array' 0765 0766 class QVectorPrinter: 0767 """Print a Qt5 QVector""" 0768 0769 def __init__(self, val): 0770 self.val = val 0771 0772 def children(self): 0773 d = self.val['d'] 0774 el_type = self.val.type.template_argument(0) 0775 data_len = int(d['size']) 0776 0777 if data_len == 0: 0778 return [] 0779 0780 data_char = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset'] 0781 data = data_char.reinterpret_cast(el_type.pointer()) 0782 0783 return ArrayIter(data, data_len) 0784 0785 def to_string(self): 0786 # if we return an empty list from children, gdb doesn't print anything 0787 if self.val['d']['size'] == 0: 0788 return '<empty>' 0789 return None 0790 0791 def display_hint(self): 0792 return 'array' 0793 0794 class QUrlPrinter: 0795 """Print a Qt5 QUrl""" 0796 0797 def __init__(self, val): 0798 self.val = val 0799 0800 def to_string(self): 0801 d = self.val['d'] 0802 if not d: 0803 return '<empty>' 0804 0805 int_t = gdb.lookup_type('int') 0806 try: 0807 atomicint_t = gdb.lookup_type('QAtomicInt') 0808 except gdb.error: 0809 # let's hope it's the same size as an int 0810 atomicint_t = int_t 0811 qstring_t = gdb.lookup_type('QString') 0812 uchar_t = gdb.lookup_type('uchar') 0813 0814 reader = StructReader(d) 0815 0816 # These fields (including order) are unstable, and 0817 # may change between even patch-level Qt releases 0818 reader.next_val(atomicint_t) 0819 port = int(reader.next_val(int_t)) 0820 scheme = reader.next_val(qstring_t) 0821 userName = reader.next_val(qstring_t) 0822 password = reader.next_val(qstring_t) 0823 host = reader.next_val(qstring_t) 0824 path = reader.next_val(qstring_t) 0825 query = reader.next_val(qstring_t) 0826 fragment = reader.next_val(qstring_t) 0827 reader.next_val(gdb.lookup_type('void').pointer()) 0828 sections = int(reader.next_val(uchar_t)) 0829 flags = int(reader.next_val(uchar_t)) 0830 0831 # isLocalFile and no query and no fragment 0832 if flags & 0x01 and not (sections & 0x40) and not (sections & 0x80): 0833 # local file 0834 return path 0835 0836 def qs_to_s(qstring): 0837 return QStringPrinter(qstring).to_string() 0838 0839 # QUrl::toString() is way more complicated than what we do here, 0840 # but this is good enough for debugging 0841 result = '' 0842 if sections & 0x01: 0843 result += qs_to_s(scheme) + ':' 0844 if sections & (0x02 | 0x04 | 0x08 | 0x10) or flags & 0x01: 0845 result += '//' 0846 if sections & 0x02 or sections & 0x04: 0847 result += qs_to_s(userName) 0848 if sections & 0x04: 0849 # this may appear in backtraces that will be sent to other 0850 # people 0851 result += ':<omitted>' 0852 result += '@' 0853 if sections & 0x08: 0854 result += qs_to_s(host) 0855 if port != -1: 0856 result += ':' + str(port) 0857 result += qs_to_s(path) 0858 if sections & 0x40: 0859 result += '?' + qs_to_s(query) 0860 if sections & 0x80: 0861 result += '#' + qs_to_s(fragment) 0862 return result 0863 0864 def display_hint(self): 0865 return 'string' 0866 0867 0868 def build_pretty_printer(): 0869 """Builds the pretty printer for Qt5Core.""" 0870 pp = gdb.printing.RegexpCollectionPrettyPrinter("Qt5Core") 0871 pp.add_printer('QBitArray', '^QBitArray$', QBitArrayPrinter) 0872 pp.add_printer('QByteArray', '^QByteArray$', QByteArrayPrinter) 0873 pp.add_printer('QChar', '^QChar$', QCharPrinter) 0874 pp.add_printer('QDate', '^QDate$', QDatePrinter) 0875 pp.add_printer('QDateTime', '^QDateTime$', QDateTimePrinter) 0876 pp.add_printer('QLatin1String', '^QLatin1String$', QLatin1StringPrinter) 0877 pp.add_printer('QLinkedList', '^QLinkedList<.*>$', QLinkedListPrinter) 0878 pp.add_printer('QList', '^QList<.*>$', QListPrinter) 0879 pp.add_printer('QMap', '^QMap<.*>$', QMapPrinter) 0880 pp.add_printer('QHash', '^QHash<.*>$', QHashPrinter) 0881 pp.add_printer('QQueue', '^QQueue<.*>$', QListPrinter) 0882 pp.add_printer('QSet', '^QSet<.*>$', QSetPrinter) 0883 pp.add_printer('QStack', '^QStack<.*>$', QVectorPrinter) 0884 pp.add_printer('QString', '^QString$', QStringPrinter) 0885 pp.add_printer('QStringList', '^QStringList$', QListPrinter) 0886 pp.add_printer('QTime', '^QTime$', QTimePrinter) 0887 pp.add_printer('QTimeZone', '^QTimeZone$', QTimeZonePrinter) 0888 pp.add_printer('QVariant', '^QVariant$', QVariantPrinter) 0889 pp.add_printer('QVariantList', '^QVariantList$', QListPrinter) 0890 pp.add_printer('QVariantMap', '^QVariantMap$', QMapPrinter) 0891 pp.add_printer('QVector', '^QVector<.*>$', QVectorPrinter) 0892 pp.add_printer('QVarLengthArray', '^QVarLengthArray<.*>$', QVarLengthArrayPrinter) 0893 pp.add_printer('QUrl', '^QUrl$', QUrlPrinter) 0894 return pp 0895 0896 printer = build_pretty_printer() 0897 """The pretty printer for Qt5Core. 0898 0899 This can be registered using gdb.printing.register_pretty_printer(). 0900 """