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 """