aboutsummaryrefslogtreecommitdiffstats
path: root/share/qtcreator/debugger-with-python2/dumper.py
diff options
context:
space:
mode:
authorhjk <hjk@qt.io>2024-04-08 10:09:27 +0200
committerhjk <hjk@qt.io>2024-04-08 08:25:08 +0000
commit2a1d46bbfccf290afa6a81121f7b7c81b0aa79b5 (patch)
tree592856e3e21ef460aa672eee859ba510409bd162 /share/qtcreator/debugger-with-python2/dumper.py
parentb96f5b83eaa4604189545d9b1359024145b11050 (diff)
Debugger: Move python2 supporting files out of normal bridge directory
They get in the way when working in that area. Change-Id: Id59c83472a6ce093ca9b31ad9e94ec638b4a1df8 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'share/qtcreator/debugger-with-python2/dumper.py')
-rw-r--r--share/qtcreator/debugger-with-python2/dumper.py4094
1 files changed, 4094 insertions, 0 deletions
diff --git a/share/qtcreator/debugger-with-python2/dumper.py b/share/qtcreator/debugger-with-python2/dumper.py
new file mode 100644
index 00000000000..1fd42848d4e
--- /dev/null
+++ b/share/qtcreator/debugger-with-python2/dumper.py
@@ -0,0 +1,4094 @@
+# Copyright (C) 2016 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import codecs
+import collections
+import glob
+import struct
+import sys
+import re
+import time
+import inspect
+from utils import DisplayFormat, TypeCode
+
+try:
+ # That's only used in native combined debugging right now, so
+ # we do not need to hard fail in cases of partial python installation
+ # that will never use this.
+ import json
+except:
+ print("Python module json not found. "
+ "Native combined debugging might not work.")
+ pass
+
+try:
+ # That fails on some QNX via Windows installations
+ import base64
+ def hexencode_(s):
+ return base64.b16encode(s).decode('utf8')
+except:
+ def hexencode_(s):
+ return ''.join(["%x" % c for c in s])
+
+if sys.version_info[0] >= 3:
+ toInteger = int
+else:
+ toInteger = long
+
+
+class ReportItem():
+ """
+ Helper structure to keep temporary 'best' information about a value
+ or a type scheduled to be reported. This might get overridden be
+ subsequent better guesses during a putItem() run.
+ """
+
+ def __init__(self, value=None, encoding=None, priority=-100, elided=None):
+ self.value = value
+ self.priority = priority
+ self.encoding = encoding
+ self.elided = elided
+
+ def __str__(self):
+ return 'Item(value: %s, encoding: %s, priority: %s, elided: %s)' \
+ % (self.value, self.encoding, self.priority, self.elided)
+
+
+class Timer():
+ def __init__(self, d, desc):
+ self.d = d
+ self.desc = desc + '-' + d.currentIName
+
+ def __enter__(self):
+ self.starttime = time.time()
+
+ def __exit__(self, exType, exValue, exTraceBack):
+ elapsed = int(1000 * (time.time() - self.starttime))
+ self.d.timings.append([self.desc, elapsed])
+
+
+class Children():
+ def __init__(self, d, numChild=1, childType=None, childNumChild=None,
+ maxNumChild=None, addrBase=None, addrStep=None):
+ self.d = d
+ self.numChild = numChild
+ self.childNumChild = childNumChild
+ self.maxNumChild = maxNumChild
+ if childType is None:
+ self.childType = None
+ else:
+ self.childType = childType.name
+ if not self.d.isCli:
+ self.d.putField('childtype', self.childType)
+ if childNumChild is not None:
+ self.d.putField('childnumchild', childNumChild)
+ self.childNumChild = childNumChild
+ if addrBase is not None and addrStep is not None:
+ self.d.put('addrbase="0x%x",addrstep="%d",' % (addrBase, addrStep))
+
+ def __enter__(self):
+ self.savedChildType = self.d.currentChildType
+ self.savedChildNumChild = self.d.currentChildNumChild
+ self.savedNumChild = self.d.currentNumChild
+ self.savedMaxNumChild = self.d.currentMaxNumChild
+ self.d.currentChildType = self.childType
+ self.d.currentChildNumChild = self.childNumChild
+ self.d.currentNumChild = self.numChild
+ self.d.currentMaxNumChild = self.maxNumChild
+ self.d.put(self.d.childrenPrefix)
+
+ def __exit__(self, exType, exValue, exTraceBack):
+ if exType is not None:
+ if self.d.passExceptions:
+ self.d.showException('CHILDREN', exType, exValue, exTraceBack)
+ self.d.putSpecialValue('notaccessible')
+ self.d.putNumChild(0)
+ if self.d.currentMaxNumChild is not None:
+ if self.d.currentMaxNumChild < self.d.currentNumChild:
+ self.d.put('{name="<load more>",value="",type="",numchild="1"},')
+ self.d.currentChildType = self.savedChildType
+ self.d.currentChildNumChild = self.savedChildNumChild
+ self.d.currentNumChild = self.savedNumChild
+ self.d.currentMaxNumChild = self.savedMaxNumChild
+ if self.d.isCli:
+ self.d.put('\n' + ' ' * self.d.indent)
+ self.d.put(self.d.childrenSuffix)
+ return True
+
+
+class SubItem():
+ def __init__(self, d, component):
+ self.d = d
+ self.name = component
+ self.iname = None
+
+ def __enter__(self):
+ self.d.enterSubItem(self)
+
+ def __exit__(self, exType, exValue, exTraceBack):
+ return self.d.exitSubItem(self, exType, exValue, exTraceBack)
+
+
+class TopLevelItem(SubItem):
+ def __init__(self, d, iname):
+ self.d = d
+ self.iname = iname
+ self.name = None
+
+
+class UnnamedSubItem(SubItem):
+ def __init__(self, d, component):
+ self.d = d
+ self.iname = '%s.%s' % (self.d.currentIName, component)
+ self.name = None
+
+
+class DumperBase():
+ @staticmethod
+ def warn(message):
+ print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode('latin1'))
+
+ @staticmethod
+ def showException(msg, exType, exValue, exTraceback):
+ DumperBase.warn('**** CAUGHT EXCEPTION: %s ****' % msg)
+ try:
+ import traceback
+ for line in traceback.format_exception(exType, exValue, exTraceback):
+ DumperBase.warn('%s' % line)
+ except:
+ pass
+
+ def timer(self, desc):
+ return Timer(self, desc)
+
+ def __init__(self):
+ self.isCdb = False
+ self.isGdb = False
+ self.isLldb = False
+ self.isCli = False
+ self.isDebugBuild = None
+
+ # Later set, or not set:
+ self.stringCutOff = 10000
+ self.displayStringLimit = 100
+ self.useTimeStamps = False
+
+ self.output = []
+ self.typesReported = {}
+ self.typesToReport = {}
+ self.qtNamespaceToReport = None
+ self.qtCustomEventFunc = 0
+ self.qtCustomEventPltFunc = 0
+ self.qtPropertyFunc = 0
+ self.fallbackQtVersion = 0x60200
+ self.passExceptions = False
+ self.isTesting = False
+
+ self.typeData = {}
+ self.isBigEndian = False
+ self.packCode = '<'
+
+ self.resetCaches()
+ self.resetStats()
+
+ self.childrenPrefix = 'children=['
+ self.childrenSuffix = '],'
+
+ self.dumpermodules = []
+
+ try:
+ # Fails in the piping case
+ self.dumpermodules = [
+ os.path.splitext(os.path.basename(p))[0] for p in
+ glob.glob(os.path.join(os.path.dirname(__file__), '*types.py'))
+ ]
+ except:
+ pass
+
+ # These values are never used, but the variables need to have
+ # some value base for the swapping logic in Children.__enter__()
+ # and Children.__exit__().
+ self.currentIName = None
+ self.currentValue = None
+ self.currentType = None
+ self.currentNumChild = None
+ self.currentMaxNumChild = None
+ self.currentPrintsAddress = True
+ self.currentChildType = None
+ self.currentChildNumChild = None
+ self.registerKnownTypes()
+
+ def setVariableFetchingOptions(self, args):
+ self.resultVarName = args.get('resultvarname', '')
+ self.expandedINames = args.get('expanded', {})
+ self.stringCutOff = int(args.get('stringcutoff', 10000))
+ self.displayStringLimit = int(args.get('displaystringlimit', 100))
+ self.typeformats = args.get('typeformats', {})
+ self.formats = args.get('formats', {})
+ self.watchers = args.get('watchers', {})
+ self.useDynamicType = int(args.get('dyntype', '0'))
+ self.useFancy = int(args.get('fancy', '0'))
+ self.forceQtNamespace = int(args.get('forcens', '0'))
+ self.passExceptions = int(args.get('passexceptions', '0'))
+ self.isTesting = int(args.get('testing', '0'))
+ self.showQObjectNames = int(args.get('qobjectnames', '1'))
+ self.nativeMixed = int(args.get('nativemixed', '0'))
+ self.autoDerefPointers = int(args.get('autoderef', '0'))
+ self.useTimeStamps = int(args.get('timestamps', '0'))
+ self.partialVariable = args.get('partialvar', '')
+ self.uninitialized = args.get('uninitialized', [])
+ self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
+ self.partialUpdate = int(args.get('partial', '0'))
+ #DumperBase.warn('NAMESPACE: "%s"' % self.qtNamespace())
+ #DumperBase.warn('EXPANDED INAMES: %s' % self.expandedINames)
+ #DumperBase.warn('WATCHERS: %s' % self.watchers)
+
+ def setFallbackQtVersion(self, args):
+ version = int(args.get('version', self.fallbackQtVersion))
+ self.fallbackQtVersion = version
+
+ def resetPerStepCaches(self):
+ self.perStepCache = {}
+ pass
+
+ def resetCaches(self):
+ # This is a cache mapping from 'type name' to 'display alternatives'.
+ self.qqFormats = {'QVariant (QVariantMap)': [DisplayFormat.CompactMap]}
+
+ # This is a cache of all known dumpers.
+ self.qqDumpers = {} # Direct type match
+ self.qqDumpersEx = {} # Using regexp
+
+ # This is a cache of all dumpers that support writing.
+ self.qqEditable = {}
+
+ # This keeps canonical forms of the typenames, without array indices etc.
+ self.cachedFormats = {}
+
+ # Maps type names to static metaobjects. If a type is known
+ # to not be QObject derived, it contains a 0 value.
+ self.knownStaticMetaObjects = {}
+
+ # A dictionary to serve as a per debugging step cache.
+ # Cleared on each step over / into / continue.
+ self.perStepCache = {}
+
+ # A dictionary to serve as a general cache throughout the whole
+ # debug session.
+ self.generalCache = {}
+
+ self.counts = {}
+ self.structPatternCache = {}
+ self.timings = []
+ self.expandableINames = set({})
+
+ def resetStats(self):
+ # Timing collection
+ self.timings = []
+ pass
+
+ def dumpStats(self):
+ msg = [self.counts, self.timings]
+ self.resetStats()
+ return msg
+
+ def bump(self, key):
+ if key in self.counts:
+ self.counts[key] += 1
+ else:
+ self.counts[key] = 1
+
+ def childRange(self):
+ if self.currentMaxNumChild is None:
+ return range(0, self.currentNumChild)
+ return range(min(self.currentMaxNumChild, self.currentNumChild))
+
+ def maxArrayCount(self):
+ if self.currentIName in self.expandedINames:
+ return self.expandedINames[self.currentIName]
+ return 100
+
+ def enterSubItem(self, item):
+ if self.useTimeStamps:
+ item.startTime = time.time()
+ if not item.iname:
+ item.iname = '%s.%s' % (self.currentIName, item.name)
+ if not self.isCli:
+ self.put('{')
+ if isinstance(item.name, str):
+ self.putField('name', item.name)
+ else:
+ self.indent += 1
+ self.put('\n' + ' ' * self.indent)
+ if isinstance(item.name, str):
+ self.put(item.name + ' = ')
+ item.savedIName = self.currentIName
+ item.savedValue = self.currentValue
+ item.savedType = self.currentType
+ self.currentIName = item.iname
+ self.currentValue = ReportItem()
+ self.currentType = ReportItem()
+
+ def exitSubItem(self, item, exType, exValue, exTraceBack):
+ #DumperBase.warn('CURRENT VALUE: %s: %s %s' %
+ # (self.currentIName, self.currentValue, self.currentType))
+ if exType is not None:
+ if self.passExceptions:
+ self.showException('SUBITEM', exType, exValue, exTraceBack)
+ self.putSpecialValue('notaccessible')
+ self.putNumChild(0)
+ if not self.isCli:
+ try:
+ if self.currentType.value:
+ typeName = self.currentType.value
+ if len(typeName) > 0 and typeName != self.currentChildType:
+ self.putField('type', typeName)
+ if self.currentValue.value is None:
+ self.put('value="",encoding="notaccessible",numchild="0",')
+ else:
+ if self.currentValue.encoding is not None:
+ self.put('valueencoded="%s",' % self.currentValue.encoding)
+ if self.currentValue.elided:
+ self.put('valueelided="%s",' % self.currentValue.elided)
+ self.put('value="%s",' % self.currentValue.value)
+ except:
+ pass
+ if self.useTimeStamps:
+ self.put('time="%s",' % (time.time() - item.startTime))
+ self.put('},')
+ else:
+ self.indent -= 1
+ try:
+ if self.currentType.value:
+ typeName = self.currentType.value
+ self.put('<%s> = {' % typeName)
+
+ if self.currentValue.value is None:
+ self.put('<not accessible>')
+ else:
+ value = self.currentValue.value
+ if self.currentValue.encoding == 'latin1':
+ value = self.hexdecode(value)
+ elif self.currentValue.encoding == 'utf8':
+ value = self.hexdecode(value)
+ elif self.currentValue.encoding == 'utf16':
+ b = bytes(bytearray.fromhex(value))
+ value = codecs.decode(b, 'utf-16')
+ self.put('"%s"' % value)
+ if self.currentValue.elided:
+ self.put('...')
+
+ if self.currentType.value:
+ self.put('}')
+ except:
+ pass
+ self.currentIName = item.savedIName
+ self.currentValue = item.savedValue
+ self.currentType = item.savedType
+ return True
+
+ def stripForFormat(self, typeName):
+ if not isinstance(typeName, str):
+ raise RuntimeError('Expected string in stripForFormat(), got %s' % type(typeName))
+ if typeName in self.cachedFormats:
+ return self.cachedFormats[typeName]
+ stripped = ''
+ inArray = 0
+ for c in typeName:
+ if c == '<':
+ break
+ if c == ' ':
+ continue
+ if c == '[':
+ inArray += 1
+ elif c == ']':
+ inArray -= 1
+ if inArray and ord(c) >= 48 and ord(c) <= 57:
+ continue
+ stripped += c
+ self.cachedFormats[typeName] = stripped
+ return stripped
+
+ def templateArgument(self, typeobj, position):
+ return typeobj.templateArgument(position)
+
+ def intType(self):
+ result = self.lookupType('int')
+ self.intType = lambda: result
+ return result
+
+ def charType(self):
+ result = self.lookupType('char')
+ self.intType = lambda: result
+ return result
+
+ def ptrSize(self):
+ result = self.lookupType('void*').size()
+ self.ptrSize = lambda: result
+ return result
+
+ def lookupType(self, typeName):
+ nativeType = self.lookupNativeType(typeName)
+ return None if nativeType is None else self.fromNativeType(nativeType)
+
+ def registerKnownTypes(self):
+ tdata = self.TypeData(self, 'unsigned short')
+ tdata.lbitsize = 16
+ tdata.lalignment = 2
+ tdata.code = TypeCode.Integral
+
+ tdata = self.TypeData(self, 'QChar')
+ tdata.lbitsize = 16
+ tdata.lalignment = 2
+ tdata.code = TypeCode.Struct
+ tdata.lfields = [self.Field(dumper=self, name='ucs',
+ type='unsigned short', bitsize=16, bitpos=0)]
+ tdata.templateArguments = lambda: []
+
+ def nativeDynamicType(self, address, baseType):
+ return baseType # Override in backends.
+
+ def listTemplateParameters(self, typename):
+ return self.listTemplateParametersManually(typename)
+
+ def listTemplateParametersManually(self, typename):
+ targs = []
+ if not typename.endswith('>'):
+ return targs
+
+ def push(inner):
+ # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
+ inner = inner.strip()[::-1]
+ p = inner.find(')::')
+ if p > -1:
+ inner = inner[p + 3:].strip()
+ if inner.startswith('const '):
+ inner = inner[6:].strip()
+ if inner.endswith(' const'):
+ inner = inner[:-6].strip()
+ #DumperBase.warn("FOUND: %s" % inner)
+ targs.append(inner)
+
+ #DumperBase.warn("SPLITTING %s" % typename)
+ level = 0
+ inner = ''
+ for c in typename[::-1]: # Reversed...
+ #DumperBase.warn("C: %s" % c)
+ if c == '>':
+ if level > 0:
+ inner += c
+ level += 1
+ elif c == '<':
+ level -= 1
+ if level > 0:
+ inner += c
+ else:
+ push(inner)
+ inner = ''
+ break
+ elif c == ',':
+ #DumperBase.warn('c: %s level: %s' % (c, level))
+ if level == 1:
+ push(inner)
+ inner = ''
+ else:
+ inner += c
+ else:
+ inner += c
+
+ #DumperBase.warn("TARGS: %s %s" % (typename, targs))
+ res = []
+ for item in targs[::-1]:
+ if len(item) == 0:
+ continue
+ c = ord(item[0])
+ if c in (45, 46) or (c >= 48 and c < 58): # '-', '.' or digit.
+ if item.find('.') > -1:
+ res.append(float(item))
+ else:
+ if item.endswith('l'):
+ item = item[:-1]
+ if item.endswith('u'):
+ item = item[:-1]
+ val = toInteger(item)
+ if val > 0x80000000:
+ val -= 0x100000000
+ res.append(val)
+ else:
+ res.append(self.Type(self, item))
+ #DumperBase.warn("RES: %s %s" % (typename, [(None if t is None else t.name) for t in res]))
+ return res
+
+ # Hex decoding operating on str, return str.
+ @staticmethod
+ def hexdecode(s, encoding='utf8'):
+ if sys.version_info[0] == 2:
+ # For python2 we need an extra str() call to return str instead of unicode
+ return str(s.decode('hex').decode(encoding))
+ return bytes.fromhex(s).decode(encoding)
+
+ # Hex encoding operating on str or bytes, return str.
+ @staticmethod
+ def hexencode(s):
+ if s is None:
+ s = ''
+ if sys.version_info[0] == 2:
+ if isinstance(s, buffer):
+ return bytes(s).encode('hex')
+ return s.encode('hex')
+ if isinstance(s, str):
+ s = s.encode('utf8')
+ return hexencode_(s)
+
+ def isQt3Support(self):
+ # assume no Qt 3 support by default
+ return False
+
+ # Clamps size to limit.
+ def computeLimit(self, size, limit):
+ if limit == 0:
+ limit = self.displayStringLimit
+ if limit is None or size <= limit:
+ return 0, size
+ return size, limit
+
+ def vectorData(self, value):
+ if self.qtVersion() >= 0x060000:
+ data, size, alloc = self.qArrayData(value)
+ elif self.qtVersion() >= 0x050000:
+ vector_data_ptr = self.extractPointer(value)
+ if self.ptrSize() == 4:
+ (ref, size, alloc, offset) = self.split('IIIp', vector_data_ptr)
+ else:
+ (ref, size, alloc, pad, offset) = self.split('IIIIp', vector_data_ptr)
+ alloc = alloc & 0x7ffffff
+ data = vector_data_ptr + offset
+ else:
+ vector_data_ptr = self.extractPointer(value)
+ (ref, alloc, size) = self.split('III', vector_data_ptr)
+ data = vector_data_ptr + 16
+ self.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
+ return data, size
+
+ def qArrayData(self, value):
+ if self.qtVersion() >= 0x60000:
+ dd, data, size = self.split('ppp', value)
+ if dd:
+ _, _, alloc = self.split('iip', dd)
+ else: # fromRawData
+ alloc = size
+ return data, size, alloc
+ return self.qArrayDataHelper(self.extractPointer(value))
+
+ def qArrayDataHelper(self, array_data_ptr):
+ # array_data_ptr is what is e.g. stored in a QByteArray's d_ptr.
+ if self.qtVersion() >= 0x050000:
+ # QTypedArray:
+ # - QtPrivate::RefCount ref
+ # - int size
+ # - uint alloc : 31, capacityReserved : 1
+ # - qptrdiff offset
+ (ref, size, alloc, offset) = self.split('IIpp', array_data_ptr)
+ alloc = alloc & 0x7ffffff
+ data = array_data_ptr + offset
+ if self.ptrSize() == 4:
+ data = data & 0xffffffff
+ else:
+ data = data & 0xffffffffffffffff
+ elif self.qtVersion() >= 0x040000:
+ # Data:
+ # - QBasicAtomicInt ref;
+ # - int alloc, size;
+ # - [padding]
+ # - char *data;
+ if self.ptrSize() == 4:
+ (ref, alloc, size, data) = self.split('IIIp', array_data_ptr)
+ else:
+ (ref, alloc, size, pad, data) = self.split('IIIIp', array_data_ptr)
+ else:
+ # Data:
+ # - QShared count;
+ # - QChar *unicode
+ # - char *ascii
+ # - uint len: 30
+ (dummy, dummy, dummy, size) = self.split('IIIp', array_data_ptr)
+ size = self.extractInt(array_data_ptr + 3 * self.ptrSize()) & 0x3ffffff
+ alloc = size # pretend.
+ data = self.extractPointer(array_data_ptr + self.ptrSize())
+ return data, size, alloc
+
+ def encodeStringHelper(self, value, limit):
+ data, size, alloc = self.qArrayData(value)
+ if alloc != 0:
+ self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
+ elided, shown = self.computeLimit(2 * size, 2 * limit)
+ return elided, self.readMemory(data, shown)
+
+ def encodeByteArrayHelper(self, value, limit):
+ data, size, alloc = self.qArrayData(value)
+ if alloc != 0:
+ self.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000)
+ elided, shown = self.computeLimit(size, limit)
+ return elided, self.readMemory(data, shown)
+
+ def putCharArrayValue(self, data, size, charSize,
+ displayFormat=DisplayFormat.Automatic):
+ bytelen = size * charSize
+ elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
+ mem = self.readMemory(data, shown)
+ if charSize == 1:
+ if displayFormat in (DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String):
+ encodingType = 'latin1'
+ else:
+ encodingType = 'utf8'
+ #childType = 'char'
+ elif charSize == 2:
+ encodingType = 'utf16'
+ #childType = 'short'
+ else:
+ encodingType = 'ucs4'
+ #childType = 'int'
+
+ self.putValue(mem, encodingType, elided=elided)
+
+ if displayFormat in (
+ DisplayFormat.SeparateLatin1String,
+ DisplayFormat.SeparateUtf8String,
+ DisplayFormat.Separate):
+ elided, shown = self.computeLimit(bytelen, 100000)
+ self.putDisplay(encodingType + ':separate', self.readMemory(data, shown))
+
+ def putCharArrayHelper(self, data, size, charType,
+ displayFormat=DisplayFormat.Automatic,
+ makeExpandable=True):
+ charSize = charType.size()
+ self.putCharArrayValue(data, size, charSize, displayFormat=displayFormat)
+
+ if makeExpandable:
+ self.putNumChild(size)
+ if self.isExpanded():
+ with Children(self):
+ for i in range(size):
+ self.putSubItem(size, self.createValue(data + i * charSize, charType))
+
+ def readMemory(self, addr, size):
+ return self.hexencode(bytes(self.readRawMemory(addr, size)))
+
+ def encodeByteArray(self, value, limit=0):
+ elided, data = self.encodeByteArrayHelper(value, limit)
+ return data
+
+ def putByteArrayValue(self, value):
+ elided, data = self.encodeByteArrayHelper(value, self.displayStringLimit)
+ self.putValue(data, 'latin1', elided=elided)
+
+ def encodeString(self, value, limit=0):
+ elided, data = self.encodeStringHelper(value, limit)
+ return data
+
+ def encodedUtf16ToUtf8(self, s):
+ return ''.join([chr(int(s[i:i + 2], 16)) for i in range(0, len(s), 4)])
+
+ def encodeStringUtf8(self, value, limit=0):
+ return self.encodedUtf16ToUtf8(self.encodeString(value, limit))
+
+ def stringData(self, value): # -> (data, size, alloc)
+ return self.qArrayData(value)
+
+ def extractTemplateArgument(self, typename, position):
+ level = 0
+ skipSpace = False
+ inner = ''
+ for c in typename[typename.find('<') + 1: -1]:
+ if c == '<':
+ inner += c
+ level += 1
+ elif c == '>':
+ level -= 1
+ inner += c
+ elif c == ',':
+ if level == 0:
+ if position == 0:
+ return inner.strip()
+ position -= 1
+ inner = ''
+ else:
+ inner += c
+ skipSpace = True
+ else:
+ if skipSpace and c == ' ':
+ pass
+ else:
+ inner += c
+ skipSpace = False
+ # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
+ inner = inner.strip()
+ p = inner.find(')::')
+ if p > -1:
+ inner = inner[p + 3:]
+ return inner
+
+ def putStringValue(self, value):
+ elided, data = self.encodeStringHelper(value, self.displayStringLimit)
+ self.putValue(data, 'utf16', elided=elided)
+
+ def putPtrItem(self, name, value):
+ with SubItem(self, name):
+ self.putValue('0x%x' % value)
+ self.putType('void*')
+
+ def putIntItem(self, name, value):
+ with SubItem(self, name):
+ if isinstance(value, self.Value):
+ self.putValue(value.display())
+ else:
+ self.putValue(value)
+ self.putType('int')
+
+ def putEnumItem(self, name, ival, typish):
+ buf = bytearray(struct.pack('i', ival))
+ val = self.Value(self)
+ val.ldata = bytes(buf)
+ val._type = self.createType(typish)
+ with SubItem(self, name):
+ self.putItem(val)
+
+ def putBoolItem(self, name, value):
+ with SubItem(self, name):
+ self.putValue(value)
+ self.putType('bool')
+
+ def putPairItem(self, index, pair, keyName='first', valueName='second'):
+ with SubItem(self, index):
+ self.putPairContents(index, pair, keyName, valueName)
+
+ def putPairContents(self, index, pair, kname, vname):
+ with Children(self):
+ first, second = pair if isinstance(pair, tuple) else pair.members(False)
+ key = self.putSubItem(kname, first)
+ value = self.putSubItem(vname, second)
+ if self.isCli:
+ self.putEmptyValue()
+ else:
+ if index is not None:
+ self.putField('keyprefix', '[%s] ' % index)
+ self.putField('key', key.value)
+ if key.encoding is not None:
+ self.putField('keyencoded', key.encoding)
+ self.putValue(value.value, value.encoding)
+
+ def putEnumValue(self, ival, vals):
+ nice = vals.get(ival, None)
+ display = ('%d' % ival) if nice is None else ('%s (%d)' % (nice, ival))
+ self.putValue(display)
+
+ def putCallItem(self, name, rettype, value, func, *args):
+ with SubItem(self, name):
+ try:
+ result = self.callHelper(rettype, value, func, args)
+ except Exception as error:
+ if self.passExceptions:
+ raise error
+ children = [('error', error)]
+ self.putSpecialValue("notcallable", children=children)
+ else:
+ self.putItem(result)
+
+ def call(self, rettype, value, func, *args):
+ return self.callHelper(rettype, value, func, args)
+
+ def putAddress(self, address):
+ if address is not None and not self.isCli:
+ self.put('address="0x%x",' % address)
+
+ def putPlainChildren(self, value, dumpBase=True):
+ self.putExpandable()
+ if self.isExpanded():
+ self.putEmptyValue(-99)
+ with Children(self):
+ self.putFields(value, dumpBase)
+
+ def putNamedChildren(self, values, names):
+ self.putEmptyValue(-99)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ for n, v in zip(names, values):
+ self.putSubItem(n, v)
+
+ def prettySymbolByAddress(self, address):
+ return '0x%x' % address
+
+ def putSymbolValue(self, address):
+ self.putValue(self.prettySymbolByAddress(address))
+
+ def putVTableChildren(self, item, itemCount):
+ p = item.pointer()
+ for i in range(itemCount):
+ deref = self.extractPointer(p)
+ if deref == 0:
+ itemCount = i
+ break
+ with SubItem(self, i):
+ self.putItem(self.createPointerValue(deref, 'void'))
+ p += self.ptrSize()
+ return itemCount
+
+ def putFields(self, value, dumpBase=True):
+ baseIndex = 0
+ for item in value.members(True):
+ if item.name is not None:
+ if item.name.startswith('_vptr.') or item.name.startswith('__vfptr'):
+ with SubItem(self, '[vptr]'):
+ # int (**)(void)
+ self.putType(' ')
+ self.putSortGroup(20)
+ self.putValue(item.name)
+ n = 100
+ if self.isExpanded():
+ with Children(self):
+ n = self.putVTableChildren(item, n)
+ self.putNumChild(n)
+ continue
+
+ if item.isBaseClass and dumpBase:
+ baseIndex += 1
+ # We cannot use nativeField.name as part of the iname as
+ # it might contain spaces and other strange characters.
+ with UnnamedSubItem(self, "@%d" % baseIndex):
+ self.putField('iname', self.currentIName)
+ self.putField('name', '[%s]' % item.name)
+ if not self.isCli:
+ self.putSortGroup(1000 - baseIndex)
+ self.putAddress(item.address())
+ self.putItem(item)
+ continue
+
+ with SubItem(self, item.name):
+ self.putItem(item)
+
+ def putExpandable(self):
+ self.putNumChild(1)
+ self.expandableINames.add(self.currentIName)
+ if self.isCli:
+ self.putValue('{...}', -99)
+
+ def putMembersItem(self, value, sortorder=10):
+ with SubItem(self, '[members]'):
+ self.putSortGroup(sortorder)
+ self.putPlainChildren(value)
+
+ def put(self, stuff):
+ self.output.append(stuff)
+
+ def takeOutput(self):
+ res = ''.join(self.output)
+ self.output = []
+ return res
+
+ def check(self, exp):
+ if not exp:
+ raise RuntimeError('Check failed: %s' % exp)
+
+ def checkRef(self, ref):
+ # Assume there aren't a million references to any object.
+ self.check(ref >= -1)
+ self.check(ref < 1000000)
+
+ def checkIntType(self, thing):
+ if not self.isInt(thing):
+ raise RuntimeError('Expected an integral value, got %s' % type(thing))
+
+ def readToFirstZero(self, base, tsize, maximum):
+ self.checkIntType(base)
+ self.checkIntType(tsize)
+ self.checkIntType(maximum)
+
+ code = self.packCode + (None, 'b', 'H', None, 'I')[tsize]
+ #blob = self.readRawMemory(base, 1)
+ blob = bytes()
+ while maximum > 1:
+ try:
+ blob = self.readRawMemory(base, maximum)
+ break
+ except:
+ maximum = int(maximum / 2)
+ self.warn('REDUCING READING MAXIMUM TO %s' % maximum)
+
+ #DumperBase.warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, tsize, maximum))
+ for i in range(0, maximum, tsize):
+ t = struct.unpack_from(code, blob, i)[0]
+ if t == 0:
+ return 0, i, self.hexencode(blob[:i])
+
+ # Real end is unknown.
+ return -1, maximum, self.hexencode(blob[:maximum])
+
+ def encodeCArray(self, p, tsize, limit):
+ elided, shown, blob = self.readToFirstZero(p, tsize, limit)
+ return elided, blob
+
+ def putItemCount(self, count, maximum=1000000000):
+ # This needs to override the default value, so don't use 'put' directly.
+ if count > maximum:
+ self.putSpecialValue('minimumitemcount', maximum)
+ else:
+ self.putSpecialValue('itemcount', count)
+ self.putNumChild(count)
+
+ def resultToMi(self, value):
+ if isinstance(value, bool):
+ return '"%d"' % int(value)
+ if isinstance(value, dict):
+ return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v))
+ for (k, v) in list(value.items())]) + '}'
+ if isinstance(value, list):
+ return '[' + ','.join([self.resultToMi(k)
+ for k in value]) + ']'
+ return '"%s"' % value
+
+ def variablesToMi(self, value, prefix):
+ if isinstance(value, bool):
+ return '"%d"' % int(value)
+ if isinstance(value, dict):
+ pairs = []
+ for (k, v) in list(value.items()):
+ if k == 'iname':
+ if v.startswith('.'):
+ v = '"%s%s"' % (prefix, v)
+ else:
+ v = '"%s"' % v
+ else:
+ v = self.variablesToMi(v, prefix)
+ pairs.append('%s=%s' % (k, v))
+ return '{' + ','.join(pairs) + '}'
+ if isinstance(value, list):
+ index = 0
+ pairs = []
+ for item in value:
+ if item.get('type', '') == 'function':
+ continue
+ name = item.get('name', '')
+ if len(name) == 0:
+ name = str(index)
+ index += 1
+ pairs.append((name, self.variablesToMi(item, prefix)))
+ pairs.sort(key=lambda pair: pair[0])
+ return '[' + ','.join([pair[1] for pair in pairs]) + ']'
+ return '"%s"' % value
+
+ def filterPrefix(self, prefix, items):
+ return [i[len(prefix):] for i in items if i.startswith(prefix)]
+
+ def tryFetchInterpreterVariables(self, args):
+ if not int(args.get('nativemixed', 0)):
+ return (False, '')
+ context = args.get('context', '')
+ if not len(context):
+ return (False, '')
+
+ expanded = args.get('expanded')
+ args['expanded'] = self.filterPrefix('local', expanded)
+
+ res = self.sendInterpreterRequest('variables', args)
+ if not res:
+ return (False, '')
+
+ reslist = []
+ for item in res.get('variables', {}):
+ if 'iname' not in item:
+ item['iname'] = '.' + item.get('name')
+ reslist.append(self.variablesToMi(item, 'local'))
+
+ watchers = args.get('watchers', None)
+ if watchers:
+ toevaluate = []
+ name2expr = {}
+ seq = 0
+ for watcher in watchers:
+ expr = self.hexdecode(watcher.get('exp'))
+ name = str(seq)
+ toevaluate.append({'name': name, 'expression': expr})
+ name2expr[name] = expr
+ seq += 1
+ args['expressions'] = toevaluate
+
+ args['expanded'] = self.filterPrefix('watch', expanded)
+ del args['watchers']
+ res = self.sendInterpreterRequest('expressions', args)
+
+ if res:
+ for item in res.get('expressions', {}):
+ name = item.get('name')
+ iname = 'watch.' + name
+ expr = name2expr.get(name)
+ item['iname'] = iname
+ item['wname'] = self.hexencode(expr)
+ item['exp'] = expr
+ reslist.append(self.variablesToMi(item, 'watch'))
+
+ return (True, 'data=[%s]' % ','.join(reslist))
+
+ def putField(self, name, value):
+ self.put('%s="%s",' % (name, value))
+
+ def putType(self, typish, priority=0):
+ # Higher priority values override lower ones.
+ if priority >= self.currentType.priority:
+ types = (str) if sys.version_info[0] >= 3 else (str, unicode)
+ if isinstance(typish, types):
+ self.currentType.value = typish
+ else:
+ self.currentType.value = typish.name
+ self.currentType.priority = priority
+
+ def putValue(self, value, encoding=None, priority=0, elided=None):
+ # Higher priority values override lower ones.
+ # elided = 0 indicates all data is available in value,
+ # otherwise it's the true length.
+ if priority >= self.currentValue.priority:
+ self.currentValue = ReportItem(value, encoding, priority, elided)
+
+ def putSpecialValue(self, encoding, value='', children=None):
+ self.putValue(value, encoding)
+ if children is not None:
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ for name, value in children:
+ with SubItem(self, name):
+ self.putValue(str(value).replace('"', '$'))
+
+ def putEmptyValue(self, priority=-10):
+ if priority >= self.currentValue.priority:
+ self.currentValue = ReportItem('', None, priority, None)
+
+ def putName(self, name):
+ self.putField('name', name)
+
+ def putBetterType(self, typish):
+ if isinstance(typish, ReportItem):
+ self.currentType.value = typish.value
+ elif isinstance(typish, str):
+ self.currentType.value = typish.replace('@', self.qtNamespace())
+ else:
+ self.currentType.value = typish.name
+ self.currentType.priority += 1
+
+ def putNoType(self):
+ # FIXME: replace with something that does not need special handling
+ # in SubItem.__exit__().
+ self.putBetterType(' ')
+
+ def putInaccessible(self):
+ #self.putBetterType(' ')
+ self.putNumChild(0)
+ self.currentValue.value = None
+
+ def putNamedSubItem(self, component, value, name):
+ with SubItem(self, component):
+ self.putName(name)
+ self.putItem(value)
+
+ def isExpanded(self):
+ #DumperBase.warn('IS EXPANDED: %s in %s: %s' % (self.currentIName,
+ # self.expandedINames, self.currentIName in self.expandedINames))
+ return self.currentIName in self.expandedINames
+
+ def mangleName(self, typeName):
+ return '_ZN%sE' % ''.join(map(lambda x: '%d%s' % (len(x), x),
+ typeName.split('::')))
+
+ def arrayItemCountFromTypeName(self, typeName, fallbackMax=1):
+ itemCount = typeName[typeName.find('[') + 1:typeName.find(']')]
+ return int(itemCount) if itemCount else fallbackMax
+
+ def putCStyleArray(self, value):
+ arrayType = value.type.unqualified()
+ innerType = arrayType.ltarget
+ if innerType is None:
+ innerType = value.type.target().unqualified()
+ address = value.address()
+ if address:
+ self.putValue('@0x%x' % address, priority=-1)
+ else:
+ self.putEmptyValue()
+ self.putType(arrayType)
+
+ displayFormat = self.currentItemFormat()
+ arrayByteSize = arrayType.size()
+ if arrayByteSize == 0:
+ # This should not happen. But it does, see QTCREATORBUG-14755.
+ # GDB/GCC produce sizeof == 0 for QProcess arr[3]
+ # And in the Nim string dumper.
+ itemCount = self.arrayItemCountFromTypeName(value.type.name, 100)
+ arrayByteSize = int(itemCount) * innerType.size()
+
+ n = arrayByteSize // innerType.size()
+ p = value.address()
+ if displayFormat != DisplayFormat.Raw and p:
+ if innerType.name in (
+ 'char',
+ 'int8_t',
+ 'qint8',
+ 'wchar_t',
+ 'unsigned char',
+ 'uint8_t',
+ 'quint8',
+ 'signed char',
+ 'CHAR',
+ 'WCHAR',
+ 'char8_t',
+ 'char16_t',
+ 'char32_t'
+ ):
+ self.putCharArrayHelper(p, n, innerType, self.currentItemFormat(),
+ makeExpandable=False)
+ else:
+ self.tryPutSimpleFormattedPointer(p, arrayType, innerType,
+ displayFormat, arrayByteSize)
+ self.putNumChild(n)
+
+ if self.isExpanded():
+ self.putArrayData(p, n, innerType)
+
+ self.putPlotDataHelper(p, n, innerType)
+
+ def cleanAddress(self, addr):
+ if addr is None:
+ return '<no address>'
+ return '0x%x' % toInteger(hex(addr), 16)
+
+ def stripNamespaceFromType(self, typeName):
+ ns = self.qtNamespace()
+ if len(ns) > 0 and typeName.startswith(ns):
+ typeName = typeName[len(ns):]
+ # DumperBase.warn( 'stripping %s' % typeName )
+ lvl = 0
+ pos = None
+ stripChunks = []
+ sz = len(typeName)
+ for index in range(0, sz):
+ s = typeName[index]
+ if s == '<':
+ lvl += 1
+ if lvl == 1:
+ pos = index
+ continue
+ elif s == '>':
+ lvl -= 1
+ if lvl < 0:
+ raise RuntimeError("Unbalanced '<' in type, @index %d" % index)
+ if lvl == 0:
+ stripChunks.append((pos, index + 1))
+ if lvl != 0:
+ raise RuntimeError("unbalanced at end of type name")
+ for (f, l) in reversed(stripChunks):
+ typeName = typeName[:f] + typeName[l:]
+ return typeName
+
+ def tryPutPrettyItem(self, typeName, value):
+ value.check()
+ if self.useFancy and self.currentItemFormat() != DisplayFormat.Raw:
+ self.putType(typeName)
+
+ nsStrippedType = self.stripNamespaceFromType(typeName)\
+ .replace('::', '__')
+
+ # Strip leading 'struct' for C structs
+ if nsStrippedType.startswith('struct '):
+ nsStrippedType = nsStrippedType[7:]
+
+ #DumperBase.warn('STRIPPED: %s' % nsStrippedType)
+ # The following block is only needed for D.
+ if nsStrippedType.startswith('_A'):
+ # DMD v2.058 encodes string[] as _Array_uns long long.
+ # With spaces.
+ if nsStrippedType.startswith('_Array_'):
+ qdump_Array(self, value)
+ return True
+ if nsStrippedType.startswith('_AArray_'):
+ qdump_AArray(self, value)
+ return True
+
+ dumper = self.qqDumpers.get(nsStrippedType)
+ #DumperBase.warn('DUMPER: %s' % dumper)
+ if dumper is not None:
+ dumper(self, value)
+ return True
+
+ for pattern in self.qqDumpersEx.keys():
+ dumper = self.qqDumpersEx[pattern]
+ if re.match(pattern, nsStrippedType):
+ dumper(self, value)
+ return True
+
+ return False
+
+ def putSimpleCharArray(self, base, size=None):
+ if size is None:
+ elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
+ else:
+ elided, shown = self.computeLimit(int(size), self.displayStringLimit)
+ data = self.readMemory(base, shown)
+ self.putValue(data, 'latin1', elided=elided)
+
+ def putDisplay(self, editFormat, value):
+ self.putField('editformat', editFormat)
+ self.putField('editvalue', value)
+
+ # This is shared by pointer and array formatting.
+ def tryPutSimpleFormattedPointer(self, ptr, typeName, innerType, displayFormat, limit):
+ if displayFormat == DisplayFormat.Automatic:
+ targetType = innerType
+ if innerType.code == TypeCode.Typedef:
+ targetType = innerType.ltarget
+
+ if targetType.name in ('char', 'signed char', 'unsigned char', 'uint8_t', 'CHAR'):
+ # Use UTF-8 as default for char *.
+ self.putType(typeName)
+ (elided, shown, data) = self.readToFirstZero(ptr, 1, limit)
+ self.putValue(data, 'utf8', elided=elided)
+ if self.isExpanded():
+ self.putArrayData(ptr, shown, innerType)
+ return True
+
+ if targetType.name in ('wchar_t', 'WCHAR'):
+ self.putType(typeName)
+ charSize = self.lookupType('wchar_t').size()
+ (elided, data) = self.encodeCArray(ptr, charSize, limit)
+ if charSize == 2:
+ self.putValue(data, 'utf16', elided=elided)
+ else:
+ self.putValue(data, 'ucs4', elided=elided)
+ return True
+
+ if displayFormat == DisplayFormat.Latin1String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 1, limit)
+ self.putValue(data, 'latin1', elided=elided)
+ return True
+
+ if displayFormat == DisplayFormat.SeparateLatin1String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 1, limit)
+ self.putValue(data, 'latin1', elided=elided)
+ self.putDisplay('latin1:separate', data)
+ return True
+
+ if displayFormat == DisplayFormat.Utf8String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 1, limit)
+ self.putValue(data, 'utf8', elided=elided)
+ return True
+
+ if displayFormat == DisplayFormat.SeparateUtf8String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 1, limit)
+ self.putValue(data, 'utf8', elided=elided)
+ self.putDisplay('utf8:separate', data)
+ return True
+
+ if displayFormat == DisplayFormat.Local8BitString:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 1, limit)
+ self.putValue(data, 'local8bit', elided=elided)
+ return True
+
+ if displayFormat == DisplayFormat.Utf16String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 2, limit)
+ self.putValue(data, 'utf16', elided=elided)
+ return True
+
+ if displayFormat == DisplayFormat.Ucs4String:
+ self.putType(typeName)
+ (elided, data) = self.encodeCArray(ptr, 4, limit)
+ self.putValue(data, 'ucs4', elided=elided)
+ return True
+
+ return False
+
+ def putFormattedPointer(self, value):
+ #with self.timer('formattedPointer'):
+ self.putFormattedPointerX(value)
+
+ def putDerefedPointer(self, value):
+ derefValue = value.dereference()
+ innerType = value.type.target() # .unqualified()
+ self.putType(innerType)
+ savedCurrentChildType = self.currentChildType
+ self.currentChildType = innerType.name
+ derefValue.name = '*'
+ derefValue.autoDerefCount = value.autoDerefCount + 1
+
+ if derefValue.type.code != TypeCode.Pointer:
+ self.putField('autoderefcount', '{}'.format(derefValue.autoDerefCount))
+
+ self.putItem(derefValue)
+ self.currentChildType = savedCurrentChildType
+
+ def putFormattedPointerX(self, value):
+ self.putOriginalAddress(value.address())
+ #DumperBase.warn("PUT FORMATTED: %s" % value)
+ pointer = value.pointer()
+ self.putAddress(pointer)
+ #DumperBase.warn('POINTER: 0x%x' % pointer)
+ if pointer == 0:
+ #DumperBase.warn('NULL POINTER')
+ self.putType(value.type)
+ self.putValue('0x0')
+ return
+
+ typeName = value.type.name
+
+ try:
+ self.readRawMemory(pointer, 1)
+ except:
+ # Failure to dereference a pointer should at least
+ # show the value of a pointer.
+ #DumperBase.warn('BAD POINTER: %s' % value)
+ self.putValue('0x%x' % pointer)
+ self.putType(typeName)
+ return
+
+ if self.currentIName.endswith('.this'):
+ self.putDerefedPointer(value)
+ return
+
+ displayFormat = self.currentItemFormat(value.type.name)
+ innerType = value.type.target() # .unqualified()
+
+ if innerType.name == 'void':
+ #DumperBase.warn('VOID POINTER: %s' % displayFormat)
+ self.putType(typeName)
+ self.putSymbolValue(pointer)
+ return
+
+ if displayFormat == DisplayFormat.Raw:
+ # Explicitly requested bald pointer.
+ #DumperBase.warn('RAW')
+ self.putType(typeName)
+ self.putValue('0x%x' % pointer)
+ self.putExpandable()
+ if self.currentIName in self.expandedINames:
+ with Children(self):
+ with SubItem(self, '*'):
+ self.putItem(value.dereference())
+ return
+
+ limit = self.displayStringLimit
+ if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String):
+ limit = 1000000
+ if self.tryPutSimpleFormattedPointer(pointer, typeName,
+ innerType, displayFormat, limit):
+ self.putExpandable()
+ return
+
+ if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array10000:
+ n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10]
+ self.putType(typeName)
+ self.putItemCount(n)
+ self.putArrayData(value.pointer(), n, innerType)
+ return
+
+ if innerType.code == TypeCode.Function:
+ # A function pointer.
+ self.putSymbolValue(pointer)
+ self.putType(typeName)
+ return
+
+ #DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers)
+ #DumperBase.warn('INAME: %s' % self.currentIName)
+ #DumperBase.warn('INNER: %s' % innerType.name)
+ if self.autoDerefPointers:
+ # Generic pointer type with AutomaticFormat, but never dereference char types:
+ if innerType.name not in (
+ 'char',
+ 'signed char',
+ 'int8_t',
+ 'qint8',
+ 'unsigned char',
+ 'uint8_t',
+ 'quint8',
+ 'wchar_t',
+ 'CHAR',
+ 'WCHAR',
+ 'char8_t',
+ 'char16_t',
+ 'char32_t'
+ ):
+ self.putDerefedPointer(value)
+ return
+
+ #DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type)
+ #DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress)
+ self.putType(typeName)
+ self.putSymbolValue(pointer)
+ self.putExpandable()
+ if self.currentIName in self.expandedINames:
+ with Children(self):
+ with SubItem(self, '*'):
+ self.putItem(value.dereference())
+
+ def putOriginalAddress(self, address):
+ if address is not None:
+ self.put('origaddr="0x%x",' % address)
+
+ def putQObjectNameValue(self, value):
+ try:
+ # dd = value['d_ptr']['d'] is just behind the vtable.
+ (vtable, dd) = self.split('pp', value)
+ if not self.couldBeQObjectVTable(vtable):
+ return False
+
+ intSize = 4
+ ptrSize = self.ptrSize()
+ if self.qtVersion() >= 0x060000:
+ # Size of QObjectData: 9 pointer + 2 int
+ # - vtable
+ # - QObject *q_ptr;
+ # - QObject *parent;
+ # - QObjectList children;
+ # - uint isWidget : 1; etc...
+ # - int postedEvents;
+ # - QDynamicMetaObjectData *metaObject;
+ # - QBindingStorage bindingStorage;
+ extra = self.extractPointer(dd + 9 * ptrSize + 2 * intSize)
+ if extra == 0:
+ return False
+
+ # Offset of objectName in ExtraData: 12 pointer
+ # - QList<QByteArray> propertyNames;
+ # - QList<QVariant> propertyValues;
+ # - QVector<int> runningTimers;
+ # - QList<QPointer<QObject> > eventFilters;
+ # - QString objectName
+ objectNameAddress = extra + 12 * ptrSize
+ elif self.qtVersion() >= 0x050000:
+ # Size of QObjectData: 5 pointer + 2 int
+ # - vtable
+ # - QObject *q_ptr;
+ # - QObject *parent;
+ # - QObjectList children;
+ # - uint isWidget : 1; etc...
+ # - int postedEvents;
+ # - QDynamicMetaObjectData *metaObject;
+ extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
+ if extra == 0:
+ return False
+
+ # Offset of objectName in ExtraData: 6 pointer
+ # - QVector<QObjectUserData *> userData; only #ifndef QT_NO_USERDATA
+ # - QList<QByteArray> propertyNames;
+ # - QList<QVariant> propertyValues;
+ # - QVector<int> runningTimers;
+ # - QList<QPointer<QObject> > eventFilters;
+ # - QString objectName
+ objectNameAddress = extra + 5 * ptrSize
+ else:
+ # Size of QObjectData: 5 pointer + 2 int
+ # - vtable
+ # - QObject *q_ptr;
+ # - QObject *parent;
+ # - QObjectList children;
+ # - uint isWidget : 1; etc..
+ # - int postedEvents;
+ # - QMetaObject *metaObject;
+
+ # Offset of objectName in QObjectPrivate: 5 pointer + 2 int
+ # - [QObjectData base]
+ # - QString objectName
+ objectNameAddress = dd + 5 * ptrSize + 2 * intSize
+
+
+ data, size, alloc = self.qArrayData(objectNameAddress)
+
+ # Object names are short, and GDB can crash on to big chunks.
+ # Since this here is a convenience feature only, limit it.
+ if size <= 0 or size > 80:
+ return False
+
+ raw = self.readMemory(data, 2 * size)
+ self.putValue(raw, 'utf16', 1)
+ return True
+
+ except:
+ # warn('NO QOBJECT: %s' % value.type)
+ return False
+
+ def couldBePointer(self, p):
+ if self.ptrSize() == 4:
+ return p > 100000 and (p & 0x3 == 0)
+ else:
+ return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff)
+
+ def couldBeVTableEntry(self, p):
+ if self.ptrSize() == 4:
+ return p > 100000 and (p & 0x1 == 0)
+ else:
+ return p > 100000 and (p & 0x1 == 0) and (p < 0x7fffffffffff)
+
+ def couldBeQObjectPointer(self, objectPtr):
+ try:
+ vtablePtr, dd = self.split('pp', objectPtr)
+ except:
+ self.bump('nostruct-1')
+ return False
+
+ try:
+ dvtablePtr, qptr, parentPtr = self.split('ppp', dd)
+ except:
+ self.bump('nostruct-2')
+ return False
+ # Check d_ptr.d.q_ptr == objectPtr
+ if qptr != objectPtr:
+ self.bump('q_ptr')
+ return False
+
+ return self.couldBeQObjectVTable(vtablePtr)
+
+ def couldBeQObjectVTable(self, vtablePtr):
+ def getJumpAddress_x86(dumper, address):
+ relativeJumpCode = 0xe9
+ jumpCode = 0xff
+ try:
+ data = dumper.readRawMemory(address, 6)
+ except:
+ return 0
+ primaryOpcode = data[0]
+ if primaryOpcode == relativeJumpCode:
+ # relative jump on 32 and 64 bit with a 32bit offset
+ offset = int.from_bytes(data[1:5], byteorder='little')
+ return address + 5 + offset
+ if primaryOpcode == jumpCode:
+ if data[1] != 0x25: # check for known extended opcode
+ return 0
+ # 0xff25 is a relative jump on 64bit and an absolute jump on 32 bit
+ if self.ptrSize() == 8:
+ offset = int.from_bytes(data[2:6], byteorder='little')
+ return address + 6 + offset
+ else:
+ return int.from_bytes(data[2:6], byteorder='little')
+ return 0
+
+ # Do not try to extract a function pointer if there are no values to compare with
+ if self.qtCustomEventFunc == 0 and self.qtCustomEventPltFunc == 0:
+ return False
+
+ try:
+ customEventOffset = 8 if self.isMsvcTarget() else 9
+ customEventFunc = self.extractPointer(vtablePtr + customEventOffset * self.ptrSize())
+ except:
+ self.bump('nostruct-3')
+ return False
+
+ if self.isWindowsTarget():
+ if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
+ return True
+ # The vtable may point to a function that is just calling the customEvent function
+ customEventFunc = getJumpAddress_x86(self, customEventFunc)
+ if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
+ return True
+ customEventFunc = self.extractPointer(customEventFunc)
+ if customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc):
+ return True
+ # If the object is defined in another module there may be another level of indirection
+ customEventFunc = getJumpAddress_x86(self, customEventFunc)
+
+ return customEventFunc in (self.qtCustomEventFunc, self.qtCustomEventPltFunc)
+
+# def extractQObjectProperty(objectPtr):
+# vtablePtr = self.extractPointer(objectPtr)
+# metaObjectFunc = self.extractPointer(vtablePtr)
+# cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr)
+# try:
+# #DumperBase.warn('MO CMD: %s' % cmd)
+# res = self.parseAndEvaluate(cmd)
+# #DumperBase.warn('MO RES: %s' % res)
+# self.bump('successfulMetaObjectCall')
+# return res.pointer()
+# except:
+# self.bump('failedMetaObjectCall')
+# #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
+# return 0
+
+ def extractMetaObjectPtr(self, objectPtr, typeobj):
+ """ objectPtr - address of *potential* instance of QObject derived class
+ typeobj - type of *objectPtr if known, None otherwise. """
+
+ if objectPtr is not None:
+ self.checkIntType(objectPtr)
+
+ def extractMetaObjectPtrFromAddress():
+ #return 0
+ # FIXME: Calling 'works' but seems to impact memory contents(!)
+ # in relevant places. One symptom is that object name
+ # contents 'vanishes' as the reported size of the string
+ # gets zeroed out(?).
+ # Try vtable, metaObject() is the first entry.
+ vtablePtr = self.extractPointer(objectPtr)
+ metaObjectFunc = self.extractPointer(vtablePtr)
+ cmd = '((void*(*)(void*))0x%x)((void*)0x%x)' % (metaObjectFunc, objectPtr)
+ try:
+ #DumperBase.warn('MO CMD: %s' % cmd)
+ res = self.parseAndEvaluate(cmd)
+ #DumperBase.warn('MO RES: %s' % res)
+ self.bump('successfulMetaObjectCall')
+ return res.pointer()
+ except:
+ self.bump('failedMetaObjectCall')
+ #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
+ return 0
+
+ def extractStaticMetaObjectFromTypeHelper(someTypeObj):
+ if someTypeObj.isSimpleType():
+ return 0
+
+ typeName = someTypeObj.name
+ isQObjectProper = typeName == self.qtNamespace() + 'QObject'
+
+ # No templates for now.
+ if typeName.find('<') >= 0:
+ return 0
+
+ result = self.findStaticMetaObject(someTypeObj)
+
+ # We need to distinguish Q_OBJECT from Q_GADGET:
+ # a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself),
+ # a Q_GADGET SMO has a null superdata (hopefully)
+ if result and not isQObjectProper:
+ if self.qtVersion() >= 0x60000 and self.isWindowsTarget():
+ (direct, indirect) = self.split('pp', result)
+ # since Qt 6 there is an additional indirect super data getter on windows
+ if direct == 0 and indirect == 0:
+ # This looks like a Q_GADGET
+ return 0
+ else:
+ if self.extractPointer(result) == 0:
+ # This looks like a Q_GADGET
+ return 0
+
+ return result
+
+ def extractStaticMetaObjectPtrFromType(someTypeObj):
+ if someTypeObj is None:
+ return 0
+ someTypeName = someTypeObj.name
+ self.bump('metaObjectFromType')
+ known = self.knownStaticMetaObjects.get(someTypeName, None)
+ if known is not None: # Is 0 or the static metaobject.
+ return known
+
+ result = 0
+ #try:
+ result = extractStaticMetaObjectFromTypeHelper(someTypeObj)
+ #except RuntimeError as error:
+ # warn('METAOBJECT EXTRACTION FAILED: %s' % error)
+ #except:
+ # warn('METAOBJECT EXTRACTION FAILED FOR UNKNOWN REASON')
+
+ #if not result:
+ # base = someTypeObj.firstBase()
+ # if base is not None and base != someTypeObj: # sanity check
+ # result = extractStaticMetaObjectPtrFromType(base)
+
+ if result:
+ self.knownStaticMetaObjects[someTypeName] = result
+ return result
+
+ if not self.useFancy:
+ return 0
+
+ ptrSize = self.ptrSize()
+
+ typeName = typeobj.name
+ result = self.knownStaticMetaObjects.get(typeName, None)
+ if result is not None: # Is 0 or the static metaobject.
+ self.bump('typecached')
+ #DumperBase.warn('CACHED RESULT: %s %s 0x%x' % (self.currentIName, typeName, result))
+ return result
+
+ if not self.couldBeQObjectPointer(objectPtr):
+ self.bump('cannotBeQObject')
+ #DumperBase.warn('DOES NOT LOOK LIKE A QOBJECT: %s' % self.currentIName)
+ return 0
+
+ metaObjectPtr = 0
+ if not metaObjectPtr:
+ # measured: 3 ms (example had one level of inheritance)
+ #with self.timer('metaObjectType-' + self.currentIName):
+ metaObjectPtr = extractStaticMetaObjectPtrFromType(typeobj)
+
+ if not metaObjectPtr and not self.isWindowsTarget():
+ # measured: 200 ms (example had one level of inheritance)
+ #with self.timer('metaObjectCall-' + self.currentIName):
+ metaObjectPtr = extractMetaObjectPtrFromAddress()
+
+ #if metaObjectPtr:
+ # self.bump('foundMetaObject')
+ # self.knownStaticMetaObjects[typeName] = metaObjectPtr
+
+ return metaObjectPtr
+
+ def split(self, pattern, value):
+ if isinstance(value, self.Value):
+ return value.split(pattern)
+ if self.isInt(value):
+ val = self.Value(self)
+ val.laddress = value
+ return val.split(pattern)
+ raise RuntimeError('CANNOT EXTRACT STRUCT FROM %s' % type(value))
+
+ def extractCString(self, addr):
+ result = bytearray()
+ while True:
+ d = self.extractByte(addr)
+ if d == 0:
+ break
+ result.append(d)
+ addr += 1
+ return result
+
+ def listData(self, value, check=True):
+ if self.qtVersion() >= 0x60000:
+ dd, data, size = self.split('ppi', value)
+ return data, size
+
+ base = self.extractPointer(value)
+ (ref, alloc, begin, end) = self.split('IIII', base)
+ array = base + 16
+ if self.qtVersion() < 0x50000:
+ array += self.ptrSize()
+ size = end - begin
+
+ if check:
+ self.check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000)
+ size = end - begin
+ self.check(size >= 0)
+
+ stepSize = self.ptrSize()
+ data = array + begin * stepSize
+ return data, size
+
+ def putTypedPointer(self, name, addr, typeName):
+ """ Prints a typed pointer, expandable if the type can be resolved,
+ and without children otherwise """
+ with SubItem(self, name):
+ self.putAddress(addr)
+ self.putValue('@0x%x' % addr)
+ typeObj = self.lookupType(typeName)
+ if typeObj:
+ self.putType(typeObj)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ self.putFields(self.createValue(addr, typeObj))
+ else:
+ self.putType(typeName)
+
+ # This is called is when a QObject derived class is expanded
+ def tryPutQObjectGuts(self, value):
+ metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type)
+ if metaObjectPtr:
+ self.putQObjectGutsHelper(value, value.address(),
+ -1, metaObjectPtr, 'QObject')
+
+ def metaString(self, metaObjectPtr, index, revision):
+ ptrSize = self.ptrSize()
+ stringdataOffset = ptrSize
+ if self.isWindowsTarget() and self.qtVersion() >= 0x060000:
+ stringdataOffset += ptrSize # indirect super data member
+ stringdata = self.extractPointer(toInteger(metaObjectPtr) + stringdataOffset)
+
+ def unpackString(base, size):
+ try:
+ s = struct.unpack_from('%ds' % size, self.readRawMemory(base, size))[0]
+ return s if sys.version_info[0] == 2 else s.decode('utf8')
+ except:
+ return '<not available>'
+
+ if revision >= 9: # Qt 6.
+ pos, size = self.split('II', stringdata + 8 * index)
+ return unpackString(stringdata + pos, size)
+
+ if revision >= 7: # Qt 5.
+ byteArrayDataSize = 24 if ptrSize == 8 else 16
+ literal = stringdata + toInteger(index) * byteArrayDataSize
+ base, size, _ = self.qArrayDataHelper(literal)
+ return unpackString(base, size)
+
+ ldata = stringdata + index
+ return self.extractCString(ldata).decode('utf8')
+
+ def putSortGroup(self, sortorder):
+ if not self.isCli:
+ self.putField('sortgroup', sortorder)
+
+ def putQMetaStuff(self, value, origType):
+ if self.qtVersion() >= 0x060000:
+ metaObjectPtr, handle = value.split('pp')
+ else:
+ metaObjectPtr, handle = value.split('pI')
+ if metaObjectPtr != 0:
+ if self.qtVersion() >= 0x060000:
+ if handle == 0:
+ self.putEmptyValue()
+ return
+ revision = 9
+ name, alias, flags, keyCount, data = self.split('IIIII', handle)
+ index = name
+ elif self.qtVersion() >= 0x050000:
+ revision = 7
+ dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize())
+ index = self.extractInt(dataPtr + 4 * handle)
+ else:
+ revision = 6
+ dataPtr = self.extractPointer(metaObjectPtr + 2 * self.ptrSize())
+ index = self.extractInt(dataPtr + 4 * handle)
+ #self.putValue("index: %s rev: %s" % (index, revision))
+ name = self.metaString(metaObjectPtr, index, revision)
+ self.putValue(name)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ self.putFields(value)
+ self.putQObjectGutsHelper(0, 0, handle, metaObjectPtr, origType)
+ else:
+ self.putEmptyValue()
+ if self.isExpanded():
+ with Children(self):
+ self.putFields(value)
+
+ # basically all meta things go through this here.
+ # qobject and qobjectPtr are non-null if coming from a real structure display
+ # qobject == 0, qobjectPtr != 0 is possible for builds without QObject debug info
+ # if qobject == 0, properties and d-ptr cannot be shown.
+ # handle is what's store in QMetaMethod etc, pass -1 for QObject/QMetaObject
+ # itself metaObjectPtr needs to point to a valid QMetaObject.
+ def putQObjectGutsHelper(self, qobject, qobjectPtr, handle, metaObjectPtr, origType):
+ ptrSize = self.ptrSize()
+
+ def putt(name, value, typeName=' '):
+ with SubItem(self, name):
+ self.putValue(value)
+ self.putType(typeName)
+
+ def extractSuperDataPtr(someMetaObjectPtr):
+ #return someMetaObjectPtr['d']['superdata']
+ return self.extractPointer(someMetaObjectPtr)
+
+ def extractDataPtr(someMetaObjectPtr):
+ # dataPtr = metaObjectPtr['d']['data']
+ if self.qtVersion() >= 0x60000 and self.isWindowsTarget():
+ offset = 3
+ else:
+ offset = 2
+ return self.extractPointer(someMetaObjectPtr + offset * ptrSize)
+
+ isQMetaObject = origType == 'QMetaObject'
+ isQObject = origType == 'QObject'
+
+ #DumperBase.warn('OBJECT GUTS: %s 0x%x ' % (self.currentIName, metaObjectPtr))
+ dataPtr = extractDataPtr(metaObjectPtr)
+ #DumperBase.warn('DATA PTRS: %s 0x%x ' % (self.currentIName, dataPtr))
+ (revision, classname,
+ classinfo, classinfo2,
+ methodCount, methods,
+ propertyCount, properties,
+ enumCount, enums,
+ constructorCount, constructors,
+ flags, signalCount) = self.split('I' * 14, dataPtr)
+
+ largestStringIndex = -1
+ for i in range(methodCount):
+ t = self.split('IIIII', dataPtr + 56 + i * 20)
+ if largestStringIndex < t[0]:
+ largestStringIndex = t[0]
+
+ ns = self.qtNamespace()
+ extraData = 0
+ if qobjectPtr:
+ dd = self.extractPointer(qobjectPtr + ptrSize)
+ if self.qtVersion() >= 0x60000:
+ (dvtablePtr, qptr, parent, children, bindingStorageData, bindingStatus,
+ flags, postedEvents, dynMetaObjectPtr, # Up to here QObjectData.
+ extraData, threadDataPtr, connectionListsPtr,
+ sendersPtr, currentSenderPtr) \
+ = self.split('pp{@QObject*}{@QList<@QObject *>}ppIIp' + 'ppppp', dd)
+ elif self.qtVersion() >= 0x50000:
+ (dvtablePtr, qptr, parent, children, flags, postedEvents,
+ dynMetaObjectPtr, # Up to here QObjectData.
+ extraData, threadDataPtr, connectionListsPtr,
+ sendersPtr, currentSenderPtr) \
+ = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'ppppp', dd)
+ else:
+ (dvtablePtr, qptr, parent, children, flags, postedEvents,
+ dynMetaObjectPtr, # Up to here QObjectData
+ objectName, extraData, threadDataPtr, connectionListsPtr,
+ sendersPtr, currentSenderPtr) \
+ = self.split('pp{@QObject*}{@QList<@QObject *>}IIp' + 'pppppp', dd)
+
+ with SubItem(self, '[parent]'):
+ if not self.isCli:
+ self.putSortGroup(9)
+ self.putItem(parent)
+
+ with SubItem(self, '[children]'):
+ if not self.isCli:
+ self.putSortGroup(8)
+
+ dvtablePtr, qptr, parentPtr, children = self.split('ppp{QList<QObject *>}', dd)
+ self.putItem(children)
+
+ if isQMetaObject:
+ with SubItem(self, '[strings]'):
+ if not self.isCli:
+ self.putSortGroup(2)
+ if largestStringIndex > 0:
+ self.putSpecialValue('minimumitemcount', largestStringIndex)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self, largestStringIndex + 1):
+ for i in self.childRange():
+ with SubItem(self, i):
+ s = self.metaString(metaObjectPtr, i, revision)
+ self.putValue(self.hexencode(s), 'latin1')
+ else:
+ self.putValue(' ')
+
+ if isQMetaObject:
+ with SubItem(self, '[raw]'):
+ self.putSortGroup(1)
+ self.putEmptyValue()
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ putt('revision', revision)
+ putt('classname', classname)
+ putt('classinfo', classinfo)
+ putt('methods', '%d %d' % (methodCount, methods))
+ putt('properties', '%d %d' % (propertyCount, properties))
+ putt('enums/sets', '%d %d' % (enumCount, enums))
+ putt('constructors', '%d %d' % (constructorCount, constructors))
+ putt('flags', flags)
+ putt('signalCount', signalCount)
+ for i in range(methodCount):
+ t = self.split('IIIII', dataPtr + 56 + i * 20)
+ putt('method %d' % i, '%s %s %s %s %s' % t)
+
+ if isQObject:
+ with SubItem(self, '[extra]'):
+ self.putSortGroup(1)
+ self.putEmptyValue()
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ if extraData:
+ self.putTypedPointer('[extraData]', extraData,
+ ns + 'QObjectPrivate::ExtraData')
+
+ with SubItem(self, '[metaObject]'):
+ self.putAddress(metaObjectPtr)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ self.putQObjectGutsHelper(
+ 0, 0, -1, metaObjectPtr, 'QMetaObject')
+
+ if False:
+ with SubItem(self, '[connections]'):
+ if connectionListsPtr:
+ typeName = '@QObjectConnectionListVector'
+ self.putItem(self.createValue(connectionListsPtr, typeName))
+ else:
+ self.putItemCount(0)
+
+ if False:
+ with SubItem(self, '[signals]'):
+ self.putItemCount(signalCount)
+ if self.isExpanded():
+ with Children(self):
+ j = -1
+ for i in range(signalCount):
+ t = self.split('IIIII', dataPtr + 56 + 20 * i)
+ flags = t[4]
+ if flags != 0x06:
+ continue
+ j += 1
+ with SubItem(self, j):
+ name = self.metaString(
+ metaObjectPtr, t[0], revision)
+ self.putType(' ')
+ self.putValue(name)
+ self.putExpandable()
+ with Children(self):
+ putt('[nameindex]', t[0])
+ #putt('[type]', 'signal')
+ putt('[argc]', t[1])
+ putt('[parameter]', t[2])
+ putt('[tag]', t[3])
+ putt('[flags]', t[4])
+ putt('[localindex]', str(i))
+ putt('[globalindex]', str(globalOffset + i))
+ #self.putQObjectConnections(dd)
+
+ if isQMetaObject or isQObject:
+ with SubItem(self, '[properties]'):
+ self.putSortGroup(5)
+ if self.isExpanded():
+ dynamicPropertyCount = 0
+ with Children(self):
+ # Static properties.
+ for i in range(propertyCount):
+ if self.qtVersion() >= 0x60000:
+ t = self.split('IIIII', dataPtr + properties * 4 + 20 * i)
+ else:
+ t = self.split('III', dataPtr + properties * 4 + 12 * i)
+ name = self.metaString(metaObjectPtr, t[0], revision)
+ if qobject and self.qtPropertyFunc:
+ # LLDB doesn't like calling it on a derived class, possibly
+ # due to type information living in a different shared object.
+ #base = self.createValue(qobjectPtr, '@QObject')
+ #DumperBase.warn("CALL FUNC: 0x%x" % self.qtPropertyFunc)
+ cmd = '((QVariant(*)(void*,char*))0x%x)((void*)0x%x,"%s")' \
+ % (self.qtPropertyFunc, qobjectPtr, name)
+ try:
+ #DumperBase.warn('PROP CMD: %s' % cmd)
+ res = self.parseAndEvaluate(cmd)
+ #DumperBase.warn('PROP RES: %s' % res)
+ except:
+ self.bump('failedMetaObjectCall')
+ putt(name, ' ')
+ continue
+ #DumperBase.warn('COULD NOT EXECUTE: %s' % cmd)
+ #self.putCallItem(name, '@QVariant', base, 'property', '"' + name + '"')
+ if res is None:
+ self.bump('failedMetaObjectCall2')
+ putt(name, ' ')
+ continue
+ self.putSubItem(name, res)
+ else:
+ putt(name, ' ')
+
+ # Dynamic properties.
+ if extraData:
+ def list6Generator(addr, innerType):
+ data, size = self.listData(addr)
+ for i in range(size):
+ yield self.createValue(data + i * innerType.size(), innerType)
+
+ def list5Generator(addr, innerType):
+ data, size = self.listData(addr)
+ for i in range(size):
+ yield self.createValue(data + i * ptrSize, innerType)
+
+ def vectorGenerator(addr, innerType):
+ data, size = self.vectorData(addr)
+ for i in range(size):
+ yield self.createValue(data + i * innerType.size(), innerType)
+
+ byteArrayType = self.createType('@QByteArray')
+ variantType = self.createType('@QVariant')
+ if self.qtVersion() >= 0x60000:
+ values = vectorGenerator(extraData + 3 * ptrSize, variantType)
+ elif self.qtVersion() >= 0x50600:
+ values = vectorGenerator(extraData + 2 * ptrSize, variantType)
+ elif self.qtVersion() >= 0x50000:
+ values = list5Generator(extraData + 2 * ptrSize, variantType)
+ else:
+ values = list5Generator(extraData + 2 * ptrSize,
+ variantType.pointer())
+
+ if self.qtVersion() >= 0x60000:
+ names = list6Generator(extraData, byteArrayType)
+ else:
+ names = list5Generator(extraData + ptrSize, byteArrayType)
+
+ for (k, v) in zip(names, values):
+ with SubItem(self, propertyCount + dynamicPropertyCount):
+ if not self.isCli:
+ self.putField('key', self.encodeByteArray(k))
+ self.putField('keyencoded', 'latin1')
+ self.putItem(v)
+ dynamicPropertyCount += 1
+ self.putItemCount(propertyCount + dynamicPropertyCount)
+ else:
+ # We need a handle to [x] for the user to expand the item
+ # before we know whether there are actual children. Counting
+ # them is too expensive.
+ self.putSpecialValue('minimumitemcount', propertyCount)
+ self.putExpandable()
+
+ superDataPtr = extractSuperDataPtr(metaObjectPtr)
+
+ globalOffset = 0
+ superDataIterator = superDataPtr
+ while superDataIterator:
+ sdata = extractDataPtr(superDataIterator)
+ globalOffset += self.extractInt(sdata + 16) # methodCount member
+ superDataIterator = extractSuperDataPtr(superDataIterator)
+
+ if isQMetaObject or isQObject:
+ with SubItem(self, '[methods]'):
+ self.putSortGroup(3)
+ self.putItemCount(methodCount)
+ if self.isExpanded():
+ with Children(self):
+ for i in range(methodCount):
+ t = self.split('IIIII', dataPtr + 56 + 20 * i)
+ name = self.metaString(metaObjectPtr, t[0], revision)
+ with SubItem(self, i):
+ self.putValue(name)
+ self.putType(' ')
+ self.putNumChild(1)
+ isSignal = False
+ flags = t[4]
+ if flags == 0x06:
+ typ = 'signal'
+ isSignal = True
+ elif flags == 0x0a:
+ typ = 'slot'
+ elif flags == 0x0a:
+ typ = 'invokable'
+ else:
+ typ = '<unknown>'
+ with Children(self):
+ putt('[nameindex]', t[0])
+ putt('[type]', typ)
+ putt('[argc]', t[1])
+ putt('[parameter]', t[2])
+ putt('[tag]', t[3])
+ putt('[flags]', t[4])
+ putt('[localindex]', str(i))
+ putt('[globalindex]', str(globalOffset + i))
+
+ if isQObject:
+ with SubItem(self, '[d]'):
+ self.putItem(self.createValue(dd, '@QObjectPrivate'))
+ self.putSortGroup(15)
+
+ if isQMetaObject:
+ with SubItem(self, '[superdata]'):
+ self.putSortGroup(12)
+ if superDataPtr:
+ self.putType('@QMetaObject')
+ self.putAddress(superDataPtr)
+ self.putExpandable()
+ if self.isExpanded():
+ with Children(self):
+ self.putQObjectGutsHelper(0, 0, -1, superDataPtr, 'QMetaObject')
+ else:
+ self.putType('@QMetaObject *')
+ self.putValue('0x0')
+
+ if handle >= 0:
+ localIndex = int((handle - methods) / 5)
+ with SubItem(self, '[localindex]'):
+ self.putSortGroup(12)
+ self.putValue(localIndex)
+ with SubItem(self, '[globalindex]'):
+ self.putSortGroup(11)
+ self.putValue(globalOffset + localIndex)
+
+ def putQObjectConnections(self, dd):
+ with SubItem(self, '[connections]'):
+ ptrSize = self.ptrSize()
+ self.putNoType()
+ privateType = self.createType('@QObjectPrivate')
+ d_ptr = dd.cast(privateType.pointer()).dereference()
+ connections = d_ptr['connectionLists']
+ if self.connections.integer() == 0:
+ self.putItemCount(0)
+ else:
+ connections = connections.dereference()
+ #connections = connections.cast(connections.type.firstBase())
+ self.putSpecialValue('minimumitemcount', 0)
+ self.putExpandable()
+ if self.isExpanded():
+ pp = 0
+ with Children(self):
+ innerType = connections.type[0]
+ # Should check: innerType == ns::QObjectPrivate::ConnectionList
+ data, size = self.vectorData(connections)
+ connectionType = self.createType('@QObjectPrivate::Connection')
+ for i in range(size):
+ first = self.extractPointer(data + i * 2 * ptrSize)
+ while first:
+ self.putSubItem('%s' % pp,
+ self.createPointerValue(first, connectionType))
+ first = self.extractPointer(first + 3 * ptrSize)
+ # We need to enforce some upper limit.
+ pp += 1
+ if pp > 1000:
+ break
+
+ def currentItemFormat(self, typeName=None):
+ displayFormat = self.formats.get(self.currentIName, DisplayFormat.Automatic)
+ if displayFormat == DisplayFormat.Automatic:
+ if typeName is None:
+ typeName = self.currentType.value
+ needle = None if typeName is None else self.stripForFormat(typeName)
+ displayFormat = self.typeformats.get(needle, DisplayFormat.Automatic)
+ return displayFormat
+
+ def putSubItem(self, component, value): # -> ReportItem
+ if not isinstance(value, self.Value):
+ raise RuntimeError('WRONG VALUE TYPE IN putSubItem: %s' % type(value))
+ if not isinstance(value.type, self.Type):
+ raise RuntimeError('WRONG TYPE TYPE IN putSubItem: %s' % type(value.type))
+ res = None
+ with SubItem(self, component):
+ self.putItem(value)
+ res = self.currentValue
+ return res # The 'short' display.
+
+ def putArrayData(self, base, n, innerType, childNumChild=None):
+ self.checkIntType(base)
+ self.checkIntType(n)
+ addrBase = base
+ innerSize = innerType.size()
+ self.putNumChild(n)
+ #DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
+ enc = innerType.simpleEncoding()
+ maxNumChild = self.maxArrayCount()
+ if enc:
+ self.put('childtype="%s",' % innerType.name)
+ self.put('addrbase="0x%x",' % addrBase)
+ self.put('addrstep="0x%x",' % innerSize)
+ self.put('arrayencoding="%s",' % enc)
+ self.put('endian="%s",' % self.packCode)
+ if n > maxNumChild:
+ self.put('childrenelided="%s",' % n)
+ n = maxNumChild
+ self.put('arraydata="')
+ self.put(self.readMemory(addrBase, n * innerSize))
+ self.put('",')
+ else:
+ with Children(self, n, innerType, childNumChild, maxNumChild,
+ addrBase=addrBase, addrStep=innerSize):
+ for i in self.childRange():
+ self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType))
+
+ def putArrayItem(self, name, addr, n, typeName):
+ self.checkIntType(addr)
+ self.checkIntType(n)
+ with SubItem(self, name):
+ self.putEmptyValue()
+ self.putType('%s [%d]' % (typeName, n))
+ self.putArrayData(addr, n, self.lookupType(typeName))
+ self.putAddress(addr)
+
+ def putPlotDataHelper(self, base, n, innerType, maxNumChild=1000 * 1000):
+ if n > maxNumChild:
+ self.putField('plotelided', n) # FIXME: Act on that in frontend
+ n = maxNumChild
+ if self.currentItemFormat() == DisplayFormat.ArrayPlot and innerType.isSimpleType():
+ enc = innerType.simpleEncoding()
+ if enc:
+ self.putField('editencoding', enc)
+ self.putDisplay('plotdata:separate',
+ self.readMemory(base, n * innerType.size()))
+
+ def putPlotData(self, base, n, innerType, maxNumChild=1000 * 1000):
+ self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild)
+ if self.isExpanded():
+ self.putArrayData(base, n, innerType)
+
+ def putSpecialArgv(self, value):
+ """
+ Special handling for char** argv.
+ """
+ n = 0
+ p = value
+ # p is 0 for "optimized out" cases. Or contains rubbish.
+ try:
+ if value.integer():
+ while p.dereference().integer() and n <= 100:
+ p += 1
+ n += 1
+ except:
+ pass
+
+ with TopLevelItem(self, 'local.argv'):
+ self.put('iname="local.argv",name="argv",')
+ self.putItemCount(n, 100)
+ self.putType('char **')
+ if self.currentIName in self.expandedINames:
+ p = value
+ with Children(self, n):
+ for i in range(n):
+ self.putSubItem(i, p.dereference())
+ p += 1
+
+ def extractPointer(self, value):
+ try:
+ if value.type.code == TypeCode.Array:
+ return value.address()
+ except:
+ pass
+ code = 'I' if self.ptrSize() == 4 else 'Q'
+ return self.extractSomething(value, code, 8 * self.ptrSize())
+
+ def extractInt64(self, value):
+ return self.extractSomething(value, 'q', 64)
+
+ def extractUInt64(self, value):
+ return self.extractSomething(value, 'Q', 64)
+
+ def extractInt(self, value):
+ return self.extractSomething(value, 'i', 32)
+
+ def extractUInt(self, value):
+ return self.extractSomething(value, 'I', 32)
+
+ def extractShort(self, value):
+ return self.extractSomething(value, 'h', 16)
+
+ def extractUShort(self, value):
+ return self.extractSomething(value, 'H', 16)
+
+ def extractByte(self, value):
+ return self.extractSomething(value, 'b', 8)
+
+ def extractSomething(self, value, pattern, bitsize):
+ if self.isInt(value):
+ val = self.Value(self)
+ val.laddress = value
+ return val.extractSomething(pattern, bitsize)
+ if isinstance(value, self.Value):
+ return value.extractSomething(pattern, bitsize)
+ raise RuntimeError('CANT EXTRACT FROM %s' % type(value))
+
+ # Parses a..b and a.(s).b
+ def parseRange(self, exp):
+
+ # Search for the first unbalanced delimiter in s
+ def searchUnbalanced(s, upwards):
+ paran = 0
+ bracket = 0
+ if upwards:
+ open_p, close_p, open_b, close_b = '(', ')', '[', ']'
+ else:
+ open_p, close_p, open_b, close_b = ')', '(', ']', '['
+ for i in range(len(s)):
+ c = s[i]
+ if c == open_p:
+ paran += 1
+ elif c == open_b:
+ bracket += 1
+ elif c == close_p:
+ paran -= 1
+ if paran < 0:
+ return i
+ elif c == close_b:
+ bracket -= 1
+ if bracket < 0:
+ return i
+ return len(s)
+
+ match = re.search(r'(\.)(\(.+?\))?(\.)', exp)
+ if match:
+ s = match.group(2)
+ left_e = match.start(1)
+ left_s = 1 + left_e - searchUnbalanced(exp[left_e::-1], False)
+ right_s = match.end(3)
+ right_e = right_s + searchUnbalanced(exp[right_s:], True)
+ template = exp[:left_s] + '%s' + exp[right_e:]
+
+ a = exp[left_s:left_e]
+ b = exp[right_s:right_e]
+
+ try:
+ # Allow integral expressions.
+ ss = self.parseAndEvaluate(s[1:len(s) - 1]).integer() if s else 1
+ aa = self.parseAndEvaluate(a).integer()
+ bb = self.parseAndEvaluate(b).integer()
+ if aa < bb and ss > 0:
+ return True, aa, ss, bb + 1, template
+ except:
+ pass
+ return False, 0, 1, 1, exp
+
+ def putNumChild(self, numchild):
+ if numchild != self.currentChildNumChild:
+ self.putField('numchild', numchild)
+
+ def handleLocals(self, variables):
+ #DumperBase.warn('VARIABLES: %s' % variables)
+ #with self.timer('locals'):
+ shadowed = {}
+ for value in variables:
+ if value.name == 'argv':
+ if value.type.code == TypeCode.Pointer:
+ if value.type.ltarget.code == TypeCode.Pointer:
+ if value.type.ltarget.ltarget.name == 'char':
+ self.putSpecialArgv(value)
+ continue
+
+ name = value.name
+ if name in shadowed:
+ level = shadowed[name]
+ shadowed[name] = level + 1
+ name += '@%d' % level
+ else:
+ shadowed[name] = 1
+ # A 'normal' local variable or parameter.
+ iname = value.iname if hasattr(value, 'iname') else 'local.' + name
+ with TopLevelItem(self, iname):
+ #with self.timer('all-' + iname):
+ self.putField('iname', iname)
+ self.putField('name', name)
+ self.putItem(value)
+
+ def handleWatches(self, args):
+ #with self.timer('watches'):
+ for watcher in args.get('watchers', []):
+ iname = watcher['iname']
+ exp = self.hexdecode(watcher['exp'])
+ self.handleWatch(exp, exp, iname)
+
+ def handleWatch(self, origexp, exp, iname):
+ exp = str(exp).strip()
+ escapedExp = self.hexencode(exp)
+ #DumperBase.warn('HANDLING WATCH %s -> %s, INAME: "%s"' % (origexp, exp, iname))
+
+ # Grouped items separated by semicolon.
+ if exp.find(';') >= 0:
+ exps = exp.split(';')
+ n = len(exps)
+ with TopLevelItem(self, iname):
+ self.putField('iname', iname)
+ #self.putField('wname', escapedExp)
+ self.putField('name', exp)
+ self.putField('exp', exp)
+ self.putItemCount(n)
+ self.putNoType()
+ for i in range(n):
+ self.handleWatch(exps[i], exps[i], '%s.%d' % (iname, i))
+ return
+
+ # Special array index: e.g a[1..199] or a[1.(3).199] for stride 3.
+ isRange, begin, step, end, template = self.parseRange(exp)
+ if isRange:
+ #DumperBase.warn('RANGE: %s %s %s in %s' % (begin, step, end, template))
+ r = range(begin, end, step)
+ n = len(r)
+ with TopLevelItem(self, iname):
+ self.putField('iname', iname)
+ #self.putField('wname', escapedExp)
+ self.putField('name', exp)
+ self.putField('exp', exp)
+ self.putItemCount(n)
+ self.putNoType()
+ with Children(self, n):
+ for i in r:
+ e = template % i
+ self.handleWatch(e, e, '%s.%s' % (iname, i))
+ return
+
+ # Fall back to less special syntax
+ #return self.handleWatch(origexp, exp, iname)
+
+ with TopLevelItem(self, iname):
+ self.putField('iname', iname)
+ self.putField('wname', escapedExp)
+ try:
+ value = self.parseAndEvaluate(exp)
+ self.putItem(value)
+ except Exception:
+ self.currentType.value = ' '
+ self.currentValue.value = '<no such value>'
+ self.currentChildNumChild = -1
+ self.currentNumChild = 0
+ self.putNumChild(0)
+
+ def registerDumper(self, funcname, function):
+ try:
+ if funcname.startswith('qdump__'):
+ typename = funcname[7:]
+ if sys.version_info > (3,):
+ spec = inspect.getfullargspec(function)
+ else:
+ spec = inspect.getargspec(function)
+ if len(spec.args) == 2:
+ self.qqDumpers[typename] = function
+ elif len(spec.args) == 3 and len(spec.defaults) == 1:
+ self.qqDumpersEx[spec.defaults[0]] = function
+ self.qqFormats[typename] = self.qqFormats.get(typename, [])
+ elif funcname.startswith('qform__'):
+ typename = funcname[7:]
+ try:
+ self.qqFormats[typename] = function()
+ except:
+ self.qqFormats[typename] = []
+ elif funcname.startswith('qedit__'):
+ typename = funcname[7:]
+ try:
+ self.qqEditable[typename] = function
+ except:
+ pass
+ except:
+ pass
+
+ def setupDumpers(self, _={}):
+ self.resetCaches()
+
+ for mod in self.dumpermodules:
+ try:
+ m = __import__(mod)
+ dic = m.__dict__
+ for name in dic.keys():
+ item = dic[name]
+ self.registerDumper(name, item)
+ except Exception as e:
+ print('Failed to load dumper module: %s (%s)' % (mod, e))
+
+ msg = 'dumpers=['
+ for key, value in self.qqFormats.items():
+ editable = ',editable="true"' if key in self.qqEditable else ''
+ formats = (',formats=\"%s\"' % str(value)[1:-1]) if len(value) else ''
+ msg += '{type="%s"%s%s},' % (key, editable, formats)
+ msg += '],'
+ v = 10000 * sys.version_info[0] + 100 * sys.version_info[1] + sys.version_info[2]
+ msg += 'python="%d"' % v
+ return msg
+
+ def reloadDumpers(self, args):
+ for mod in self.dumpermodules:
+ m = sys.modules[mod]
+ if sys.version_info[0] >= 3:
+ import importlib
+ importlib.reload(m)
+ else:
+ reload(m)
+ self.setupDumpers(args)
+
+ def loadDumpers(self, args):
+ msg = self.setupDumpers()
+ self.reportResult(msg, args)
+
+ def addDumperModule(self, args):
+ path = args['path']
+ (head, tail) = os.path.split(path)
+ sys.path.insert(1, head)
+ self.dumpermodules.append(os.path.splitext(tail)[0])
+
+ def extractQStringFromQDataStream(self, buf, offset):
+ """ Read a QString from the stream """
+ size = struct.unpack_from('!I', buf, offset)[0]
+ offset += 4
+ string = buf[offset:offset + size].decode('utf-16be')
+ return (string, offset + size)
+
+ def extractQByteArrayFromQDataStream(self, buf, offset):
+ """ Read a QByteArray from the stream """
+ size = struct.unpack_from('!I', buf, offset)[0]
+ offset += 4
+ string = buf[offset:offset + size].decode('latin1')
+ return (string, offset + size)
+
+ def extractIntFromQDataStream(self, buf, offset):
+ """ Read an int from the stream """
+ value = struct.unpack_from('!I', buf, offset)[0]
+ return (value, offset + 4)
+
+ def handleInterpreterMessage(self):
+ """ Return True if inferior stopped """
+ resdict = self.fetchInterpreterResult()
+ return resdict.get('event') == 'break'
+
+ def reportInterpreterResult(self, resdict, args):
+ print('interpreterresult=%s,token="%s"'
+ % (self.resultToMi(resdict), args.get('token', -1)))
+
+ def reportInterpreterAsync(self, resdict, asyncclass):
+ print('interpreterasync=%s,asyncclass="%s"'
+ % (self.resultToMi(resdict), asyncclass))
+
+ def removeInterpreterBreakpoint(self, args):
+ res = self.sendInterpreterRequest('removebreakpoint', {'id': args['id']})
+ return res
+
+ def insertInterpreterBreakpoint(self, args):
+ args['condition'] = self.hexdecode(args.get('condition', ''))
+ # Will fail if the service is not yet up and running.
+ response = self.sendInterpreterRequest('setbreakpoint', args)
+ resdict = args.copy()
+ bp = None if response is None else response.get('breakpoint', None)
+ if bp:
+ resdict['number'] = bp
+ resdict['pending'] = 0
+ else:
+ self.createResolvePendingBreakpointsHookBreakpoint(args)
+ resdict['number'] = -1
+ resdict['pending'] = 1
+ resdict['warning'] = 'Direct interpreter breakpoint insertion failed.'
+ self.reportInterpreterResult(resdict, args)
+
+ def resolvePendingInterpreterBreakpoint(self, args):
+ self.parseAndEvaluate('qt_qmlDebugEnableService("NativeQmlDebugger")')
+ response = self.sendInterpreterRequest('setbreakpoint', args)
+ bp = None if response is None else response.get('breakpoint', None)
+ resdict = args.copy()
+ if bp:
+ resdict['number'] = bp
+ resdict['pending'] = 0
+ else:
+ resdict['number'] = -1
+ resdict['pending'] = 0
+ resdict['error'] = 'Pending interpreter breakpoint insertion failed.'
+ self.reportInterpreterAsync(resdict, 'breakpointmodified')
+
+ def fetchInterpreterResult(self):
+ buf = self.parseAndEvaluate('qt_qmlDebugMessageBuffer')
+ size = self.parseAndEvaluate('qt_qmlDebugMessageLength')
+ msg = self.hexdecode(self.readMemory(buf, size))
+ # msg is a sequence of 'servicename<space>msglen<space>msg' items.
+ resdict = {} # Native payload.
+ while len(msg):
+ pos0 = msg.index(' ') # End of service name
+ pos1 = msg.index(' ', pos0 + 1) # End of message length
+ service = msg[0:pos0]
+ msglen = int(msg[pos0 + 1:pos1])
+ msgend = pos1 + 1 + msglen
+ payload = msg[pos1 + 1:msgend]
+ msg = msg[msgend:]
+ if service == 'NativeQmlDebugger':
+ try:
+ resdict = json.loads(payload)
+ continue
+ except:
+ self.warn('Cannot parse native payload: %s' % payload)
+ else:
+ print('interpreteralien=%s'
+ % {'service': service, 'payload': self.hexencode(payload)})
+ try:
+ expr = 'qt_qmlDebugClearBuffer()'
+ res = self.parseAndEvaluate(expr)
+ except RuntimeError as error:
+ self.warn('Cleaning buffer failed: %s: %s' % (expr, error))
+
+ return resdict
+
+ def sendInterpreterRequest(self, command, args={}):
+ encoded = json.dumps({'command': command, 'arguments': args})
+ hexdata = self.hexencode(encoded)
+ expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
+ try:
+ res = self.parseAndEvaluate(expr)
+ except RuntimeError as error:
+ self.warn('Interpreter command failed: %s: %s' % (encoded, error))
+ return {}
+ except AttributeError as error:
+ # Happens with LLDB and 'None' current thread.
+ self.warn('Interpreter command failed: %s: %s' % (encoded, error))
+ return {}
+ if not res:
+ self.warn('Interpreter command failed: %s ' % encoded)
+ return {}
+ return self.fetchInterpreterResult()
+
+ def executeStep(self, args):
+ if self.nativeMixed:
+ response = self.sendInterpreterRequest('stepin', args)
+ self.doContinue()
+
+ def executeStepOut(self, args):
+ if self.nativeMixed:
+ response = self.sendInterpreterRequest('stepout', args)
+ self.doContinue()
+
+ def executeNext(self, args):
+ if self.nativeMixed:
+ response = self.sendInterpreterRequest('stepover', args)
+ self.doContinue()
+
+ def executeContinue(self, args):
+ if self.nativeMixed:
+ response = self.sendInterpreterRequest('continue', args)
+ self.doContinue()
+
+ def doInsertInterpreterBreakpoint(self, args, wasPending):
+ #DumperBase.warn('DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s' % wasPending)
+ # Will fail if the service is not yet up and running.
+ response = self.sendInterpreterRequest('setbreakpoint', args)
+ bp = None if response is None else response.get('breakpoint', None)
+ if wasPending:
+ if not bp:
+ self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
+ 'error': 'Pending interpreter breakpoint insertion failed.'}, args)
+ return
+ else:
+ if not bp:
+ self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
+ 'warning': 'Direct interpreter breakpoint insertion failed.'}, args)
+ self.createResolvePendingBreakpointsHookBreakpoint(args)
+ return
+ self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args)
+
+ def isInternalInterpreterFrame(self, functionName):
+ if functionName is None:
+ return False
+ if functionName.startswith('qt_v4'):
+ return True
+ return functionName.startswith(self.qtNamespace() + 'QV4::')
+
+ # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit
+ # due to misaligned %ebx in SSE calls (qstring.cpp:findChar)
+ def canCallLocale(self):
+ return True
+
+ def isReportableInterpreterFrame(self, functionName):
+ return functionName and functionName.find('QV4::Moth::VME::exec') >= 0
+
+ def extractInterpreterStack(self):
+ return self.sendInterpreterRequest('backtrace', {'limit': 10})
+
+ def isInt(self, thing):
+ if isinstance(thing, int):
+ return True
+ if sys.version_info[0] == 2:
+ if isinstance(thing, long):
+ return True
+ return False
+
+ def putItems(self, count, generator, maxNumChild=10000):
+ self.putItemCount(count)
+ if self.isExpanded():
+ with Children(self, count, maxNumChild=maxNumChild):
+ for i, val in zip(self.childRange(), generator):
+ self.putSubItem(i, val)
+
+ def putItem(self, value):
+ #with self.timer('putItem'):
+ self.putItemX(value)
+
+ def putItemX(self, value):
+ #DumperBase.warn('PUT ITEM: %s' % value.stringify())
+
+ typeobj = value.type # unqualified()
+ typeName = typeobj.name
+
+ self.addToCache(typeobj) # Fill type cache
+
+ if not value.lIsInScope:
+ self.putSpecialValue('optimizedout')
+ #self.putType(typeobj)
+ #self.putSpecialValue('outofscope')
+ self.putNumChild(0)
+ return
+
+ if not isinstance(value, self.Value):
+ raise RuntimeError('WRONG TYPE IN putItem: %s' % type(self.Value))
+
+ # Try on possibly typedefed type first.
+ if self.tryPutPrettyItem(typeName, value):
+ if typeobj.code == TypeCode.Pointer:
+ self.putOriginalAddress(value.address())
+ else:
+ self.putAddress(value.address())
+ return
+
+ if typeobj.code == TypeCode.Typedef:
+ #DumperBase.warn('TYPEDEF VALUE: %s' % value.stringify())
+ self.putItem(value.detypedef())
+ self.putBetterType(typeName)
+ return
+
+ if typeobj.code == TypeCode.Pointer:
+ self.putFormattedPointer(value)
+ if value.summary and self.useFancy:
+ self.putValue(self.hexencode(value.summary), 'utf8:1:0')
+ return
+
+ self.putAddress(value.address())
+ if value.lbitsize is not None:
+ self.putField('size', value.lbitsize // 8)
+
+ if typeobj.code == TypeCode.Function:
+ #DumperBase.warn('FUNCTION VALUE: %s' % value)
+ self.putType(typeobj)
+ self.putSymbolValue(value.pointer())
+ self.putNumChild(0)
+ return
+
+ if typeobj.code == TypeCode.Enum:
+ #DumperBase.warn('ENUM VALUE: %s' % value.stringify())
+ self.putType(typeobj.name)
+ self.putValue(value.display())
+ self.putNumChild(0)
+ return
+
+ if typeobj.code == TypeCode.Array:
+ #DumperBase.warn('ARRAY VALUE: %s' % value)
+ self.putCStyleArray(value)
+ return
+
+ if typeobj.code == TypeCode.Bitfield:
+ #DumperBase.warn('BITFIELD VALUE: %s %d %s' % (value.name, value.lvalue, typeName))
+ self.putNumChild(0)
+ dd = typeobj.ltarget.tdata.enumDisplay
+ self.putValue(str(value.lvalue) if dd is None else dd(
+ value.lvalue, value.laddress, '%d'))
+ self.putType(typeName)
+ return
+
+ if typeobj.code == TypeCode.Integral:
+ #DumperBase.warn('INTEGER: %s %s' % (value.name, value))
+ val = value.value()
+ self.putNumChild(0)
+ self.putValue(val)
+ self.putType(typeName)
+ return
+
+ if typeobj.code == TypeCode.Float:
+ #DumperBase.warn('FLOAT VALUE: %s' % value)
+ self.putValue(value.value())
+ self.putNumChild(0)
+ self.putType(typeobj.name)
+ return
+
+ if typeobj.code in (TypeCode.Reference, TypeCode.RValueReference):
+ #DumperBase.warn('REFERENCE VALUE: %s' % value)
+ val = value.dereference()
+ if val.laddress != 0:
+ self.putItem(val)
+ else:
+ self.putSpecialValue('nullreference')
+ self.putBetterType(typeName)
+ return
+
+ if typeobj.code == TypeCode.Complex:
+ self.putType(typeobj)
+ self.putValue(value.display())
+ self.putNumChild(0)
+ return
+
+ if typeobj.code == TypeCode.FortranString:
+ self.putValue(self.hexencode(value.data()), 'latin1')
+ self.putNumChild(0)
+ self.putType(typeobj)
+
+ if typeName.endswith('[]'):
+ # D arrays, gdc compiled.
+ n = value['length']
+ base = value['ptr']
+ self.putType(typeName)
+ self.putItemCount(n)
+ if self.isExpanded():
+ self.putArrayData(base.pointer(), n, base.type.target())
+ return
+
+ #DumperBase.warn('SOME VALUE: %s' % value)
+ #DumperBase.warn('GENERIC STRUCT: %s' % typeobj)
+ #DumperBase.warn('INAME: %s ' % self.currentIName)
+ #DumperBase.warn('INAMES: %s ' % self.expandedINames)
+ #DumperBase.warn('EXPANDED: %s ' % (self.currentIName in self.expandedINames))
+ self.putType(typeName)
+
+ if value.summary is not None and self.useFancy:
+ self.putValue(self.hexencode(value.summary), 'utf8:1:0')
+ self.putNumChild(0)
+ return
+
+ self.putExpandable()
+ self.putEmptyValue()
+ #DumperBase.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address()))
+ if self.showQObjectNames:
+ #with self.timer(self.currentIName):
+ self.putQObjectNameValue(value)
+ if self.isExpanded():
+ if not self.isCli:
+ self.putField('sortable', 1)
+ with Children(self, 1, childType=None):
+ self.putFields(value)
+ if self.showQObjectNames:
+ self.tryPutQObjectGuts(value)
+
+ def symbolAddress(self, symbolName):
+ res = self.parseAndEvaluate('(size_t)&' + symbolName)
+ return None if res is None else res.pointer()
+
+ def qtHookDataSymbolName(self):
+ return 'qtHookData'
+
+ def qtTypeInfoVersion(self):
+ addr = self.symbolAddress(self.qtHookDataSymbolName())
+ if addr:
+ # Only available with Qt 5.3+
+ (hookVersion, x, x, x, x, x, tiVersion) = self.split('ppppppp', addr)
+ #DumperBase.warn('HOOK: %s TI: %s' % (hookVersion, tiVersion))
+ if hookVersion >= 3:
+ self.qtTypeInfoVersion = lambda: tiVersion
+ return tiVersion
+ return None
+
+ def qtDeclarativeHookDataSymbolName(self):
+ return 'qtDeclarativeHookData'
+
+ def qtDeclarativeTypeInfoVersion(self):
+ addr = self.symbolAddress(self.qtDeclarativeHookDataSymbolName())
+ if addr:
+ # Only available with Qt 5.6+
+ (hookVersion, x, tiVersion) = self.split('ppp', addr)
+ if hookVersion >= 1:
+ self.qtTypeInfoVersion = lambda: tiVersion
+ return tiVersion
+ return None
+
+ def addToCache(self, typeobj):
+ typename = typeobj.name
+ if typename in self.typesReported:
+ return
+ self.typesReported[typename] = True
+ self.typesToReport[typename] = typeobj
+
+ class Value():
+ def __init__(self, dumper):
+ # This can be helpful to track down from where a Value was created
+ #self._stack = inspect.stack()
+ self.dumper = dumper
+ self.name = None
+ self._type = None
+ self.ldata = None # Target address in case of references and pointers.
+ self.laddress = None # Own address.
+ self.lvalue = None
+ self.lIsInScope = True
+ self.ldisplay = None
+ self.summary = None # Always hexencoded UTF-8.
+ self.lbitpos = None
+ self.lbitsize = None
+ self.targetValue = None # For references.
+ self.isBaseClass = None
+ self.nativeValue = None
+ self.autoDerefCount = 0
+
+ def copy(self):
+ val = self.dumper.Value(self.dumper)
+ val.dumper = self.dumper
+ val.name = self.name
+ val._type = self._type
+ val.ldata = self.ldata
+ val.laddress = self.laddress
+ val.lIsInScope = self.lIsInScope
+ val.ldisplay = self.ldisplay
+ val.summary = self.summary
+ val.lbitpos = self.lbitpos
+ val.lbitsize = self.lbitsize
+ val.targetValue = self.targetValue
+ val.nativeValue = self.nativeValue
+ return val
+
+ @property
+ def type(self):
+ if self._type is None and self.nativeValue is not None:
+ self._type = self.dumper.nativeValueType(self.nativeValue)
+ return self._type
+
+ def check(self):
+ if self.laddress is not None and not self.dumper.isInt(self.laddress):
+ raise RuntimeError('INCONSISTENT ADDRESS: %s' % type(self.laddress))
+ if self.type is not None and not isinstance(self.type, self.dumper.Type):
+ raise RuntimeError('INCONSISTENT TYPE: %s' % type(self.type))
+
+ def __str__(self):
+ #raise RuntimeError('Not implemented')
+ return self.stringify()
+
+ def __int__(self):
+ return self.integer()
+
+ def stringify(self):
+ addr = 'None' if self.laddress is None else ('0x%x' % self.laddress)
+ return "Value(name='%s',type=%s,bsize=%s,bpos=%s,data=%s,address=%s)" \
+ % (self.name, self.type.name, self.lbitsize, self.lbitpos,
+ self.dumper.hexencode(self.ldata), addr)
+
+ def displayEnum(self, form='%d', bitsize=None):
+ intval = self.integer(bitsize)
+ dd = self.type.tdata.enumDisplay
+ if dd is None:
+ return str(intval)
+ return dd(intval, self.laddress, form)
+
+ def display(self):
+ if self.ldisplay is not None:
+ return self.ldisplay
+ simple = self.value()
+ if simple is not None:
+ return str(simple)
+ #if self.ldata is not None:
+ # if sys.version_info[0] == 2 and isinstance(self.ldata, buffer):
+ # return bytes(self.ldata).encode('hex')
+ # return self.ldata.encode('hex')
+ if self.laddress is not None:
+ return 'value of type %s at address 0x%x' % (self.type.name, self.laddress)
+ return '<unknown data>'
+
+ def pointer(self):
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().pointer()
+ return self.extractInteger(self.dumper.ptrSize() * 8, True)
+
+ def integer(self, bitsize=None):
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().integer()
+ elif isinstance(self.lvalue, int):
+ return self.lvalue
+ # Could be something like 'short unsigned int'
+ unsigned = self.type.name == 'unsigned' \
+ or self.type.name.startswith('unsigned ') \
+ or self.type.name.find(' unsigned ') != -1
+ if bitsize is None:
+ bitsize = self.type.bitsize()
+ return self.extractInteger(bitsize, unsigned)
+
+ def floatingPoint(self):
+ if self.nativeValue is not None and not self.dumper.isCdb:
+ return str(self.nativeValue)
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().floatingPoint()
+ if self.type.size() == 8:
+ return self.extractSomething('d', 64)
+ if self.type.size() == 4:
+ return self.extractSomething('f', 32)
+ # Fall back in case we don't have a nativeValue at hand.
+ # FIXME: This assumes Intel's 80bit extended floats. Which might
+ # be wrong.
+ l, h = self.split('QQ')
+ if True: # 80 bit floats
+ sign = (h >> 15) & 1
+ exp = (h & 0x7fff)
+ fraction = l
+ bit63 = (l >> 63) & 1
+ #DumperBase.warn("SIGN: %s EXP: %s H: 0x%x L: 0x%x" % (sign, exp, h, l))
+ if exp == 0:
+ if bit63 == 0:
+ if l == 0:
+ res = '-0' if sign else '0'
+ else:
+ res = (-1)**sign * l * 2**(-16382) # subnormal
+ else:
+ res = 'pseudodenormal'
+ elif exp == 0x7fff:
+ res = 'special'
+ else:
+ res = (-1)**sign * l * 2**(exp - 16383 - 63)
+ else: # 128 bits
+ sign = h >> 63
+ exp = (h >> 48) & 0x7fff
+ fraction = h & (2**48 - 1)
+ #DumperBase.warn("SIGN: %s EXP: %s FRAC: %s H: 0x%x L: 0x%x" % (sign, exp, fraction, h, l))
+ if exp == 0:
+ if fraction == 0:
+ res = -0.0 if sign else 0.0
+ else:
+ res = (-1)**sign * fraction / 2**48 * 2**(-62) # subnormal
+ elif exp == 0x7fff:
+ res = ('-inf' if sign else 'inf') if fraction == 0 else 'nan'
+ else:
+ res = (-1)**sign * (1 + fraction / 2**48) * 2**(exp - 63)
+ return res
+
+ def value(self):
+ if self.type is not None:
+ if self.type.code == TypeCode.Enum:
+ return self.displayEnum()
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().value()
+ if self.type.code == TypeCode.Integral:
+ return self.integer()
+ if self.type.code == TypeCode.Bitfield:
+ return self.integer()
+ if self.type.code == TypeCode.Float:
+ return self.floatingPoint()
+ if self.type.code == TypeCode.Pointer:
+ return self.pointer()
+ return None
+
+ def extractPointer(self):
+ return self.split('p')[0]
+
+ def hasMember(self, name):
+ return self.findMemberByName(name) is not None
+
+ def findMemberByName(self, name):
+ self.check()
+ if self.type.code == TypeCode.Typedef:
+ return self.findMemberByName(self.detypedef())
+ if self.type.code in (
+ TypeCode.Pointer,
+ TypeCode.Reference,
+ TypeCode.RValueReference):
+ res = self.dereference().findMemberByName(name)
+ if res is not None:
+ return res
+ if self.type.code == TypeCode.Struct:
+ #DumperBase.warn('SEARCHING FOR MEMBER: %s IN %s' % (name, self.type.name))
+ members = self.members(True)
+ #DumperBase.warn('MEMBERS: %s' % members)
+ for member in members:
+ #DumperBase.warn('CHECKING FIELD %s' % member.name)
+ if member.type.code == TypeCode.Typedef:
+ member = member.detypedef()
+ if member.name == name:
+ return member
+ for member in members:
+ if member.type.code == TypeCode.Typedef:
+ member = member.detypedef()
+ if member.name == name: # Could be base class.
+ return member
+ if member.type.code == TypeCode.Struct:
+ res = member.findMemberByName(name)
+ if res is not None:
+ return res
+ return None
+
+ def __getitem__(self, index):
+ #DumperBase.warn('GET ITEM %s %s' % (self, index))
+ self.check()
+ if self.type.code == TypeCode.Typedef:
+ #DumperBase.warn('GET ITEM STRIP TYPEDEFS TO %s' % self.type.ltarget)
+ return self.cast(self.type.ltarget).__getitem__(index)
+ if isinstance(index, str):
+ if self.type.code == TypeCode.Pointer:
+ #DumperBase.warn('GET ITEM %s DEREFERENCE TO %s' % (self, self.dereference()))
+ return self.dereference().__getitem__(index)
+ res = self.findMemberByName(index)
+ if res is None:
+ raise RuntimeError('No member named %s in type %s'
+ % (index, self.type.name))
+ return res
+ elif isinstance(index, self.dumper.Field):
+ field = index
+ elif self.dumper.isInt(index):
+ if self.type.code == TypeCode.Array:
+ addr = self.laddress + int(index) * self.type.ltarget.size()
+ return self.dumper.createValue(addr, self.type.ltarget)
+ if self.type.code == TypeCode.Pointer:
+ addr = self.pointer() + int(index) * self.type.ltarget.size()
+ return self.dumper.createValue(addr, self.type.ltarget)
+ return self.members(False)[index]
+ else:
+ raise RuntimeError('BAD INDEX TYPE %s' % type(index))
+ field.check()
+
+ #DumperBase.warn('EXTRACT FIELD: %s, BASE 0x%x' % (field, self.address()))
+ if self.type.code == TypeCode.Pointer:
+ #DumperBase.warn('IS TYPEDEFED POINTER!')
+ res = self.dereference()
+ #DumperBase.warn('WAS POINTER: %s' % res)
+
+ return field.extract(self)
+
+ def extractField(self, field):
+ if not isinstance(field, self.dumper.Field):
+ raise RuntimeError('BAD INDEX TYPE %s' % type(field))
+
+ if field.extractor is not None:
+ val = field.extractor(self)
+ if val is not None:
+ #DumperBase.warn('EXTRACTOR SUCCEEDED: %s ' % val)
+ return val
+
+ if self.type.code == TypeCode.Typedef:
+ return self.cast(self.type.ltarget).extractField(field)
+ if self.type.code in (TypeCode.Reference, TypeCode.RValueReference):
+ return self.dereference().extractField(field)
+ #DumperBase.warn('FIELD: %s ' % (field,))
+ val = self.dumper.Value(self.dumper)
+ val.name = field.name
+ val.isBaseClass = field.isBase
+ val._type = field.fieldType()
+
+ if field.isArtificial:
+ if self.laddress is not None:
+ val.laddress = self.laddress
+ if self.ldata is not None:
+ val.ldata = self.ldata
+ return val
+
+ fieldBitsize = field.bitsize
+ fieldSize = (fieldBitsize + 7) // 8
+ fieldBitpos = field.bitpos
+ fieldOffset = fieldBitpos // 8
+ fieldType = field.fieldType()
+
+ if fieldType.code == TypeCode.Bitfield:
+ fieldBitpos -= fieldOffset * 8
+ ldata = self.data()
+ data = 0
+ for i in range(fieldSize):
+ data = data << 8
+ if self.dumper.isBigEndian:
+ lbyte = ldata[i]
+ else:
+ lbyte = ldata[fieldOffset + fieldSize - 1 - i]
+ if isinstance(lbyte, (str, bytes)):
+ data += ord(lbyte)
+ else:
+ data += lbyte
+ data = data >> fieldBitpos
+ data = data & ((1 << fieldBitsize) - 1)
+ val.lvalue = data
+ val.laddress = None
+ return val
+
+ if self.laddress is not None:
+ val.laddress = self.laddress + fieldOffset
+ elif self.ldata is not None:
+ val.ldata = self.ldata[fieldOffset:fieldOffset + fieldSize]
+ else:
+ self.dumper.check(False)
+
+ if fieldType.code in (TypeCode.Reference, TypeCode.RValueReference):
+ if val.laddress is not None:
+ val = self.dumper.createReferenceValue(val.laddress, fieldType.ltarget)
+ val.name = field.name
+
+ #DumperBase.warn('GOT VAL %s FOR FIELD %s' % (val, field))
+ val.lbitsize = fieldBitsize
+ val.check()
+ return val
+
+ # This is the generic version for synthetic values.
+ # The native backends replace it in their fromNativeValue()
+ # implementations.
+ def members(self, includeBases):
+ #DumperBase.warn("LISTING MEMBERS OF %s" % self)
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().members(includeBases)
+
+ tdata = self.type.tdata
+ #if isinstance(tdata.lfields, list):
+ # return tdata.lfields
+
+ fields = []
+ if tdata.lfields is not None:
+ if isinstance(tdata.lfields, list):
+ fields = tdata.lfields
+ else:
+ fields = list(tdata.lfields(self))
+
+ #DumperBase.warn("FIELDS: %s" % fields)
+ res = []
+ for field in fields:
+ if isinstance(field, self.dumper.Value):
+ #DumperBase.warn("USING VALUE DIRECTLY %s" % field.name)
+ res.append(field)
+ continue
+ if field.isBase and not includeBases:
+ #DumperBase.warn("DROPPING BASE %s" % field.name)
+ continue
+ res.append(self.extractField(field))
+ #DumperBase.warn("GOT MEMBERS: %s" % res)
+ return res
+
+ def __add__(self, other):
+ self.check()
+ if self.dumper.isInt(other):
+ stripped = self.type.stripTypedefs()
+ if stripped.code == TypeCode.Pointer:
+ address = self.pointer() + stripped.dereference().size() * other
+ val = self.dumper.Value(self.dumper)
+ val.laddress = None
+ val.ldata = bytes(struct.pack(self.dumper.packCode + 'Q', address))
+ val._type = self._type
+ return val
+ raise RuntimeError('BAD DATA TO ADD TO: %s %s' % (self.type, other))
+
+ def __sub__(self, other):
+ self.check()
+ if self.type.name == other.type.name:
+ stripped = self.type.stripTypedefs()
+ if stripped.code == TypeCode.Pointer:
+ return (self.pointer() - other.pointer()) // stripped.dereference().size()
+ raise RuntimeError('BAD DATA TO SUB TO: %s %s' % (self.type, other))
+
+ def dereference(self):
+ self.check()
+ if self.type.code == TypeCode.Typedef:
+ return self.detypedef().dereference()
+ val = self.dumper.Value(self.dumper)
+ if self.type.code in (TypeCode.Reference, TypeCode.RValueReference):
+ val.summary = self.summary
+ if self.nativeValue is None:
+ val.laddress = self.pointer()
+ if val.laddress is None and self.laddress is not None:
+ val.laddress = self.laddress
+ val._type = self.type.dereference()
+ if self.dumper.useDynamicType:
+ val._type = self.dumper.nativeDynamicType(val.laddress, val.type)
+ else:
+ val = self.dumper.nativeValueDereferenceReference(self)
+ elif self.type.code == TypeCode.Pointer:
+ try:
+ val = self.dumper.nativeValueDereferencePointer(self)
+ except:
+ val.laddress = self.pointer()
+ val._type = self.type.dereference()
+ if self.dumper.useDynamicType:
+ val._type = self.dumper.nativeDynamicType(val.laddress, val.type)
+ else:
+ raise RuntimeError("WRONG: %s" % self.type.code)
+ #DumperBase.warn("DEREFERENCING FROM: %s" % self)
+ #DumperBase.warn("DEREFERENCING TO: %s" % val)
+ #dynTypeName = val.type.dynamicTypeName(val.laddress)
+ #if dynTypeName is not None:
+ # val._type = self.dumper.createType(dynTypeName)
+ return val
+
+ def detypedef(self):
+ self.check()
+ if self.type.code != TypeCode.Typedef:
+ raise RuntimeError("WRONG")
+ val = self.copy()
+ val._type = self.type.ltarget
+ #DumperBase.warn("DETYPEDEF FROM: %s" % self)
+ #DumperBase.warn("DETYPEDEF TO: %s" % val)
+ return val
+
+ def extend(self, size):
+ if self.type.size() < size:
+ val = self.dumper.Value(self.dumper)
+ val.laddress = None
+ val.ldata = self.zeroExtend(self.ldata)
+ return val
+ if self.type.size() == size:
+ return self
+ raise RuntimeError('NOT IMPLEMENTED')
+
+ def zeroExtend(self, data, size):
+ ext = '\0' * (size - len(data))
+ if sys.version_info[0] == 3:
+ pad = bytes(ext, encoding='latin1')
+ else:
+ pad = bytes(ext)
+ return pad + data if self.dumper.isBigEndian else data + pad
+
+ def cast(self, typish):
+ self.check()
+ val = self.dumper.Value(self.dumper)
+ val.laddress = self.laddress
+ val.lbitsize = self.lbitsize
+ val.ldata = self.ldata
+ val._type = self.dumper.createType(typish)
+ return val
+
+ def address(self):
+ self.check()
+ return self.laddress
+
+ def data(self, size=None):
+ self.check()
+ if self.ldata is not None:
+ if len(self.ldata) > 0:
+ if size is None:
+ return self.ldata
+ if size == len(self.ldata):
+ return self.ldata
+ if size < len(self.ldata):
+ return self.ldata[:size]
+ #raise RuntimeError('ZERO-EXTENDING DATA TO %s BYTES: %s' % (size, self))
+ return self.zeroExtend(self.ldata, size)
+ if self.laddress is not None:
+ if size is None:
+ size = self.type.size()
+ res = self.dumper.readRawMemory(self.laddress, size)
+ if len(res) > 0:
+ return res
+ raise RuntimeError('CANNOT CONVERT ADDRESS TO BYTES: %s' % self)
+ raise RuntimeError('CANNOT CONVERT TO BYTES: %s' % self)
+
+ def extractInteger(self, bitsize, unsigned):
+ #with self.dumper.timer('extractInt'):
+ self.check()
+ if bitsize > 32:
+ size = 8
+ code = 'Q' if unsigned else 'q'
+ elif bitsize > 16:
+ size = 4
+ code = 'I' if unsigned else 'i'
+ elif bitsize > 8:
+ size = 2
+ code = 'H' if unsigned else 'h'
+ else:
+ size = 1
+ code = 'B' if unsigned else 'b'
+ rawBytes = self.data(size)
+ res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0]
+ #DumperBase.warn('Extract: Code: %s Bytes: %s Bitsize: %s Size: %s'
+ # % (self.dumper.packCode + code, self.dumper.hexencode(rawBytes), bitsize, size))
+ return res
+
+ def extractSomething(self, code, bitsize):
+ #with self.dumper.timer('extractSomething'):
+ self.check()
+ size = (bitsize + 7) >> 3
+ rawBytes = self.data(size)
+ res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0]
+ return res
+
+ def to(self, pattern):
+ return self.split(pattern)[0]
+
+ def split(self, pattern):
+ #with self.dumper.timer('split'):
+ #DumperBase.warn('EXTRACT STRUCT FROM: %s' % self.type)
+ (pp, size, fields) = self.dumper.describeStruct(pattern)
+ #DumperBase.warn('SIZE: %s ' % size)
+ result = struct.unpack_from(self.dumper.packCode + pp, self.data(size))
+
+ def structFixer(field, thing):
+ #DumperBase.warn('STRUCT MEMBER: %s' % type(thing))
+ if field.isStruct:
+ #if field.type != field.fieldType():
+ # raise RuntimeError('DO NOT SIMPLIFY')
+ #DumperBase.warn('FIELD POS: %s' % field.type.stringify())
+ #DumperBase.warn('FIELD TYE: %s' % field.fieldType().stringify())
+ res = self.dumper.createValue(thing, field.fieldType())
+ #DumperBase.warn('RES TYPE: %s' % res.type)
+ if self.laddress is not None:
+ res.laddress = self.laddress + field.offset()
+ return res
+ return thing
+ if len(fields) != len(result):
+ raise RuntimeError('STRUCT ERROR: %s %s' % (fields, result))
+ return tuple(map(structFixer, fields, result))
+
+ def checkPointer(self, p, align=1):
+ ptr = p if self.isInt(p) else p.pointer()
+ self.readRawMemory(ptr, 1)
+
+ def type(self, typeId):
+ return self.typeData.get(typeId)
+
+ def splitArrayType(self, type_name):
+ # "foo[2][3][4]" -> ("foo", "[3][4]", 2)
+ pos1 = len(type_name)
+ # In case there are more dimensions we need the inner one.
+ while True:
+ pos1 = type_name.rfind('[', 0, pos1 - 1)
+ pos2 = type_name.find(']', pos1)
+ if type_name[pos1 - 1] != ']':
+ break
+
+ item_count = type_name[pos1 + 1:pos2]
+ return (type_name[0:pos1].strip(), type_name[pos2 + 1:].strip(), int(item_count))
+
+ def registerTypeAlias(self, existingTypeId, aliasId):
+ #DumperBase.warn('REGISTER ALIAS %s FOR %s' % (aliasId, existingTypeId))
+ self.typeData[aliasId] = self.typeData[existingTypeId]
+
+ class TypeData():
+ def __init__(self, dumper, type_id):
+ self.dumper = dumper
+ self.lfields = None # None or Value -> list of member Values
+ self.lalignment = None # Function returning alignment of this struct
+ self.lbitsize = None
+ self.ltarget = None # Inner type for arrays
+ self.templateArguments = None
+ self.code = None
+ self.name = type_id
+ self.typeId = type_id
+ self.enumDisplay = None
+ self.moduleName = None
+ #DumperBase.warn('REGISTER TYPE: %s' % type_id)
+ dumper.typeData[type_id] = self
+
+ def copy(self):
+ tdata = self.dumper.TypeData(self.dumper, self.typeId)
+ tdata.dumper = self.dumper
+ tdata.lfields = self.lfields
+ tdata.lalignment = self.lalignment
+ tdata.lbitsize = self.lbitsize
+ tdata.ltarget = self.ltarget
+ tdata.templateArguments = self.templateArguments
+ tdata.code = self.code
+ tdata.name = self.name
+ tdata.typeId = self.typeId
+ tdata.enumDisplay = self.enumDisplay
+ tdata.moduleName = self.moduleName
+ return tdata
+
+ class Type():
+ def __init__(self, dumper, typeId):
+ self.typeId = typeId
+ self.dumper = dumper
+ self.tdata = dumper.typeData.get(typeId, None)
+ if self.tdata is None:
+ #DumperBase.warn('USING : %s' % self.typeId)
+ self.dumper.lookupType(self.typeId)
+ self.tdata = self.dumper.typeData.get(self.typeId)
+
+ def __str__(self):
+ #return self.typeId
+ return self.stringify()
+
+ @property
+ def name(self):
+ tdata = self.dumper.typeData.get(self.typeId)
+ if tdata is None:
+ return self.typeId
+ return tdata.name
+
+ @property
+ def code(self):
+ return self.tdata.code
+
+ @property
+ def lbitsize(self):
+ return self.tdata.lbitsize
+
+ @property
+ def lbitpos(self):
+ return self.tdata.lbitpos
+
+ @property
+ def ltarget(self):
+ return self.tdata.ltarget
+
+ @property
+ def moduleName(self):
+ return self.tdata.moduleName
+
+ def stringify(self):
+ return 'Type(name="%s",bsize=%s,code=%s)' \
+ % (self.tdata.name, self.tdata.lbitsize, self.tdata.code)
+
+ def __getitem__(self, index):
+ if self.dumper.isInt(index):
+ return self.templateArgument(index)
+ raise RuntimeError('CANNOT INDEX TYPE')
+
+ def dynamicTypeName(self, address):
+ if self.tdata.code != TypeCode.Struct:
+ return None
+ try:
+ vtbl = self.dumper.extractPointer(address)
+ except:
+ return None
+ #DumperBase.warn('VTBL: 0x%x' % vtbl)
+ if not self.dumper.couldBePointer(vtbl):
+ return None
+ return self.dumper.nativeDynamicTypeName(address, self)
+
+ def dynamicType(self, address):
+ # FIXME: That buys some performance at the cost of a fail
+ # of Gdb13393 dumper test.
+ #return self
+ #with self.dumper.timer('dynamicType %s 0x%s' % (self.name, address)):
+ dynTypeName = self.dynamicTypeName(address)
+ if dynTypeName is not None:
+ return self.dumper.createType(dynTypeName)
+ return self
+
+ def check(self):
+ if self.tdata.name is None:
+ raise RuntimeError('TYPE WITHOUT NAME: %s' % self.typeId)
+
+ def dereference(self):
+ if self.code == TypeCode.Typedef:
+ return self.ltarget.dereference()
+ self.check()
+ return self.ltarget
+
+ def unqualified(self):
+ return self
+
+ def templateArguments(self):
+ if self.tdata is None:
+ return self.dumper.listTemplateParameters(self.typeId)
+ return self.tdata.templateArguments()
+
+ def templateArgument(self, position):
+ #DumperBase.warn('TDATA: %s' % self.tdata)
+ #DumperBase.warn('ID: %s' % self.typeId)
+ if self.tdata is None:
+ # Native lookups didn't help. Happens for 'wrong' placement of 'const'
+ # etc. with LLDB. But not all is lost:
+ ta = self.dumper.listTemplateParameters(self.typeId)
+ #DumperBase.warn('MANUAL: %s' % ta)
+ res = ta[position]
+ #DumperBase.warn('RES: %s' % res.typeId)
+ return res
+ #DumperBase.warn('TA: %s %s' % (position, self.typeId))
+ #DumperBase.warn('ARGS: %s' % self.tdata.templateArguments())
+ return self.tdata.templateArguments()[position]
+
+ def simpleEncoding(self):
+ res = {
+ 'bool': 'int:1',
+ 'char': 'int:1',
+ 'int8_t': 'int:1',
+ 'qint8': 'int:1',
+ 'signed char': 'int:1',
+ 'char8_t': 'uint:1',
+ 'unsigned char': 'uint:1',
+ 'uint8_t': 'uint:1',
+ 'quint8': 'uint:1',
+ 'short': 'int:2',
+ 'int16_t': 'int:2',
+ 'qint16': 'int:2',
+ 'unsigned short': 'uint:2',
+ 'char16_t': 'uint:2',
+ 'uint16_t': 'uint:2',
+ 'quint16': 'uint:2',
+ 'int': 'int:4',
+ 'int32_t': 'int:4',
+ 'qint32': 'int:4',
+ 'unsigned int': 'uint:4',
+ 'char32_t': 'uint:4',
+ 'uint32_t': 'uint:4',
+ 'quint32': 'uint:4',
+ 'long long': 'int:8',
+ 'int64_t': 'int:8',
+ 'qint64': 'int:8',
+ 'unsigned long long': 'uint:8',
+ 'uint64_t': 'uint:8',
+ 'quint64': 'uint:8',
+ 'float': 'float:4',
+ 'double': 'float:8',
+ 'QChar': 'uint:2'
+ }.get(self.name, None)
+ return res
+
+ def isSimpleType(self):
+ return self.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum)
+
+ def alignment(self):
+ if self.tdata.code == TypeCode.Typedef:
+ return self.tdata.ltarget.alignment()
+ if self.tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum):
+ if self.tdata.name in ('double', 'long long', 'unsigned long long'):
+ # Crude approximation.
+ return 8 if self.dumper.isWindowsTarget() else self.dumper.ptrSize()
+ return self.size()
+ if self.tdata.code in (TypeCode.Pointer, TypeCode.Reference, TypeCode.RValueReference):
+ return self.dumper.ptrSize()
+ if self.tdata.lalignment is not None:
+ #if isinstance(self.tdata.lalignment, function): # Does not work that way.
+ if hasattr(self.tdata.lalignment, '__call__'):
+ return self.tdata.lalignment()
+ return self.tdata.lalignment
+ return 1
+
+ def pointer(self):
+ return self.dumper.createPointerType(self)
+
+ def target(self):
+ return self.tdata.ltarget
+
+ def stripTypedefs(self):
+ if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
+ #DumperBase.warn('NO TYPEDEF: %s' % self)
+ return self
+ return self.ltarget
+
+ def size(self):
+ bs = self.bitsize()
+ if bs % 8 != 0:
+ DumperBase.warn('ODD SIZE: %s' % self)
+ return (7 + bs) >> 3
+
+ def bitsize(self):
+ if self.lbitsize is not None:
+ return self.lbitsize
+ raise RuntimeError('DONT KNOW SIZE: %s' % self)
+
+ def isMovableType(self):
+ if self.code in (TypeCode.Pointer, TypeCode.Integral, TypeCode.Float):
+ return True
+ strippedName = self.dumper.stripNamespaceFromType(self.name)
+ if strippedName in (
+ 'QBrush', 'QBitArray', 'QByteArray', 'QCustomTypeInfo',
+ 'QChar', 'QDate', 'QDateTime', 'QFileInfo', 'QFixed',
+ 'QFixedPoint', 'QFixedSize', 'QHashDummyValue', 'QIcon',
+ 'QImage', 'QLine', 'QLineF', 'QLatin1Char', 'QLocale',
+ 'QMatrix', 'QModelIndex', 'QPoint', 'QPointF', 'QPen',
+ 'QPersistentModelIndex', 'QResourceRoot', 'QRect', 'QRectF',
+ 'QRegExp', 'QSize', 'QSizeF', 'QString', 'QTime', 'QTextBlock',
+ 'QUrl', 'QVariant',
+ 'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration',
+ 'QXmlStreamNotationDeclaration', 'QXmlStreamEntityDeclaration'
+ ):
+ return True
+ if strippedName == 'QStringList':
+ return self.dumper.qtVersion() >= 0x050000
+ if strippedName == 'QList':
+ return self.dumper.qtVersion() >= 0x050600
+ return False
+
+ class Field(collections.namedtuple('Field',
+ ['dumper', 'name', 'type', 'bitsize', 'bitpos',
+ 'extractor', 'isBase', 'isStruct', 'isArtificial'])):
+
+ def __new__(cls, dumper, name=None, type=None, bitsize=None, bitpos=None,
+ extractor=None, isBase=False, isStruct=False, isArtificial=False):
+ return super(DumperBase.Field, cls).__new__(
+ cls, dumper, name, type, bitsize, bitpos,
+ extractor, isBase, isStruct, isArtificial)
+
+ __slots__ = ()
+
+ def __str__(self):
+ return self.stringify()
+
+ def stringify(self):
+ #return 'Field(name="%s")' % self.name
+ typename = None if self.type is None else self.type.stringify()
+ return 'Field(name="%s",type=%s,bitpos=%s,bitsize=%s)' \
+ % (self.name, typename, self.bitpos, self.bitsize)
+
+ def check(self):
+ pass
+
+ def size(self):
+ return self.bitsize() // 8
+
+ def offset(self):
+ return self.bitpos // 8
+
+ def fieldType(self):
+ if self.type is not None:
+ return self.type
+ raise RuntimeError('CANT GET FIELD TYPE FOR %s' % self)
+ return None
+
+ def ptrCode(self):
+ return 'I' if self.ptrSize() == 4 else 'Q'
+
+ def toPointerData(self, address):
+ if not self.isInt(address):
+ raise RuntimeError('wrong')
+ return bytes(struct.pack(self.packCode + self.ptrCode(), address))
+
+ def fromPointerData(self, bytes_value):
+ return struct.unpack(self.packCode + self.ptrCode(), bytes_value)
+
+ def createPointerValue(self, targetAddress, targetTypish):
+ if not isinstance(targetTypish, self.Type) and not isinstance(targetTypish, str):
+ raise RuntimeError('Expected type in createPointerValue(), got %s'
+ % type(targetTypish))
+ if not self.isInt(targetAddress):
+ raise RuntimeError('Expected integral address value in createPointerValue(), got %s'
+ % type(targetTypish))
+ val = self.Value(self)
+ val.ldata = self.toPointerData(targetAddress)
+ targetType = self.createType(targetTypish)
+ if self.useDynamicType:
+ targetType = targetType.dynamicType(targetAddress)
+ val._type = self.createPointerType(targetType)
+ return val
+
+ def createReferenceValue(self, targetAddress, targetType):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createReferenceValue(), got %s'
+ % type(targetType))
+ if not self.isInt(targetAddress):
+ raise RuntimeError('Expected integral address value in createReferenceValue(), got %s'
+ % type(targetType))
+ val = self.Value(self)
+ val.ldata = self.toPointerData(targetAddress)
+ if self.useDynamicType:
+ targetType = targetType.dynamicType(targetAddress)
+ val._type = self.createReferenceType(targetType)
+ return val
+
+ def createPointerType(self, targetType):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createPointerType(), got %s'
+ % type(targetType))
+ typeId = targetType.typeId + ' *'
+ tdata = self.TypeData(self, typeId)
+ tdata.name = targetType.name + '*'
+ tdata.lbitsize = 8 * self.ptrSize()
+ tdata.code = TypeCode.Pointer
+ tdata.ltarget = targetType
+ return self.Type(self, typeId)
+
+ def createReferenceType(self, targetType):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createReferenceType(), got %s'
+ % type(targetType))
+ typeId = targetType.typeId + ' &'
+ tdata = self.TypeData(self, typeId)
+ tdata.name = targetType.name + ' &'
+ tdata.code = TypeCode.Reference
+ tdata.ltarget = targetType
+ tdata.lbitsize = 8 * self.ptrSize() # Needed for Gdb13393 test.
+ #tdata.lbitsize = None
+ return self.Type(self, typeId)
+
+ def createRValueReferenceType(self, targetType):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createRValueReferenceType(), got %s'
+ % type(targetType))
+ typeId = targetType.typeId + ' &&'
+ tdata = self.TypeData(self, typeId)
+ tdata.name = targetType.name + ' &&'
+ tdata.code = TypeCode.RValueReference
+ tdata.ltarget = targetType
+ tdata.lbitsize = None
+ return self.Type(self, typeId)
+
+ def createArrayType(self, targetType, count):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createArrayType(), got %s'
+ % type(targetType))
+ targetTypeId = targetType.typeId
+
+ if targetTypeId.endswith(']'):
+ (prefix, suffix, inner_count) = self.splitArrayType(targetTypeId)
+ type_id = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix)
+ type_name = type_id
+ else:
+ type_id = '%s[%d]' % (targetTypeId, count)
+ type_name = '%s[%d]' % (targetType.name, count)
+
+ tdata = self.TypeData(self, type_id)
+ tdata.name = type_name
+ tdata.code = TypeCode.Array
+ tdata.ltarget = targetType
+ tdata.lbitsize = targetType.lbitsize * count
+ return self.Type(self, type_id)
+
+ def createBitfieldType(self, targetType, bitsize):
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createBitfieldType(), got %s'
+ % type(targetType))
+ typeId = '%s:%d' % (targetType.typeId, bitsize)
+ tdata = self.TypeData(self, typeId)
+ tdata.name = '%s : %d' % (targetType.typeId, bitsize)
+ tdata.code = TypeCode.Bitfield
+ tdata.ltarget = targetType
+ tdata.lbitsize = bitsize
+ return self.Type(self, typeId)
+
+ def createTypedefedType(self, targetType, typeName, typeId=None):
+ if typeId is None:
+ typeId = typeName
+ if not isinstance(targetType, self.Type):
+ raise RuntimeError('Expected type in createTypedefType(), got %s'
+ % type(targetType))
+ # Happens for C-style struct in GDB: typedef { int x; } struct S1;
+ if targetType.typeId == typeId:
+ return targetType
+ tdata = self.TypeData(self, typeId)
+ tdata.name = typeName
+ tdata.code = TypeCode.Typedef
+ tdata.ltarget = targetType
+ tdata.lbitsize = targetType.lbitsize
+ #tdata.lfields = targetType.lfields
+ tdata.lbitsize = targetType.lbitsize
+ return self.Type(self, typeId)
+
+ def knownArrayTypeSize(self):
+ return 3 * self.ptrSize() if self.qtVersion() >= 0x060000 else self.ptrSize()
+
+ def knownTypeSize(self, typish):
+ if typish[0] == 'Q':
+ if typish.startswith('QList<') or typish.startswith('QVector<'):
+ return self.knownArrayTypeSize()
+ if typish == 'QObject':
+ return 2 * self.ptrSize()
+ if typish == 'QStandardItemData':
+ return 4 * self.ptrSize() if self.qtVersion() >= 0x060000 else 2 * self.ptrSize()
+ if typish == 'Qt::ItemDataRole':
+ return 4
+ if typish == 'QChar':
+ return 2
+ if typish in ('quint32', 'qint32'):
+ return 4
+ return None
+
+ def createType(self, typish, size=None):
+ if isinstance(typish, self.Type):
+ #typish.check()
+ if hasattr(typish, 'lbitsize') and typish.lbitsize is not None and typish.lbitsize > 0:
+ return typish
+ # Size 0 is sometimes reported by GDB but doesn't help at all.
+ # Force using the fallback:
+ typish = typish.name
+
+ if isinstance(typish, str):
+ ns = self.qtNamespace()
+ typish = typish.replace('@', ns)
+ if typish.startswith(ns):
+ if size is None:
+ size = self.knownTypeSize(typish[len(ns):])
+ else:
+ if size is None:
+ size = self.knownTypeSize(typish)
+ if size is not None:
+ typish = ns + typish
+
+ tdata = self.typeData.get(typish, None)
+ if tdata is not None:
+ if tdata.lbitsize is not None:
+ if tdata.lbitsize > 0:
+ return self.Type(self, typish)
+
+ knownType = self.lookupType(typish)
+ #DumperBase.warn('KNOWN: %s' % knownType)
+ if knownType is not None:
+ #DumperBase.warn('USE FROM NATIVE')
+ return knownType
+
+ #DumperBase.warn('FAKING: %s SIZE: %s' % (typish, size))
+ tdata = self.TypeData(self, typish)
+ tdata.templateArguments = lambda: self.listTemplateParameters(typish)
+ if size is not None:
+ tdata.lbitsize = 8 * size
+ if typish.endswith('*'):
+ tdata.code = TypeCode.Pointer
+ tdata.lbitsize = 8 * self.ptrSize()
+ tdata.ltarget = self.createType(typish[:-1].strip())
+
+ typeobj = self.Type(self, tdata.typeId)
+ #DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
+ typeobj.check()
+ return typeobj
+ raise RuntimeError('NEED TYPE, NOT %s' % type(typish))
+
+ def createValueFromAddressAndType(self, address, typish):
+ val = self.Value(self)
+ val._type = self.createType(typish)
+ #DumperBase.warn('CREATING %s AT 0x%x' % (val.type.name, datish))
+ val.laddress = address
+ if self.useDynamicType:
+ val._type = val.type.dynamicType(address)
+ return val
+
+ def createValue(self, datish, typish):
+ if self.isInt(datish): # Used as address.
+ return self.createValueFromAddressAndType(datish, typish)
+ if isinstance(datish, bytes):
+ val = self.Value(self)
+ val._type = self.createType(typish)
+ #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish)))
+ val.ldata = datish
+ val.check()
+ return val
+ raise RuntimeError('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish))
+
+ def createProxyValue(self, proxy_data, type_name):
+ tdata = self.TypeData(self, type_name)
+ tdata.code = TypeCode.Struct
+ val = self.Value(self)
+ val._type = self.Type(self, type_name)
+ val.ldata = proxy_data
+ return val
+
+ class StructBuilder():
+ def __init__(self, dumper):
+ self.dumper = dumper
+ self.pattern = ''
+ self.currentBitsize = 0
+ self.fields = []
+ self.autoPadNext = False
+ self.maxAlign = 1
+
+ def addField(self, fieldSize, fieldCode=None, fieldIsStruct=False,
+ fieldName=None, fieldType=None, fieldAlign=1):
+
+ if fieldType is not None:
+ fieldType = self.dumper.createType(fieldType)
+ if fieldSize is None and fieldType is not None:
+ fieldSize = fieldType.size()
+ if fieldCode is None:
+ fieldCode = '%ss' % fieldSize
+
+ if self.autoPadNext:
+ self.currentBitsize = 8 * ((self.currentBitsize + 7) >> 3) # Fill up byte.
+ padding = (fieldAlign - (self.currentBitsize >> 3)) % fieldAlign
+ #DumperBase.warn('AUTO PADDING AT %s BITS BY %s BYTES' % (self.currentBitsize, padding))
+ field = self.dumper.Field(self.dumper, bitpos=self.currentBitsize,
+ bitsize=padding * 8)
+ self.pattern += '%ds' % padding
+ self.currentBitsize += padding * 8
+ self.fields.append(field)
+ self.autoPadNext = False
+
+ if fieldAlign > self.maxAlign:
+ self.maxAlign = fieldAlign
+ #DumperBase.warn("MAX ALIGN: %s" % self.maxAlign)
+
+ field = self.dumper.Field(dumper=self.dumper, name=fieldName, type=fieldType,
+ isStruct=fieldIsStruct, bitpos=self.currentBitsize,
+ bitsize=fieldSize * 8)
+
+ self.pattern += fieldCode
+ self.currentBitsize += fieldSize * 8
+ self.fields.append(field)
+
+ def describeStruct(self, pattern):
+ if pattern in self.structPatternCache:
+ return self.structPatternCache[pattern]
+ ptrSize = self.ptrSize()
+ builder = self.StructBuilder(self)
+ n = None
+ typeName = ''
+ readingTypeName = False
+ for c in pattern:
+ if readingTypeName:
+ if c == '}':
+ readingTypeName = False
+ fieldType = self.createType(typeName)
+ fieldAlign = fieldType.alignment()
+ builder.addField(n, fieldIsStruct=True,
+ fieldType=fieldType, fieldAlign=fieldAlign)
+ typeName = None
+ n = None
+ else:
+ typeName += c
+ elif c == 't': # size_t
+ builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize)
+ elif c == 'p': # Pointer as int
+ builder.addField(ptrSize, self.ptrCode(), fieldAlign=ptrSize)
+ elif c == 'P': # Pointer as Value
+ builder.addField(ptrSize, '%ss' % ptrSize, fieldAlign=ptrSize)
+ elif c in ('d'):
+ builder.addField(8, c, fieldAlign=ptrSize) # fieldType = 'double' ?
+ elif c in ('q', 'Q'):
+ builder.addField(8, c, fieldAlign=ptrSize)
+ elif c in ('i', 'I', 'f'):
+ builder.addField(4, c, fieldAlign=4)
+ elif c in ('h', 'H'):
+ builder.addField(2, c, fieldAlign=2)
+ elif c in ('b', 'B', 'c'):
+ builder.addField(1, c, fieldAlign=1)
+ elif c >= '0' and c <= '9':
+ if n is None:
+ n = ''
+ n += c
+ elif c == 's':
+ builder.addField(int(n), fieldAlign=1)
+ n = None
+ elif c == '{':
+ readingTypeName = True
+ typeName = ''
+ elif c == '@':
+ if n is None:
+ # Automatic padding depending on next item
+ builder.autoPadNext = True
+ else:
+ # Explicit padding.
+ builder.currentBitsize = 8 * ((builder.currentBitsize + 7) >> 3)
+ padding = (int(n) - (builder.currentBitsize >> 3)) % int(n)
+ field = self.Field(self)
+ builder.pattern += '%ds' % padding
+ builder.currentBitsize += padding * 8
+ builder.fields.append(field)
+ n = None
+ else:
+ raise RuntimeError('UNKNOWN STRUCT CODE: %s' % c)
+ pp = builder.pattern
+ size = (builder.currentBitsize + 7) >> 3
+ fields = builder.fields
+ tailPad = (builder.maxAlign - size) % builder.maxAlign
+ size += tailPad
+ self.structPatternCache[pattern] = (pp, size, fields)
+ return (pp, size, fields)