1############################################################################ 2# 3# Copyright (C) 2016 The Qt Company Ltd. 4# Contact: https://www.qt.io/licensing/ 5# 6# This file is part of Qt Creator. 7# 8# Commercial License Usage 9# Licensees holding valid commercial Qt licenses may use this file in 10# accordance with the commercial license agreement provided with the 11# Software or, alternatively, in accordance with the terms contained in 12# a written agreement between you and The Qt Company. For licensing terms 13# and conditions see https://www.qt.io/terms-conditions. For further 14# information use the contact form at https://www.qt.io/contact-us. 15# 16# GNU General Public License Usage 17# Alternatively, this file may be used under the terms of the GNU 18# General Public License version 3 as published by the Free Software 19# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20# included in the packaging of this file. Please review the following 21# information to ensure the GNU General Public License requirements will 22# be met: https://www.gnu.org/licenses/gpl-3.0.html. 23# 24############################################################################ 25 26import os 27import codecs 28import collections 29import glob 30import struct 31import sys 32import base64 33import re 34import time 35import inspect 36from utils import DisplayFormat, TypeCode 37 38try: 39 # That's only used in native combined debugging right now, so 40 # we do not need to hard fail in cases of partial python installation 41 # that will never use this. 42 import json 43except: 44 print("Python module json not found. " 45 "Native combined debugging might not work.") 46 pass 47 48 49if sys.version_info[0] >= 3: 50 toInteger = int 51else: 52 toInteger = long 53 54 55class ReportItem(): 56 """ 57 Helper structure to keep temporary 'best' information about a value 58 or a type scheduled to be reported. This might get overridden be 59 subsequent better guesses during a putItem() run. 60 """ 61 62 def __init__(self, value=None, encoding=None, priority=-100, elided=None): 63 self.value = value 64 self.priority = priority 65 self.encoding = encoding 66 self.elided = elided 67 68 def __str__(self): 69 return 'Item(value: %s, encoding: %s, priority: %s, elided: %s)' \ 70 % (self.value, self.encoding, self.priority, self.elided) 71 72 73class Timer(): 74 def __init__(self, d, desc): 75 self.d = d 76 self.desc = desc + '-' + d.currentIName 77 78 def __enter__(self): 79 self.starttime = time.time() 80 81 def __exit__(self, exType, exValue, exTraceBack): 82 elapsed = int(1000 * (time.time() - self.starttime)) 83 self.d.timings.append([self.desc, elapsed]) 84 85 86class Children(): 87 def __init__(self, d, numChild=1, childType=None, childNumChild=None, 88 maxNumChild=None, addrBase=None, addrStep=None): 89 self.d = d 90 self.numChild = numChild 91 self.childNumChild = childNumChild 92 self.maxNumChild = maxNumChild 93 if childType is None: 94 self.childType = None 95 else: 96 self.childType = childType.name 97 if not self.d.isCli: 98 self.d.putField('childtype', self.childType) 99 if childNumChild is not None: 100 self.d.putField('childnumchild', childNumChild) 101 self.childNumChild = childNumChild 102 if addrBase is not None and addrStep is not None: 103 self.d.put('addrbase="0x%x",addrstep="%d",' % (addrBase, addrStep)) 104 105 def __enter__(self): 106 self.savedChildType = self.d.currentChildType 107 self.savedChildNumChild = self.d.currentChildNumChild 108 self.savedNumChild = self.d.currentNumChild 109 self.savedMaxNumChild = self.d.currentMaxNumChild 110 self.d.currentChildType = self.childType 111 self.d.currentChildNumChild = self.childNumChild 112 self.d.currentNumChild = self.numChild 113 self.d.currentMaxNumChild = self.maxNumChild 114 self.d.put(self.d.childrenPrefix) 115 116 def __exit__(self, exType, exValue, exTraceBack): 117 if exType is not None: 118 if self.d.passExceptions: 119 self.d.showException('CHILDREN', exType, exValue, exTraceBack) 120 self.d.putSpecialValue('notaccessible') 121 self.d.putNumChild(0) 122 if self.d.currentMaxNumChild is not None: 123 if self.d.currentMaxNumChild < self.d.currentNumChild: 124 self.d.put('{name="<incomplete>",value="",type="",numchild="0"},') 125 self.d.currentChildType = self.savedChildType 126 self.d.currentChildNumChild = self.savedChildNumChild 127 self.d.currentNumChild = self.savedNumChild 128 self.d.currentMaxNumChild = self.savedMaxNumChild 129 if self.d.isCli: 130 self.d.output += '\n' + ' ' * self.d.indent 131 self.d.put(self.d.childrenSuffix) 132 return True 133 134 135class SubItem(): 136 def __init__(self, d, component): 137 self.d = d 138 self.name = component 139 self.iname = None 140 141 def __enter__(self): 142 self.d.enterSubItem(self) 143 144 def __exit__(self, exType, exValue, exTraceBack): 145 return self.d.exitSubItem(self, exType, exValue, exTraceBack) 146 147 148class TopLevelItem(SubItem): 149 def __init__(self, d, iname): 150 self.d = d 151 self.iname = iname 152 self.name = None 153 154 155class UnnamedSubItem(SubItem): 156 def __init__(self, d, component): 157 self.d = d 158 self.iname = '%s.%s' % (self.d.currentIName, component) 159 self.name = None 160 161 162class DumperBase(): 163 @staticmethod 164 def warn(message): 165 print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode('latin1')) 166 167 @staticmethod 168 def showException(msg, exType, exValue, exTraceback): 169 DumperBase.warn('**** CAUGHT EXCEPTION: %s ****' % msg) 170 try: 171 import traceback 172 for line in traceback.format_exception(exType, exValue, exTraceback): 173 DumperBase.warn('%s' % line) 174 except: 175 pass 176 177 def timer(self, desc): 178 return Timer(self, desc) 179 180 def __init__(self): 181 self.isCdb = False 182 self.isGdb = False 183 self.isLldb = False 184 self.isCli = False 185 186 # Later set, or not set: 187 self.stringCutOff = 10000 188 self.displayStringLimit = 100 189 self.useTimeStamps = False 190 191 self.output = '' 192 self.typesReported = {} 193 self.typesToReport = {} 194 self.qtNamespaceToReport = None 195 self.qtCustomEventFunc = 0 196 self.qtCustomEventPltFunc = 0 197 self.qtPropertyFunc = 0 198 self.passExceptions = False 199 self.isTesting = False 200 201 self.typeData = {} 202 self.isBigEndian = False 203 self.packCode = '<' 204 205 self.resetCaches() 206 self.resetStats() 207 208 self.childrenPrefix = 'children=[' 209 self.childrenSuffix = '],' 210 211 self.dumpermodules = [ 212 os.path.splitext(os.path.basename(p))[0] for p in 213 glob.glob(os.path.join(os.path.dirname(__file__), '*types.py')) 214 ] 215 216 # These values are never used, but the variables need to have 217 # some value base for the swapping logic in Children.__enter__() 218 # and Children.__exit__(). 219 self.currentIName = None 220 self.currentValue = None 221 self.currentType = None 222 self.currentNumChild = None 223 self.currentMaxNumChild = None 224 self.currentPrintsAddress = True 225 self.currentChildType = None 226 self.currentChildNumChild = None 227 self.registerKnownTypes() 228 229 def setVariableFetchingOptions(self, args): 230 self.resultVarName = args.get('resultvarname', '') 231 self.expandedINames = set(args.get('expanded', [])) 232 self.stringCutOff = int(args.get('stringcutoff', 10000)) 233 self.displayStringLimit = int(args.get('displaystringlimit', 100)) 234 self.typeformats = args.get('typeformats', {}) 235 self.formats = args.get('formats', {}) 236 self.watchers = args.get('watchers', {}) 237 self.useDynamicType = int(args.get('dyntype', '0')) 238 self.useFancy = int(args.get('fancy', '0')) 239 self.forceQtNamespace = int(args.get('forcens', '0')) 240 self.passExceptions = int(args.get('passexceptions', '0')) 241 self.isTesting = int(args.get('testing', '0')) 242 self.showQObjectNames = int(args.get('qobjectnames', '1')) 243 self.nativeMixed = int(args.get('nativemixed', '0')) 244 self.autoDerefPointers = int(args.get('autoderef', '0')) 245 self.useTimeStamps = int(args.get('timestamps', '0')) 246 self.partialVariable = args.get('partialvar', '') 247 self.uninitialized = args.get('uninitialized', []) 248 self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized)) 249 self.partialUpdate = int(args.get('partial', '0')) 250 self.fallbackQtVersion = 0x50200 251 #DumperBase.warn('NAMESPACE: "%s"' % self.qtNamespace()) 252 #DumperBase.warn('EXPANDED INAMES: %s' % self.expandedINames) 253 #DumperBase.warn('WATCHERS: %s' % self.watchers) 254 255 def resetPerStepCaches(self): 256 self.perStepCache = {} 257 pass 258 259 def resetCaches(self): 260 # This is a cache mapping from 'type name' to 'display alternatives'. 261 self.qqFormats = {'QVariant (QVariantMap)': [DisplayFormat.CompactMap]} 262 263 # This is a cache of all known dumpers. 264 self.qqDumpers = {} # Direct type match 265 self.qqDumpersEx = {} # Using regexp 266 267 # This is a cache of all dumpers that support writing. 268 self.qqEditable = {} 269 270 # This keeps canonical forms of the typenames, without array indices etc. 271 self.cachedFormats = {} 272 273 # Maps type names to static metaobjects. If a type is known 274 # to not be QObject derived, it contains a 0 value. 275 self.knownStaticMetaObjects = {} 276 277 # A dictionary to serve as a per debugging step cache. 278 # Cleared on each step over / into / continue. 279 self.perStepCache = {} 280 281 # A dictionary to serve as a general cache throughout the whole 282 # debug session. 283 self.generalCache = {} 284 285 self.counts = {} 286 self.structPatternCache = {} 287 self.timings = [] 288 self.expandableINames = set({}) 289 290 def resetStats(self): 291 # Timing collection 292 self.timings = [] 293 pass 294 295 def dumpStats(self): 296 msg = [self.counts, self.timings] 297 self.resetStats() 298 return msg 299 300 def bump(self, key): 301 if key in self.counts: 302 self.counts[key] += 1 303 else: 304 self.counts[key] = 1 305 306 def childRange(self): 307 if self.currentMaxNumChild is None: 308 return range(0, self.currentNumChild) 309 return range(min(self.currentMaxNumChild, self.currentNumChild)) 310 311 def enterSubItem(self, item): 312 if self.useTimeStamps: 313 item.startTime = time.time() 314 if not item.iname: 315 item.iname = '%s.%s' % (self.currentIName, item.name) 316 if not self.isCli: 317 self.put('{') 318 if isinstance(item.name, str): 319 self.putField('name', item.name) 320 else: 321 self.indent += 1 322 self.output += '\n' + ' ' * self.indent 323 if isinstance(item.name, str): 324 self.output += item.name + ' = ' 325 item.savedIName = self.currentIName 326 item.savedValue = self.currentValue 327 item.savedType = self.currentType 328 self.currentIName = item.iname 329 self.currentValue = ReportItem() 330 self.currentType = ReportItem() 331 332 def exitSubItem(self, item, exType, exValue, exTraceBack): 333 #DumperBase.warn('CURRENT VALUE: %s: %s %s' % 334 # (self.currentIName, self.currentValue, self.currentType)) 335 if exType is not None: 336 if self.passExceptions: 337 self.showException('SUBITEM', exType, exValue, exTraceBack) 338 self.putSpecialValue('notaccessible') 339 self.putNumChild(0) 340 if not self.isCli: 341 try: 342 if self.currentType.value: 343 typeName = self.currentType.value 344 if len(typeName) > 0 and typeName != self.currentChildType: 345 self.putField('type', typeName) 346 if self.currentValue.value is None: 347 self.put('value="",encoding="notaccessible",numchild="0",') 348 else: 349 if self.currentValue.encoding is not None: 350 self.put('valueencoded="%s",' % self.currentValue.encoding) 351 if self.currentValue.elided: 352 self.put('valueelided="%s",' % self.currentValue.elided) 353 self.put('value="%s",' % self.currentValue.value) 354 except: 355 pass 356 if self.useTimeStamps: 357 self.put('time="%s",' % (time.time() - item.startTime)) 358 self.put('},') 359 else: 360 self.indent -= 1 361 try: 362 if self.currentType.value: 363 typeName = self.currentType.value 364 self.put('<%s> = {' % typeName) 365 366 if self.currentValue.value is None: 367 self.put('<not accessible>') 368 else: 369 value = self.currentValue.value 370 if self.currentValue.encoding == 'latin1': 371 value = self.hexdecode(value) 372 elif self.currentValue.encoding == 'utf8': 373 value = self.hexdecode(value) 374 elif self.currentValue.encoding == 'utf16': 375 b = bytes(bytearray.fromhex(value)) 376 value = codecs.decode(b, 'utf-16') 377 self.put('"%s"' % value) 378 if self.currentValue.elided: 379 self.put('...') 380 381 if self.currentType.value: 382 self.put('}') 383 except: 384 pass 385 self.currentIName = item.savedIName 386 self.currentValue = item.savedValue 387 self.currentType = item.savedType 388 return True 389 390 def stripForFormat(self, typeName): 391 if not isinstance(typeName, str): 392 raise RuntimeError('Expected string in stripForFormat(), got %s' % type(typeName)) 393 if typeName in self.cachedFormats: 394 return self.cachedFormats[typeName] 395 stripped = '' 396 inArray = 0 397 for c in typeName: 398 if c == '<': 399 break 400 if c == ' ': 401 continue 402 if c == '[': 403 inArray += 1 404 elif c == ']': 405 inArray -= 1 406 if inArray and ord(c) >= 48 and ord(c) <= 57: 407 continue 408 stripped += c 409 self.cachedFormats[typeName] = stripped 410 return stripped 411 412 def templateArgument(self, typeobj, position): 413 return typeobj.templateArgument(position) 414 415 def intType(self): 416 result = self.lookupType('int') 417 self.intType = lambda: result 418 return result 419 420 def charType(self): 421 result = self.lookupType('char') 422 self.intType = lambda: result 423 return result 424 425 def ptrSize(self): 426 result = self.lookupType('void*').size() 427 self.ptrSize = lambda: result 428 return result 429 430 def lookupType(self, typeName): 431 nativeType = self.lookupNativeType(typeName) 432 return None if nativeType is None else self.fromNativeType(nativeType) 433 434 def registerKnownTypes(self): 435 typeId = 'unsigned short' 436 tdata = self.TypeData(self) 437 tdata.name = typeId 438 tdata.typeId = typeId 439 tdata.lbitsize = 16 440 tdata.code = TypeCode.Integral 441 self.registerType(typeId, tdata) 442 443 typeId = 'QChar' 444 tdata = self.TypeData(self) 445 tdata.name = typeId 446 tdata.typeId = typeId 447 tdata.lbitsize = 16 448 tdata.code = TypeCode.Struct 449 tdata.lfields = [self.Field(dumper=self, name='ucs', 450 type='unsigned short', bitsize=16, bitpos=0)] 451 tdata.lalignment = 2 452 tdata.templateArguments = [] 453 self.registerType(typeId, tdata) 454 455 def nativeDynamicType(self, address, baseType): 456 return baseType # Override in backends. 457 458 def listTemplateParameters(self, typename): 459 return self.listTemplateParametersManually(typename) 460 461 def listTemplateParametersManually(self, typename): 462 targs = [] 463 if not typename.endswith('>'): 464 return targs 465 466 def push(inner): 467 # Handle local struct definitions like QList<main(int, char**)::SomeStruct> 468 inner = inner.strip()[::-1] 469 p = inner.find(')::') 470 if p > -1: 471 inner = inner[p + 3:].strip() 472 if inner.startswith('const '): 473 inner = inner[6:].strip() 474 if inner.endswith(' const'): 475 inner = inner[:-6].strip() 476 #DumperBase.warn("FOUND: %s" % inner) 477 targs.append(inner) 478 479 #DumperBase.warn("SPLITTING %s" % typename) 480 level = 0 481 inner = '' 482 for c in typename[::-1]: # Reversed... 483 #DumperBase.warn("C: %s" % c) 484 if c == '>': 485 if level > 0: 486 inner += c 487 level += 1 488 elif c == '<': 489 level -= 1 490 if level > 0: 491 inner += c 492 else: 493 push(inner) 494 inner = '' 495 break 496 elif c == ',': 497 #DumperBase.warn('c: %s level: %s' % (c, level)) 498 if level == 1: 499 push(inner) 500 inner = '' 501 else: 502 inner += c 503 else: 504 inner += c 505 506 #DumperBase.warn("TARGS: %s %s" % (typename, targs)) 507 res = [] 508 for item in targs[::-1]: 509 if len(item) == 0: 510 continue 511 c = ord(item[0]) 512 if c in (45, 46) or (c >= 48 and c < 58): # '-', '.' or digit. 513 if item.find('.') > -1: 514 res.append(float(item)) 515 else: 516 if item.endswith('l'): 517 item = item[:-1] 518 if item.endswith('u'): 519 item = item[:-1] 520 val = toInteger(item) 521 if val > 0x80000000: 522 val -= 0x100000000 523 res.append(val) 524 else: 525 res.append(self.Type(self, item)) 526 #DumperBase.warn("RES: %s %s" % (typename, [(None if t is None else t.name) for t in res])) 527 return res 528 529 # Hex decoding operating on str, return str. 530 def hexdecode(self, s): 531 if sys.version_info[0] == 2: 532 return s.decode('hex') 533 return bytes.fromhex(s).decode('utf8') 534 535 # Hex encoding operating on str or bytes, return str. 536 def hexencode(self, s): 537 if s is None: 538 s = '' 539 if sys.version_info[0] == 2: 540 if isinstance(s, buffer): 541 return bytes(s).encode('hex') 542 return s.encode('hex') 543 if isinstance(s, str): 544 s = s.encode('utf8') 545 return base64.b16encode(s).decode('utf8') 546 547 def isQt3Support(self): 548 # assume no Qt 3 support by default 549 return False 550 551 # Clamps size to limit. 552 def computeLimit(self, size, limit): 553 if limit == 0: 554 limit = self.displayStringLimit 555 if limit is None or size <= limit: 556 return 0, size 557 return size, limit 558 559 def vectorData(self, value): 560 if self.qtVersion() >= 0x060000: 561 data, size, alloc = self.qArrayData(value) 562 elif self.qtVersion() >= 0x050000: 563 vector_data_ptr = self.extractPointer(value) 564 if self.ptrSize() == 4: 565 (ref, size, alloc, offset) = self.split('IIIp', vector_data_ptr) 566 else: 567 (ref, size, alloc, pad, offset) = self.split('IIIIp', vector_data_ptr) 568 alloc = alloc & 0x7ffffff 569 data = vector_data_ptr + offset 570 else: 571 vector_data_ptr = self.extractPointer(value) 572 (ref, alloc, size) = self.split('III', vector_data_ptr) 573 data = vector_data_ptr + 16 574 self.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000) 575 return data, size 576 577 def qArrayData(self, value): 578 if self.qtVersion() >= 0x60000: 579 dd, data, size = self.split('ppp', value) 580 if dd: 581 _, _, alloc = self.split('iip', dd) 582 else: # fromRawData 583 alloc = size 584 return data, size, alloc 585 return self.qArrayDataHelper(self.extractPointer(value)) 586 587 def qArrayDataHelper(self, array_data_ptr): 588 # array_data_ptr is what is e.g. stored in a QByteArray's d_ptr. 589 if self.qtVersion() >= 0x050000: 590 # QTypedArray: 591 # - QtPrivate::RefCount ref 592 # - int size 593 # - uint alloc : 31, capacityReserved : 1 594 # - qptrdiff offset 595 (ref, size, alloc, offset) = self.split('IIpp', array_data_ptr) 596 alloc = alloc & 0x7ffffff 597 data = array_data_ptr + offset 598 if self.ptrSize() == 4: 599 data = data & 0xffffffff 600 else: 601 data = data & 0xffffffffffffffff 602 elif self.qtVersion() >= 0x040000: 603 # Data: 604 # - QBasicAtomicInt ref; 605 # - int alloc, size; 606 # - [padding] 607 # - char *data; 608 if self.ptrSize() == 4: 609 (ref, alloc, size, data) = self.split('IIIp', array_data_ptr) 610 else: 611 (ref, alloc, size, pad, data) = self.split('IIIIp', array_data_ptr) 612 else: 613 # Data: 614 # - QShared count; 615 # - QChar *unicode 616 # - char *ascii 617 # - uint len: 30 618 (dummy, dummy, dummy, size) = self.split('IIIp', array_data_ptr) 619 size = self.extractInt(array_data_ptr + 3 * self.ptrSize()) & 0x3ffffff 620 alloc = size # pretend. 621 data = self.extractPointer(array_data_ptr + self.ptrSize()) 622 return data, size, alloc 623 624 def encodeStringHelper(self, value, limit): 625 data, size, alloc = self.qArrayData(value) 626 if alloc != 0: 627 self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000) 628 elided, shown = self.computeLimit(2 * size, 2 * limit) 629 return elided, self.readMemory(data, shown) 630 631 def encodeByteArrayHelper(self, value, limit): 632 data, size, alloc = self.qArrayData(value) 633 if alloc != 0: 634 self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000) 635 elided, shown = self.computeLimit(size, limit) 636 return elided, self.readMemory(data, shown) 637 638 def putCharArrayValue(self, data, size, charSize, 639 displayFormat=DisplayFormat.Automatic): 640 bytelen = size * charSize 641 elided, shown = self.computeLimit(bytelen, self.displayStringLimit) 642 mem = self.readMemory(data, shown) 643 if charSize == 1: 644 if displayFormat in (DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String): 645 encodingType = 'latin1' 646 else: 647 encodingType = 'utf8' 648 #childType = 'char' 649 elif charSize == 2: 650 encodingType = 'utf16' 651 #childType = 'short' 652 else: 653 encodingType = 'ucs4' 654 #childType = 'int' 655 656 self.putValue(mem, encodingType, elided=elided) 657 658 if displayFormat in ( 659 DisplayFormat.SeparateLatin1String, 660 DisplayFormat.SeparateUtf8String, 661 DisplayFormat.Separate): 662 elided, shown = self.computeLimit(bytelen, 100000) 663 self.putDisplay(encodingType + ':separate', self.readMemory(data, shown)) 664 665 def putCharArrayHelper(self, data, size, charType, 666 displayFormat=DisplayFormat.Automatic, 667 makeExpandable=True): 668 charSize = charType.size() 669 self.putCharArrayValue(data, size, charSize, displayFormat=displayFormat) 670 671 if makeExpandable: 672 self.putNumChild(size) 673 if self.isExpanded(): 674 with Children(self): 675 for i in range(size): 676 self.putSubItem(size, self.createValue(data + i * charSize, charType)) 677 678 def readMemory(self, addr, size): 679 return self.hexencode(bytes(self.readRawMemory(addr, size))) 680 681 def encodeByteArray(self, value, limit=0): 682 elided, data = self.encodeByteArrayHelper(value, limit) 683 return data 684 685 def putByteArrayValue(self, value): 686 elided, data = self.encodeByteArrayHelper(value, self.displayStringLimit) 687 self.putValue(data, 'latin1', elided=elided) 688 689 def encodeString(self, value, limit=0): 690 elided, data = self.encodeStringHelper(value, limit) 691 return data 692 693 def encodedUtf16ToUtf8(self, s): 694 return ''.join([chr(int(s[i:i + 2], 16)) for i in range(0, len(s), 4)]) 695 696 def encodeStringUtf8(self, value, limit=0): 697 return self.encodedUtf16ToUtf8(self.encodeString(value, limit)) 698 699 def stringData(self, value): # -> (data, size, alloc) 700 return self.qArrayData(value) 701 702 def extractTemplateArgument(self, typename, position): 703 level = 0 704 skipSpace = False 705 inner = '' 706 for c in typename[typename.find('<') + 1: -1]: 707 if c == '<': 708 inner += c 709 level += 1 710 elif c == '>': 711 level -= 1 712 inner += c 713 elif c == ',': 714 if level == 0: 715 if position == 0: 716 return inner.strip() 717 position -= 1 718 inner = '' 719 else: 720 inner += c 721 skipSpace = True 722 else: 723 if skipSpace and c == ' ': 724 pass 725 else: 726 inner += c 727 skipSpace = False 728 # Handle local struct definitions like QList<main(int, char**)::SomeStruct> 729 inner = inner.strip() 730 p = inner.find(')::') 731 if p > -1: 732 inner = inner[p + 3:] 733 return inner 734 735 def putStringValue(self, value): 736 elided, data = self.encodeStringHelper(value, self.displayStringLimit) 737 self.putValue(data, 'utf16', elided=elided) 738 739 def putPtrItem(self, name, value): 740 with SubItem(self, name): 741 self.putValue('0x%x' % value) 742 self.putType('void*') 743 744 def putIntItem(self, name, value): 745 with SubItem(self, name): 746 if isinstance(value, self.Value): 747 self.putValue(value.display()) 748 else: 749 self.putValue(value) 750 self.putType('int') 751 752 def putEnumItem(self, name, ival, typish): 753 buf = bytearray(struct.pack('i', ival)) 754 val = self.Value(self) 755 val.ldata = bytes(buf) 756 val._type = self.createType(typish) 757 with SubItem(self, name): 758 self.putItem(val) 759 760 def putBoolItem(self, name, value): 761 with SubItem(self, name): 762 self.putValue(value) 763 self.putType('bool') 764 765 def putPairItem(self, index, pair, keyName='first', valueName='second'): 766 with SubItem(self, index): 767 self.putPairContents(index, pair, keyName, valueName) 768 769 def putPairContents(self, index, pair, kname, vname): 770 with Children(self): 771 first, second = pair if isinstance(pair, tuple) else pair.members(False) 772 key = self.putSubItem(kname, first) 773 value = self.putSubItem(vname, second) 774 if self.isCli: 775 self.putEmptyValue() 776 else: 777 if index is not None: 778 self.putField('keyprefix', '[%s] ' % index) 779 self.putField('key', key.value) 780 if key.encoding is not None: 781 self.putField('keyencoded', key.encoding) 782 self.putValue(value.value, value.encoding) 783 784 def putEnumValue(self, ival, vals): 785 nice = vals.get(ival, None) 786 display = ('%d' % ival) if nice is None else ('%s (%d)' % (nice, ival)) 787 self.putValue(display) 788 789 def putCallItem(self, name, rettype, value, func, *args): 790 with SubItem(self, name): 791 try: 792 result = self.callHelper(rettype, value, func, args) 793 except Exception as error: 794 if self.passExceptions: 795 raise error 796 children = [('error', error)] 797 self.putSpecialValue("notcallable", children=children) 798 else: 799 self.putItem(result) 800 801 def call(self, rettype, value, func, *args): 802 return self.callHelper(rettype, value, func, args) 803 804 def putAddress(self, address): 805 if address is not None and not self.isCli: 806 self.put('address="0x%x",' % address) 807 808 def putPlainChildren(self, value, dumpBase=True): 809 self.putExpandable() 810 if self.isExpanded(): 811 self.putEmptyValue(-99) 812 with Children(self): 813 self.putFields(value, dumpBase) 814 815 def putNamedChildren(self, values, names): 816 self.putEmptyValue(-99) 817 self.putExpandable() 818 if self.isExpanded(): 819 with Children(self): 820 for n, v in zip(names, values): 821 self.putSubItem(n, v) 822 823 def prettySymbolByAddress(self, address): 824 return '0x%x' % address 825 826 def putSymbolValue(self, address): 827 self.putValue(self.prettySymbolByAddress(address)) 828 829 def putVTableChildren(self, item, itemCount): 830 p = item.pointer() 831 for i in range(itemCount): 832 deref = self.extractPointer(p) 833 if deref == 0: 834 itemCount = i 835 break 836 with SubItem(self, i): 837 self.putItem(self.createPointerValue(deref, 'void')) 838 p += self.ptrSize() 839 return itemCount 840 841 def putFields(self, value, dumpBase=True): 842 baseIndex = 0 843 for item in value.members(True): 844 if item.name is not None: 845 if item.name.startswith('_vptr.') or item.name.startswith('__vfptr'): 846 with SubItem(self, '[vptr]'): 847 # int (**)(void) 848 self.putType(' ') 849 self.putSortGroup(20) 850 self.putValue(item.name) 851 n = 100 852 if self.isExpanded(): 853 with Children(self): 854 n = self.putVTableChildren(item, n) 855 self.putNumChild(n) 856 continue 857 858 if item.isBaseClass and dumpBase: 859 baseIndex += 1 860 # We cannot use nativeField.name as part of the iname as 861 # it might contain spaces and other strange characters. 862 with UnnamedSubItem(self, "@%d" % baseIndex): 863 self.putField('iname', self.currentIName) 864 self.putField('name', '[%s]' % item.name) 865 if not self.isCli: 866 self.putSortGroup(1000 - baseIndex) 867 self.putAddress(item.address()) 868 self.putItem(item) 869 continue 870 871 with SubItem(self, item.name): 872 self.putItem(item) 873 874 def putExpandable(self): 875 self.putNumChild(1) 876 self.expandableINames.add(self.currentIName) 877 if self.isCli: 878 self.putValue('{...}', -99) 879 880 def putMembersItem(self, value, sortorder=10): 881 with SubItem(self, '[members]'): 882 self.putSortGroup(sortorder) 883 self.putPlainChildren(value) 884 885 def put(self, stuff): 886 self.output += stuff 887 888 def check(self, exp): 889 if not exp: 890 raise RuntimeError('Check failed: %s' % exp) 891 892 def checkRef(self, ref): 893 # Assume there aren't a million references to any object. 894 self.check(ref >= -1) 895 self.check(ref < 1000000) 896 897 def checkIntType(self, thing): 898 if not self.isInt(thing): 899 raise RuntimeError('Expected an integral value, got %s' % type(thing)) 900 901 def readToFirstZero(self, base, tsize, maximum): 902 self.checkIntType(base) 903 self.checkIntType(tsize) 904 self.checkIntType(maximum) 905 906 code = self.packCode + (None, 'b', 'H', None, 'I')[tsize] 907 #blob = self.readRawMemory(base, 1) 908 blob = bytes() 909 while maximum > 1: 910 try: 911 blob = self.readRawMemory(base, maximum) 912 break 913 except: 914 maximum = int(maximum / 2) 915 self.warn('REDUCING READING MAXIMUM TO %s' % maximum) 916 917 #DumperBase.warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, tsize, maximum)) 918 for i in range(0, maximum, tsize): 919 t = struct.unpack_from(code, blob, i)[0] 920 if t == 0: 921 return 0, i, self.hexencode(blob[:i]) 922 923 # Real end is unknown. 924 return -1, maximum, self.hexencode(blob[:maximum]) 925 926 def encodeCArray(self, p, tsize, limit): 927 elided, shown, blob = self.readToFirstZero(p, tsize, limit) 928 return elided, blob 929 930 def putItemCount(self, count, maximum=1000000000): 931 # This needs to override the default value, so don't use 'put' directly. 932 if count > maximum: 933 self.putSpecialValue('minimumitemcount', maximum) 934 else: 935 self.putSpecialValue('itemcount', count) 936 self.putNumChild(count) 937 938 def resultToMi(self, value): 939 if isinstance(value, bool): 940 return '"%d"' % int(value) 941 if isinstance(value, dict): 942 return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v)) 943 for (k, v) in list(value.items())]) + '}' 944 if isinstance(value, list): 945 return '[' + ','.join([self.resultToMi(k) 946 for k in value]) + ']' 947 return '"%s"' % value 948 949 def variablesToMi(self, value, prefix): 950 if isinstance(value, bool): 951 return '"%d"' % int(value) 952 if isinstance(value, dict): 953 pairs = [] 954 for (k, v) in list(value.items()): 955 if k == 'iname': 956 if v.startswith('.'): 957 v = '"%s%s"' % (prefix, v) 958 else: 959 v = '"%s"' % v 960 else: 961 v = self.variablesToMi(v, prefix) 962 pairs.append('%s=%s' % (k, v)) 963 return '{' + ','.join(pairs) + '}' 964 if isinstance(value, list): 965 index = 0 966 pairs = [] 967 for item in value: 968 if item.get('type', '') == 'function': 969 continue 970 name = item.get('name', '') 971 if len(name) == 0: 972 name = str(index) 973 index += 1 974 pairs.append((name, self.variablesToMi(item, prefix))) 975 pairs.sort(key=lambda pair: pair[0]) 976 return '[' + ','.join([pair[1] for pair in pairs]) + ']' 977 return '"%s"' % value 978 979 def filterPrefix(self, prefix, items): 980 return [i[len(prefix):] for i in items if i.startswith(prefix)] 981 982 def tryFetchInterpreterVariables(self, args): 983 if not int(args.get('nativemixed', 0)): 984 return (False, '') 985 context = args.get('context', '') 986 if not len(context): 987 return (False, '') 988 989 expanded = args.get('expanded') 990 args['expanded'] = self.filterPrefix('local', expanded) 991 992 res = self.sendInterpreterRequest('variables', args) 993 if not res: 994 return (False, '') 995 996 reslist = [] 997 for item in res.get('variables', {}): 998 if 'iname' not in item: 999 item['iname'] = '.' + item.get('name') 1000 reslist.append(self.variablesToMi(item, 'local')) 1001 1002 watchers = args.get('watchers', None) 1003 if watchers: 1004 toevaluate = [] 1005 name2expr = {} 1006 seq = 0 1007 for watcher in watchers: 1008 expr = self.hexdecode(watcher.get('exp')) 1009 name = str(seq) 1010 toevaluate.append({'name': name, 'expression': expr}) 1011 name2expr[name] = expr 1012 seq += 1 1013 args['expressions'] = toevaluate 1014 1015 args['expanded'] = self.filterPrefix('watch', expanded) 1016 del args['watchers'] 1017 res = self.sendInterpreterRequest('expressions', args) 1018 1019 if res: 1020 for item in res.get('expressions', {}): 1021 name = item.get('name') 1022 iname = 'watch.' + name 1023 expr = name2expr.get(name) 1024 item['iname'] = iname 1025 item['wname'] = self.hexencode(expr) 1026 item['exp'] = expr 1027 reslist.append(self.variablesToMi(item, 'watch')) 1028 1029 return (True, 'data=[%s]' % ','.join(reslist)) 1030 1031 def putField(self, name, value): 1032 self.put('%s="%s",' % (name, value)) 1033 1034 def putType(self, typish, priority=0): 1035 # Higher priority values override lower ones. 1036 if priority >= self.currentType.priority: 1037 types = (str) if sys.version_info[0] >= 3 else (str, unicode) 1038 if isinstance(typish, types): 1039 self.currentType.value = typish 1040 else: 1041 self.currentType.value = typish.name 1042 self.currentType.priority = priority 1043 1044 def putValue(self, value, encoding=None, priority=0, elided=None): 1045 # Higher priority values override lower ones. 1046 # elided = 0 indicates all data is available in value, 1047 # otherwise it's the true length. 1048 if priority >= self.currentValue.priority: 1049 self.currentValue = ReportItem(value, encoding, priority, elided) 1050 1051 def putSpecialValue(self, encoding, value='', children=None): 1052 self.putValue(value, encoding) 1053 if children is not None: 1054 self.putExpandable() 1055 if self.isExpanded(): 1056 with Children(self): 1057 for name, value in children: 1058 with SubItem(self, name): 1059 self.putValue(str(value).replace('"', '$')) 1060 1061 def putEmptyValue(self, priority=-10): 1062 if priority >= self.currentValue.priority: 1063 self.currentValue = ReportItem('', None, priority, None) 1064 1065 def putName(self, name): 1066 self.putField('name', name) 1067 1068 def putBetterType(self, typish): 1069 if isinstance(typish, ReportItem): 1070 self.currentType.value = typish.value 1071 elif isinstance(typish, str): 1072 self.currentType.value = typish.replace('@', self.qtNamespace()) 1073 else: 1074 self.currentType.value = typish.name 1075 self.currentType.priority += 1 1076 1077 def putNoType(self): 1078 # FIXME: replace with something that does not need special handling 1079 # in SubItem.__exit__(). 1080 self.putBetterType(' ') 1081 1082 def putInaccessible(self): 1083 #self.putBetterType(' ') 1084 self.putNumChild(0) 1085 self.currentValue.value = None 1086 1087 def putNamedSubItem(self, component, value, name): 1088 with SubItem(self, component): 1089 self.putName(name) 1090 self.putItem(value) 1091 1092 def isExpanded(self): 1093 #DumperBase.warn('IS EXPANDED: %s in %s: %s' % (self.currentIName, 1094 # self.expandedINames, self.currentIName in self.expandedINames)) 1095 return self.currentIName in self.expandedINames 1096 1097 def mangleName(self, typeName): 1098 return '_ZN%sE' % ''.join(map(lambda x: '%d%s' % (len(x), x), 1099 typeName.split('::'))) 1100 1101 def arrayItemCountFromTypeName(self, typeName, fallbackMax=1): 1102 itemCount = typeName[typeName.find('[') + 1:typeName.find(']')] 1103 return int(itemCount) if itemCount else fallbackMax 1104 1105 def putCStyleArray(self, value): 1106 arrayType = value.type.unqualified() 1107 innerType = arrayType.ltarget 1108 if innerType is None: 1109 innerType = value.type.target().unqualified() 1110 address = value.address() 1111 if address: 1112 self.putValue('@0x%x' % address, priority=-1) 1113 else: 1114 self.putEmptyValue() 1115 self.putType(arrayType) 1116 1117 displayFormat = self.currentItemFormat() 1118 arrayByteSize = arrayType.size() 1119 if arrayByteSize == 0: 1120 # This should not happen. But it does, see QTCREATORBUG-14755. 1121 # GDB/GCC produce sizeof == 0 for QProcess arr[3] 1122 # And in the Nim string dumper. 1123 itemCount = self.arrayItemCountFromTypeName(value.type.name, 100) 1124 arrayByteSize = int(itemCount) * innerType.size() 1125 1126 n = arrayByteSize // innerType.size() 1127 p = value.address() 1128 if displayFormat != DisplayFormat.Raw and p: 1129 if innerType.name in ( 1130 'char', 1131 'wchar_t', 1132 'unsigned char', 1133 'signed char', 1134 'CHAR', 1135 'WCHAR' 1136 ): 1137 self.putCharArrayHelper(p, n, innerType, self.currentItemFormat(), 1138 makeExpandable=False) 1139 else: 1140 self.tryPutSimpleFormattedPointer(p, arrayType, innerType, 1141 displayFormat, arrayByteSize) 1142 self.putNumChild(n) 1143 1144 if self.isExpanded(): 1145 self.putArrayData(p, n, innerType) 1146 1147 self.putPlotDataHelper(p, n, innerType) 1148 1149 def cleanAddress(self, addr): 1150 if addr is None: 1151 return '<no address>' 1152 return '0x%x' % toInteger(hex(addr), 16) 1153 1154 def stripNamespaceFromType(self, typeName): 1155 ns = self.qtNamespace() 1156 if len(ns) > 0 and typeName.startswith(ns): 1157 typeName = typeName[len(ns):] 1158 # DumperBase.warn( 'stripping %s' % typeName ) 1159 lvl = 0 1160 pos = None 1161 stripChunks = [] 1162 sz = len(typeName) 1163 for index in range(0, sz): 1164 s = typeName[index] 1165 if s == '<': 1166 lvl += 1 1167 if lvl == 1: 1168 pos = index 1169 continue 1170 elif s == '>': 1171 lvl -= 1 1172 if lvl < 0: 1173 raise RuntimeError("Unbalanced '<' in type, @index %d" % index) 1174 if lvl == 0: 1175 stripChunks.append((pos, index + 1)) 1176 if lvl != 0: 1177 raise RuntimeError("unbalanced at end of type name") 1178 for (f, l) in reversed(stripChunks): 1179 typeName = typeName[:f] + typeName[l:] 1180 return typeName 1181 1182 def tryPutPrettyItem(self, typeName, value): 1183 value.check() 1184 if self.useFancy and self.currentItemFormat() != DisplayFormat.Raw: 1185 self.putType(typeName) 1186 1187 nsStrippedType = self.stripNamespaceFromType(typeName)\ 1188 .replace('::', '__') 1189 1190 # Strip leading 'struct' for C structs 1191 if nsStrippedType.startswith('struct '): 1192 nsStrippedType = nsStrippedType[7:] 1193 1194 #DumperBase.warn('STRIPPED: %s' % nsStrippedType) 1195 # The following block is only needed for D. 1196 if nsStrippedType.startswith('_A'): 1197 # DMD v2.058 encodes string[] as _Array_uns long long. 1198 # With spaces. 1199 if nsStrippedType.startswith('_Array_'): 1200 qdump_Array(self, value) 1201 return True 1202 if nsStrippedType.startswith('_AArray_'): 1203 qdump_AArray(self, value) 1204 return True 1205 1206 dumper = self.qqDumpers.get(nsStrippedType) 1207 #DumperBase.warn('DUMPER: %s' % dumper) 1208 if dumper is not None: 1209 dumper(self, value) 1210 return True 1211 1212 for pattern in self.qqDumpersEx.keys(): 1213 dumper = self.qqDumpersEx[pattern] 1214 if re.match(pattern, nsStrippedType): 1215 dumper(self, value) 1216 return True 1217 1218 return False 1219 1220 def putSimpleCharArray(self, base, size=None): 1221 if size is None: 1222 elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit) 1223 else: 1224 elided, shown = self.computeLimit(int(size), self.displayStringLimit) 1225 data = self.readMemory(base, shown) 1226 self.putValue(data, 'latin1', elided=elided) 1227 1228 def putDisplay(self, editFormat, value): 1229 self.putField('editformat', editFormat) 1230 self.putField('editvalue', value) 1231 1232 # This is shared by pointer and array formatting. 1233 def tryPutSimpleFormattedPointer(self, ptr, typeName, innerType, displayFormat, limit): 1234 if displayFormat == DisplayFormat.Automatic: 1235 targetType = innerType 1236 if innerType.code == TypeCode.Typedef: 1237 targetType = innerType.ltarget 1238 1239 if targetType.name in ('char', 'signed char', 'unsigned char', 'CHAR'): 1240 # Use UTF-8 as default for char *. 1241 self.putType(typeName) 1242 (elided, shown, data) = self.readToFirstZero(ptr, 1, limit) 1243 self.putValue(data, 'utf8', elided=elided) 1244 if self.isExpanded(): 1245 self.putArrayData(ptr, shown, innerType) 1246 return True 1247 1248 if targetType.name in ('wchar_t', 'WCHAR'): 1249 self.putType(typeName) 1250 charSize = self.lookupType('wchar_t').size() 1251 (elided, data) = self.encodeCArray(ptr, charSize, limit) 1252 if charSize == 2: 1253 self.putValue(data, 'utf16', elided=elided) 1254 else: 1255 self.putValue(data, 'ucs4', elided=elided) 1256 return True 1257 1258 if displayFormat == DisplayFormat.Latin1String: 1259 self.putType(typeName) 1260 (elided, data) = self.encodeCArray(ptr, 1, limit) 1261 self.putValue(data, 'latin1', elided=elided) 1262 return True 1263 1264 if displayFormat == DisplayFormat.SeparateLatin1String: 1265 self.putType(typeName) 1266 (elided, data) = self.encodeCArray(ptr, 1, limit) 1267 self.putValue(data, 'latin1', elided=elided) 1268 self.putDisplay('latin1:separate', data) 1269 return True 1270 1271 if displayFormat == DisplayFormat.Utf8String: 1272 self.putType(typeName) 1273 (elided, data) = self.encodeCArray(ptr, 1, limit) 1274 self.putValue(data, 'utf8', elided=elided) 1275 return True 1276 1277 if displayFormat == DisplayFormat.SeparateUtf8String: 1278 self.putType(typeName) 1279 (elided, data) = self.encodeCArray(ptr, 1, limit) 1280 self.putValue(data, 'utf8', elided=elided) 1281 self.putDisplay('utf8:separate', data) 1282 return True 1283 1284 if displayFormat == DisplayFormat.Local8BitString: 1285 self.putType(typeName) 1286 (elided, data) = self.encodeCArray(ptr, 1, limit) 1287 self.putValue(data, 'local8bit', elided=elided) 1288 return True 1289 1290 if displayFormat == DisplayFormat.Utf16String: 1291 self.putType(typeName) 1292 (elided, data) = self.encodeCArray(ptr, 2, limit) 1293 self.putValue(data, 'utf16', elided=elided) 1294 return True 1295 1296 if displayFormat == DisplayFormat.Ucs4String: 1297 self.putType(typeName) 1298 (elided, data) = self.encodeCArray(ptr, 4, limit) 1299 self.putValue(data, 'ucs4', elided=elided) 1300 return True 1301 1302 return False 1303 1304 def putFormattedPointer(self, value): 1305 #with self.timer('formattedPointer'): 1306 self.putFormattedPointerX(value) 1307 1308 def putDerefedPointer(self, value): 1309 derefValue = value.dereference() 1310 innerType = value.type.target() # .unqualified() 1311 self.putType(innerType) 1312 savedCurrentChildType = self.currentChildType 1313 self.currentChildType = innerType.name 1314 derefValue.name = '*' 1315 derefValue.autoDerefCount = value.autoDerefCount + 1 1316 1317 if derefValue.type.code != TypeCode.Pointer: 1318 self.putField('autoderefcount', '{}'.format(derefValue.autoDerefCount)) 1319 1320 self.putItem(derefValue) 1321 self.currentChildType = savedCurrentChildType 1322 1323 def putFormattedPointerX(self, value): 1324 self.putOriginalAddress(value.address()) 1325 #DumperBase.warn("PUT FORMATTED: %s" % value) 1326 pointer = value.pointer() 1327 self.putAddress(pointer) 1328 #DumperBase.warn('POINTER: 0x%x' % pointer) 1329 if pointer == 0: 1330 #DumperBase.warn('NULL POINTER') 1331 self.putType(value.type) 1332 self.putValue('0x0') 1333 return 1334 1335 typeName = value.type.name 1336 1337 try: 1338 self.readRawMemory(pointer, 1) 1339 except: 1340 # Failure to dereference a pointer should at least 1341 # show the value of a pointer. 1342 #DumperBase.warn('BAD POINTER: %s' % value) 1343 self.putValue('0x%x' % pointer) 1344 self.putType(typeName) 1345 return 1346 1347 if self.currentIName.endswith('.this'): 1348 self.putDerefedPointer(value) 1349 return 1350 1351 displayFormat = self.currentItemFormat(value.type.name) 1352 innerType = value.type.target() # .unqualified() 1353 1354 if innerType.name == 'void': 1355 #DumperBase.warn('VOID POINTER: %s' % displayFormat) 1356 self.putType(typeName) 1357 self.putSymbolValue(pointer) 1358 return 1359 1360 if displayFormat == DisplayFormat.Raw: 1361 # Explicitly requested bald pointer. 1362 #DumperBase.warn('RAW') 1363 self.putType(typeName) 1364 self.putValue('0x%x' % pointer) 1365 self.putExpandable() 1366 if self.currentIName in self.expandedINames: 1367 with Children(self): 1368 with SubItem(self, '*'): 1369 self.putItem(value.dereference()) 1370 return 1371 1372 limit = self.displayStringLimit 1373 if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String): 1374 limit = 1000000 1375 if self.tryPutSimpleFormattedPointer(pointer, typeName, 1376 innerType, displayFormat, limit): 1377 self.putExpandable() 1378 return 1379 1380 if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array1000: 1381 n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10] 1382 self.putType(typeName) 1383 self.putItemCount(n) 1384 self.putArrayData(value.pointer(), n, innerType) 1385 return 1386 1387 if innerType.code == TypeCode.Function: 1388 # A function pointer. 1389 self.putSymbolValue(pointer) 1390 self.putType(typeName) 1391 return 1392 1393 #DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers) 1394 #DumperBase.warn('INAME: %s' % self.currentIName) 1395 #DumperBase.warn('INNER: %s' % innerType.name) 1396 if self.autoDerefPointers: 1397 # Generic pointer type with AutomaticFormat, but never dereference char types: 1398 if innerType.name not in ( 1399 'char', 1400 'signed char', 1401 'unsigned char', 1402 'wchar_t', 1403 'CHAR', 1404 'WCHAR' 1405 ): 1406 self.putDerefedPointer(value) 1407 return 1408 1409 #DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type) 1410 #DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress) 1411 self.putType(typeName) 1412 self.putSymbolValue(pointer) 1413 self.putExpandable() 1414 if self.currentIName in self.expandedINames: 1415 with Children(self): 1416 with SubItem(self, '*'): 1417 self.putItem(value.dereference()) 1418 1419 def putOriginalAddress(self, address): 1420 if address is not None: 1421 self.put('origaddr="0x%x",' % address) 1422 1423 def putQObjectNameValue(self, value): 1424 try: 1425 # dd = value['d_ptr']['d'] is just behind the vtable. 1426 (vtable, dd) = self.split('pp', value) 1427 if not self.couldBeQObjectVTable(vtable): 1428 return False 1429 1430 intSize = 4 1431 ptrSize = self.ptrSize() 1432 if self.qtVersion() >= 0x060000: 1433 # Size of QObjectData: 9 pointer + 2 int 1434 # - vtable 1435 # - QObject *q_ptr; 1436 # - QObject *parent; 1437 # - QObjectList children; 1438 # - uint isWidget : 1; etc... 1439 # - int postedEvents; 1440 # - QDynamicMetaObjectData *metaObject; 1441 # - QBindingStorage bindingStorage; 1442 extra = self.extractPointer(dd + 9 * ptrSize + 2 * intSize) 1443 if extra == 0: 1444 return False 1445 1446 # Offset of objectName in ExtraData: 12 pointer 1447 # - QList<QByteArray> propertyNames; 1448 # - QList<QVariant> propertyValues; 1449 # - QVector<int> runningTimers; 1450 # - QList<QPointer<QObject> > eventFilters; 1451 # - QString objectName 1452 objectNameAddress = extra + 12 * ptrSize 1453 elif self.qtVersion() >= 0x050000: 1454 # Size of QObjectData: 5 pointer + 2 int 1455 # - vtable 1456 # - QObject *q_ptr; 1457 # - QObject *parent; 1458 # - QObjectList children; 1459 # - uint isWidget : 1; etc... 1460 # - int postedEvents; 1461 # - QDynamicMetaObjectData *metaObject; 1462 extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize) 1463 if extra == 0: 1464 return False 1465 1466 # Offset of objectName in ExtraData: 6 pointer 1467 # - QVector<QObjectUserData *> userData; only #ifndef QT_NO_USERDATA 1468 # - QList<QByteArray> propertyNames; 1469 # - QList<QVariant> propertyValues; 1470 # - QVector<int> runningTimers; 1471 # - QList<QPointer<QObject> > eventFilters; 1472 # - QString objectName 1473 objectNameAddress = extra + 5 * ptrSize 1474 else: 1475 # Size of QObjectData: 5 pointer + 2 int 1476 # - vtable 1477 # - QObject *q_ptr; 1478 # - QObject *parent; 1479 # - QObjectList children; 1480 # - uint isWidget : 1; etc.. 1481 # - int postedEvents; 1482 # - QMetaObject *metaObject; 1483 1484 # Offset of objectName in QObjectPrivate: 5 pointer + 2 int 1485 # - [QObjectData base] 1486 # - QString objectName 1487 objectNameAddress = dd + 5 * ptrSize + 2 * intSize 1488 1489 1490 data, size, alloc = self.qArrayData(objectNameAddress) 1491 1492 # Object names are short, and GDB can crash on to big chunks. 1493 # Since this here is a convenience feature only, limit it. 1494 if size <= 0 or size > 80: 1495 return False 1496 1497 raw = self.readMemory(data, 2 * size) 1498 self.putValue(raw, 'utf16', 1) 1499 return True 1500 1501 except: 1502 # warn('NO QOBJECT: %s' % value.type) 1503 return False 1504 1505 def couldBePointer(self, p): 1506 if self.ptrSize() == 4: 1507 return p > 100000 and (p & 0x3 == 0) 1508 else: 1509 return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff) 1510 1511 def couldBeVTableEntry(self, p): 1512 if self.ptrSize() == 4: 1513 return p > 100000 and (p & 0x1 == 0) 1514 else: 1515 return p > 100000 and (p & 0x1 == 0) and (p < 0x7fffffffffff) 1516 1517 def couldBeQObjectPointer(self, objectPtr): 1518 try: 1519 vtablePtr, dd = self.split('pp', objectPtr) 1520 except: 1521 self.bump('nostruct-1') 1522 return False 1523 1524 try: 1525 dvtablePtr, qptr, parentPtr = self.split('ppp', dd) 1526 except: 1527 self.bump('nostruct-2') 1528 return False 1529 # Check d_ptr.d.q_ptr == objectPtr 1530 if qptr != objectPtr: 1531 self.bump('q_ptr') 1532 return False 1533 1534 return self.couldBeQObjectVTable(vtablePtr) 1535 1536 def couldBeQObjectVTable(self, vtablePtr): 1537 def getJumpAddress_x86(dumper, address): 1538 relativeJumpCode = 0xe9 1539 jumpCode = 0xff 1540 try: 1541 data = dumper.readRawMemory(address, 6) 1542 except: 1543 return 0 1544 primaryOpcode = data[0] 1545 if primaryOpcode == relativeJumpCode: 1546 # relative jump on 32 and 64 bit with a 32bit offset 1547 offset = int.from_bytes(data[1:5], byteorder='little') 1548 return address + 5 + offset 1549 if primaryOpcode == jumpCode: 1550 if data[1] != 0x25: # check for known extended opcode 1551 return 0 1552 # 0xff25 is a relative jump on 64bit and an absolute jump on 32 bit 1553 if self.ptrSize() == 8: 1554 offset = int.from_bytes(data[2:6], byteorder='little') 1555 return address + 6 + offset 1556 else: 1557 return int.from_bytes(data[2:6], byteorder='little') 1558 return 0 1559 1560 # Do not try to extract a function pointer if there are no values to compare with 1561 if self.qtCustomEventFunc == 0 and self.qtCustomEventPltFunc == 0: 1562 return False 1563 1564 try: 1565 customEventOffset = 8 if self.isMsvcTarget() else 9 1566 customEventFunc = self.extractPointer(vtablePtr + customEventOffset * self.ptrSize()) 1567 except: 1568 self.bump('nostruct-3') 1569 return False 1570 1571 if self.isWindowsTarget(): 1572 if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc): 1573 return True 1574 # The vtable may point to a function that is just calling the customEvent function 1575 customEventFunc = getJumpAddress_x86(self, customEventFunc) 1576 if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc): 1577 return True 1578 customEventFunc = self.extractPointer(customEventFunc) 1579 if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc): 1580 return True 1581 # If the object is defined in another module there may be another level of indirection 1582 customEventFunc = getJumpAddress_x86(self, customEventFunc) 1583 1584 return customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc) 1585 1586# def extractQObjectProperty(objectPtr): 1587# vtablePtr = self.extractPointer(objectPtr) 1588# metaObjectFunc = self.extractPointer(vtablePtr) 1589# cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr) 1590# try: 1591# #DumperBase.warn('MO CMD: %s' % cmd) 1592# res = self.parseAndEvaluate(cmd) 1593# #DumperBase.warn('MO RES: %s' % res) 1594# self.bump('successfulMetaObjectCall') 1595# return res.pointer() 1596# except: 1597# self.bump('failedMetaObjectCall') 1598# #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd) 1599# return 0 1600 1601 def extractMetaObjectPtr(self, objectPtr, typeobj): 1602 """ objectPtr - address of *potential* instance of QObject derived class 1603 typeobj - type of *objectPtr if known, None otherwise. """ 1604 1605 if objectPtr is not None: 1606 self.checkIntType(objectPtr) 1607 1608 def extractMetaObjectPtrFromAddress(): 1609 #return 0 1610 # FIXME: Calling 'works' but seems to impact memory contents(!) 1611 # in relevant places. One symptom is that object name 1612 # contents 'vanishes' as the reported size of the string 1613 # gets zeroed out(?). 1614 # Try vtable, metaObject() is the first entry. 1615 vtablePtr = self.extractPointer(objectPtr) 1616 metaObjectFunc = self.extractPointer(vtablePtr) 1617 cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr) 1618 try: 1619 #DumperBase.warn('MO CMD: %s' % cmd) 1620 res = self.parseAndEvaluate(cmd) 1621 #DumperBase.warn('MO RES: %s' % res) 1622 self.bump('successfulMetaObjectCall') 1623 return res.pointer() 1624 except: 1625 self.bump('failedMetaObjectCall') 1626 #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd) 1627 return 0 1628 1629 def extractStaticMetaObjectFromTypeHelper(someTypeObj): 1630 if someTypeObj.isSimpleType(): 1631 return 0 1632 1633 typeName = someTypeObj.name 1634 isQObjectProper = typeName == self.qtNamespace() + 'QObject' 1635 1636 # No templates for now. 1637 if typeName.find('<') >= 0: 1638 return 0 1639 1640 result = self.findStaticMetaObject(someTypeObj) 1641 1642 # We need to distinguish Q_OBJECT from Q_GADGET: 1643 # a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself), 1644 # a Q_GADGET SMO has a null superdata (hopefully) 1645 if result and not isQObjectProper: 1646 if self.qtVersion() >= 0x60000 and self.isWindowsTarget(): 1647 (direct, indirect) = self.split('pp', result) 1648 # since Qt 6 there is an additional indirect super data getter on windows 1649 if direct == 0 and indirect == 0: 1650 # This looks like a Q_GADGET 1651 return 0 1652 else: 1653 if self.extractPointer(result) == 0: 1654 # This looks like a Q_GADGET 1655 return 0 1656 1657 return result 1658 1659 def extractStaticMetaObjectPtrFromType(someTypeObj): 1660 if someTypeObj is None: 1661 return 0 1662 someTypeName = someTypeObj.name 1663 self.bump('metaObjectFromType') 1664 known = self.knownStaticMetaObjects.get(someTypeName, None) 1665 if known is not None: # Is 0 or the static metaobject. 1666 return known 1667 1668 result = 0 1669 #try: 1670 result = extractStaticMetaObjectFromTypeHelper(someTypeObj) 1671 #except RuntimeError as error: 1672 # warn('METAOBJECT EXTRACTION FAILED: %s' % error) 1673 #except: 1674 # warn('METAOBJECT EXTRACTION FAILED FOR UNKNOWN REASON') 1675 1676 #if not result: 1677 # base = someTypeObj.firstBase() 1678 # if base is not None and base != someTypeObj: # sanity check 1679 # result = extractStaticMetaObjectPtrFromType(base) 1680 1681 if result: 1682 self.knownStaticMetaObjects[someTypeName] = result 1683 return result 1684 1685 if not self.useFancy: 1686 return 0 1687 1688 ptrSize = self.ptrSize() 1689 1690 typeName = typeobj.name 1691 result = self.knownStaticMetaObjects.get(typeName, None) 1692 if result is not None: # Is 0 or the static metaobject. 1693 self.bump('typecached') 1694 #DumperBase.warn('CACHED RESULT: %s %s 0x%x' % (self.currentIName, typeName, result)) 1695 return result 1696 1697 if not self.couldBeQObjectPointer(objectPtr): 1698 self.bump('cannotBeQObject') 1699 #DumperBase.warn('DOES NOT LOOK LIKE A QOBJECT: %s' % self.currentIName) 1700 return 0 1701 1702 metaObjectPtr = 0 1703 if not metaObjectPtr: 1704 # measured: 3 ms (example had one level of inheritance) 1705 #with self.timer('metaObjectType-' + self.currentIName): 1706 metaObjectPtr = extractStaticMetaObjectPtrFromType(typeobj) 1707 1708 if not metaObjectPtr and not self.isWindowsTarget(): 1709 # measured: 200 ms (example had one level of inheritance) 1710 #with self.timer('metaObjectCall-' + self.currentIName): 1711 metaObjectPtr = extractMetaObjectPtrFromAddress() 1712 1713 #if metaObjectPtr: 1714 # self.bump('foundMetaObject') 1715 # self.knownStaticMetaObjects[typeName] = metaObjectPtr 1716 1717 return metaObjectPtr 1718 1719 def split(self, pattern, value): 1720 if isinstance(value, self.Value): 1721 return value.split(pattern) 1722 if self.isInt(value): 1723 val = self.Value(self) 1724 val.laddress = value 1725 return val.split(pattern) 1726 raise RuntimeError('CANNOT EXTRACT STRUCT FROM %s' % type(value)) 1727 1728 def extractCString(self, addr): 1729 result = bytearray() 1730 while True: 1731 d = self.extractByte(addr) 1732 if d == 0: 1733 break 1734 result.append(d) 1735 addr += 1 1736 return result 1737 1738 def listData(self, value, check=True): 1739 if self.qtVersion() >= 0x60000: 1740 dd, data, size = self.split('ppi', value) 1741 return data, size 1742 1743 base = self.extractPointer(value) 1744 (ref, alloc, begin, end) = self.split('IIII', base) 1745 array = base + 16 1746 if self.qtVersion() < 0x50000: 1747 array += self.ptrSize() 1748 size = end - begin 1749 1750 if check: 1751 self.check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000) 1752 size = end - begin 1753 self.check(size >= 0) 1754 1755 stepSize = self.ptrSize() 1756 data = array + begin * stepSize 1757 return data, size 1758 1759 def putTypedPointer(self, name, addr, typeName): 1760 """ Prints a typed pointer, expandable if the type can be resolved, 1761 and without children otherwise """ 1762 with SubItem(self, name): 1763 self.putAddress(addr) 1764 self.putValue('@0x%x' % addr) 1765 typeObj = self.lookupType(typeName) 1766 if typeObj: 1767 self.putType(typeObj) 1768 self.putExpandable() 1769 if self.isExpanded(): 1770 with Children(self): 1771 self.putFields(self.createValue(addr, typeObj)) 1772 else: 1773 self.putType(typeName) 1774 1775 # This is called is when a QObject derived class is expanded 1776 def tryPutQObjectGuts(self, value): 1777 metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) 1778 if metaObjectPtr: 1779 self.putQObjectGutsHelper(value, value.address(), 1780 -1, metaObjectPtr, 'QObject') 1781 1782 def metaString(self, metaObjectPtr, index, revision): 1783 ptrSize = self.ptrSize() 1784 stringdataOffset = ptrSize 1785 if self.isWindowsTarget() and self.qtVersion() >= 0x060000: 1786 stringdataOffset += ptrSize # indirect super data member 1787 stringdata = self.extractPointer(toInteger(metaObjectPtr) + stringdataOffset) 1788 1789 def unpackString(base, size): 1790 try: 1791 s = struct.unpack_from('%ds' % size, self.readRawMemory(base, size))[0] 1792 return s if sys.version_info[0] == 2 else s.decode('utf8') 1793 except: 1794 return '<not available>' 1795 1796 if revision >= 9: # Qt 6. 1797 pos, size = self.split('II', stringdata + 8 * index) 1798 return unpackString(stringdata + pos, size) 1799 1800 if revision >= 7: # Qt 5. 1801 byteArrayDataSize = 24 if ptrSize == 8 else 16 1802 literal = stringdata + toInteger(index) * byteArrayDataSize 1803 base, size, _ = self.qArrayDataHelper(literal) 1804 return unpackString(base, size) 1805 1806 ldata = stringdata + index 1807 return self.extractCString(ldata).decode('utf8') 1808 1809 def putSortGroup(self, sortorder): 1810 if not self.isCli: 1811 self.putField('sortgroup', sortorder) 1812 1813 def putQMetaStuff(self, value, origType): 1814 if self.qtVersion() >= 0x060000: 1815 metaObjectPtr, handle = value.split('pp') 1816 else: 1817 metaObjectPtr, handle = value.split('pI') 1818 if metaObjectPtr != 0: 1819 if self.qtVersion() >= 0x060000: 1820 if handle == 0: 1821 self.putEmptyValue() 1822 return 1823 revision = 9 1824 name, alias, flags, keyCount, data = self.split('IIIII', handle) 1825 index = name 1826 elif self.qtVersion() >= 0x050000: 1827 revision = 7 1828 dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize()) 1829 index = self.extractInt(dataPtr + 4 * handle) 1830 else: 1831 revision = 6 1832 dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize()) 1833 index = self.extractInt(dataPtr + 4 * handle) 1834 #self.putValue("index: %s rev: %s" % (index, revision)) 1835 name = self.metaString(metaObjectPtr, index, revision) 1836 self.putValue(name) 1837 self.putExpandable() 1838 if self.isExpanded(): 1839 with Children(self): 1840 self.putFields(value) 1841 self.putQObjectGutsHelper(0, 0, handle, metaObjectPtr, origType) 1842 else: 1843 self.putEmptyValue() 1844 if self.isExpanded(): 1845 with Children(self): 1846 self.putFields(value) 1847 1848 # basically all meta things go through this here. 1849 # qobject and qobjectPtr are non-null if coming from a real structure display 1850 # qobject == 0, qobjectPtr != 0 is possible for builds without QObject debug info 1851 # if qobject == 0, properties and d-ptr cannot be shown. 1852 # handle is what's store in QMetaMethod etc, pass -1 for QObject/QMetaObject 1853 # itself metaObjectPtr needs to point to a valid QMetaObject. 1854 def putQObjectGutsHelper(self, qobject, qobjectPtr, handle, metaObjectPtr, origType): 1855 ptrSize = self.ptrSize() 1856 1857 def putt(name, value, typeName=' '): 1858 with SubItem(self, name): 1859 self.putValue(value) 1860 self.putType(typeName) 1861 1862 def extractSuperDataPtr(someMetaObjectPtr): 1863 #return someMetaObjectPtr['d']['superdata'] 1864 return self.extractPointer(someMetaObjectPtr) 1865 1866 def extractDataPtr(someMetaObjectPtr): 1867 # dataPtr = metaObjectPtr['d']['data'] 1868 if self.qtVersion() >= 0x60000 and self.isWindowsTarget(): 1869 offset = 3 1870 else: 1871 offset = 2 1872 return self.extractPointer(someMetaObjectPtr + offset * ptrSize) 1873 1874 isQMetaObject = origType == 'QMetaObject' 1875 isQObject = origType == 'QObject' 1876 1877 #DumperBase.warn('OBJECT GUTS: %s 0x%x ' % (self.currentIName, metaObjectPtr)) 1878 dataPtr = extractDataPtr(metaObjectPtr) 1879 #DumperBase.warn('DATA PTRS: %s 0x%x ' % (self.currentIName, dataPtr)) 1880 (revision, classname, 1881 classinfo, classinfo2, 1882 methodCount, methods, 1883 propertyCount, properties, 1884 enumCount, enums, 1885 constructorCount, constructors, 1886 flags, signalCount) = self.split('I' * 14, dataPtr) 1887 1888 largestStringIndex = -1 1889 for i in range(methodCount): 1890 t = self.split('IIIII', dataPtr + 56 + i * 20) 1891 if largestStringIndex < t[0]: 1892 largestStringIndex = t[0] 1893 1894 ns = self.qtNamespace() 1895 extraData = 0 1896 if qobjectPtr: 1897 dd = self.extractPointer(qobjectPtr + ptrSize) 1898 if self.qtVersion() >= 0x60000: 1899 (dvtablePtr, qptr, parent, children, bindingStorageData, bindingStatus, 1900 flags, postedEvents, dynMetaObjectPtr, # Up to here QObjectData. 1901 extraData, threadDataPtr, connectionListsPtr, 1902 sendersPtr, currentSenderPtr) \ 1903 = self.split('pp{@QObject*}{@QList<@QObject *>}ppIIp' + 'ppppp', dd) 1904 elif self.qtVersion() >= 0x50000: 1905 (dvtablePtr, qptr, parent, children, flags, postedEvents, 1906 dynMetaObjectPtr, # Up to here QObjectData. 1907 extraData, threadDataPtr, connectionListsPtr, 1908 sendersPtr, currentSenderPtr) \ 1909 = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'ppppp', dd) 1910 else: 1911 (dvtablePtr, qptr, parent, children, flags, postedEvents, 1912 dynMetaObjectPtr, # Up to here QObjectData 1913 objectName, extraData, threadDataPtr, connectionListsPtr, 1914 sendersPtr, currentSenderPtr) \ 1915 = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'pppppp', dd) 1916 1917 with SubItem(self, '[parent]'): 1918 if not self.isCli: 1919 self.putSortGroup(9) 1920 self.putItem(parent) 1921 1922 with SubItem(self, '[children]'): 1923 if not self.isCli: 1924 self.putSortGroup(8) 1925 1926 dvtablePtr, qptr, parentPtr, children = self.split('ppp{QList<QObject *>}', dd) 1927 self.putItem(children) 1928 1929 if isQMetaObject: 1930 with SubItem(self, '[strings]'): 1931 if not self.isCli: 1932 self.putSortGroup(2) 1933 if largestStringIndex > 0: 1934 self.putSpecialValue('minimumitemcount', largestStringIndex) 1935 self.putExpandable() 1936 if self.isExpanded(): 1937 with Children(self, largestStringIndex + 1): 1938 for i in self.childRange(): 1939 with SubItem(self, i): 1940 s = self.metaString(metaObjectPtr, i, revision) 1941 self.putValue(self.hexencode(s), 'latin1') 1942 else: 1943 self.putValue(' ') 1944 1945 if isQMetaObject: 1946 with SubItem(self, '[raw]'): 1947 self.putSortGroup(1) 1948 self.putEmptyValue() 1949 self.putExpandable() 1950 if self.isExpanded(): 1951 with Children(self): 1952 putt('revision', revision) 1953 putt('classname', classname) 1954 putt('classinfo', classinfo) 1955 putt('methods', '%d %d' % (methodCount, methods)) 1956 putt('properties', '%d %d' % (propertyCount, properties)) 1957 putt('enums/sets', '%d %d' % (enumCount, enums)) 1958 putt('constructors', '%d %d' % (constructorCount, constructors)) 1959 putt('flags', flags) 1960 putt('signalCount', signalCount) 1961 for i in range(methodCount): 1962 t = self.split('IIIII', dataPtr + 56 + i * 20) 1963 putt('method %d' % i, '%s %s %s %s %s' % t) 1964 1965 if isQObject: 1966 with SubItem(self, '[extra]'): 1967 self.putSortGroup(1) 1968 self.putEmptyValue() 1969 self.putExpandable() 1970 if self.isExpanded(): 1971 with Children(self): 1972 if extraData: 1973 self.putTypedPointer('[extraData]', extraData, 1974 ns + 'QObjectPrivate::ExtraData') 1975 1976 with SubItem(self, '[metaObject]'): 1977 self.putAddress(metaObjectPtr) 1978 self.putExpandable() 1979 if self.isExpanded(): 1980 with Children(self): 1981 self.putQObjectGutsHelper( 1982 0, 0, -1, metaObjectPtr, 'QMetaObject') 1983 1984 if False: 1985 with SubItem(self, '[connections]'): 1986 if connectionListsPtr: 1987 typeName = '@QObjectConnectionListVector' 1988 self.putItem(self.createValue(connectionListsPtr, typeName)) 1989 else: 1990 self.putItemCount(0) 1991 1992 if False: 1993 with SubItem(self, '[signals]'): 1994 self.putItemCount(signalCount) 1995 if self.isExpanded(): 1996 with Children(self): 1997 j = -1 1998 for i in range(signalCount): 1999 t = self.split('IIIII', dataPtr + 56 + 20 * i) 2000 flags = t[4] 2001 if flags != 0x06: 2002 continue 2003 j += 1 2004 with SubItem(self, j): 2005 name = self.metaString( 2006 metaObjectPtr, t[0], revision) 2007 self.putType(' ') 2008 self.putValue(name) 2009 self.putExpandable() 2010 with Children(self): 2011 putt('[nameindex]', t[0]) 2012 #putt('[type]', 'signal') 2013 putt('[argc]', t[1]) 2014 putt('[parameter]', t[2]) 2015 putt('[tag]', t[3]) 2016 putt('[flags]', t[4]) 2017 putt('[localindex]', str(i)) 2018 putt('[globalindex]', str(globalOffset + i)) 2019 #self.putQObjectConnections(dd) 2020 2021 if isQMetaObject or isQObject: 2022 with SubItem(self, '[properties]'): 2023 self.putSortGroup(5) 2024 if self.isExpanded(): 2025 dynamicPropertyCount = 0 2026 with Children(self): 2027 # Static properties. 2028 for i in range(propertyCount): 2029 if self.qtVersion() >= 0x60000: 2030 t = self.split('IIIII', dataPtr + properties * 4 + 20 * i) 2031 else: 2032 t = self.split('III', dataPtr + properties * 4 + 12 * i) 2033 name = self.metaString(metaObjectPtr, t[0], revision) 2034 if qobject and self.qtPropertyFunc: 2035 # LLDB doesn't like calling it on a derived class, possibly 2036 # due to type information living in a different shared object. 2037 #base = self.createValue(qobjectPtr, '@QObject') 2038 #DumperBase.warn("CALL FUNC: 0x%x" % self.qtPropertyFunc) 2039 cmd = '((QVariant(*)(void*,char*))0x%x)((void*)0x%x,"%s")' \ 2040 % (self.qtPropertyFunc, qobjectPtr, name) 2041 try: 2042 #DumperBase.warn('PROP CMD: %s' % cmd) 2043 res = self.parseAndEvaluate(cmd) 2044 #DumperBase.warn('PROP RES: %s' % res) 2045 except: 2046 self.bump('failedMetaObjectCall') 2047 putt(name, ' ') 2048 continue 2049 #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd) 2050 #self.putCallItem(name, '@QVariant', base, 'property', '"' + name + '"') 2051 if res is None: 2052 self.bump('failedMetaObjectCall2') 2053 putt(name, ' ') 2054 continue 2055 self.putSubItem(name, res) 2056 else: 2057 putt(name, ' ') 2058 2059 # Dynamic properties. 2060 if extraData: 2061 def list6Generator(addr, innerType): 2062 data, size = self.listData(addr) 2063 for i in range(size): 2064 yield self.createValue(data + i * innerType.size(), innerType) 2065 2066 def list5Generator(addr, innerType): 2067 data, size = self.listData(addr) 2068 for i in range(size): 2069 yield self.createValue(data + i * ptrSize, innerType) 2070 2071 def vectorGenerator(addr, innerType): 2072 data, size = self.vectorData(addr) 2073 for i in range(size): 2074 yield self.createValue(data + i * innerType.size(), innerType) 2075 2076 byteArrayType = self.createType('@QByteArray') 2077 variantType = self.createType('@QVariant') 2078 if self.qtVersion() >= 0x60000: 2079 values = vectorGenerator(extraData + 3 * ptrSize, variantType) 2080 elif self.qtVersion() >= 0x50600: 2081 values = vectorGenerator(extraData + 2 * ptrSize, variantType) 2082 elif self.qtVersion() >= 0x50000: 2083 values = list5Generator(extraData + 2 * ptrSize, variantType) 2084 else: 2085 values = list5Generator(extraData + 2 * ptrSize, 2086 variantType.pointer()) 2087 2088 if self.qtVersion() >= 0x60000: 2089 names = list6Generator(extraData, byteArrayType) 2090 else: 2091 names = list5Generator(extraData + ptrSize, byteArrayType) 2092 2093 for (k, v) in zip(names, values): 2094 with SubItem(self, propertyCount + dynamicPropertyCount): 2095 if not self.isCli: 2096 self.putField('key', self.encodeByteArray(k)) 2097 self.putField('keyencoded', 'latin1') 2098 self.putItem(v) 2099 dynamicPropertyCount += 1 2100 self.putItemCount(propertyCount + dynamicPropertyCount) 2101 else: 2102 # We need a handle to [x] for the user to expand the item 2103 # before we know whether there are actual children. Counting 2104 # them is too expensive. 2105 self.putSpecialValue('minimumitemcount', propertyCount) 2106 self.putExpandable() 2107 2108 superDataPtr = extractSuperDataPtr(metaObjectPtr) 2109 2110 globalOffset = 0 2111 superDataIterator = superDataPtr 2112 while superDataIterator: 2113 sdata = extractDataPtr(superDataIterator) 2114 globalOffset += self.extractInt(sdata + 16) # methodCount member 2115 superDataIterator = extractSuperDataPtr(superDataIterator) 2116 2117 if isQMetaObject or isQObject: 2118 with SubItem(self, '[methods]'): 2119 self.putSortGroup(3) 2120 self.putItemCount(methodCount) 2121 if self.isExpanded(): 2122 with Children(self): 2123 for i in range(methodCount): 2124 t = self.split('IIIII', dataPtr + 56 + 20 * i) 2125 name = self.metaString(metaObjectPtr, t[0], revision) 2126 with SubItem(self, i): 2127 self.putValue(name) 2128 self.putType(' ') 2129 self.putNumChild(1) 2130 isSignal = False 2131 flags = t[4] 2132 if flags == 0x06: 2133 typ = 'signal' 2134 isSignal = True 2135 elif flags == 0x0a: 2136 typ = 'slot' 2137 elif flags == 0x0a: 2138 typ = 'invokable' 2139 else: 2140 typ = '<unknown>' 2141 with Children(self): 2142 putt('[nameindex]', t[0]) 2143 putt('[type]', typ) 2144 putt('[argc]', t[1]) 2145 putt('[parameter]', t[2]) 2146 putt('[tag]', t[3]) 2147 putt('[flags]', t[4]) 2148 putt('[localindex]', str(i)) 2149 putt('[globalindex]', str(globalOffset + i)) 2150 2151 if isQObject: 2152 with SubItem(self, '[d]'): 2153 self.putItem(self.createValue(dd, '@QObjectPrivate')) 2154 self.putSortGroup(15) 2155 2156 if isQMetaObject: 2157 with SubItem(self, '[superdata]'): 2158 self.putSortGroup(12) 2159 if superDataPtr: 2160 self.putType('@QMetaObject') 2161 self.putAddress(superDataPtr) 2162 self.putExpandable() 2163 if self.isExpanded(): 2164 with Children(self): 2165 self.putQObjectGutsHelper(0, 0, -1, superDataPtr, 'QMetaObject') 2166 else: 2167 self.putType('@QMetaObject *') 2168 self.putValue('0x0') 2169 2170 if handle >= 0: 2171 localIndex = int((handle - methods) / 5) 2172 with SubItem(self, '[localindex]'): 2173 self.putSortGroup(12) 2174 self.putValue(localIndex) 2175 with SubItem(self, '[globalindex]'): 2176 self.putSortGroup(11) 2177 self.putValue(globalOffset + localIndex) 2178 2179 def putQObjectConnections(self, dd): 2180 with SubItem(self, '[connections]'): 2181 ptrSize = self.ptrSize() 2182 self.putNoType() 2183 privateType = self.createType('@QObjectPrivate') 2184 d_ptr = dd.cast(privateType.pointer()).dereference() 2185 connections = d_ptr['connectionLists'] 2186 if self.connections.integer() == 0: 2187 self.putItemCount(0) 2188 else: 2189 connections = connections.dereference() 2190 #connections = connections.cast(connections.type.firstBase()) 2191 self.putSpecialValue('minimumitemcount', 0) 2192 self.putExpandable() 2193 if self.isExpanded(): 2194 pp = 0 2195 with Children(self): 2196 innerType = connections.type[0] 2197 # Should check: innerType == ns::QObjectPrivate::ConnectionList 2198 data, size = self.vectorData(connections) 2199 connectionType = self.createType('@QObjectPrivate::Connection') 2200 for i in range(size): 2201 first = self.extractPointer(data + i * 2 * ptrSize) 2202 while first: 2203 self.putSubItem('%s' % pp, 2204 self.createPointerValue(first, connectionType)) 2205 first = self.extractPointer(first + 3 * ptrSize) 2206 # We need to enforce some upper limit. 2207 pp += 1 2208 if pp > 1000: 2209 break 2210 2211 def currentItemFormat(self, typeName=None): 2212 displayFormat = self.formats.get(self.currentIName, DisplayFormat.Automatic) 2213 if displayFormat == DisplayFormat.Automatic: 2214 if typeName is None: 2215 typeName = self.currentType.value 2216 needle = None if typeName is None else self.stripForFormat(typeName) 2217 displayFormat = self.typeformats.get(needle, DisplayFormat.Automatic) 2218 return displayFormat 2219 2220 def putSubItem(self, component, value): # -> ReportItem 2221 if not isinstance(value, self.Value): 2222 raise RuntimeError('WRONG VALUE TYPE IN putSubItem: %s' % type(value)) 2223 if not isinstance(value.type, self.Type): 2224 raise RuntimeError('WRONG TYPE TYPE IN putSubItem: %s' % type(value.type)) 2225 res = None 2226 with SubItem(self, component): 2227 self.putItem(value) 2228 res = self.currentValue 2229 return res # The 'short' display. 2230 2231 def putArrayData(self, base, n, innerType, childNumChild=None, maxNumChild=10000): 2232 self.checkIntType(base) 2233 self.checkIntType(n) 2234 addrBase = base 2235 innerSize = innerType.size() 2236 self.putNumChild(n) 2237 #DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType)) 2238 enc = innerType.simpleEncoding() 2239 if enc: 2240 self.put('childtype="%s",' % innerType.name) 2241 self.put('addrbase="0x%x",' % addrBase) 2242 self.put('addrstep="0x%x",' % innerSize) 2243 self.put('arrayencoding="%s",' % enc) 2244 if n > maxNumChild: 2245 self.put('childrenelided="%s",' % n) # FIXME: Act on that in frontend 2246 n = maxNumChild 2247 self.put('arraydata="') 2248 self.put(self.readMemory(addrBase, n * innerSize)) 2249 self.put('",') 2250 else: 2251 with Children(self, n, innerType, childNumChild, maxNumChild, 2252 addrBase=addrBase, addrStep=innerSize): 2253 for i in self.childRange(): 2254 self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType)) 2255 2256 def putArrayItem(self, name, addr, n, typeName): 2257 self.checkIntType(addr) 2258 self.checkIntType(n) 2259 with SubItem(self, name): 2260 self.putEmptyValue() 2261 self.putType('%s [%d]' % (typeName, n)) 2262 self.putArrayData(addr, n, self.lookupType(typeName)) 2263 self.putAddress(addr) 2264 2265 def putPlotDataHelper(self, base, n, innerType, maxNumChild=1000 * 1000): 2266 if n > maxNumChild: 2267 self.putField('plotelided', n) # FIXME: Act on that in frontend 2268 n = maxNumChild 2269 if self.currentItemFormat() == DisplayFormat.ArrayPlot and innerType.isSimpleType(): 2270 enc = innerType.simpleEncoding() 2271 if enc: 2272 self.putField('editencoding', enc) 2273 self.putDisplay('plotdata:separate', 2274 self.readMemory(base, n * innerType.size())) 2275 2276 def putPlotData(self, base, n, innerType, maxNumChild=1000 * 1000): 2277 self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild) 2278 if self.isExpanded(): 2279 self.putArrayData(base, n, innerType, maxNumChild=maxNumChild) 2280 2281 def putSpecialArgv(self, value): 2282 """ 2283 Special handling for char** argv. 2284 """ 2285 n = 0 2286 p = value 2287 # p is 0 for "optimized out" cases. Or contains rubbish. 2288 try: 2289 if value.integer(): 2290 while p.dereference().integer() and n <= 100: 2291 p += 1 2292 n += 1 2293 except: 2294 pass 2295 2296 with TopLevelItem(self, 'local.argv'): 2297 self.put('iname="local.argv",name="argv",') 2298 self.putItemCount(n, 100) 2299 self.putType('char **') 2300 if self.currentIName in self.expandedINames: 2301 p = value 2302 with Children(self, n): 2303 for i in range(n): 2304 self.putSubItem(i, p.dereference()) 2305 p += 1 2306 2307 def extractPointer(self, value): 2308 try: 2309 if value.type.code == TypeCode.Array: 2310 return value.address() 2311 except: 2312 pass 2313 code = 'I' if self.ptrSize() == 4 else 'Q' 2314 return self.extractSomething(value, code, 8 * self.ptrSize()) 2315 2316 def extractInt64(self, value): 2317 return self.extractSomething(value, 'q', 64) 2318 2319 def extractUInt64(self, value): 2320 return self.extractSomething(value, 'Q', 64) 2321 2322 def extractInt(self, value): 2323 return self.extractSomething(value, 'i', 32) 2324 2325 def extractUInt(self, value): 2326 return self.extractSomething(value, 'I', 32) 2327 2328 def extractShort(self, value): 2329 return self.extractSomething(value, 'h', 16) 2330 2331 def extractUShort(self, value): 2332 return self.extractSomething(value, 'H', 16) 2333 2334 def extractByte(self, value): 2335 return self.extractSomething(value, 'b', 8) 2336 2337 def extractSomething(self, value, pattern, bitsize): 2338 if self.isInt(value): 2339 val = self.Value(self) 2340 val.laddress = value 2341 return val.extractSomething(pattern, bitsize) 2342 if isinstance(value, self.Value): 2343 return value.extractSomething(pattern, bitsize) 2344 raise RuntimeError('CANT EXTRACT FROM %s' % type(value)) 2345 2346 # Parses a..b and a.(s).b 2347 def parseRange(self, exp): 2348 2349 # Search for the first unbalanced delimiter in s 2350 def searchUnbalanced(s, upwards): 2351 paran = 0 2352 bracket = 0 2353 if upwards: 2354 open_p, close_p, open_b, close_b = '(', ')', '[', ']' 2355 else: 2356 open_p, close_p, open_b, close_b = ')', '(', ']', '[' 2357 for i in range(len(s)): 2358 c = s[i] 2359 if c == open_p: 2360 paran += 1 2361 elif c == open_b: 2362 bracket += 1 2363 elif c == close_p: 2364 paran -= 1 2365 if paran < 0: 2366 return i 2367 elif c == close_b: 2368 bracket -= 1 2369 if bracket < 0: 2370 return i 2371 return len(s) 2372 2373 match = re.search(r'(\.)(\(.+?\))?(\.)', exp) 2374 if match: 2375 s = match.group(2) 2376 left_e = match.start(1) 2377 left_s = 1 + left_e - searchUnbalanced(exp[left_e::-1], False) 2378 right_s = match.end(3) 2379 right_e = right_s + searchUnbalanced(exp[right_s:], True) 2380 template = exp[:left_s] + '%s' + exp[right_e:] 2381 2382 a = exp[left_s:left_e] 2383 b = exp[right_s:right_e] 2384 2385 try: 2386 # Allow integral expressions. 2387 ss = self.parseAndEvaluate(s[1:len(s) - 1]).integer() if s else 1 2388 aa = self.parseAndEvaluate(a).integer() 2389 bb = self.parseAndEvaluate(b).integer() 2390 if aa < bb and ss > 0: 2391 return True, aa, ss, bb + 1, template 2392 except: 2393 pass 2394 return False, 0, 1, 1, exp 2395 2396 def putNumChild(self, numchild): 2397 if numchild != self.currentChildNumChild: 2398 self.putField('numchild', numchild) 2399 2400 def handleLocals(self, variables): 2401 #DumperBase.warn('VARIABLES: %s' % variables) 2402 #with self.timer('locals'): 2403 shadowed = {} 2404 for value in variables: 2405 if value.name == 'argv': 2406 if value.type.code == TypeCode.Pointer: 2407 if value.type.ltarget.code == TypeCode.Pointer: 2408 if value.type.ltarget.ltarget.name == 'char': 2409 self.putSpecialArgv(value) 2410 continue 2411 2412 name = value.name 2413 if name in shadowed: 2414 level = shadowed[name] 2415 shadowed[name] = level + 1 2416 name += '@%d' % level 2417 else: 2418 shadowed[name] = 1 2419 # A 'normal' local variable or parameter. 2420 iname = value.iname if hasattr(value, 'iname') else 'local.' + name 2421 with TopLevelItem(self, iname): 2422 #with self.timer('all-' + iname): 2423 self.putField('iname', iname) 2424 self.putField('name', name) 2425 self.putItem(value) 2426 2427 def handleWatches(self, args): 2428 #with self.timer('watches'): 2429 for watcher in args.get('watchers', []): 2430 iname = watcher['iname'] 2431 exp = self.hexdecode(watcher['exp']) 2432 self.handleWatch(exp, exp, iname) 2433 2434 def handleWatch(self, origexp, exp, iname): 2435 exp = str(exp).strip() 2436 escapedExp = self.hexencode(exp) 2437 #DumperBase.warn('HANDLING WATCH %s -> %s, INAME: "%s"' % (origexp, exp, iname)) 2438 2439 # Grouped items separated by semicolon. 2440 if exp.find(';') >= 0: 2441 exps = exp.split(';') 2442 n = len(exps) 2443 with TopLevelItem(self, iname): 2444 self.putField('iname', iname) 2445 #self.putField('wname', escapedExp) 2446 self.putField('name', exp) 2447 self.putField('exp', exp) 2448 self.putItemCount(n) 2449 self.putNoType() 2450 for i in range(n): 2451 self.handleWatch(exps[i], exps[i], '%s.%d' % (iname, i)) 2452 return 2453 2454 # Special array index: e.g a[1..199] or a[1.(3).199] for stride 3. 2455 isRange, begin, step, end, template = self.parseRange(exp) 2456 if isRange: 2457 #DumperBase.warn('RANGE: %s %s %s in %s' % (begin, step, end, template)) 2458 r = range(begin, end, step) 2459 n = len(r) 2460 with TopLevelItem(self, iname): 2461 self.putField('iname', iname) 2462 #self.putField('wname', escapedExp) 2463 self.putField('name', exp) 2464 self.putField('exp', exp) 2465 self.putItemCount(n) 2466 self.putNoType() 2467 with Children(self, n): 2468 for i in r: 2469 e = template % i 2470 self.handleWatch(e, e, '%s.%s' % (iname, i)) 2471 return 2472 2473 # Fall back to less special syntax 2474 #return self.handleWatch(origexp, exp, iname) 2475 2476 with TopLevelItem(self, iname): 2477 self.putField('iname', iname) 2478 self.putField('wname', escapedExp) 2479 try: 2480 value = self.parseAndEvaluate(exp) 2481 self.putItem(value) 2482 except Exception: 2483 self.currentType.value = ' ' 2484 self.currentValue.value = '<no such value>' 2485 self.currentChildNumChild = -1 2486 self.currentNumChild = 0 2487 self.putNumChild(0) 2488 2489 def registerDumper(self, funcname, function): 2490 try: 2491 if funcname.startswith('qdump__'): 2492 typename = funcname[7:] 2493 spec = inspect.getargspec(function) 2494 if len(spec.args) == 2: 2495 self.qqDumpers[typename] = function 2496 elif len(spec.args) == 3 and len(spec.defaults) == 1: 2497 self.qqDumpersEx[spec.defaults[0]] = function 2498 self.qqFormats[typename] = self.qqFormats.get(typename, []) 2499 elif funcname.startswith('qform__'): 2500 typename = funcname[7:] 2501 try: 2502 self.qqFormats[typename] = function() 2503 except: 2504 self.qqFormats[typename] = [] 2505 elif funcname.startswith('qedit__'): 2506 typename = funcname[7:] 2507 try: 2508 self.qqEditable[typename] = function 2509 except: 2510 pass 2511 except: 2512 pass 2513 2514 def setupDumpers(self, _={}): 2515 self.resetCaches() 2516 2517 for mod in self.dumpermodules: 2518 m = __import__(mod) 2519 dic = m.__dict__ 2520 for name in dic.keys(): 2521 item = dic[name] 2522 self.registerDumper(name, item) 2523 2524 msg = 'dumpers=[' 2525 for key, value in self.qqFormats.items(): 2526 editable = ',editable="true"' if key in self.qqEditable else '' 2527 formats = (',formats=\"%s\"' % str(value)[1:-1]) if len(value) else '' 2528 msg += '{type="%s"%s%s},' % (key, editable, formats) 2529 msg += '],' 2530 v = 10000 * sys.version_info[0] + 100 * sys.version_info[1] + sys.version_info[2] 2531 msg += 'python="%d"' % v 2532 return msg 2533 2534 def reloadDumpers(self, args): 2535 for mod in self.dumpermodules: 2536 m = sys.modules[mod] 2537 if sys.version_info[0] >= 3: 2538 import importlib 2539 importlib.reload(m) 2540 else: 2541 reload(m) 2542 self.setupDumpers(args) 2543 2544 def loadDumpers(self, args): 2545 msg = self.setupDumpers() 2546 self.reportResult(msg, args) 2547 2548 def addDumperModule(self, args): 2549 path = args['path'] 2550 (head, tail) = os.path.split(path) 2551 sys.path.insert(1, head) 2552 self.dumpermodules.append(os.path.splitext(tail)[0]) 2553 2554 def extractQStringFromQDataStream(self, buf, offset): 2555 """ Read a QString from the stream """ 2556 size = struct.unpack_from('!I', buf, offset)[0] 2557 offset += 4 2558 string = buf[offset:offset + size].decode('utf-16be') 2559 return (string, offset + size) 2560 2561 def extractQByteArrayFromQDataStream(self, buf, offset): 2562 """ Read a QByteArray from the stream """ 2563 size = struct.unpack_from('!I', buf, offset)[0] 2564 offset += 4 2565 string = buf[offset:offset + size].decode('latin1') 2566 return (string, offset + size) 2567 2568 def extractIntFromQDataStream(self, buf, offset): 2569 """ Read an int from the stream """ 2570 value = struct.unpack_from('!I', buf, offset)[0] 2571 return (value, offset + 4) 2572 2573 def handleInterpreterMessage(self): 2574 """ Return True if inferior stopped """ 2575 resdict = self.fetchInterpreterResult() 2576 return resdict.get('event') == 'break' 2577 2578 def reportInterpreterResult(self, resdict, args): 2579 print('interpreterresult=%s,token="%s"' 2580 % (self.resultToMi(resdict), args.get('token', -1))) 2581 2582 def reportInterpreterAsync(self, resdict, asyncclass): 2583 print('interpreterasync=%s,asyncclass="%s"' 2584 % (self.resultToMi(resdict), asyncclass)) 2585 2586 def removeInterpreterBreakpoint(self, args): 2587 res = self.sendInterpreterRequest('removebreakpoint', {'id': args['id']}) 2588 return res 2589 2590 def insertInterpreterBreakpoint(self, args): 2591 args['condition'] = self.hexdecode(args.get('condition', '')) 2592 # Will fail if the service is not yet up and running. 2593 response = self.sendInterpreterRequest('setbreakpoint', args) 2594 resdict = args.copy() 2595 bp = None if response is None else response.get('breakpoint', None) 2596 if bp: 2597 resdict['number'] = bp 2598 resdict['pending'] = 0 2599 else: 2600 self.createResolvePendingBreakpointsHookBreakpoint(args) 2601 resdict['number'] = -1 2602 resdict['pending'] = 1 2603 resdict['warning'] = 'Direct interpreter breakpoint insertion failed.' 2604 self.reportInterpreterResult(resdict, args) 2605 2606 def resolvePendingInterpreterBreakpoint(self, args): 2607 self.parseAndEvaluate('qt_qmlDebugEnableService("NativeQmlDebugger")') 2608 response = self.sendInterpreterRequest('setbreakpoint', args) 2609 bp = None if response is None else response.get('breakpoint', None) 2610 resdict = args.copy() 2611 if bp: 2612 resdict['number'] = bp 2613 resdict['pending'] = 0 2614 else: 2615 resdict['number'] = -1 2616 resdict['pending'] = 0 2617 resdict['error'] = 'Pending interpreter breakpoint insertion failed.' 2618 self.reportInterpreterAsync(resdict, 'breakpointmodified') 2619 2620 def fetchInterpreterResult(self): 2621 buf = self.parseAndEvaluate('qt_qmlDebugMessageBuffer') 2622 size = self.parseAndEvaluate('qt_qmlDebugMessageLength') 2623 msg = self.hexdecode(self.readMemory(buf, size)) 2624 # msg is a sequence of 'servicename<space>msglen<space>msg' items. 2625 resdict = {} # Native payload. 2626 while len(msg): 2627 pos0 = msg.index(' ') # End of service name 2628 pos1 = msg.index(' ', pos0 + 1) # End of message length 2629 service = msg[0:pos0] 2630 msglen = int(msg[pos0 + 1:pos1]) 2631 msgend = pos1 + 1 + msglen 2632 payload = msg[pos1 + 1:msgend] 2633 msg = msg[msgend:] 2634 if service == 'NativeQmlDebugger': 2635 try: 2636 resdict = json.loads(payload) 2637 continue 2638 except: 2639 self.warn('Cannot parse native payload: %s' % payload) 2640 else: 2641 print('interpreteralien=%s' 2642 % {'service': service, 'payload': self.hexencode(payload)}) 2643 try: 2644 expr = 'qt_qmlDebugClearBuffer()' 2645 res = self.parseAndEvaluate(expr) 2646 except RuntimeError as error: 2647 self.warn('Cleaning buffer failed: %s: %s' % (expr, error)) 2648 2649 return resdict 2650 2651 def sendInterpreterRequest(self, command, args={}): 2652 encoded = json.dumps({'command': command, 'arguments': args}) 2653 hexdata = self.hexencode(encoded) 2654 expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata 2655 try: 2656 res = self.parseAndEvaluate(expr) 2657 except RuntimeError as error: 2658 self.warn('Interpreter command failed: %s: %s' % (encoded, error)) 2659 return {} 2660 except AttributeError as error: 2661 # Happens with LLDB and 'None' current thread. 2662 self.warn('Interpreter command failed: %s: %s' % (encoded, error)) 2663 return {} 2664 if not res: 2665 self.warn('Interpreter command failed: %s ' % encoded) 2666 return {} 2667 return self.fetchInterpreterResult() 2668 2669 def executeStep(self, args): 2670 if self.nativeMixed: 2671 response = self.sendInterpreterRequest('stepin', args) 2672 self.doContinue() 2673 2674 def executeStepOut(self, args): 2675 if self.nativeMixed: 2676 response = self.sendInterpreterRequest('stepout', args) 2677 self.doContinue() 2678 2679 def executeNext(self, args): 2680 if self.nativeMixed: 2681 response = self.sendInterpreterRequest('stepover', args) 2682 self.doContinue() 2683 2684 def executeContinue(self, args): 2685 if self.nativeMixed: 2686 response = self.sendInterpreterRequest('continue', args) 2687 self.doContinue() 2688 2689 def doInsertInterpreterBreakpoint(self, args, wasPending): 2690 #DumperBase.warn('DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s' % wasPending) 2691 # Will fail if the service is not yet up and running. 2692 response = self.sendInterpreterRequest('setbreakpoint', args) 2693 bp = None if response is None else response.get('breakpoint', None) 2694 if wasPending: 2695 if not bp: 2696 self.reportInterpreterResult({'bpnr': -1, 'pending': 1, 2697 'error': 'Pending interpreter breakpoint insertion failed.'}, args) 2698 return 2699 else: 2700 if not bp: 2701 self.reportInterpreterResult({'bpnr': -1, 'pending': 1, 2702 'warning': 'Direct interpreter breakpoint insertion failed.'}, args) 2703 self.createResolvePendingBreakpointsHookBreakpoint(args) 2704 return 2705 self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args) 2706 2707 def isInternalInterpreterFrame(self, functionName): 2708 if functionName is None: 2709 return False 2710 if functionName.startswith('qt_v4'): 2711 return True 2712 return functionName.startswith(self.qtNamespace() + 'QV4::') 2713 2714 # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit 2715 # due to misaligned %ebx in SSE calls (qstring.cpp:findChar) 2716 def canCallLocale(self): 2717 return True 2718 2719 def isReportableInterpreterFrame(self, functionName): 2720 return functionName and functionName.find('QV4::Moth::VME::exec') >= 0 2721 2722 def extractInterpreterStack(self): 2723 return self.sendInterpreterRequest('backtrace', {'limit': 10}) 2724 2725 def isInt(self, thing): 2726 if isinstance(thing, int): 2727 return True 2728 if sys.version_info[0] == 2: 2729 if isinstance(thing, long): 2730 return True 2731 return False 2732 2733 def putItems(self, count, generator, maxNumChild=10000): 2734 self.putItemCount(count) 2735 if self.isExpanded(): 2736 with Children(self, count, maxNumChild=maxNumChild): 2737 for i, val in zip(self.childRange(), generator): 2738 self.putSubItem(i, val) 2739 2740 def putItem(self, value): 2741 #with self.timer('putItem'): 2742 self.putItemX(value) 2743 2744 def putItemX(self, value): 2745 #DumperBase.warn('PUT ITEM: %s' % value.stringify()) 2746 2747 typeobj = value.type # unqualified() 2748 typeName = typeobj.name 2749 2750 self.addToCache(typeobj) # Fill type cache 2751 2752 if not value.lIsInScope: 2753 self.putSpecialValue('optimizedout') 2754 #self.putType(typeobj) 2755 #self.putSpecialValue('outofscope') 2756 self.putNumChild(0) 2757 return 2758 2759 if not isinstance(value, self.Value): 2760 raise RuntimeError('WRONG TYPE IN putItem: %s' % type(self.Value)) 2761 2762 # Try on possibly typedefed type first. 2763 if self.tryPutPrettyItem(typeName, value): 2764 if typeobj.code == TypeCode.Pointer: 2765 self.putOriginalAddress(value.address()) 2766 else: 2767 self.putAddress(value.address()) 2768 return 2769 2770 if typeobj.code == TypeCode.Typedef: 2771 #DumperBase.warn('TYPEDEF VALUE: %s' % value.stringify()) 2772 self.putItem(value.detypedef()) 2773 self.putBetterType(typeName) 2774 return 2775 2776 if typeobj.code == TypeCode.Pointer: 2777 self.putFormattedPointer(value) 2778 if value.summary and self.useFancy: 2779 self.putValue(self.hexencode(value.summary), 'utf8:1:0') 2780 return 2781 2782 self.putAddress(value.address()) 2783 2784 if typeobj.code == TypeCode.Function: 2785 #DumperBase.warn('FUNCTION VALUE: %s' % value) 2786 self.putType(typeobj) 2787 self.putSymbolValue(value.pointer()) 2788 self.putNumChild(0) 2789 return 2790 2791 if typeobj.code == TypeCode.Enum: 2792 #DumperBase.warn('ENUM VALUE: %s' % value.stringify()) 2793 self.putType(typeobj.name) 2794 self.putValue(value.display()) 2795 self.putNumChild(0) 2796 return 2797 2798 if typeobj.code == TypeCode.Array: 2799 #DumperBase.warn('ARRAY VALUE: %s' % value) 2800 self.putCStyleArray(value) 2801 return 2802 2803 if typeobj.code == TypeCode.Bitfield: 2804 #DumperBase.warn('BITFIELD VALUE: %s %d %s' % (value.name, value.lvalue, typeName)) 2805 self.putNumChild(0) 2806 dd = typeobj.ltarget.typeData().enumDisplay 2807 self.putValue(str(value.lvalue) if dd is None else dd( 2808 value.lvalue, value.laddress, '%d')) 2809 self.putType(typeName) 2810 return 2811 2812 if typeobj.code == TypeCode.Integral: 2813 #DumperBase.warn('INTEGER: %s %s' % (value.name, value)) 2814 val = value.value() 2815 self.putNumChild(0) 2816 self.putValue(val) 2817 self.putType(typeName) 2818 return 2819 2820 if typeobj.code == TypeCode.Float: 2821 #DumperBase.warn('FLOAT VALUE: %s' % value) 2822 self.putValue(value.value()) 2823 self.putNumChild(0) 2824 self.putType(typeobj.name) 2825 return 2826 2827 if typeobj.code in (TypeCode.Reference, TypeCode.RValueReference): 2828 #DumperBase.warn('REFERENCE VALUE: %s' % value) 2829 val = value.dereference() 2830 if val.laddress != 0: 2831 self.putItem(val) 2832 else: 2833 self.putSpecialValue('nullreference') 2834 self.putBetterType(typeName) 2835 return 2836 2837 if typeobj.code == TypeCode.Complex: 2838 self.putType(typeobj) 2839 self.putValue(value.display()) 2840 self.putNumChild(0) 2841 return 2842 2843 if typeobj.code == TypeCode.FortranString: 2844 self.putValue(self.hexencode(value.data()), 'latin1') 2845 self.putNumChild(0) 2846 self.putType(typeobj) 2847 2848 if typeName.endswith('[]'): 2849 # D arrays, gdc compiled. 2850 n = value['length'] 2851 base = value['ptr'] 2852 self.putType(typeName) 2853 self.putItemCount(n) 2854 if self.isExpanded(): 2855 self.putArrayData(base.pointer(), n, base.type.target()) 2856 return 2857 2858 #DumperBase.warn('SOME VALUE: %s' % value) 2859 #DumperBase.warn('GENERIC STRUCT: %s' % typeobj) 2860 #DumperBase.warn('INAME: %s ' % self.currentIName) 2861 #DumperBase.warn('INAMES: %s ' % self.expandedINames) 2862 #DumperBase.warn('EXPANDED: %s ' % (self.currentIName in self.expandedINames)) 2863 self.putType(typeName) 2864 2865 if value.summary is not None and self.useFancy: 2866 self.putValue(self.hexencode(value.summary), 'utf8:1:0') 2867 self.putNumChild(0) 2868 return 2869 2870 self.putExpandable() 2871 self.putEmptyValue() 2872 #DumperBase.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address())) 2873 if self.showQObjectNames: 2874 #with self.timer(self.currentIName): 2875 self.putQObjectNameValue(value) 2876 if self.isExpanded(): 2877 if not self.isCli: 2878 self.putField('sortable', 1) 2879 with Children(self, 1, childType=None): 2880 self.putFields(value) 2881 if self.showQObjectNames: 2882 self.tryPutQObjectGuts(value) 2883 2884 def symbolAddress(self, symbolName): 2885 res = self.parseAndEvaluate('(size_t)&' + symbolName) 2886 return None if res is None else res.pointer() 2887 2888 def qtHookDataSymbolName(self): 2889 return 'qtHookData' 2890 2891 def qtTypeInfoVersion(self): 2892 addr = self.symbolAddress(self.qtHookDataSymbolName()) 2893 if addr: 2894 # Only available with Qt 5.3+ 2895 (hookVersion, x, x, x, x, x, tiVersion) = self.split('ppppppp', addr) 2896 #DumperBase.warn('HOOK: %s TI: %s' % (hookVersion, tiVersion)) 2897 if hookVersion >= 3: 2898 self.qtTypeInfoVersion = lambda: tiVersion 2899 return tiVersion 2900 return None 2901 2902 def qtDeclarativeHookDataSymbolName(self): 2903 return 'qtDeclarativeHookData' 2904 2905 def qtDeclarativeTypeInfoVersion(self): 2906 addr = self.symbolAddress(self.qtDeclarativeHookDataSymbolName()) 2907 if addr: 2908 # Only available with Qt 5.6+ 2909 (hookVersion, x, tiVersion) = self.split('ppp', addr) 2910 if hookVersion >= 1: 2911 self.qtTypeInfoVersion = lambda: tiVersion 2912 return tiVersion 2913 return None 2914 2915 def addToCache(self, typeobj): 2916 typename = typeobj.name 2917 if typename in self.typesReported: 2918 return 2919 self.typesReported[typename] = True 2920 self.typesToReport[typename] = typeobj 2921 2922 class Value(): 2923 def __init__(self, dumper): 2924 self.dumper = dumper 2925 self.name = None 2926 self._type = None 2927 self.ldata = None # Target address in case of references and pointers. 2928 self.laddress = None # Own address. 2929 self.lvalue = None 2930 self.lIsInScope = True 2931 self.ldisplay = None 2932 self.summary = None # Always hexencoded UTF-8. 2933 self.lbitpos = None 2934 self.lbitsize = None 2935 self.targetValue = None # For references. 2936 self.isBaseClass = None 2937 self.nativeValue = None 2938 self.autoDerefCount = 0 2939 2940 def copy(self): 2941 val = self.dumper.Value(self.dumper) 2942 val.dumper = self.dumper 2943 val.name = self.name 2944 val._type = self._type 2945 val.ldata = self.ldata 2946 val.laddress = self.laddress 2947 val.lIsInScope = self.lIsInScope 2948 val.ldisplay = self.ldisplay 2949 val.summary = self.summary 2950 val.lbitpos = self.lbitpos 2951 val.lbitsize = self.lbitsize 2952 val.targetValue = self.targetValue 2953 val.nativeValue = self.nativeValue 2954 return val 2955 2956 @property 2957 def type(self): 2958 if self._type is None and self.nativeValue is not None: 2959 self._type = self.dumper.nativeValueType(self.nativeValue) 2960 return self._type 2961 2962 def check(self): 2963 if self.laddress is not None and not self.dumper.isInt(self.laddress): 2964 raise RuntimeError('INCONSISTENT ADDRESS: %s' % type(self.laddress)) 2965 if self.type is not None and not isinstance(self.type, self.dumper.Type): 2966 raise RuntimeError('INCONSISTENT TYPE: %s' % type(self.type)) 2967 2968 def __str__(self): 2969 #raise RuntimeError('Not implemented') 2970 return self.stringify() 2971 2972 def __int__(self): 2973 return self.integer() 2974 2975 def stringify(self): 2976 addr = 'None' if self.laddress is None else ('0x%x' % self.laddress) 2977 return "Value(name='%s',type=%s,bsize=%s,bpos=%s,data=%s,address=%s)" \ 2978 % (self.name, self.type.name, self.lbitsize, self.lbitpos, 2979 self.dumper.hexencode(self.ldata), addr) 2980 2981 def displayEnum(self, form='%d', bitsize=None): 2982 intval = self.integer(bitsize) 2983 dd = self.type.typeData().enumDisplay 2984 if dd is None: 2985 return str(intval) 2986 return dd(intval, self.laddress, form) 2987 2988 def display(self): 2989 if self.ldisplay is not None: 2990 return self.ldisplay 2991 simple = self.value() 2992 if simple is not None: 2993 return str(simple) 2994 #if self.ldata is not None: 2995 # if sys.version_info[0] == 2 and isinstance(self.ldata, buffer): 2996 # return bytes(self.ldata).encode('hex') 2997 # return self.ldata.encode('hex') 2998 if self.laddress is not None: 2999 return 'value of type %s at address 0x%x' % (self.type.name, self.laddress) 3000 return '<unknown data>' 3001 3002 def pointer(self): 3003 if self.type.code == TypeCode.Typedef: 3004 return self.detypedef().pointer() 3005 return self.extractInteger(self.dumper.ptrSize() * 8, True) 3006 3007 def integer(self, bitsize=None): 3008 if self.type.code == TypeCode.Typedef: 3009 return self.detypedef().integer() 3010 elif isinstance(self.lvalue, int): 3011 return self.lvalue 3012 # Could be something like 'short unsigned int' 3013 unsigned = self.type.name == 'unsigned' \ 3014 or self.type.name.startswith('unsigned ') \ 3015 or self.type.name.find(' unsigned ') != -1 3016 if bitsize is None: 3017 bitsize = self.type.bitsize() 3018 return self.extractInteger(bitsize, unsigned) 3019 3020 def floatingPoint(self): 3021 if self.nativeValue is not None and not self.dumper.isCdb: 3022 return str(self.nativeValue) 3023 if self.type.code == TypeCode.Typedef: 3024 return self.detypedef().floatingPoint() 3025 if self.type.size() == 8: 3026 return self.extractSomething('d', 64) 3027 if self.type.size() == 4: 3028 return self.extractSomething('f', 32) 3029 # Fall back in case we don't have a nativeValue at hand. 3030 # FIXME: This assumes Intel's 80bit extended floats. Which might 3031 # be wrong. 3032 l, h = self.split('QQ') 3033 if True: # 80 bit floats 3034 sign = (h >> 15) & 1 3035 exp = (h & 0x7fff) 3036 fraction = l 3037 bit63 = (l >> 63) & 1 3038 #DumperBase.warn("SIGN: %s EXP: %s H: 0x%x L: 0x%x" % (sign, exp, h, l)) 3039 if exp == 0: 3040 if bit63 == 0: 3041 if l == 0: 3042 res = '-0' if sign else '0' 3043 else: 3044 res = (-1)**sign * l * 2**(-16382) # subnormal 3045 else: 3046 res = 'pseudodenormal' 3047 elif exp == 0x7fff: 3048 res = 'special' 3049 else: 3050 res = (-1)**sign * l * 2**(exp - 16383 - 63) 3051 else: # 128 bits 3052 sign = h >> 63 3053 exp = (h >> 48) & 0x7fff 3054 fraction = h & (2**48 - 1) 3055 #DumperBase.warn("SIGN: %s EXP: %s FRAC: %s H: 0x%x L: 0x%x" % (sign, exp, fraction, h, l)) 3056 if exp == 0: 3057 if fraction == 0: 3058 res = -0.0 if sign else 0.0 3059 else: 3060 res = (-1)**sign * fraction / 2**48 * 2**(-62) # subnormal 3061 elif exp == 0x7fff: 3062 res = ('-inf' if sign else 'inf') if fraction == 0 else 'nan' 3063 else: 3064 res = (-1)**sign * (1 + fraction / 2**48) * 2**(exp - 63) 3065 return res 3066 3067 def value(self): 3068 if self.type is not None: 3069 if self.type.code == TypeCode.Enum: 3070 return self.displayEnum() 3071 if self.type.code == TypeCode.Typedef: 3072 return self.detypedef().value() 3073 if self.type.code == TypeCode.Integral: 3074 return self.integer() 3075 if self.type.code == TypeCode.Bitfield: 3076 return self.integer() 3077 if self.type.code == TypeCode.Float: 3078 return self.floatingPoint() 3079 if self.type.code == TypeCode.Pointer: 3080 return self.pointer() 3081 return None 3082 3083 def extractPointer(self): 3084 return self.split('p')[0] 3085 3086 def hasMember(self, name): 3087 return self.findMemberByName(name) is not None 3088 3089 def findMemberByName(self, name): 3090 self.check() 3091 if self.type.code == TypeCode.Typedef: 3092 return self.findMemberByName(self.detypedef()) 3093 if self.type.code in ( 3094 TypeCode.Pointer, 3095 TypeCode.Reference, 3096 TypeCode.RValueReference): 3097 res = self.dereference().findMemberByName(name) 3098 if res is not None: 3099 return res 3100 if self.type.code == TypeCode.Struct: 3101 #DumperBase.warn('SEARCHING FOR MEMBER: %s IN %s' % (name, self.type.name)) 3102 members = self.members(True) 3103 #DumperBase.warn('MEMBERS: %s' % members) 3104 for member in members: 3105 #DumperBase.warn('CHECKING FIELD %s' % member.name) 3106 if member.type.code == TypeCode.Typedef: 3107 member = member.detypedef() 3108 if member.name == name: 3109 return member 3110 for member in members: 3111 if member.type.code == TypeCode.Typedef: 3112 member = member.detypedef() 3113 if member.name == name: # Could be base class. 3114 return member 3115 if member.type.code == TypeCode.Struct: 3116 res = member.findMemberByName(name) 3117 if res is not None: 3118 return res 3119 return None 3120 3121 def __getitem__(self, index): 3122 #DumperBase.warn('GET ITEM %s %s' % (self, index)) 3123 self.check() 3124 if self.type.code == TypeCode.Typedef: 3125 #DumperBase.warn('GET ITEM STRIP TYPEDEFS TO %s' % self.type.ltarget) 3126 return self.cast(self.type.ltarget).__getitem__(index) 3127 if isinstance(index, str): 3128 if self.type.code == TypeCode.Pointer: 3129 #DumperBase.warn('GET ITEM %s DEREFERENCE TO %s' % (self, self.dereference())) 3130 return self.dereference().__getitem__(index) 3131 res = self.findMemberByName(index) 3132 if res is None: 3133 raise RuntimeError('No member named %s in type %s' 3134 % (index, self.type.name)) 3135 return res 3136 elif isinstance(index, self.dumper.Field): 3137 field = index 3138 elif self.dumper.isInt(index): 3139 if self.type.code == TypeCode.Array: 3140 addr = self.laddress + int(index) * self.type.ltarget.size() 3141 return self.dumper.createValue(addr, self.type.ltarget) 3142 if self.type.code == TypeCode.Pointer: 3143 addr = self.pointer() + int(index) * self.type.ltarget.size() 3144 return self.dumper.createValue(addr, self.type.ltarget) 3145 return self.members(False)[index] 3146 else: 3147 raise RuntimeError('BAD INDEX TYPE %s' % type(index)) 3148 field.check() 3149 3150 #DumperBase.warn('EXTRACT FIELD: %s, BASE 0x%x' % (field, self.address())) 3151 if self.type.code == TypeCode.Pointer: 3152 #DumperBase.warn('IS TYPEDEFED POINTER!') 3153 res = self.dereference() 3154 #DumperBase.warn('WAS POINTER: %s' % res) 3155 3156 return field.extract(self) 3157 3158 def extractField(self, field): 3159 if not isinstance(field, self.dumper.Field): 3160 raise RuntimeError('BAD INDEX TYPE %s' % type(field)) 3161 3162 if field.extractor is not None: 3163 val = field.extractor(self) 3164 if val is not None: 3165 #DumperBase.warn('EXTRACTOR SUCCEEDED: %s ' % val) 3166 return val 3167 3168 if self.type.code == TypeCode.Typedef: 3169 return self.cast(self.type.ltarget).extractField(field) 3170 if self.type.code in (TypeCode.Reference, TypeCode.RValueReference): 3171 return self.dereference().extractField(field) 3172 #DumperBase.warn('FIELD: %s ' % field) 3173 val = self.dumper.Value(self.dumper) 3174 val.name = field.name 3175 val.isBaseClass = field.isBase 3176 val._type = field.fieldType() 3177 3178 if field.isArtificial: 3179 if self.laddress is not None: 3180 val.laddress = self.laddress 3181 if self.ldata is not None: 3182 val.ldata = self.ldata 3183 return val 3184 3185 fieldBitsize = field.bitsize 3186 fieldSize = (fieldBitsize + 7) // 8 3187 fieldBitpos = field.bitpos 3188 fieldOffset = fieldBitpos // 8 3189 fieldType = field.fieldType() 3190 3191 if fieldType.code == TypeCode.Bitfield: 3192 fieldBitpos -= fieldOffset * 8 3193 ldata = self.data() 3194 data = 0 3195 for i in range(fieldSize): 3196 data = data << 8 3197 if self.dumper.isBigEndian: 3198 lbyte = ldata[i] 3199 else: 3200 lbyte = ldata[fieldOffset + fieldSize - 1 - i] 3201 if sys.version_info[0] >= 3: 3202 data += lbyte 3203 else: 3204 data += ord(lbyte) 3205 data = data >> fieldBitpos 3206 data = data & ((1 << fieldBitsize) - 1) 3207 val.lvalue = data 3208 val.laddress = None 3209 return val 3210 3211 if self.laddress is not None: 3212 val.laddress = self.laddress + fieldOffset 3213 elif self.ldata is not None: 3214 val.ldata = self.ldata[fieldOffset:fieldOffset + fieldSize] 3215 else: 3216 self.dumper.check(False) 3217 3218 if fieldType.code in (TypeCode.Reference, TypeCode.RValueReference): 3219 if val.laddress is not None: 3220 val = self.dumper.createReferenceValue(val.laddress, fieldType.ltarget) 3221 val.name = field.name 3222 3223 #DumperBase.warn('GOT VAL %s FOR FIELD %s' % (val, field)) 3224 val.lbitsize = fieldBitsize 3225 val.check() 3226 return val 3227 3228 # This is the generic version for synthetic values. 3229 # The native backends replace it in their fromNativeValue() 3230 # implementations. 3231 def members(self, includeBases): 3232 #DumperBase.warn("LISTING MEMBERS OF %s" % self) 3233 if self.type.code == TypeCode.Typedef: 3234 return self.detypedef().members(includeBases) 3235 3236 tdata = self.type.typeData() 3237 #if isinstance(tdata.lfields, list): 3238 # return tdata.lfields 3239 3240 fields = [] 3241 if tdata.lfields is not None: 3242 if isinstance(tdata.lfields, list): 3243 fields = tdata.lfields 3244 else: 3245 fields = list(tdata.lfields(self)) 3246 3247 #DumperBase.warn("FIELDS: %s" % fields) 3248 res = [] 3249 for field in fields: 3250 if isinstance(field, self.dumper.Value): 3251 #DumperBase.warn("USING VALUE DIRECTLY %s" % field.name) 3252 res.append(field) 3253 continue 3254 if field.isBase and not includeBases: 3255 #DumperBase.warn("DROPPING BASE %s" % field.name) 3256 continue 3257 res.append(self.extractField(field)) 3258 #DumperBase.warn("GOT MEMBERS: %s" % res) 3259 return res 3260 3261 def __add__(self, other): 3262 self.check() 3263 if self.dumper.isInt(other): 3264 stripped = self.type.stripTypedefs() 3265 if stripped.code == TypeCode.Pointer: 3266 address = self.pointer() + stripped.dereference().size() * other 3267 val = self.dumper.Value(self.dumper) 3268 val.laddress = None 3269 val.ldata = bytes(struct.pack(self.dumper.packCode + 'Q', address)) 3270 val._type = self._type 3271 return val 3272 raise RuntimeError('BAD DATA TO ADD TO: %s %s' % (self.type, other)) 3273 3274 def __sub__(self, other): 3275 self.check() 3276 if self.type.name == other.type.name: 3277 stripped = self.type.stripTypedefs() 3278 if stripped.code == TypeCode.Pointer: 3279 return (self.pointer() - other.pointer()) // stripped.dereference().size() 3280 raise RuntimeError('BAD DATA TO SUB TO: %s %s' % (self.type, other)) 3281 3282 def dereference(self): 3283 self.check() 3284 if self.type.code == TypeCode.Typedef: 3285 return self.detypedef().dereference() 3286 val = self.dumper.Value(self.dumper) 3287 if self.type.code in (TypeCode.Reference, TypeCode.RValueReference): 3288 val.summary = self.summary 3289 if self.nativeValue is None: 3290 val.laddress = self.pointer() 3291 if val.laddress is None and self.laddress is not None: 3292 val.laddress = self.laddress 3293 val._type = self.type.dereference() 3294 if self.dumper.useDynamicType: 3295 val._type = self.dumper.nativeDynamicType(val.laddress, val.type) 3296 else: 3297 val = self.dumper.nativeValueDereferenceReference(self) 3298 elif self.type.code == TypeCode.Pointer: 3299 if self.nativeValue is None: 3300 val.laddress = self.pointer() 3301 val._type = self.type.dereference() 3302 if self.dumper.useDynamicType: 3303 val._type = self.dumper.nativeDynamicType(val.laddress, val.type) 3304 else: 3305 val = self.dumper.nativeValueDereferencePointer(self) 3306 else: 3307 raise RuntimeError("WRONG: %s" % self.type.code) 3308 #DumperBase.warn("DEREFERENCING FROM: %s" % self) 3309 #DumperBase.warn("DEREFERENCING TO: %s" % val) 3310 #dynTypeName = val.type.dynamicTypeName(val.laddress) 3311 #if dynTypeName is not None: 3312 # val._type = self.dumper.createType(dynTypeName) 3313 return val 3314 3315 def detypedef(self): 3316 self.check() 3317 if self.type.code != TypeCode.Typedef: 3318 raise RuntimeError("WRONG") 3319 val = self.copy() 3320 val._type = self.type.ltarget 3321 #DumperBase.warn("DETYPEDEF FROM: %s" % self) 3322 #DumperBase.warn("DETYPEDEF TO: %s" % val) 3323 return val 3324 3325 def extend(self, size): 3326 if self.type.size() < size: 3327 val = self.dumper.Value(self.dumper) 3328 val.laddress = None 3329 val.ldata = self.zeroExtend(self.ldata) 3330 return val 3331 if self.type.size() == size: 3332 return self 3333 raise RuntimeError('NOT IMPLEMENTED') 3334 3335 def zeroExtend(self, data, size): 3336 ext = '\0' * (size - len(data)) 3337 if sys.version_info[0] == 3: 3338 pad = bytes(ext, encoding='latin1') 3339 else: 3340 pad = bytes(ext) 3341 return pad + data if self.dumper.isBigEndian else data + pad 3342 3343 def cast(self, typish): 3344 self.check() 3345 val = self.dumper.Value(self.dumper) 3346 val.laddress = self.laddress 3347 val.lbitsize = self.lbitsize 3348 val.ldata = self.ldata 3349 val._type = self.dumper.createType(typish) 3350 return val 3351 3352 def address(self): 3353 self.check() 3354 return self.laddress 3355 3356 def data(self, size=None): 3357 self.check() 3358 if self.ldata is not None: 3359 if len(self.ldata) > 0: 3360 if size is None: 3361 return self.ldata 3362 if size == len(self.ldata): 3363 return self.ldata 3364 if size < len(self.ldata): 3365 return self.ldata[:size] 3366 #raise RuntimeError('ZERO-EXTENDING DATA TO %s BYTES: %s' % (size, self)) 3367 return self.zeroExtend(self.ldata, size) 3368 if self.laddress is not None: 3369 if size is None: 3370 size = self.type.size() 3371 res = self.dumper.readRawMemory(self.laddress, size) 3372 if len(res) > 0: 3373 return res 3374 raise RuntimeError('CANNOT CONVERT ADDRESS TO BYTES: %s' % self) 3375 raise RuntimeError('CANNOT CONVERT TO BYTES: %s' % self) 3376 3377 def extractInteger(self, bitsize, unsigned): 3378 #with self.dumper.timer('extractInt'): 3379 self.check() 3380 if bitsize > 32: 3381 size = 8 3382 code = 'Q' if unsigned else 'q' 3383 elif bitsize > 16: 3384 size = 4 3385 code = 'I' if unsigned else 'i' 3386 elif bitsize > 8: 3387 size = 2 3388 code = 'H' if unsigned else 'h' 3389 else: 3390 size = 1 3391 code = 'B' if unsigned else 'b' 3392 rawBytes = self.data(size) 3393 res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0] 3394 #DumperBase.warn('Extract: Code: %s Bytes: %s Bitsize: %s Size: %s' 3395 # % (self.dumper.packCode + code, self.dumper.hexencode(rawBytes), bitsize, size)) 3396 return res 3397 3398 def extractSomething(self, code, bitsize): 3399 #with self.dumper.timer('extractSomething'): 3400 self.check() 3401 size = (bitsize + 7) >> 3 3402 rawBytes = self.data(size) 3403 res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0] 3404 return res 3405 3406 def to(self, pattern): 3407 return self.split(pattern)[0] 3408 3409 def split(self, pattern): 3410 #with self.dumper.timer('split'): 3411 #DumperBase.warn('EXTRACT STRUCT FROM: %s' % self.type) 3412 (pp, size, fields) = self.dumper.describeStruct(pattern) 3413 #DumperBase.warn('SIZE: %s ' % size) 3414 result = struct.unpack_from(self.dumper.packCode + pp, self.data(size)) 3415 3416 def structFixer(field, thing): 3417 #DumperBase.warn('STRUCT MEMBER: %s' % type(thing)) 3418 if field.isStruct: 3419 #if field.type != field.fieldType(): 3420 # raise RuntimeError('DO NOT SIMPLIFY') 3421 #DumperBase.warn('FIELD POS: %s' % field.type.stringify()) 3422 #DumperBase.warn('FIELD TYE: %s' % field.fieldType().stringify()) 3423 res = self.dumper.createValue(thing, field.fieldType()) 3424 #DumperBase.warn('RES TYPE: %s' % res.type) 3425 if self.laddress is not None: 3426 res.laddress = self.laddress + field.offset() 3427 return res 3428 return thing 3429 if len(fields) != len(result): 3430 raise RuntimeError('STRUCT ERROR: %s %s' % (fields, result)) 3431 return tuple(map(structFixer, fields, result)) 3432 3433 def checkPointer(self, p, align=1): 3434 ptr = p if self.isInt(p) else p.pointer() 3435 self.readRawMemory(ptr, 1) 3436 3437 def type(self, typeId): 3438 return self.typeData.get(typeId) 3439 3440 def splitArrayType(self, type_name): 3441 # "foo[2][3][4]" -> ("foo", "[3][4]", 2) 3442 pos1 = len(type_name) 3443 # In case there are more dimensions we need the inner one. 3444 while True: 3445 pos1 = type_name.rfind('[', 0, pos1 - 1) 3446 pos2 = type_name.find(']', pos1) 3447 if type_name[pos1 - 1] != ']': 3448 break 3449 3450 item_count = type_name[pos1 + 1:pos2] 3451 return (type_name[0:pos1].strip(), type_name[pos2 + 1:].strip(), int(item_count)) 3452 3453 def registerType(self, typeId, tdata): 3454 #DumperBase.warn('REGISTER TYPE: %s' % typeId) 3455 self.typeData[typeId] = tdata 3456 #typeId = typeId.replace(' ', '') 3457 #self.typeData[typeId] = tdata 3458 #DumperBase.warn('REGISTERED: %s' % self.typeData) 3459 3460 def registerTypeAlias(self, existingTypeId, aliasId): 3461 #DumperBase.warn('REGISTER ALIAS %s FOR %s' % (aliasId, existingTypeId)) 3462 self.typeData[aliasId] = self.typeData[existingTypeId] 3463 3464 class TypeData(): 3465 def __init__(self, dumper): 3466 self.dumper = dumper 3467 self.lfields = None # None or Value -> list of member Values 3468 self.lalignment = None # Function returning alignment of this struct 3469 self.lbitsize = None 3470 self.ltarget = None # Inner type for arrays 3471 self.templateArguments = [] 3472 self.code = None 3473 self.name = None 3474 self.typeId = None 3475 self.enumDisplay = None 3476 self.moduleName = None 3477 3478 def copy(self): 3479 tdata = self.dumper.TypeData(self.dumper) 3480 tdata.dumper = self.dumper 3481 tdata.lfields = self.lfields 3482 tdata.lalignment = self.lalignment 3483 tdata.lbitsize = self.lbitsize 3484 tdata.ltarget = self.ltarget 3485 tdata.templateArguments = self.templateArguments 3486 tdata.code = self.code 3487 tdata.name = self.name 3488 tdata.typeId = self.typeId 3489 tdata.enumDisplay = self.enumDisplay 3490 tdata.moduleName = self.moduleName 3491 return tdata 3492 3493 class Type(): 3494 def __init__(self, dumper, typeId): 3495 self.typeId = typeId 3496 self.dumper = dumper 3497 3498 def __str__(self): 3499 #return self.typeId 3500 return self.stringify() 3501 3502 def typeData(self): 3503 tdata = self.dumper.typeData.get(self.typeId, None) 3504 if tdata is not None: 3505 #DumperBase.warn('USING : %s' % self.typeId) 3506 return tdata 3507 typeId = self.typeId.replace(' ', '') 3508 if tdata is not None: 3509 #DumperBase.warn('USING FALLBACK : %s' % self.typeId) 3510 return tdata 3511 #DumperBase.warn('EXPANDING LAZILY: %s' % self.typeId) 3512 self.dumper.lookupType(self.typeId) 3513 return self.dumper.typeData.get(self.typeId) 3514 3515 @property 3516 def name(self): 3517 tdata = self.dumper.typeData.get(self.typeId) 3518 if tdata is None: 3519 return self.typeId 3520 return tdata.name 3521 3522 @property 3523 def code(self): 3524 return self.typeData().code 3525 3526 @property 3527 def lbitsize(self): 3528 return self.typeData().lbitsize 3529 3530 @property 3531 def lbitpos(self): 3532 return self.typeData().lbitpos 3533 3534 @property 3535 def ltarget(self): 3536 return self.typeData().ltarget 3537 3538 @property 3539 def moduleName(self): 3540 return self.typeData().moduleName 3541 3542 def stringify(self): 3543 tdata = self.typeData() 3544 if tdata is None: 3545 return 'Type(id="%s")' % self.typeId 3546 return 'Type(name="%s",bsize=%s,code=%s)' \ 3547 % (tdata.name, tdata.lbitsize, tdata.code) 3548 3549 def __getitem__(self, index): 3550 if self.dumper.isInt(index): 3551 return self.templateArgument(index) 3552 raise RuntimeError('CANNOT INDEX TYPE') 3553 3554 def dynamicTypeName(self, address): 3555 tdata = self.typeData() 3556 if tdata is None: 3557 return None 3558 if tdata.code != TypeCode.Struct: 3559 return None 3560 try: 3561 vtbl = self.dumper.extractPointer(address) 3562 except: 3563 return None 3564 #DumperBase.warn('VTBL: 0x%x' % vtbl) 3565 if not self.dumper.couldBePointer(vtbl): 3566 return None 3567 return self.dumper.nativeDynamicTypeName(address, self) 3568 3569 def dynamicType(self, address): 3570 # FIXME: That buys some performance at the cost of a fail 3571 # of Gdb13393 dumper test. 3572 #return self 3573 #with self.dumper.timer('dynamicType %s 0x%s' % (self.name, address)): 3574 dynTypeName = self.dynamicTypeName(address) 3575 if dynTypeName is not None: 3576 return self.dumper.createType(dynTypeName) 3577 return self 3578 3579 def check(self): 3580 tdata = self.typeData() 3581 if tdata is None: 3582 raise RuntimeError('TYPE WITHOUT DATA: %s ALL: %s' % 3583 (self.typeId, self.dumper.typeData.keys())) 3584 if tdata.name is None: 3585 raise RuntimeError('TYPE WITHOUT NAME: %s' % self.typeId) 3586 3587 def dereference(self): 3588 if self.code == TypeCode.Typedef: 3589 return self.ltarget.dereference() 3590 self.check() 3591 return self.ltarget 3592 3593 def unqualified(self): 3594 return self 3595 3596 def templateArguments(self): 3597 tdata = self.typeData() 3598 if tdata is None: 3599 return self.dumper.listTemplateParameters(self.typeId) 3600 return tdata.templateArguments 3601 3602 def templateArgument(self, position): 3603 tdata = self.typeData() 3604 #DumperBase.warn('TDATA: %s' % tdata) 3605 #DumperBase.warn('ID: %s' % self.typeId) 3606 if tdata is None: 3607 # Native lookups didn't help. Happens for 'wrong' placement of 'const' 3608 # etc. with LLDB. But not all is lost: 3609 ta = self.dumper.listTemplateParameters(self.typeId) 3610 #DumperBase.warn('MANUAL: %s' % ta) 3611 res = ta[position] 3612 #DumperBase.warn('RES: %s' % res.typeId) 3613 return res 3614 #DumperBase.warn('TA: %s %s' % (position, self.typeId)) 3615 #DumperBase.warn('ARGS: %s' % tdata.templateArguments) 3616 return tdata.templateArguments[position] 3617 3618 def simpleEncoding(self): 3619 res = { 3620 'bool': 'int:1', 3621 'char': 'int:1', 3622 'signed char': 'int:1', 3623 'unsigned char': 'uint:1', 3624 'short': 'int:2', 3625 'unsigned short': 'uint:2', 3626 'int': 'int:4', 3627 'unsigned int': 'uint:4', 3628 'long long': 'int:8', 3629 'unsigned long long': 'uint:8', 3630 'float': 'float:4', 3631 'double': 'float:8', 3632 'QChar': 'uint:2' 3633 }.get(self.name, None) 3634 return res 3635 3636 def isSimpleType(self): 3637 return self.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum) 3638 3639 def alignment(self): 3640 tdata = self.typeData() 3641 if tdata.code == TypeCode.Typedef: 3642 return tdata.ltarget.alignment() 3643 if tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum): 3644 if tdata.name in ('double', 'long long', 'unsigned long long'): 3645 # Crude approximation. 3646 return 8 if self.dumper.isWindowsTarget() else self.dumper.ptrSize() 3647 return self.size() 3648 if tdata.code in (TypeCode.Pointer, TypeCode.Reference, TypeCode.RValueReference): 3649 return self.dumper.ptrSize() 3650 if tdata.lalignment is not None: 3651 #if isinstance(tdata.lalignment, function): # Does not work that way. 3652 if hasattr(tdata.lalignment, '__call__'): 3653 return tdata.lalignment() 3654 return tdata.lalignment 3655 return 1 3656 3657 def pointer(self): 3658 return self.dumper.createPointerType(self) 3659 3660 def target(self): 3661 return self.typeData().ltarget 3662 3663 def stripTypedefs(self): 3664 if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef: 3665 #DumperBase.warn('NO TYPEDEF: %s' % self) 3666 return self 3667 return self.ltarget 3668 3669 def size(self): 3670 bs = self.bitsize() 3671 if bs % 8 != 0: 3672 DumperBase.warn('ODD SIZE: %s' % self) 3673 return (7 + bs) >> 3 3674 3675 def bitsize(self): 3676 if self.lbitsize is not None: 3677 return self.lbitsize 3678 raise RuntimeError('DONT KNOW SIZE: %s' % self) 3679 3680 def isMovableType(self): 3681 if self.code in (TypeCode.Pointer, TypeCode.Integral, TypeCode.Float): 3682 return True 3683 strippedName = self.dumper.stripNamespaceFromType(self.name) 3684 if strippedName in ( 3685 'QBrush', 'QBitArray', 'QByteArray', 'QCustomTypeInfo', 3686 'QChar', 'QDate', 'QDateTime', 'QFileInfo', 'QFixed', 3687 'QFixedPoint', 'QFixedSize', 'QHashDummyValue', 'QIcon', 3688 'QImage', 'QLine', 'QLineF', 'QLatin1Char', 'QLocale', 3689 'QMatrix', 'QModelIndex', 'QPoint', 'QPointF', 'QPen', 3690 'QPersistentModelIndex', 'QResourceRoot', 'QRect', 'QRectF', 3691 'QRegExp', 'QSize', 'QSizeF', 'QString', 'QTime', 'QTextBlock', 3692 'QUrl', 'QVariant', 3693 'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration', 3694 'QXmlStreamNotationDeclaration', 'QXmlStreamEntityDeclaration' 3695 ): 3696 return True 3697 if strippedName == 'QStringList': 3698 return self.dumper.qtVersion() >= 0x050000 3699 if strippedName == 'QList': 3700 return self.dumper.qtVersion() >= 0x050600 3701 return False 3702 3703 class Field(collections.namedtuple('Field', 3704 ['dumper', 'name', 'type', 'bitsize', 'bitpos', 3705 'extractor', 'isBase', 'isStruct', 'isArtificial'])): 3706 3707 def __new__(cls, dumper, name=None, type=None, bitsize=None, bitpos=None, 3708 extractor=None, isBase=False, isStruct=False, isArtificial=False): 3709 return super(DumperBase.Field, cls).__new__( 3710 cls, dumper, name, type, bitsize, bitpos, 3711 extractor, isBase, isStruct, isArtificial) 3712 3713 __slots__ = () 3714 3715 def __str__(self): 3716 return self.stringify() 3717 3718 def stringify(self): 3719 #return 'Field(name="%s")' % self.name 3720 typename = None if self.type is None else self.type.stringify() 3721 return 'Field(name="%s",type=%s,bitpos=%s,bitsize=%s)' \ 3722 % (self.name, typename, self.bitpos, self.bitsize) 3723 3724 def check(self): 3725 pass 3726 3727 def size(self): 3728 return self.bitsize() // 8 3729 3730 def offset(self): 3731 return self.bitpos // 8 3732 3733 def fieldType(self): 3734 if self.type is not None: 3735 return self.type 3736 raise RuntimeError('CANT GET FIELD TYPE FOR %s' % self) 3737 return None 3738 3739 def ptrCode(self): 3740 return 'I' if self.ptrSize() == 4 else 'Q' 3741 3742 def toPointerData(self, address): 3743 if not self.isInt(address): 3744 raise RuntimeError('wrong') 3745 return bytes(struct.pack(self.packCode + self.ptrCode(), address)) 3746 3747 def fromPointerData(self, bytes_value): 3748 return struct.unpack(self.packCode + self.ptrCode(), bytes_value) 3749 3750 def createPointerValue(self, targetAddress, targetTypish): 3751 if not isinstance(targetTypish, self.Type) and not isinstance(targetTypish, str): 3752 raise RuntimeError('Expected type in createPointerValue(), got %s' 3753 % type(targetTypish)) 3754 if not self.isInt(targetAddress): 3755 raise RuntimeError('Expected integral address value in createPointerValue(), got %s' 3756 % type(targetTypish)) 3757 val = self.Value(self) 3758 val.ldata = self.toPointerData(targetAddress) 3759 targetType = self.createType(targetTypish) 3760 if self.useDynamicType: 3761 targetType = targetType.dynamicType(targetAddress) 3762 val._type = self.createPointerType(targetType) 3763 return val 3764 3765 def createReferenceValue(self, targetAddress, targetType): 3766 if not isinstance(targetType, self.Type): 3767 raise RuntimeError('Expected type in createReferenceValue(), got %s' 3768 % type(targetType)) 3769 if not self.isInt(targetAddress): 3770 raise RuntimeError('Expected integral address value in createReferenceValue(), got %s' 3771 % type(targetType)) 3772 val = self.Value(self) 3773 val.ldata = self.toPointerData(targetAddress) 3774 if self.useDynamicType: 3775 targetType = targetType.dynamicType(targetAddress) 3776 val._type = self.createReferenceType(targetType) 3777 return val 3778 3779 def createPointerType(self, targetType): 3780 if not isinstance(targetType, self.Type): 3781 raise RuntimeError('Expected type in createPointerType(), got %s' 3782 % type(targetType)) 3783 typeId = targetType.typeId + ' *' 3784 tdata = self.TypeData(self) 3785 tdata.name = targetType.name + '*' 3786 tdata.typeId = typeId 3787 tdata.lbitsize = 8 * self.ptrSize() 3788 tdata.code = TypeCode.Pointer 3789 tdata.ltarget = targetType 3790 self.registerType(typeId, tdata) 3791 return self.Type(self, typeId) 3792 3793 def createReferenceType(self, targetType): 3794 if not isinstance(targetType, self.Type): 3795 raise RuntimeError('Expected type in createReferenceType(), got %s' 3796 % type(targetType)) 3797 typeId = targetType.typeId + ' &' 3798 tdata = self.TypeData(self) 3799 tdata.name = targetType.name + ' &' 3800 tdata.typeId = typeId 3801 tdata.code = TypeCode.Reference 3802 tdata.ltarget = targetType 3803 tdata.lbitsize = 8 * self.ptrSize() # Needed for Gdb13393 test. 3804 #tdata.lbitsize = None 3805 self.registerType(typeId, tdata) 3806 return self.Type(self, typeId) 3807 3808 def createRValueReferenceType(self, targetType): 3809 if not isinstance(targetType, self.Type): 3810 raise RuntimeError('Expected type in createRValueReferenceType(), got %s' 3811 % type(targetType)) 3812 typeId = targetType.typeId + ' &&' 3813 tdata = self.TypeData(self) 3814 tdata.name = targetType.name + ' &&' 3815 tdata.typeId = typeId 3816 tdata.code = TypeCode.RValueReference 3817 tdata.ltarget = targetType 3818 tdata.lbitsize = None 3819 self.registerType(typeId, tdata) 3820 return self.Type(self, typeId) 3821 3822 def createArrayType(self, targetType, count): 3823 if not isinstance(targetType, self.Type): 3824 raise RuntimeError('Expected type in createArrayType(), got %s' 3825 % type(targetType)) 3826 targetTypeId = targetType.typeId 3827 3828 if targetTypeId.endswith(']'): 3829 (prefix, suffix, inner_count) = self.splitArrayType(targetTypeId) 3830 type_id = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix) 3831 type_name = type_id 3832 else: 3833 type_id = '%s[%d]' % (targetTypeId, count) 3834 type_name = '%s[%d]' % (targetType.name, count) 3835 3836 tdata = self.TypeData(self) 3837 tdata.name = type_name 3838 tdata.typeId = type_id 3839 tdata.code = TypeCode.Array 3840 tdata.ltarget = targetType 3841 tdata.lbitsize = targetType.lbitsize * count 3842 self.registerType(type_id, tdata) 3843 return self.Type(self, type_id) 3844 3845 def createBitfieldType(self, targetType, bitsize): 3846 if not isinstance(targetType, self.Type): 3847 raise RuntimeError('Expected type in createBitfieldType(), got %s' 3848 % type(targetType)) 3849 typeId = '%s:%d' % (targetType.typeId, bitsize) 3850 tdata = self.TypeData(self) 3851 tdata.name = '%s : %d' % (targetType.typeId, bitsize) 3852 tdata.typeId = typeId 3853 tdata.code = TypeCode.Bitfield 3854 tdata.ltarget = targetType 3855 tdata.lbitsize = bitsize 3856 self.registerType(typeId, tdata) 3857 return self.Type(self, typeId) 3858 3859 def createTypedefedType(self, targetType, typeName, typeId=None): 3860 if typeId is None: 3861 typeId = typeName 3862 if not isinstance(targetType, self.Type): 3863 raise RuntimeError('Expected type in createTypedefType(), got %s' 3864 % type(targetType)) 3865 # Happens for C-style struct in GDB: typedef { int x; } struct S1; 3866 if targetType.typeId == typeId: 3867 return targetType 3868 tdata = self.TypeData(self) 3869 tdata.name = typeName 3870 tdata.typeId = typeId 3871 tdata.code = TypeCode.Typedef 3872 tdata.ltarget = targetType 3873 tdata.lbitsize = targetType.lbitsize 3874 #tdata.lfields = targetType.lfields 3875 tdata.lbitsize = targetType.lbitsize 3876 self.registerType(typeId, tdata) 3877 return self.Type(self, typeId) 3878 3879 def knownArrayTypeSize(self): 3880 return 3 * self.ptrSize() if self.qtVersion() >= 0x060000 else self.ptrSize() 3881 3882 def knownTypeSize(self, typish): 3883 if typish[0] == 'Q': 3884 if typish.startswith('QList<') or typish.startswith('QVector<'): 3885 return self.knownArrayTypeSize() 3886 if typish == 'QObject': 3887 return 2 * self.ptrSize() 3888 if typish == 'QStandardItemData': 3889 return 4 * self.ptrSize() if self.qtVersion() >= 0x060000 else 2 * self.ptrSize() 3890 if typish == 'Qt::ItemDataRole': 3891 return 4 3892 if typish == 'QChar': 3893 return 2 3894 if typish in ('quint32', 'qint32'): 3895 return 4 3896 return None 3897 3898 def createType(self, typish, size=None): 3899 if isinstance(typish, self.Type): 3900 #typish.check() 3901 if hasattr(typish, 'lbitsize') and typish.lbitsize is not None and typish.lbitsize > 0: 3902 return typish 3903 # Size 0 is sometimes reported by GDB but doesn't help at all. 3904 # Force using the fallback: 3905 typish = typish.name 3906 3907 if isinstance(typish, str): 3908 ns = self.qtNamespace() 3909 typish = typish.replace('@', ns) 3910 if typish.startswith(ns): 3911 if size is None: 3912 size = self.knownTypeSize(typish[len(ns):]) 3913 else: 3914 if size is None: 3915 size = self.knownTypeSize(typish) 3916 if size is not None: 3917 typish = ns + typish 3918 3919 tdata = self.typeData.get(typish, None) 3920 if tdata is not None: 3921 if tdata.lbitsize is not None: 3922 if tdata.lbitsize > 0: 3923 return self.Type(self, typish) 3924 3925 knownType = self.lookupType(typish) 3926 #DumperBase.warn('KNOWN: %s' % knownType) 3927 if knownType is not None: 3928 #DumperBase.warn('USE FROM NATIVE') 3929 return knownType 3930 3931 #DumperBase.warn('FAKING: %s SIZE: %s' % (typish, size)) 3932 tdata = self.TypeData(self) 3933 tdata.name = typish 3934 tdata.typeId = typish 3935 tdata.templateArguments = self.listTemplateParameters(typish) 3936 if size is not None: 3937 tdata.lbitsize = 8 * size 3938 if typish.endswith('*'): 3939 tdata.code = TypeCode.Pointer 3940 tdata.lbitsize = 8 * self.ptrSize() 3941 tdata.ltarget = self.createType(typish[:-1].strip()) 3942 3943 self.registerType(typish, tdata) 3944 typeobj = self.Type(self, typish) 3945 #DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify()) 3946 typeobj.check() 3947 return typeobj 3948 raise RuntimeError('NEED TYPE, NOT %s' % type(typish)) 3949 3950 def createValue(self, datish, typish): 3951 val = self.Value(self) 3952 val._type = self.createType(typish) 3953 if self.isInt(datish): # Used as address. 3954 #DumperBase.warn('CREATING %s AT 0x%x' % (val.type.name, datish)) 3955 val.laddress = datish 3956 if self.useDynamicType: 3957 val._type = val.type.dynamicType(datish) 3958 return val 3959 if isinstance(datish, bytes): 3960 #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish))) 3961 val.ldata = datish 3962 val.check() 3963 return val 3964 raise RuntimeError('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish)) 3965 3966 def createProxyValue(self, proxy_data, type_name): 3967 tdata = self.TypeData(self) 3968 tdata.name = type_name 3969 tdata.typeId = type_name 3970 tdata.code = TypeCode.Struct 3971 self.registerType(type_name, tdata) 3972 val = self.Value(self) 3973 val._type = self.Type(self, type_name) 3974 val.ldata = proxy_data 3975 return val 3976 3977 class StructBuilder(): 3978 def __init__(self, dumper): 3979 self.dumper = dumper 3980 self.pattern = '' 3981 self.currentBitsize = 0 3982 self.fields = [] 3983 self.autoPadNext = False 3984 self.maxAlign = 1 3985 3986 def addField(self, fieldSize, fieldCode=None, fieldIsStruct=False, 3987 fieldName=None, fieldType=None, fieldAlign=1): 3988 3989 if fieldType is not None: 3990 fieldType = self.dumper.createType(fieldType) 3991 if fieldSize is None and fieldType is not None: 3992 fieldSize = fieldType.size() 3993 if fieldCode is None: 3994 fieldCode = '%ss' % fieldSize 3995 3996 if self.autoPadNext: 3997 self.currentBitsize = 8 * ((self.currentBitsize + 7) >> 3) # Fill up byte. 3998 padding = (fieldAlign - (self.currentBitsize >> 3)) % fieldAlign 3999 #DumperBase.warn('AUTO PADDING AT %s BITS BY %s BYTES' % (self.currentBitsize, padding)) 4000 field = self.dumper.Field(self.dumper, bitpos=self.currentBitsize, 4001 bitsize=padding * 8) 4002 self.pattern += '%ds' % padding 4003 self.currentBitsize += padding * 8 4004 self.fields.append(field) 4005 self.autoPadNext = False 4006 4007 if fieldAlign > self.maxAlign: 4008 self.maxAlign = fieldAlign 4009 #DumperBase.warn("MAX ALIGN: %s" % self.maxAlign) 4010 4011 field = self.dumper.Field(dumper=self.dumper, name=fieldName, type=fieldType, 4012 isStruct=fieldIsStruct, bitpos=self.currentBitsize, 4013 bitsize=fieldSize * 8) 4014 4015 self.pattern += fieldCode 4016 self.currentBitsize += fieldSize * 8 4017 self.fields.append(field) 4018 4019 def describeStruct(self, pattern): 4020 if pattern in self.structPatternCache: 4021 return self.structPatternCache[pattern] 4022 ptrSize = self.ptrSize() 4023 builder = self.StructBuilder(self) 4024 n = None 4025 typeName = '' 4026 readingTypeName = False 4027 for c in pattern: 4028 if readingTypeName: 4029 if c == '}': 4030 readingTypeName = False 4031 fieldType = self.createType(typeName) 4032 fieldAlign = fieldType.alignment() 4033 builder.addField(n, fieldIsStruct=True, 4034 fieldType=fieldType, fieldAlign=fieldAlign) 4035 typeName = None 4036 n = None 4037 else: 4038 typeName += c 4039 elif c == 't': # size_t 4040 builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize) 4041 elif c == 'p': # Pointer as int 4042 builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize) 4043 elif c == 'P': # Pointer as Value 4044 builder.addField(ptrSize, '%ss' % ptrSize, fieldAlign=ptrSize) 4045 elif c in ('d'): 4046 builder.addField(8, c, fieldAlign=ptrSize) # fieldType = 'double' ? 4047 elif c in ('q', 'Q'): 4048 builder.addField(8, c, fieldAlign=ptrSize) 4049 elif c in ('i', 'I', 'f'): 4050 builder.addField(4, c, fieldAlign=4) 4051 elif c in ('h', 'H'): 4052 builder.addField(2, c, fieldAlign=2) 4053 elif c in ('b', 'B', 'c'): 4054 builder.addField(1, c, fieldAlign=1) 4055 elif c >= '0' and c <= '9': 4056 if n is None: 4057 n = '' 4058 n += c 4059 elif c == 's': 4060 builder.addField(int(n), fieldAlign=1) 4061 n = None 4062 elif c == '{': 4063 readingTypeName = True 4064 typeName = '' 4065 elif c == '@': 4066 if n is None: 4067 # Automatic padding depending on next item 4068 builder.autoPadNext = True 4069 else: 4070 # Explicit padding. 4071 builder.currentBitsize = 8 * ((builder.currentBitsize + 7) >> 3) 4072 padding = (int(n) - (builder.currentBitsize >> 3)) % int(n) 4073 field = self.Field(self) 4074 builder.pattern += '%ds' % padding 4075 builder.currentBitsize += padding * 8 4076 builder.fields.append(field) 4077 n = None 4078 else: 4079 raise RuntimeError('UNKNOWN STRUCT CODE: %s' % c) 4080 pp = builder.pattern 4081 size = (builder.currentBitsize + 7) >> 3 4082 fields = builder.fields 4083 tailPad = (builder.maxAlign - size) % builder.maxAlign 4084 size += tailPad 4085 self.structPatternCache[pattern] = (pp, size, fields) 4086 return (pp, size, fields) 4087