File indexing completed on 2024-05-12 04:40:08
0001 # 0002 # LLDB data formatters for Qt types 0003 # 0004 # SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz> 0005 # 0006 # SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 # 0008 0009 from __future__ import print_function 0010 0011 import time 0012 import datetime as dt 0013 import string 0014 from urllib.parse import urlsplit, urlunsplit 0015 0016 import locale 0017 import lldb 0018 0019 from helpers import (HiddenMemberProvider, quote, unquote, unichr, toSBPointer, Iterator, validAddr, 0020 validPointer, invoke, rename, canonicalized_type_name) 0021 0022 0023 def __lldb_init_module(debugger, unused): 0024 debugger.HandleCommand('type synthetic add QString -w kdevelop-qt -l qt.QStringFormatter') 0025 debugger.HandleCommand('type summary add QString -w kdevelop-qt -F qt.QStringSummaryProvider') 0026 0027 debugger.HandleCommand('type summary add QChar -w kdevelop-qt -F qt.QCharSummaryProvider') 0028 0029 debugger.HandleCommand('type synthetic add QByteArray -w kdevelop-qt -l qt.QByteArrayFormatter') 0030 debugger.HandleCommand('type summary add QByteArray -w kdevelop-qt -e -F qt.QByteArraySummaryProvider') 0031 0032 debugger.HandleCommand('type synthetic add -x "^QList<.+>$" -w kdevelop-qt -l qt.QListFormatter') 0033 debugger.HandleCommand('type summary add -x "^QList<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0034 0035 debugger.HandleCommand('type synthetic add QStringList -w kdevelop-qt -l qt.QStringListFormatter') 0036 debugger.HandleCommand('type summary add QStringList -w kdevelop-qt -e -s "<size=${svar%#}>"') 0037 0038 debugger.HandleCommand('type synthetic add -x "^QQueue<.+>$" -w kdevelop-qt -l qt.QQueueFormatter') 0039 debugger.HandleCommand('type summary add -x "^QQueue<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0040 0041 debugger.HandleCommand('type synthetic add -x "^QVector<.+>$" -w kdevelop-qt -l qt.QVectorFormatter') 0042 debugger.HandleCommand('type summary add -x "^QVector<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0043 0044 debugger.HandleCommand('type synthetic add -x "^QStack<.+>$" -w kdevelop-qt -l qt.QStackFormatter') 0045 debugger.HandleCommand('type summary add -x "^QStack<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0046 0047 debugger.HandleCommand('type synthetic add -x "^QLinkedList<.+>$" -w kdevelop-qt -l qt.QLinkedListFormatter') 0048 debugger.HandleCommand('type summary add -x "^QLinkedList<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0049 0050 debugger.HandleCommand('type synthetic add -x "^QMapNode<.+>$" -w kdevelop-qt -l qt.KeyValueFormatter') 0051 debugger.HandleCommand('type summary add -x "^QMapNode<.+>$" -w kdevelop-qt -F qt.KeyValueSummaryProvider') 0052 0053 debugger.HandleCommand('type synthetic add -x "^QMap<.+>$" -w kdevelop-qt -l qt.QMapFormatter') 0054 debugger.HandleCommand('type summary add -x "^QMap<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0055 0056 debugger.HandleCommand('type synthetic add -x "^QMultiMap<.+>$" -w kdevelop-qt -l qt.QMultiMapFormatter') 0057 debugger.HandleCommand('type summary add -x "^QMultiMap<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0058 0059 debugger.HandleCommand('type synthetic add -x "^QHashNode<.+>$" -w kdevelop-qt -l qt.KeyValueFormatter') 0060 debugger.HandleCommand('type summary add -x "^QHashNode<.+>$" -w kdevelop-qt -F qt.KeyValueSummaryProvider') 0061 0062 debugger.HandleCommand('type synthetic add -x "^QHash<.+>$" -w kdevelop-qt -l qt.QHashFormatter') 0063 debugger.HandleCommand('type summary add -x "^QHash<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0064 0065 debugger.HandleCommand('type synthetic add -x "^QMultiHash<.+>$" -w kdevelop-qt -l qt.QMultiHashFormatter') 0066 debugger.HandleCommand('type summary add -x "^QMultiHash<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0067 0068 debugger.HandleCommand('type synthetic add -x "^QSet<.+>$" -w kdevelop-qt -l qt.QSetFormatter') 0069 debugger.HandleCommand('type summary add -x "^QSet<.+>$" -w kdevelop-qt -e -s "<size=${svar%#}>"') 0070 0071 debugger.HandleCommand('type synthetic add QDate -w kdevelop-qt -l qt.QDateFormatter') 0072 debugger.HandleCommand('type summary add QDate -w kdevelop-qt -e -F qt.QDateSummaryProvider') 0073 0074 debugger.HandleCommand('type synthetic add QTime -w kdevelop-qt -l qt.QTimeFormatter') 0075 debugger.HandleCommand('type summary add -x QTime -w kdevelop-qt -e -F qt.QTimeSummaryProvider') 0076 0077 debugger.HandleCommand('type synthetic add QDateTime -w kdevelop-qt -l qt.QDateTimeFormatter') 0078 debugger.HandleCommand('type summary add -x QDateTime -w kdevelop-qt -e -F qt.QDateTimeSummaryProvider') 0079 0080 debugger.HandleCommand('type synthetic add QUrl -w kdevelop-qt -l qt.QUrlFormatter') 0081 debugger.HandleCommand('type summary add QUrl -w kdevelop-qt -e -F qt.QUrlSummaryProvider') 0082 0083 debugger.HandleCommand('type synthetic add QUuid -w kdevelop-qt -l qt.QUuidFormatter') 0084 debugger.HandleCommand('type summary add QUuid -w kdevelop-qt -F qt.QUuidSummaryProvider') 0085 0086 debugger.HandleCommand('type category enable kdevelop-qt') 0087 0088 0089 def printableQString(valobj): 0090 pointer = 0 0091 length = 0 0092 if valobj.IsValid(): 0093 d = valobj.GetChildMemberWithName('d') 0094 data = d.GetChildMemberWithName('data') 0095 offset = d.GetChildMemberWithName('offset') 0096 size = d.GetChildMemberWithName('size') 0097 0098 isQt4 = data.IsValid() 0099 size_val = size.GetValueAsSigned(-1) 0100 alloc = d.GetChildMemberWithName('alloc').GetValueAsUnsigned(0) 0101 if isQt4: 0102 alloc += 1 0103 0104 # some sanity check to see if we are dealing with garbage 0105 if size_val < 0 or size_val >= alloc: 0106 return None, 0, 0 0107 0108 tooLarge = u'' 0109 if size_val > HiddenMemberProvider._capping_size(): 0110 tooLarge = u'...' 0111 size_val = HiddenMemberProvider._capping_size() 0112 0113 if isQt4: 0114 pointer = data.GetValueAsUnsigned(0) 0115 elif offset.IsValid(): 0116 pointer = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0) 0117 else: 0118 qarraydata_t = valobj.GetTarget().FindFirstType('QArrayData') 0119 if qarraydata_t.IsValid(): 0120 pointer = d.GetValueAsUnsigned(0) + qarraydata_t.GetByteSize() 0121 else: 0122 pointer = d.GetValueAsUnsigned(0) + 24 # Fallback to hardcoded value 0123 0124 # size in the number of chars, each char is 2 bytes in UTF16 0125 length = size_val * 2 0126 if length == 0: 0127 return u'', pointer, length 0128 0129 try: 0130 error = lldb.SBError() 0131 string_data = valobj.process.ReadMemory(pointer, length, error) 0132 # The QString object might be not yet initialized. In this case size is a bogus value, 0133 # and memory access may fail 0134 if error.Success(): 0135 content = string_data.decode('utf-16') 0136 return content + tooLarge, pointer, length 0137 except: 0138 pass 0139 return None, 0, 0 0140 0141 0142 def QStringSummaryProvider(valobj, internal_dict): 0143 if valobj.IsValid(): 0144 # content = valobj.GetChildMemberWithName('(content)') 0145 # if content.IsValid(): 0146 # try: 0147 # error = lldb.SBError() 0148 # rawprintable = content.GetData().GetString(error, 0) 0149 # if error.Success(): 0150 # printable = rawprintable.decode() 0151 # return quote(printable) 0152 # except: 0153 # pass 0154 0155 # FIXME: there's no reliable way to pass data from formatter to 0156 # summary provider currently. So directly pull data from inferior 0157 0158 # Something wrong with synthetic provider, or 0159 # no synthetic provider installed, get the content by ourselves 0160 printable, _, _ = printableQString(valobj) 0161 if printable is not None: 0162 return quote(printable) 0163 return '<Invalid>' 0164 0165 0166 class QStringFormatter(HiddenMemberProvider): 0167 """A lldb synthetic provider for QString""" 0168 0169 def __init__(self, valobj, internal_dict): 0170 super(QStringFormatter, self).__init__(valobj, internal_dict) 0171 self._qchar_type = valobj.GetTarget().FindFirstType('QChar') 0172 self._qchar_size = self._qchar_type.GetByteSize() 0173 0174 def _update(self): 0175 printable, dataPointer, byteLength = printableQString(self.valobj) 0176 strLength = int(byteLength / 2) 0177 0178 if printable is not None: 0179 for idx in range(0, strLength): 0180 var = self.valobj.CreateValueFromAddress('[{}]'.format(idx), 0181 dataPointer + idx * self._qchar_size, 0182 self._qchar_type) 0183 self._addChild(var) 0184 self._num_children = strLength 0185 0186 0187 def QCharSummaryProvider(valobj, internal_dict): 0188 if valobj.IsValid(): 0189 ucs = valobj.GetChildMemberWithName('ucs').GetValueAsUnsigned(0) 0190 if ucs == 39: 0191 # for '\'', python returns "'" rather than '\'' 0192 return u"'\\''" 0193 else: 0194 return unichr(ucs).__repr__()[1:] 0195 return None 0196 0197 0198 def printableQByteArray(valobj): 0199 if valobj.IsValid(): 0200 d = valobj.GetChildMemberWithName('d') 0201 data = d.GetChildMemberWithName('data') 0202 offset = d.GetChildMemberWithName('offset') 0203 size = d.GetChildMemberWithName('size') 0204 0205 isQt4 = data.IsValid() 0206 size_val = size.GetValueAsSigned(-1) 0207 alloc = d.GetChildMemberWithName('alloc').GetValueAsUnsigned(0) 0208 if isQt4: 0209 alloc += 1 0210 0211 # sanity check 0212 if size_val < 0 or size_val >= alloc: 0213 return None, 0, 0 0214 0215 tooLarge = u'' 0216 if size_val > HiddenMemberProvider._capping_size(): 0217 tooLarge = u'...' 0218 size_val = HiddenMemberProvider._capping_size() 0219 0220 if isQt4: 0221 pointer = data.GetValueAsUnsigned(0) 0222 elif offset.IsValid(): 0223 pointer = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0) 0224 else: 0225 pointer = d.GetValueAsUnsigned(0) + 24 # Fallback to hardcoded value 0226 0227 length = size_val 0228 if length == 0: 0229 return u'', pointer, length 0230 0231 try: 0232 error = lldb.SBError() 0233 string_data = valobj.process.ReadMemory(pointer, length, error) 0234 # The object might be not yet initialized. In this case size is a bogus value, 0235 # and memory access may fail 0236 if error.Success(): 0237 # replace non-ascii byte with a space and get a printable version 0238 ls = list(string_data) 0239 for idx in range(length): 0240 if ls[idx] in string.printable: 0241 if ls[idx] != "'": 0242 # convert tab, nl, ..., and '\\' to r'\\' 0243 ls[idx] = ls[idx].__repr__()[1:-1] 0244 else: 0245 ls[idx] = r'\x{:02x}'.format(ord(ls[idx])) 0246 content = u''.join(ls) 0247 return content + tooLarge, pointer, length 0248 except: 0249 pass 0250 return None, 0, 0 0251 0252 0253 def QByteArraySummaryProvider(valobj, internal_dict): 0254 if valobj.IsValid(): 0255 content = valobj.GetChildMemberWithName('(content)') 0256 if content.IsValid(): 0257 summary = content.GetSummary() 0258 if summary is not None: 0259 # unlike QString, we quoted the (content) twice to preserve our own quotation, 0260 # must undo the quotation done by GetSummary 0261 return 'b' + unquote(summary) 0262 # Something wrong with our synthetic provider, get the content by ourselves 0263 printable, _, _ = printableQByteArray(valobj) 0264 if printable is not None: 0265 # first replace " to \", and surround by "", no need to escape other things which 0266 # are handled in printableQByteArray. 0267 return 'b"{}"'.format(printable.replace('"', '\\"')) 0268 return '<Invalid>' 0269 0270 0271 class QByteArrayFormatter(HiddenMemberProvider): 0272 """A lldb synthetic provider for QByteArray""" 0273 0274 def __init__(self, valobj, internal_dict): 0275 super(QByteArrayFormatter, self).__init__(valobj, internal_dict) 0276 self._char_type = valobj.GetType().GetBasicType(lldb.eBasicTypeChar) 0277 self._char_size = self._char_type.GetByteSize() 0278 0279 def _update(self): 0280 printable, dataPointer, byteLength = printableQByteArray(self.valobj) 0281 self._num_children = byteLength 0282 0283 if printable is not None: 0284 for idx in range(0, self._num_children): 0285 var = self.valobj.CreateValueFromAddress('[{}]'.format(idx), 0286 dataPointer + idx * self._char_size, 0287 self._char_type) 0288 self._addChild(var) 0289 0290 # first replace " to \", and surround by "", no need to escape other things which 0291 # are handled in printableQByteArray. 0292 printable = b'"{}"'.format(printable.replace(b'"', b'\\"')) 0293 self._addChild(('(content)', printable), hidden=True) 0294 0295 0296 class BasicListFormatter(HiddenMemberProvider): 0297 """A lldb synthetic provider for QList like types""" 0298 0299 def __init__(self, valobj, internal_dict, item_typename): 0300 super(BasicListFormatter, self).__init__(valobj, internal_dict) 0301 if item_typename is None: 0302 self._item_type = valobj.GetType().GetTemplateArgumentType(0) 0303 else: 0304 self._item_type = valobj.GetTarget().FindFirstType(item_typename) 0305 pvoid_type = valobj.GetTarget().GetBasicType(lldb.eBasicTypeVoid).GetPointerType() 0306 self._pvoid_size = pvoid_type.GetByteSize() 0307 0308 # from QTypeInfo::isLarge 0309 isLarge = self._item_type.GetByteSize() > self._pvoid_size 0310 0311 # unfortunately we can't use QTypeInfo<T>::isStatic as it's all inlined, so use 0312 # this list of types that use Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE) 0313 # (obviously it won't work for custom types) 0314 movableTypes = ['QRect', 'QRectF', 'QString', 'QMargins', 'QLocale', 'QChar', 'QDate', 0315 'QTime', 'QDateTime', 'QVector', 'QRegExpr', 'QPoint', 'QPointF', 'QByteArray', 0316 'QSize', 'QSizeF', 'QBitArray', 'QLine', 'QLineF', 'QModelIndex', 0317 'QPersitentModelIndex', 'QVariant', 'QFileInfo', 'QUrl', 'QXmlStreamAttribute', 0318 'QXmlStreamNamespaceDeclaration', 'QXmlStreamNotationDeclaration', 0319 'QXmlStreamEntityDeclaration', 'QPair<int, int>'] 0320 movableTypes = [valobj.GetTarget().FindFirstType(t) for t in movableTypes] 0321 # this list of types that use Q_DECLARE_TYPEINFO(T, Q_PRIMITIVE_TYPE) (from qglobal.h) 0322 primitiveTypes = ['bool', 'char', 'signed char', 'unsigned char', 'short', 'unsigned short', 0323 'int', 'unsigned int', 'long', 'unsigned long', 'long long', 0324 'unsigned long long', 'float', 'double'] 0325 primitiveTypes = [valobj.GetTarget().FindFirstType(t) for t in primitiveTypes] 0326 0327 if self._item_type in movableTypes or self._item_type in primitiveTypes: 0328 isStatic = False 0329 else: 0330 isStatic = not self._item_type.IsPointerType() 0331 0332 # see QList::Node::t() 0333 self._externalStorage = isLarge or isStatic 0334 # If is external storage, then the node (a void*) is a pointer to item 0335 # else the item is stored inside the node 0336 if self._externalStorage: 0337 self._node_type = self._item_type.GetPointerType() 0338 else: 0339 self._node_type = self._item_type 0340 0341 def _update(self): 0342 d = self.valobj.GetChildMemberWithName('d') 0343 begin = d.GetChildMemberWithName('begin').GetValueAsSigned(-1) 0344 end = d.GetChildMemberWithName('end').GetValueAsSigned(-1) 0345 array = d.GetChildMemberWithName('array') 0346 0347 # sanity check 0348 if begin < 0 or end < 0 or end < begin: 0349 return 0350 0351 self._num_children = end - begin 0352 0353 for idx in range(0, self._num_children): 0354 offset = (begin + idx) * self._pvoid_size 0355 name = '[{}]'.format(idx) 0356 var = array.CreateChildAtOffset(name, offset, self._node_type) 0357 if self._externalStorage: 0358 # can't use var.Dereference() directly, as the returned SBValue has '*' prepended 0359 # to its name. And SBValue name can't be changed once constructed. 0360 var = self.valobj.CreateValueFromData(name, var.GetPointeeData(), 0361 self._item_type) 0362 self._addChild(var) 0363 0364 0365 class QListFormatter(BasicListFormatter): 0366 """lldb synthetic provider for QList""" 0367 0368 def __init__(self, valobj, internal_dict): 0369 super(QListFormatter, self).__init__(valobj, internal_dict, None) 0370 0371 0372 class QStringListFormatter(BasicListFormatter): 0373 """lldb synthetic provider for QStringList""" 0374 0375 def __init__(self, valobj, internal_dict): 0376 super(QStringListFormatter, self).__init__(valobj, internal_dict, 'QString') 0377 0378 0379 class QQueueFormatter(BasicListFormatter): 0380 """lldb synthetic provider for QQueue""" 0381 0382 def __init__(self, valobj, internal_dict): 0383 super(QQueueFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict, None) 0384 self.actualobj = valobj 0385 0386 def update(self): 0387 self.valobj = self.actualobj.GetChildAtIndex(0) 0388 super(QQueueFormatter, self).update() 0389 0390 0391 class BasicVectorFormatter(HiddenMemberProvider): 0392 """A lldb synthetic provider for QVector like types""" 0393 0394 def __init__(self, valobj, internal_dict): 0395 super(BasicVectorFormatter, self).__init__(valobj, internal_dict) 0396 self._item_type = valobj.GetType().GetTemplateArgumentType(0) 0397 self._item_size = self._item_type.GetByteSize() 0398 0399 def _update(self): 0400 d = self.valobj.GetChildMemberWithName('p') 0401 # Qt4 has 'p', Qt5 doesn't 0402 isQt4 = d.IsValid() 0403 if isQt4: 0404 pArray = d.GetChildMemberWithName('array').AddressOf().GetValueAsUnsigned(0) 0405 else: 0406 d = self.valobj.GetChildMemberWithName('d') 0407 offset = d.GetChildMemberWithName('offset') 0408 pArray = d.GetValueAsUnsigned(0) + offset.GetValueAsUnsigned(0) 0409 0410 # sanity check 0411 if not toSBPointer(self.valobj, pArray, self._item_type).IsValid(): 0412 return 0413 0414 # self._num_children = d.GetChildMemberWithName('size').GetValueAsUnsigned(0) 0415 self._num_children = d.GetChildMemberWithName('size').GetValueAsSigned(-1) 0416 if self._num_children < 0: 0417 return 0418 0419 if self._num_children > self._capping_size(): 0420 self._num_children = self._capping_size() 0421 0422 for idx in range(0, self._num_children): 0423 var = self.valobj.CreateValueFromAddress('[{}]'.format(idx), 0424 pArray + idx * self._item_size, 0425 self._item_type) 0426 self._addChild(var) 0427 0428 0429 class QVectorFormatter(BasicVectorFormatter): 0430 """lldb synthetic provider for QVector""" 0431 def __init__(self, valobj, internal_dict): 0432 super(QVectorFormatter, self).__init__(valobj, internal_dict) 0433 0434 0435 class QStackFormatter(BasicVectorFormatter): 0436 """lldb synthetic provider for QStack""" 0437 0438 def __init__(self, valobj, internal_dict): 0439 super(QStackFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict) 0440 self.actualobj = valobj 0441 0442 def update(self): 0443 self.valobj = self.actualobj.GetChildAtIndex(0) 0444 super(QStackFormatter, self).update() 0445 0446 0447 class QLinkedListFormatter(HiddenMemberProvider): 0448 """A lldb synthetic provider for QLinkedList""" 0449 0450 def __init__(self, valobj, internal_dict): 0451 super(QLinkedListFormatter, self).__init__(valobj, internal_dict) 0452 self._item_type = valobj.GetType().GetTemplateArgumentType(0) 0453 0454 def _update(self): 0455 d = self.valobj.GetChildMemberWithName('d') 0456 self._num_children = d.GetChildMemberWithName('size').GetValueAsSigned(-1) 0457 0458 if self._num_children < 0: 0459 return 0460 0461 node = self.valobj.GetChildMemberWithName('e').GetChildMemberWithName('n') 0462 0463 for idx in range(0, self._num_children): 0464 if not node.IsValid(): 0465 self._members = [] 0466 self._num_children = 0 0467 return 0468 var = node.GetChildMemberWithName('t') 0469 node = node.GetChildMemberWithName('n') 0470 0471 var = self.valobj.CreateValueFromData('[{}]'.format(idx), 0472 var.GetData(), 0473 self._item_type) 0474 self._addChild(var) 0475 0476 0477 class KeyValueFormatter(object): 0478 """A lldb synthetic provider for (key,value) pair like types""" 0479 0480 def __init__(self, valobj, internal_dict): 0481 self.valobj = valobj 0482 self._key_item = None 0483 self._val_item = None 0484 0485 def num_children(self): 0486 return 2 0487 0488 def has_children(self): 0489 return True 0490 0491 def get_child_index(self, name): 0492 if name == 'key': 0493 return 0 0494 elif name == 'value': 0495 return 1 0496 return None 0497 0498 def get_child_at_index(self, idx): 0499 if idx < 0 or idx >= 2: 0500 return None 0501 if idx == 0: 0502 return self._key_item 0503 elif idx == 1: 0504 return self._val_item 0505 return None 0506 0507 def update(self): 0508 if not self.valobj.IsValid(): 0509 return 0510 self._key_item = self.valobj.GetChildMemberWithName('key') 0511 self._val_item = self.valobj.GetChildMemberWithName('value') 0512 0513 0514 def KeyValueSummaryProvider(valobj, internal_dict): 0515 if not valobj.IsValid(): 0516 return None 0517 0518 key = valobj.GetChildMemberWithName('key') 0519 value = valobj.GetChildMemberWithName('value') 0520 key_summary = key.GetSummary() or key.GetValue() # show value if summary is empty or None 0521 val_summary = value.GetSummary() or value.GetValue() # show value if summary is empty or None 0522 return '({}, {})'.format(key_summary, val_summary) 0523 0524 0525 class BasicMapFormatter(HiddenMemberProvider): 0526 """A lldb synthetic provider for QMap like types""" 0527 0528 def __init__(self, valobj, internal_dict): 0529 super(BasicMapFormatter, self).__init__(valobj, internal_dict) 0530 self_type = valobj.GetType() 0531 key_type = self_type.GetTemplateArgumentType(0) 0532 val_type = self_type.GetTemplateArgumentType(1) 0533 # the ' ' between two template arguments is significant, 0534 # otherwise FindFirstType returns None 0535 node_typename = 'QMapNode<{}, {}>'.format(key_type.GetName(), val_type.GetName()) 0536 node_typename = canonicalized_type_name(node_typename) 0537 self._node_type = valobj.GetTarget().FindFirstType(node_typename) 0538 0539 e = self.valobj.GetChildMemberWithName('e') 0540 self.isQt4 = e.IsValid() 0541 if self.isQt4: 0542 self._payload_size = self._qt4_calc_payload(key_type, val_type) 0543 0544 def _qt4_calc_payload(self, key_type, val_type): 0545 """calculate payload size for Qt4""" 0546 str = lldb.SBStream() 0547 self.valobj.GetExpressionPath(str, True) 0548 expr = '{}.payload()'.format(str.GetData()) 0549 ret = lldb.frame.EvaluateExpression(expr).GetValueAsUnsigned(0) 0550 if ret != 0: 0551 return ret 0552 else: 0553 # if the inferior function call didn't work, let's try to calculate ourselves 0554 target = self.valobj.GetTarget() 0555 pvoid_type = target.GetBasicType(lldb.eBasicTypeVoid).GetPointerType() 0556 pvoid_size = pvoid_type.GetByteSize() 0557 0558 # we can't use QMapPayloadNode as it's inlined 0559 # as a workaround take the sum of sizeof(members) 0560 ret = key_type.GetByteSize() 0561 ret += val_type.GetByteSize() 0562 ret += pvoid_size 0563 0564 # but because of data alignment the value can be higher 0565 # so guess it's aliged by sizeof(void*) 0566 # TODO: find a real solution for this problem 0567 ret += ret % pvoid_size 0568 0569 # for some reason booleans are different 0570 if val_type == target.GetBasicType(lldb.eBasicTypeBool): 0571 ret += 2 0572 0573 ret -= pvoid_size 0574 return ret 0575 0576 class _iteratorQt4(Iterator): 0577 """Map iterator for Qt4""" 0578 0579 def __init__(self, headerobj, node_type, payload_size): 0580 self.current = headerobj.GetChildMemberWithName('forward').GetChildAtIndex(0) 0581 self.header_addr = headerobj.GetValueAsUnsigned(0) 0582 self.node_type = node_type 0583 self.payload_size = payload_size 0584 0585 # sanity check 0586 self.is_garbage = False 0587 if not validAddr(headerobj, self.header_addr): 0588 self.is_garbage = True 0589 if not validPointer(self.current): 0590 self.is_garbage = True 0591 0592 def __iter__(self): 0593 return self 0594 0595 def concrete(self, pdata_node): 0596 pnode_addr = pdata_node.GetValueAsUnsigned(0) 0597 pnode_addr -= self.payload_size 0598 0599 return toSBPointer(self.current, pnode_addr, self.node_type) 0600 0601 def __next__(self): 0602 if self.is_garbage: 0603 raise StopIteration 0604 if self.current.GetValueAsUnsigned(0) == self.header_addr: 0605 raise StopIteration 0606 pnode = self.concrete(self.current) 0607 self.current = self.current.GetChildMemberWithName('forward').GetChildAtIndex(0) 0608 return pnode 0609 0610 class _iteratorQt5(Iterator): 0611 """Map iterator for Qt5""" 0612 0613 def __init__(self, dataobj, pnode_type): 0614 self.pnode_type = pnode_type 0615 self.root = dataobj.GetChildMemberWithName('header') 0616 self.current = lldb.SBValue() 0617 0618 # We store the path here to avoid keeping re-fetching 0619 # values from the inferior (also, skip the pointer 0620 # arithmetic involved in using the parent pointer 0621 self.path = [] 0622 0623 def __iter__(self): 0624 return self 0625 0626 def moveToNextNode(self): 0627 def isNullPointer(val): 0628 return not val.IsValid() or val.GetValueAsUnsigned(0) == 0 0629 0630 if isNullPointer(self.current): 0631 # find the leftmost node 0632 left = self.root.GetChildMemberWithName('left') 0633 if isNullPointer(left): 0634 return False 0635 self.current = self.root 0636 while not isNullPointer(left): 0637 self.path.append(self.current) 0638 self.current = left 0639 left = self.current.GetChildMemberWithName('left') 0640 else: 0641 right = self.current.GetChildMemberWithName('right') 0642 if not isNullPointer(right): 0643 self.path.append(self.current) 0644 self.current = right 0645 left = self.current.GetChildMemberWithName('left') 0646 while not isNullPointer(left): 0647 self.path.append(self.current) 0648 self.current = left 0649 left = self.current.GetChildMemberWithName('left') 0650 else: 0651 last = self.current 0652 self.current = self.path.pop() 0653 right = self.current.GetChildMemberWithName('right') 0654 while right.GetValueAsUnsigned(0) == last.GetValueAsUnsigned(0): 0655 last = self.current 0656 self.current = self.path.pop() 0657 right = self.current.GetChildMemberWithName('right') 0658 # if there are no more parents, we are at the root 0659 if len(self.path) == 0: 0660 return False 0661 return True 0662 0663 def __next__(self): 0664 if not self.moveToNextNode(): 0665 raise StopIteration 0666 return self.current.Cast(self.pnode_type) 0667 0668 def _update(self): 0669 pnode_type = self._node_type.GetPointerType() 0670 if self.isQt4: 0671 e = self.valobj.GetChildMemberWithName('e') 0672 it = self._iteratorQt4(e, self._node_type, self._payload_size) 0673 else: 0674 d = self.valobj.GetChildMemberWithName('d') 0675 it = self._iteratorQt5(d, pnode_type) 0676 0677 self._num_children = 0 0678 for pnode in it: 0679 # dereference node and change to a user friendly name 0680 name = '[{}]'.format(self._num_children) 0681 self._num_children += 1 0682 var = self.valobj.CreateValueFromData(name, pnode.GetPointeeData(), 0683 self._node_type) 0684 self._addChild(var) 0685 0686 0687 class QMapFormatter(BasicMapFormatter): 0688 """lldb synthetic provider for QMap""" 0689 0690 def __init__(self, valobj, internal_dict): 0691 super(QMapFormatter, self).__init__(valobj, internal_dict) 0692 0693 0694 class QMultiMapFormatter(BasicMapFormatter): 0695 """lldb synthetic provider for QMap""" 0696 0697 def __init__(self, valobj, internal_dict): 0698 super(QMultiMapFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict) 0699 self.actualobj = valobj 0700 0701 def update(self): 0702 self.valobj = self.actualobj.GetChildAtIndex(0) 0703 super(QMultiMapFormatter, self).update() 0704 0705 0706 class BasicHashFormatter(HiddenMemberProvider): 0707 """A lldb synthetic provider for QHash like types""" 0708 0709 def __init__(self, valobj, internal_dict): 0710 super(BasicHashFormatter, self).__init__(valobj, internal_dict) 0711 self_type = valobj.GetType() 0712 self._key_type = self_type.GetTemplateArgumentType(0) 0713 self._val_type = self_type.GetTemplateArgumentType(1) 0714 node_typename = 'QHashNode<{}, {}>'.format(self._key_type.GetName(), 0715 self._val_type.GetName()) 0716 node_typename = canonicalized_type_name(node_typename) 0717 0718 self._node_type = valobj.GetTarget().FindFirstType(node_typename) 0719 0720 class _iterator(Iterator): 0721 """Hash iterator""" 0722 def __init__(self, valobj, pnode_type): 0723 d = valobj.GetChildMemberWithName('d') 0724 self.buckets = d.GetChildMemberWithName('buckets') 0725 self.null_node = valobj.GetChildMemberWithName('e') 0726 self.pnode_type = pnode_type 0727 0728 self.num_buckets = d.GetChildMemberWithName('numBuckets').GetValueAsSigned(-1) 0729 0730 self.is_garbage = False 0731 if self.num_buckets < -1: 0732 self.is_garbage = True 0733 return 0734 self.current = self.firstNode() 0735 0736 def __iter__(self): 0737 return self 0738 0739 def findNode(self, start=0): 0740 """Iterate through buckets, start at `start`, 0741 return any bucket the is not the null_node, or the null_node itself if nothing found. 0742 adapted from QHashData::firstNode 0743 """ 0744 null_node_addr = self.null_node.GetValueAsUnsigned(0) 0745 for idx in range(start, self.num_buckets): 0746 # self.buckets has type QHashData::Node**, not an array 0747 # calling GetChildAtIndex with use_synthetic=True so the pointer is used as an array 0748 bucket = self.buckets.GetChildAtIndex(idx, lldb.eDynamicCanRunTarget, True) 0749 if bucket.GetValueAsUnsigned(0) != null_node_addr: 0750 # in Qt4, QHashData::Node is incomplete type, but QHashNode is complete, 0751 # so always use QHashNode 0752 return bucket.Cast(self.pnode_type) 0753 return self.null_node 0754 0755 def firstNode(self): 0756 return self.findNode() 0757 0758 def moveToNextNode(self): 0759 """Get the nextNode after the current, see also QHashData::nextNode().""" 0760 next = self.current.GetChildMemberWithName('next') 0761 0762 if next.GetValueAsUnsigned(0) != self.null_node.GetValueAsUnsigned(0): 0763 self.current = next 0764 else: 0765 h = self.current.GetChildMemberWithName('h').GetValueAsUnsigned(0) 0766 start = (h % self.num_buckets) + 1 0767 self.current = self.findNode(start) 0768 0769 def __next__(self): 0770 if self.is_garbage: 0771 raise StopIteration 0772 if self.current.GetValueAsUnsigned(0) == self.null_node.GetValueAsUnsigned(0): 0773 raise StopIteration 0774 pnode = self.current 0775 self.moveToNextNode() 0776 return pnode 0777 0778 def _update(self): 0779 self._num_children = self.valobj.GetChildMemberWithName('d').GetChildMemberWithName('size').GetValueAsSigned(-1) 0780 if self._num_children < 0: 0781 return 0782 0783 idx = 0 0784 for pnode in self._iterator(self.valobj, self._node_type.GetPointerType()): 0785 if idx >= self._num_children: 0786 self._members = [] 0787 self._num_children = 0 0788 break 0789 # dereference node and change to a user friendly name 0790 name = '[{}]'.format(idx) 0791 idx += 1 0792 var = self.valobj.CreateValueFromData(name, pnode.GetPointeeData(), 0793 self._node_type) 0794 self._addChild(var) 0795 if idx != self._num_children: 0796 self._members = [] 0797 self._num_children = 0 0798 0799 0800 class QHashFormatter(BasicHashFormatter): 0801 """lldb synthetic provider for QHash""" 0802 0803 def __init__(self, valobj, internal_dict): 0804 super(QHashFormatter, self).__init__(valobj, internal_dict) 0805 0806 0807 class QMultiHashFormatter(BasicHashFormatter): 0808 """lldb synthetic provider for QHash""" 0809 0810 def __init__(self, valobj, internal_dict): 0811 super(QMultiHashFormatter, self).__init__(valobj.GetChildAtIndex(0), internal_dict) 0812 self.actualobj = valobj 0813 0814 def update(self): 0815 self.valobj = self.actualobj.GetChildAtIndex(0) 0816 super(QMultiHashFormatter, self).update() 0817 0818 0819 class QSetFormatter(HiddenMemberProvider): 0820 """lldb synthetic provider for QSet""" 0821 0822 def __init__(self, valobj, internal_dict): 0823 super(QSetFormatter, self).__init__(valobj, internal_dict) 0824 self._hash_formatter = QHashFormatter(valobj.GetChildMemberWithName('q_hash'), 0825 internal_dict) 0826 0827 def num_children(self): 0828 return self._num_children 0829 0830 def _update(self): 0831 self._hash_formatter.valobj = self.valobj.GetChildMemberWithName('q_hash') 0832 self._hash_formatter.update() 0833 0834 self._num_children = 0 0835 for node in self._hash_formatter._members: 0836 keydata = node.GetChildMemberWithName('key').GetData() 0837 name = '[{}]'.format(self._num_children) 0838 var = self.valobj.CreateValueFromData(name, keydata, self._hash_formatter._key_type) 0839 self._addChild(var) 0840 self._num_children += 1 0841 0842 0843 class QDateFormatter(HiddenMemberProvider): 0844 """lldb synthetic provider for QDate""" 0845 def __init__(self, valobj, internal_dict): 0846 super(QDateFormatter, self).__init__(valobj, internal_dict) 0847 self._add_original = False 0848 self._qstring_type = valobj.GetTarget().FindFirstType('QString') 0849 0850 def has_children(self): 0851 return True 0852 0853 @staticmethod 0854 def parse(julianDay): 0855 """Copied from Qt srources""" 0856 if julianDay == 0: 0857 return None 0858 if julianDay >= 2299161: 0859 # Gregorian calendar starting from October 15, 1582 0860 # This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern 0861 ell = julianDay + 68569 0862 n = (4 * ell) / 146097 0863 ell = ell - (146097 * n + 3) / 4 0864 i = (4000 * (ell + 1)) / 1461001 0865 ell = ell - (1461 * i) / 4 + 31 0866 j = (80 * ell) / 2447 0867 d = ell - (2447 * j) / 80 0868 ell = j / 11 0869 m = j + 2 - (12 * ell) 0870 y = 100 * (n - 49) + i + ell 0871 else: 0872 # Julian calendar until October 4, 1582 0873 # Algorithm from Frequently Asked Questions about Calendars by Claus Toendering 0874 julianDay += 32082 0875 dd = (4 * julianDay + 3) / 1461 0876 ee = julianDay - (1461 * dd) / 4 0877 mm = ((5 * ee) + 2) / 153 0878 d = ee - (153 * mm + 2) / 5 + 1 0879 m = mm + 3 - 12 * (mm / 10) 0880 y = dd - 4800 + (mm / 10) 0881 if y <= 0: 0882 return None 0883 return dt.date(y, m, d) 0884 0885 def _update(self): 0886 # FIXME: Calling functions returns incorrect SBValue for complex type in lldb 0887 # # toString 0888 # res = invoke(self.valobj, 'toString', '0') 0889 # self._addChild(rename('toString', res)) 0890 0891 # jd 0892 julianDay = self.valobj.GetChildMemberWithName('jd') 0893 self._addChild(julianDay) 0894 0895 pydate = self.parse(julianDay.GetValueAsUnsigned(0)) 0896 if pydate is None: 0897 return 0898 # (ISO) 0899 iso_str = pydate.isoformat().decode().__repr__()[2:-1] 0900 self._addChild(('(ISO)', iso_str)) 0901 0902 # (Locale) 0903 locale_encoding = [locale.getlocale()[1]] 0904 if locale_encoding[0] is None: 0905 locale_encoding = [] 0906 locale_str = pydate.strftime('%x').decode(*locale_encoding).__repr__()[2:-1] 0907 self._addChild(('(Locale)', locale_str)) 0908 0909 0910 def QDateSummaryProvider(valobj, internal_dict): 0911 if valobj.IsValid(): 0912 content = valobj.GetChildMemberWithName('(Locale)') 0913 if content.IsValid(): 0914 summary = content.GetSummary() 0915 if summary is not None: 0916 return summary 0917 # No synthetic provider installed, get the content by ourselves 0918 pydate = QDateFormatter.parse(valobj.GetChildMemberWithName('jd').GetValueAsUnsigned(0)) 0919 if pydate is not None: 0920 return pydate.isoformat().decode().__repr__()[2:-1] 0921 return '<Invalid>' 0922 0923 0924 class QTimeFormatter(HiddenMemberProvider): 0925 """lldb synthetic provider for QTime""" 0926 def __init__(self, valobj, internal_dict): 0927 super(QTimeFormatter, self).__init__(valobj, internal_dict) 0928 self._add_original = False 0929 0930 def has_children(self): 0931 return True 0932 0933 @staticmethod 0934 def parse(ds): 0935 if ds < 0: 0936 return None 0937 MSECS_PER_HOUR = 3600000 0938 SECS_PER_MIN = 60 0939 MSECS_PER_MIN = 60000 0940 0941 hour = ds / MSECS_PER_HOUR 0942 minute = (ds % MSECS_PER_HOUR) / MSECS_PER_MIN 0943 second = (ds / 1000) % SECS_PER_MIN 0944 msec = ds % 1000 0945 return dt.time(hour, minute, second, msec) 0946 0947 def _update(self): 0948 # FIXME: Calling functions returns incorrect SBValue for complex type in lldb 0949 # # toString 0950 # res = invoke(self.valobj, 'toString', '0') 0951 # self._addChild(rename('toString', res)) 0952 0953 # mds 0954 mds = self.valobj.GetChildMemberWithName('mds') 0955 self._addChild(mds) 0956 0957 pytime = self.parse(mds.GetValueAsUnsigned(0)) 0958 if pytime is None: 0959 return 0960 # (ISO) 0961 iso_str = pytime.isoformat().decode().__repr__()[2:-1] 0962 self._addChild(('(ISO)', iso_str)) 0963 0964 # (Locale) 0965 locale_encoding = [locale.getlocale()[1]] 0966 if locale_encoding[0] is None: 0967 locale_encoding = [] 0968 locale_str = pytime.strftime('%X').decode(*locale_encoding).__repr__()[2:-1] 0969 self._addChild(('(Locale)', locale_str)) 0970 0971 0972 def QTimeSummaryProvider(valobj, internal_dict): 0973 if valobj.IsValid(): 0974 content = valobj.GetChildMemberWithName('(Locale)') 0975 if content.IsValid(): 0976 summary = content.GetSummary() 0977 if summary is not None: 0978 return summary 0979 # No synthetic provider installed, get the content by ourselves 0980 pytime = QTimeFormatter.parse(valobj.GetChildMemberWithName('mds').GetValueAsUnsigned(0)) 0981 if pytime is not None: 0982 return pytime.isoformat().decode().__repr__()[2:-1] 0983 return None 0984 0985 0986 class QDateTimeFormatter(HiddenMemberProvider): 0987 """lldb synthetic provider for QTime""" 0988 def __init__(self, valobj, internal_dict): 0989 super(QDateTimeFormatter, self).__init__(valobj, internal_dict) 0990 0991 def has_children(self): 0992 return True 0993 0994 @staticmethod 0995 def parse(time_t, utc=False): 0996 if time_t is None: 0997 return None 0998 totuple = time.gmtime if utc else time.localtime 0999 return totuple(time_t) 1000 1001 @staticmethod 1002 def getdata(var): 1003 # FIXME: data member is in private structure, which has no complete type when no debug info 1004 # available for Qt.So we can only rely on function call. 1005 # The comments in Qt source code says data member will be inlined in Qt6, 1006 res = invoke(var, 'toSecsSinceEpoch') 1007 return res 1008 1009 def _update(self): 1010 time_t = self.getdata(self.valobj) 1011 if not time_t.IsValid(): 1012 return 1013 1014 locale_encoding = [locale.getlocale()[1]] 1015 if locale_encoding[0] is None: 1016 locale_encoding = [] 1017 1018 # toTime_t 1019 self._addChild(rename('toTime_t', time_t)) 1020 1021 # time tuple in local time and utc time 1022 local_tt = self.parse(time_t.GetValueAsUnsigned(0)) 1023 utc_tt = self.parse(time_t.GetValueAsUnsigned(0), utc=True) 1024 1025 # (ISO) 1026 formatted = time.strftime('%Y-%m-%d %H:%M:%S', utc_tt).decode(*locale_encoding).__repr__() 1027 formatted = formatted[2:-1] 1028 self._addChild(('(ISO)', formatted)) 1029 1030 def locale_fmt(name, tt): 1031 formatted = time.strftime('%c', tt).decode(*locale_encoding).__repr__()[2:-1] 1032 self._addChild((name, formatted)) 1033 1034 # (Locale) 1035 locale_fmt('(Locale)', local_tt) 1036 1037 # (UTC) 1038 locale_fmt('(UTC)', utc_tt) 1039 1040 # FIXME: Calling functions returns incorrect SBValue for complex type in lldb 1041 # # toString 1042 # res = invoke(self.valobj, 'toString', '0') 1043 # print 'tostring', res 1044 # self._addChild(rename('toString', res)) 1045 1046 # # toLocalTime 1047 # res = invoke(self.valobj, 'toTimeSpec', '0') # Qt::LocalTime == 0 1048 # print 'tolocaltime', res 1049 # self._addChild(rename('toLocalTime', res)) 1050 1051 1052 def QDateTimeSummaryProvider(valobj, internal_dict): 1053 if valobj.IsValid(): 1054 content = valobj.GetChildMemberWithName('(Locale)') 1055 if content.IsValid(): 1056 summary = content.GetSummary() 1057 if summary is not None: 1058 return summary 1059 # No synthetic provider installed, get the content by ourselves 1060 pytime = QDateTimeFormatter.parse(QDateTimeFormatter.getdata(valobj).GetValueAsUnsigned(0)) 1061 if pytime is not None: 1062 formatted = time.strftime('%Y-%m-%d %H:%M:%S', pytime).decode().__repr__() 1063 formatted = formatted[2:-1] 1064 return formatted 1065 return None 1066 1067 1068 class QUrlFormatter(HiddenMemberProvider): 1069 """docstring for QUrlFormatter""" 1070 def __init__(self, valobj, internal_dict): 1071 super(QUrlFormatter, self).__init__(valobj, internal_dict) 1072 1073 target = valobj.GetTarget() 1074 self._int_type = target.GetBasicType(lldb.eBasicTypeInt) 1075 self._pvoid_type = target.GetBasicType(lldb.eBasicTypeVoid).GetPointerType() 1076 self._qstring_type = target.FindFirstType('QString') 1077 self._qbytearray_type = target.FindFirstType('QByteArray') 1078 1079 def parseQt5Data(self, dataobj): 1080 def constructEncoded(port, scheme, username, password, host, path, query, fragment): 1081 netloc = '' 1082 host_str = printableQString(host)[0] 1083 if host_str is not None: 1084 username_str = printableQString(username)[0] 1085 if username_str is not None: 1086 netloc += username_str 1087 password_str = printableQString(password)[0] 1088 if password_str is not None: 1089 netloc += ':' + password_str 1090 netloc += "@" 1091 netloc += host_str 1092 port_num = port.GetValueAsSigned(-1) 1093 if port_num != -1: 1094 netloc += ":" + str(port_num) 1095 1096 url = urlunsplit((printableQString(scheme)[0], 1097 netloc, 1098 printableQString(path)[0], 1099 printableQString(query)[0], 1100 printableQString(fragment)[0])) 1101 encoded = None 1102 if len(url) > 0: 1103 encoded = ('(encoded)', url) 1104 return (encoded, port, scheme, username, password, host, path, query, fragment) 1105 1106 # try if there's debug info available 1107 port = dataobj.GetChildMemberWithName('port') 1108 if port.IsValid(): 1109 scheme = dataobj.GetChildMemberWithName('scheme') 1110 username = dataobj.GetChildMemberWithName('userName') 1111 password = dataobj.GetChildMemberWithName('password') 1112 host = dataobj.GetChildMemberWithName('host') 1113 path = dataobj.GetChildMemberWithName('path') 1114 query = dataobj.GetChildMemberWithName('query') 1115 fragment = dataobj.GetChildMemberWithName('fragment') 1116 return constructEncoded(port, scheme, username, password, host, path, query, fragment) 1117 # if no debug information is available for Qt, try guessing the correct address 1118 # problem with this is that if QUrlPrivate members get changed, this fails 1119 addr = dataobj.GetValueAsUnsigned(0) 1120 1121 # skip QAtomicInt ref 1122 addr += self._int_type.GetByteSize() 1123 # handle int port 1124 port = dataobj.CreateValueFromAddress('(port)', addr, self._int_type) 1125 addr += self._int_type.GetByteSize() 1126 # handle QString scheme 1127 scheme = dataobj.CreateValueFromAddress('(scheme)', addr, self._qstring_type) 1128 addr += self._qstring_type.GetByteSize() 1129 # handle QString username 1130 username = dataobj.CreateValueFromAddress('(userName)', addr, self._qstring_type) 1131 addr += self._qstring_type.GetByteSize() 1132 # handle QString password 1133 password = dataobj.CreateValueFromAddress('(password)', addr, self._qstring_type) 1134 addr += self._qstring_type.GetByteSize() 1135 # handle QString host 1136 host = dataobj.CreateValueFromAddress('(host)', addr, self._qstring_type) 1137 addr += self._qstring_type.GetByteSize() 1138 # handle QString path 1139 path = dataobj.CreateValueFromAddress('(path)', addr, self._qstring_type) 1140 addr += self._qstring_type.GetByteSize() 1141 # handle QString query 1142 query = dataobj.CreateValueFromAddress('(query)', addr, self._qstring_type) 1143 addr += self._qstring_type.GetByteSize() 1144 # handle QString fragment 1145 fragment = dataobj.CreateValueFromAddress('(fragment)', addr, self._qstring_type) 1146 1147 return constructEncoded(port, scheme, username, password, host, path, query, fragment) 1148 1149 def parseQt4Data(self, dataobj): 1150 def parseComponents(encodedobj): 1151 url, _, _ = printableQByteArray(encodedobj) 1152 if url is None: 1153 return (None,) * 9 1154 res = urlsplit(url) 1155 port = dataobj.CreateValueFromExpression('(port)', str(res.port if res.port is not None else -1)) 1156 scheme = ('(scheme)', res.scheme) 1157 username = ('(username)', res.username if res.username is not None else '') 1158 password = ('(password)', res.password if res.password is not None else '') 1159 host = ('(host)', res.hostname if res.hostname is not None else '') 1160 path = ('(path)', res.path) 1161 query = ('(query)', res.query) 1162 fragment = ('(fragment)', res.fragment) 1163 encoded = ('(encoded)', url) 1164 return (encoded, port, scheme, username, password, host, path, query, fragment) 1165 1166 encodedOriginal = dataobj.GetChildMemberWithName('encodedOriginal') 1167 if encodedOriginal.IsValid(): 1168 return parseComponents(encodedOriginal) 1169 1170 # if no debug information is available for Qt, try guessing the correct address 1171 # problem with this is that if QUrlPrivate members get changed, this fails 1172 addr = dataobj.GetValueAsUnsigned(0) 1173 if not validAddr(dataobj, addr): 1174 return (None,) * 9 1175 1176 # skip QAtomicInt ref 1177 addr += self._int_type.GetByteSize() 1178 # alignment, 1179 # The largest member is QString and QByteArray, which are 8 bytes (one sizeof(void*)), 1180 # int is aligned to 8 bytes 1181 addr += self._pvoid_type.GetByteSize() - self._int_type.GetByteSize() 1182 # These members are always empty: scheme, userName, password, host, path, query (QByteArray), fragment 1183 addr += self._qstring_type.GetByteSize() * 6 1184 addr += self._qbytearray_type.GetByteSize() 1185 # handle QByteArray encodedOriginal 1186 encoded = dataobj.CreateValueFromAddress('(encoded)', addr, self._qbytearray_type) 1187 1188 if not encoded.IsValid(): 1189 return (None,) * 9 1190 return parseComponents(encoded) 1191 1192 def try_parse(self): 1193 dataobj = self.valobj.GetChildMemberWithName('d') 1194 # first try to access Qt4 data 1195 (encoded, port, scheme, 1196 username, password, host, path, query, fragment) = self.parseQt4Data(dataobj) 1197 if encoded is not None: 1198 return (encoded, port, scheme, username, password, host, path, query, fragment) 1199 1200 # if this fails, maybe we deal with Qt5 1201 (encoded, port, scheme, 1202 username, password, host, 1203 path, query, fragment) = self.parseQt5Data(dataobj) 1204 if encoded is not None: 1205 return (encoded, port, scheme, username, password, host, path, query, fragment) 1206 1207 # if above fails, try to print directly. 1208 # But this might not work, and could lead to issues 1209 # (see http://sourceware-org.1504.n7.nabble.com/help-Calling-malloc-from-a-Python-pretty-printer-td284031.html) 1210 res = invoke(self.valobj, 'toString', '(QUrl::FormattingOptions)0') # QUrl::PrettyDecoded == 0 1211 if res.IsValid(): 1212 return rename('(encoded)', res), None, None, None, None, None, None, None, None 1213 return None, None, None, None, None, None, None, None, None 1214 1215 def _update(self): 1216 (encoded, port, scheme, username, 1217 password, host, path, query, fragment) = self.try_parse() 1218 if encoded is not None: 1219 self._addChild(encoded, hidden=True) 1220 if port is not None: 1221 self._addChild(port) 1222 self._addChild(scheme) 1223 self._addChild(username) 1224 self._addChild(password) 1225 self._addChild(host) 1226 self._addChild(path) 1227 self._addChild(query) 1228 self._addChild(fragment) 1229 return 1230 # if everything fails, we have no choice but to show the original member 1231 self._add_original = False 1232 self._addChild(self.valobj.GetChildMemberWithName('d')) 1233 1234 1235 def QUrlSummaryProvider(valobj, internal_dict): 1236 if valobj.IsValid(): 1237 content = valobj.GetChildMemberWithName('(encoded)') 1238 if content.IsValid(): 1239 summary = content.GetSummary() 1240 if summary is not None: 1241 return summary 1242 # No synthetic provider installed, get the content by ourselves 1243 encoded = QUrlFormatter(valobj, internal_dict).try_parse()[0][1] 1244 if encoded is not None: 1245 return encoded 1246 return None 1247 1248 1249 class QUuidFormatter(HiddenMemberProvider): 1250 """A lldb synthetic provider for QUuid""" 1251 def __init__(self, valobj, internal_dict): 1252 super(QUuidFormatter, self).__init__(valobj, internal_dict) 1253 1254 def has_children(self): 1255 return False 1256 1257 1258 def QUuidSummaryProvider(valobj, internal_dict): 1259 data = [valobj.GetChildMemberWithName(name).GetValueAsUnsigned(0) 1260 for name in ['data1', 'data2', 'data3']] 1261 data += [val.GetValueAsUnsigned(0) for val in valobj.GetChildMemberWithName('data4')] 1262 1263 return 'QUuid({{{:02x}-{:02x}-{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}})'.format(*data)