From 311c95cfcc5b88e902e7440803164f445239d539 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 4 Dec 2023 16:18:00 +0100 Subject: [PATCH] Debugger: Create a copy of the current dumper code The current code supports Python 2 and Python 3 based debugger backends (gdb, lldb) at the same time, but we'd like to drop Python 2 support so we can take advantage of some of Python 3's goodies. This copy here is not meant to be used in general but could perhaps be used to replace the main code in situations that cannot use Python 3 yet. Change-Id: I62273bc41b5a1e3a24720e167e64e4eac2e0c056 Reviewed-by: Christian Stenger --- share/qtcreator/debugger/python2/README.txt | 8 + .../debugger/python2/android_stdtypes.py | 133 + .../qtcreator/debugger/python2/boosttypes.py | 184 + share/qtcreator/debugger/python2/cdbbridge.py | 514 +++ .../debugger/python2/creatortypes.py | 371 ++ share/qtcreator/debugger/python2/dumper.py | 4094 +++++++++++++++++ share/qtcreator/debugger/python2/gdbbridge.py | 1624 +++++++ .../debugger/python2/gdbtracepoint.py | 377 ++ .../debugger/python2/libcpp_stdtypes.py | 527 +++ .../qtcreator/debugger/python2/lldbbridge.py | 2505 ++++++++++ share/qtcreator/debugger/python2/misctypes.py | 588 +++ .../qtcreator/debugger/python2/opencvtypes.py | 44 + share/qtcreator/debugger/python2/pdbbridge.py | 1726 +++++++ .../debugger/python2/personaltypes.py | 40 + share/qtcreator/debugger/python2/qttypes.py | 3674 +++++++++++++++ share/qtcreator/debugger/python2/stdtypes.py | 1115 +++++ share/qtcreator/debugger/python2/utils.py | 129 + 17 files changed, 17653 insertions(+) create mode 100644 share/qtcreator/debugger/python2/README.txt create mode 100644 share/qtcreator/debugger/python2/android_stdtypes.py create mode 100644 share/qtcreator/debugger/python2/boosttypes.py create mode 100644 share/qtcreator/debugger/python2/cdbbridge.py create mode 100644 share/qtcreator/debugger/python2/creatortypes.py create mode 100644 share/qtcreator/debugger/python2/dumper.py create mode 100644 share/qtcreator/debugger/python2/gdbbridge.py create mode 100644 share/qtcreator/debugger/python2/gdbtracepoint.py create mode 100644 share/qtcreator/debugger/python2/libcpp_stdtypes.py create mode 100644 share/qtcreator/debugger/python2/lldbbridge.py create mode 100644 share/qtcreator/debugger/python2/misctypes.py create mode 100644 share/qtcreator/debugger/python2/opencvtypes.py create mode 100644 share/qtcreator/debugger/python2/pdbbridge.py create mode 100644 share/qtcreator/debugger/python2/personaltypes.py create mode 100644 share/qtcreator/debugger/python2/qttypes.py create mode 100644 share/qtcreator/debugger/python2/stdtypes.py create mode 100644 share/qtcreator/debugger/python2/utils.py diff --git a/share/qtcreator/debugger/python2/README.txt b/share/qtcreator/debugger/python2/README.txt new file mode 100644 index 00000000000..70e03d52ca9 --- /dev/null +++ b/share/qtcreator/debugger/python2/README.txt @@ -0,0 +1,8 @@ + +This is a copy of a previous version of share/qtcreator/debugger/*.py +which supported Python 2 and Python 3 based debugger backends (gdb, +lldb) at the same time. + +The code there is now Python-3-only. This copy here is not meant to be +used in general but could perhaps be used to replace the main code +in situations that cannot use Python 3 yet. diff --git a/share/qtcreator/debugger/python2/android_stdtypes.py b/share/qtcreator/debugger/python2/android_stdtypes.py new file mode 100644 index 00000000000..8b25d7bb358 --- /dev/null +++ b/share/qtcreator/debugger/python2/android_stdtypes.py @@ -0,0 +1,133 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + +from dumper import DumperBase +from utils import DisplayFormat + +import stdtypes +import libcpp_stdtypes + + +def qform__std____ndk1__array(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____ndk1__array(d, value): + stdtypes.qdump__std__array(d, value) + + +def qdump__std____ndk1__complex(d, value): + stdtypes.qdump__std__complex(d, value) + + +def qdump__std____ndk1__deque(d, value): + stdtypes.qdumpHelper__std__deque__libcxx(d, value) + + +def qdump__std____ndk1__list(d, value): + return libcpp_stdtypes.qdump__std____1__list(d, value) + + +def qdump__std____ndk1__set(d, value): + return libcpp_stdtypes.qdump__std____1__set(d, value) + + +def qdump__std____ndk1__multiset(d, value): + qdump__std____ndk1__set(d, value) + + +def qform__std____ndk1__map(): + return [DisplayFormat.CompactMap] + + +def qdump__std____ndk1__map(d, value): + return libcpp_stdtypes.qdump__std____1__map(d, value) + + +def qform__std____ndk1__multimap(): + return [DisplayFormat.CompactMap] + + +def qdump__std____ndk1__multimap(d, value): + qdump__std____ndk1__map(d, value) + + +def qdump__std____ndk1__map__iterator(d, value): + return libcpp_stdtypes.qdump__std____1__map__iterator(d, value) + + +def qdump__std____ndk1__map__const_iterator(d, value): + qdump__std____ndk1__map__iterator(d, value) + + +def qdump__std____ndk1__set__iterator(d, value): + return libcpp_stdtypes.qdump__std____1__set__iterator(d, value) + + +def qdump__std____ndk1__set_const_iterator(d, value): + qdump__std____ndk1__set__iterator(d, value) + + +def qdump__std____ndk1__stack(d, value): + return libcpp_stdtypes.qdump__std____1__stack(d, value) + + +def qdump__std____ndk1__string(d, value): + return libcpp_stdtypes.qdump__std____1__string(d, value) + + +def qdump__std____ndk1__wstring(d, value): + return libcpp_stdtypes.qdump__std____1__wstring(d, value) + + +def qdump__std____ndk1__basic_string(d, value): + return libcpp_stdtypes.qdump__std____1__basic_string(d, value) + + +def qdump__std____ndk1__shared_ptr(d, value): + return libcpp_stdtypes.qdump__std____1__shared_ptr(d, value) + + +def qdump__std____ndk1__weak_ptr(d, value): + return qdump__std____ndk1__shared_ptr(d, value) + + +def qdump__std____ndk1__unique_ptr(d, value): + stdtypes.qdump__std__unique_ptr(d, value) + + +def qform__std____ndk1__unordered_map(): + return [DisplayFormat.CompactMap] + + +def qdump__std____ndk1__unordered_map(d, value): + libcpp_stdtypes.qdump__std____1__unordered_map(d, value) + + +def qdump__std____ndk1__unordered_set(d, value): + return libcpp_stdtypes.qdump__std____1__unordered_set(d, value) + + +def qdump__std____ndk1__unordered_multiset(d, value): + qdump__std____ndk1__unordered_set(d, value) + + +def qform__std____ndk1__valarray(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____ndk1__valarray(d, value): + return libcpp_stdtypes.qdump__std____1__valarray(d, value) + + +def qform__std____ndk1__vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____ndk1__vector(d, value): + stdtypes.qdumpHelper__std__vector__libcxx(d, value) + + +def qdump__std____ndk1__once_flag(d, value): + stdtypes.qdump__std__once_flag(d, value) diff --git a/share/qtcreator/debugger/python2/boosttypes.py b/share/qtcreator/debugger/python2/boosttypes.py new file mode 100644 index 00000000000..5eb0c1a1be9 --- /dev/null +++ b/share/qtcreator/debugger/python2/boosttypes.py @@ -0,0 +1,184 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from utils import DisplayFormat +from dumper import Children + + +def qdump__boost__bimaps__bimap(d, value): + #leftType = value.type[0] + #rightType = value.type[1] + size = value["core"]["node_count"].integer() + d.putItemCount(size) + if d.isExpanded(): + d.putPlainChildren(value) + + +def qdump__boost__optional(d, value): + innerType = value.type[0] + (initialized, pad, payload) = d.split('b@{%s}' % innerType.name, value) + if initialized: + d.putItem(payload) + d.putBetterType(value.type) + else: + d.putSpecialValue("uninitialized") + + +def qdump__boost__shared_ptr(d, value): + # s boost::shared_ptr + # px 0x0 int * + # pn boost::detail::shared_count + # pi_ 0x0 boost::detail::sp_counted_base * + (px, pi) = value.split("pp") + if pi == 0: + d.putValue("(null)") + return + + if px == 0: + d.putValue("(null)") + return + + (vptr, usecount, weakcount) = d.split('pii', pi) + d.check(weakcount >= 0) + d.check(weakcount <= usecount) + d.check(usecount <= 10 * 1000 * 1000) + d.putItem(d.createValue(px, value.type[0])) + d.putBetterType(value.type) + + +def qdump__boost__container__list(d, value): + try: + m_icont = value["m_icont"] + except: + m_icont = value["members_"]["m_icont"] + r = m_icont["data_"]["root_plus_size_"] + n = r["size_"].integer() + d.putItemCount(n) + if d.isExpanded(): + innerType = value.type[0] + offset = 2 * d.ptrSize() + with Children(d, n): + try: + root = r["root_"] + except: + root = r["m_header"] + p = root["next_"].extractPointer() + for i in d.childRange(): + d.putSubItem(i, d.createValue(p + offset, innerType)) + p = d.extractPointer(p) + + +def qform__boost__container__vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__boost__container__vector(d, value): + holder = value["m_holder"] + size = holder["m_size"].integer() + d.putItemCount(size) + + if d.isExpanded(): + T = value.type[0] + try: + start = holder["m_start"].pointer() + except: + start = holder["storage"].address() + d.putPlotData(start, size, T) + + +def qform__boost__container__static_vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__boost__container__static_vector(d, value): + qdump__boost__container__vector(d, value) + + +def qform__boost__container__small_vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__boost__container__small_vector(d, value): + qdump__boost__container__vector(d, value) + + +def qdump__boost__gregorian__date(d, value): + d.putValue(value.integer(), "juliandate") + + +def qdump__boost__posix_time__ptime(d, value): + ms = int(value.integer() / 1000) + d.putValue("%s/%s" % divmod(ms, 86400000), "juliandateandmillisecondssincemidnight") + + +def qdump__boost__posix_time__time_duration(d, value): + d.putValue(int(value.integer() / 1000), "millisecondssincemidnight") + + +def qdump__boost__unordered__unordered_set(d, value): + innerType = value.type[0] + if value.type.size() == 7 * d.ptrSize(): # 56 for boost 1.79+ + bases, bucketCount, bcountLog2, size, mlf, maxload, buckets = value.split('ttttttp') + forward = True + elif value.type.size() == 6 * d.ptrSize(): # 48 for boost 1.55+ + # boost 1.58 or 1.55 + # bases are 3? bytes, and mlf is actually a float, but since + # its followed by size_t maxload, it's # effectively padded to a size_t + bases, bucketCount, size, mlf, maxload, buckets = value.split('tttttp') + # Distinguish 1.58 and 1.55. 1.58 used one template argument, 1.55 two. + try: + ittype = d.lookupType(value.type.name + '::iterator').target() + forward = len(ittype.templateArguments()) == 1 + except: + forward = True + elif value.type.size() == 5 * d.ptrSize(): # 40 for boost 1.48 + # boost 1.48 + # Values are stored before the next pointers. Determine the offset. + buckets, bucketCount, size, mlf, maxload = value.split('ptttt') + forward = False + else: + raise Exception("Unknown boost::unordered_set layout") + + if forward: + # boost >= 1.58 + code = 'pp{%s}' % innerType.name + + def children(p): + while True: + p, dummy, val = d.split(code, p) + yield val + else: + # boost 1.48 or 1.55 + code = '{%s}@p' % innerType.name + (pp, ssize, fields) = d.describeStruct(code) + offset = fields[2].offset() + + def children(p): + while True: + val, pad, p = d.split(code, p - offset) + yield val + + p = d.extractPointer(buckets + bucketCount * d.ptrSize()) + d.putItems(size, children(p), maxNumChild=10000) + + +def qdump__boost__variant(d, value): + allTypes = value.type.templateArguments() + realType = allTypes[value.split('i')[0]] + alignment = max([t.alignment() for t in allTypes]) + dummy, val = value.split('%is{%s}' % (max(4, alignment), realType.name)) + d.putItem(val) + d.putBetterType(value.type) + + +def qdump__boost__container__devector(d, value): + inner_type = value.type[0] + buffer = value["m_"]["buffer"].pointer() + front_idx = value["m_"]["front_idx"].integer() + back_idx = value["m_"]["back_idx"].integer() + start = buffer + (front_idx * inner_type.size()) + size = int(back_idx - front_idx) + if size > 0: + d.checkPointer(start) + d.putItemCount(size) + d.putPlotData(start, size, inner_type) diff --git a/share/qtcreator/debugger/python2/cdbbridge.py b/share/qtcreator/debugger/python2/cdbbridge.py new file mode 100644 index 00000000000..a9d86e24ee1 --- /dev/null +++ b/share/qtcreator/debugger/python2/cdbbridge.py @@ -0,0 +1,514 @@ +# 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 inspect +import os +import sys +import cdbext +import re +import threading +from utils import TypeCode + +sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) + +from dumper import DumperBase, SubItem + + +class FakeVoidType(cdbext.Type): + def __init__(self, name, dumper): + cdbext.Type.__init__(self) + self.typeName = name.strip() + self.dumper = dumper + + def name(self): + return self.typeName + + def bitsize(self): + return self.dumper.ptrSize() * 8 + + def code(self): + if self.typeName.endswith('*'): + return TypeCode.Pointer + if self.typeName.endswith(']'): + return TypeCode.Array + return TypeCode.Void + + def unqualified(self): + return self + + def target(self): + code = self.code() + if code == TypeCode.Pointer: + return FakeVoidType(self.typeName[:-1], self.dumper) + if code == TypeCode.Void: + return self + try: + return FakeVoidType(self.typeName[:self.typeName.rindex('[')], self.dumper) + except: + return FakeVoidType('void', self.dumper) + + def targetName(self): + return self.target().name() + + def arrayElements(self): + try: + return int(self.typeName[self.typeName.rindex('[') + 1:self.typeName.rindex(']')]) + except: + return 0 + + def stripTypedef(self): + return self + + def fields(self): + return [] + + def templateArgument(self, pos, numeric): + return None + + def templateArguments(self): + return [] + + +class Dumper(DumperBase): + def __init__(self): + DumperBase.__init__(self) + self.outputLock = threading.Lock() + self.isCdb = True + + def enumValue(self, nativeValue): + val = nativeValue.nativeDebuggerValue() + # remove '0n' decimal prefix of the native cdb value output + return val.replace('(0n', '(') + + def fromNativeValue(self, nativeValue): + self.check(isinstance(nativeValue, cdbext.Value)) + val = self.Value(self) + val.name = nativeValue.name() + val._type = self.fromNativeType(nativeValue.type()) + # There is no cdb api for the size of bitfields. + # Workaround this issue by parsing the native debugger text for integral types. + if val._type.code == TypeCode.Integral: + try: + integerString = nativeValue.nativeDebuggerValue() + except UnicodeDecodeError: + integerString = '' # cannot decode - read raw + if integerString == 'true': + val.ldata = int(1).to_bytes(1, byteorder='little') + elif integerString == 'false': + val.ldata = int(0).to_bytes(1, byteorder='little') + else: + integerString = integerString.replace('`', '') + integerString = integerString.split(' ')[0] + if integerString.startswith('0n'): + integerString = integerString[2:] + base = 10 + elif integerString.startswith('0x'): + base = 16 + else: + base = 10 + signed = not val._type.name.startswith('unsigned') + try: + val.ldata = int(integerString, base).to_bytes(val._type.size(), + byteorder='little', signed=signed) + except: + # read raw memory in case the integerString can not be interpreted + pass + if val._type.code == TypeCode.Enum: + val.ldisplay = self.enumValue(nativeValue) + val.isBaseClass = val.name == val._type.name + val.nativeValue = nativeValue + val.laddress = nativeValue.address() + val.lbitsize = nativeValue.bitsize() + return val + + def nativeTypeId(self, nativeType): + self.check(isinstance(nativeType, cdbext.Type)) + name = nativeType.name() + if name is None or len(name) == 0: + c = '0' + elif name == 'struct {...}': + c = 's' + elif name == 'union {...}': + c = 'u' + else: + return name + typeId = c + ''.join(['{%s:%s}' % (f.name(), self.nativeTypeId(f.type())) + for f in nativeType.fields()]) + return typeId + + def fromNativeType(self, nativeType): + self.check(isinstance(nativeType, cdbext.Type)) + typeId = self.nativeTypeId(nativeType) + if self.typeData.get(typeId, None) is not None: + return self.Type(self, typeId) + + if nativeType.name().startswith('void'): + nativeType = FakeVoidType(nativeType.name(), self) + + code = nativeType.code() + if code == TypeCode.Pointer: + if nativeType.name().startswith(''): + code = TypeCode.Function + elif nativeType.targetName() != nativeType.name(): + targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId()) + if targetType is not None and targetType is not nativeType: + return self.createPointerType(targetType) + + if code == TypeCode.Array: + # cdb reports virtual function tables as arrays those ar handled separetly by + # the DumperBase. Declare those types as structs prevents a lookup to a + # none existing type + if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith(' align else align + align = 1 + for f in nativeType.fields(): + align = handleItem(f.type(), align) + return align + + def nativeTypeEnumDisplay(self, nativeType, intval, form): + value = self.nativeParseAndEvaluate('(%s)%d' % (nativeType.name(), intval)) + if value is None: + return '' + return self.enumValue(value) + + def enumExpression(self, enumType, enumValue): + ns = self.qtNamespace() + return ns + "Qt::" + enumType + "(" \ + + ns + "Qt::" + enumType + "::" + enumValue + ")" + + def pokeValue(self, typeName, *args): + return None + + def parseAndEvaluate(self, exp): + return self.fromNativeValue(self.nativeParseAndEvaluate(exp)) + + def nativeParseAndEvaluate(self, exp): + return cdbext.parseAndEvaluate(exp) + + def isWindowsTarget(self): + return True + + def isQnxTarget(self): + return False + + def isArmArchitecture(self): + return False + + def isMsvcTarget(self): + return True + + def qtCoreModuleName(self): + modules = cdbext.listOfModules() + # first check for an exact module name match + for coreName in ['Qt6Core', 'Qt6Cored', 'Qt5Cored', 'Qt5Core', 'QtCored4', 'QtCore4']: + if coreName in modules: + self.qtCoreModuleName = lambda: coreName + return coreName + # maybe we have a libinfix build. + for pattern in ['Qt6Core.*', 'Qt5Core.*', 'QtCore.*']: + matches = [module for module in modules if re.match(pattern, module)] + if matches: + coreName = matches[0] + self.qtCoreModuleName = lambda: coreName + return coreName + return None + + def qtDeclarativeModuleName(self): + modules = cdbext.listOfModules() + for declarativeModuleName in ['Qt6Qmld', 'Qt6Qml', 'Qt5Qmld', 'Qt5Qml']: + if declarativeModuleName in modules: + self.qtDeclarativeModuleName = lambda: declarativeModuleName + return declarativeModuleName + matches = [module for module in modules if re.match('Qt[56]Qml.*', module)] + if matches: + declarativeModuleName = matches[0] + self.qtDeclarativeModuleName = lambda: declarativeModuleName + return declarativeModuleName + return None + + def qtHookDataSymbolName(self): + hookSymbolName = 'qtHookData' + coreModuleName = self.qtCoreModuleName() + if coreModuleName is not None: + hookSymbolName = '%s!%s%s' % (coreModuleName, self.qtNamespace(), hookSymbolName) + else: + resolved = cdbext.resolveSymbol('*' + hookSymbolName) + if resolved: + hookSymbolName = resolved[0] + else: + hookSymbolName = '*%s' % hookSymbolName + self.qtHookDataSymbolName = lambda: hookSymbolName + return hookSymbolName + + def qtDeclarativeHookDataSymbolName(self): + hookSymbolName = 'qtDeclarativeHookData' + declarativeModuleName = self.qtDeclarativeModuleName() + if declarativeModuleName is not None: + hookSymbolName = '%s!%s%s' % (declarativeModuleName, self.qtNamespace(), hookSymbolName) + else: + resolved = cdbext.resolveSymbol('*' + hookSymbolName) + if resolved: + hookSymbolName = resolved[0] + else: + hookSymbolName = '*%s' % hookSymbolName + + self.qtDeclarativeHookDataSymbolName = lambda: hookSymbolName + return hookSymbolName + + def qtNamespace(self): + namespace = '' + qstrdupSymbolName = '*qstrdup' + coreModuleName = self.qtCoreModuleName() + if coreModuleName is not None: + qstrdupSymbolName = '%s!%s' % (coreModuleName, qstrdupSymbolName) + resolved = cdbext.resolveSymbol(qstrdupSymbolName) + if resolved: + name = resolved[0].split('!')[1] + namespaceIndex = name.find('::') + if namespaceIndex > 0: + namespace = name[:namespaceIndex + 2] + self.qtCustomEventFunc = self.parseAndEvaluate( + '%s!%sQObject::customEvent' % + (self.qtCoreModuleName(), namespace)).address() + self.qtNamespace = lambda: namespace + return namespace + + def qtVersion(self): + qtVersion = None + try: + qtVersion = self.parseAndEvaluate( + '((void**)&%s)[2]' % self.qtHookDataSymbolName()).integer() + except: + if self.qtCoreModuleName() is not None: + try: + versionValue = cdbext.call(self.qtCoreModuleName() + '!qVersion()') + version = self.extractCString(self.fromNativeValue(versionValue).address()) + (major, minor, patch) = version.decode('latin1').split('.') + qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch) + except: + pass + if qtVersion is None: + qtVersion = self.fallbackQtVersion + self.qtVersion = lambda: qtVersion + return qtVersion + + def putVtableItem(self, address): + funcName = cdbext.getNameByAddress(address) + if funcName is None: + self.putItem(self.createPointerValue(address, 'void')) + else: + self.putValue(funcName) + self.putType('void*') + self.putAddress(address) + + def putVTableChildren(self, item, itemCount): + p = item.address() + for i in range(itemCount): + deref = self.extractPointer(p) + if deref == 0: + n = i + break + with SubItem(self, i): + self.putVtableItem(deref) + p += self.ptrSize() + return itemCount + + def ptrSize(self): + size = cdbext.pointerSize() + self.ptrSize = lambda: size + return size + + def stripQintTypedefs(self, typeName): + if typeName.startswith('qint'): + prefix = '' + size = typeName[4:] + elif typeName.startswith('quint'): + prefix = 'unsigned ' + size = typeName[5:] + else: + return typeName + if size == '8': + return '%schar' % prefix + elif size == '16': + return '%sshort' % prefix + elif size == '32': + return '%sint' % prefix + elif size == '64': + return '%sint64' % prefix + else: + return typeName + + def lookupType(self, typeNameIn, module=0): + if len(typeNameIn) == 0: + return None + typeName = self.stripQintTypedefs(typeNameIn) + if self.typeData.get(typeName, None) is None: + nativeType = self.lookupNativeType(typeName, module) + if nativeType is None: + return None + _type = self.fromNativeType(nativeType) + if _type.typeId != typeName: + self.registerTypeAlias(_type.typeId, typeName) + return _type + return self.Type(self, typeName) + + def lookupNativeType(self, name, module=0): + if name.startswith('void'): + return FakeVoidType(name, self) + return cdbext.lookupType(name, module) + + def reportResult(self, result, args): + cdbext.reportResult('result={%s}' % result) + + def readRawMemory(self, address, size): + mem = cdbext.readRawMemory(address, size) + if len(mem) != size: + raise Exception("Invalid memory request: %d bytes from 0x%x" % (size, address)) + return mem + + def findStaticMetaObject(self, type): + typeName = type.name + if type.moduleName is not None: + typeName = type.moduleName + '!' + typeName + ptr = cdbext.getAddressByName(typeName + '::staticMetaObject') + return ptr + + def warn(self, msg): + self.put('{name="%s",value="",type="",numchild="0"},' % msg) + + def fetchVariables(self, args): + self.resetStats() + (ok, res) = self.tryFetchInterpreterVariables(args) + if ok: + self.reportResult(res, args) + return + + self.setVariableFetchingOptions(args) + + self.output = [] + + self.currentIName = 'local' + self.put('data=[') + self.anonNumber = 0 + + variables = [] + for val in cdbext.listOfLocals(self.partialVariable): + dumperVal = self.fromNativeValue(val) + dumperVal.lIsInScope = dumperVal.name not in self.uninitialized + variables.append(dumperVal) + + self.handleLocals(variables) + self.handleWatches(args) + + self.put('],partial="%d"' % (len(self.partialVariable) > 0)) + self.put(',timings=%s' % self.timings) + + if self.forceQtNamespace: + self.qtNamespaceToReport = self.qtNamespace() + + if self.qtNamespaceToReport: + self.put(',qtnamespace="%s"' % self.qtNamespaceToReport) + self.qtNamespaceToReport = None + + self.reportResult(''.join(self.output), args) + self.output = [] + + def report(self, stuff): + sys.stdout.write(stuff + "\n") + + def findValueByExpression(self, exp): + return cdbext.parseAndEvaluate(exp) + + def nativeDynamicTypeName(self, address, baseType): + return None # Does not work with cdb + + def nativeValueDereferenceReference(self, value): + return self.nativeValueDereferencePointer(value) + + def nativeValueDereferencePointer(self, value): + def nativeVtCastValue(nativeValue): + # If we have a pointer to a derived instance of the pointer type cdb adds a + # synthetic '__vtcast_' member as the first child + if nativeValue.hasChildren(): + vtcastCandidate = nativeValue.childFromIndex(0) + vtcastCandidateName = vtcastCandidate.name() + if vtcastCandidateName.startswith('__vtcast_'): + # found a __vtcast member + # make sure that it is not an actual field + for field in nativeValue.type().fields(): + if field.name() == vtcastCandidateName: + return None + return vtcastCandidate + return None + + nativeValue = value.nativeValue + if nativeValue is None: + if not self.isExpanded(): + raise Exception("Casting not expanded values is to expensive") + nativeValue = self.nativeParseAndEvaluate('(%s)0x%x' % (value.type.name, value.pointer())) + castVal = nativeVtCastValue(nativeValue) + if castVal is not None: + val = self.fromNativeValue(castVal) + else: + val = self.Value(self) + val.laddress = value.pointer() + val._type = value.type.dereference() + val.nativeValue = value.nativeValue + + return val + + def callHelper(self, rettype, value, function, args): + raise Exception("cdb does not support calling functions") + + def nameForCoreId(self, id): + for dll in ['Utilsd', 'Utils']: + idName = cdbext.call('%s!Utils::nameForId(%d)' % (dll, id)) + if idName is not None: + break + return self.fromNativeValue(idName) + + def putCallItem(self, name, rettype, value, func, *args): + return + + def symbolAddress(self, symbolName): + res = self.nativeParseAndEvaluate(symbolName) + return None if res is None else res.address() diff --git a/share/qtcreator/debugger/python2/creatortypes.py b/share/qtcreator/debugger/python2/creatortypes.py new file mode 100644 index 00000000000..24c16ce0094 --- /dev/null +++ b/share/qtcreator/debugger/python2/creatortypes.py @@ -0,0 +1,371 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from dumper import Children + + +def typeTarget(type): + target = type.target() + if target: + return target + return type + + +def stripTypeName(value): + return typeTarget(value.type).unqualified().name + + +def extractPointerType(d, value): + postfix = "" + while stripTypeName(value) == "CPlusPlus::PointerType": + postfix += "*" + value = value["_elementType"]["_type"] + try: + return readLiteral(d, value["_name"]) + postfix + except: + typeName = typeTarget(value.type.unqualified()).name + if typeName == "CPlusPlus::IntegerType": + return "int" + postfix + elif typeName == "CPlusPlus::VoidType": + return "void" + postfix + return "" + + +def readTemplateName(d, value): + name = readLiteral(d, value["_identifier"]) + "<" + args = value["_templateArguments"] + impl = args["_M_impl"] + start = impl["_M_start"] + size = impl["_M_finish"] - start + try: + d.check(0 <= size and size <= 100) + d.checkPointer(start) + for i in range(int(size)): + if i > 0: + name += ", " + name += extractPointerType(d, start[i]["_type"]) + except: + return "" + name += ">" + return name + + +def readLiteral(d, value): + if not value.integer(): + return "" + type = typeTarget(value.type.unqualified()) + if type and (type.name == "CPlusPlus::TemplateNameId"): + return readTemplateName(d, value) + elif type and (type.name == "CPlusPlus::QualifiedNameId"): + return readLiteral(d, value["_base"]) + "::" + readLiteral(d, value["_name"]) + try: + return bytes(d.readRawMemory(value["_chars"], value["_size"])).decode('latin1') + except: + return "" + + +def dumpLiteral(d, value): + d.putValue(d.hexencode(readLiteral(d, value)), "latin1") + + +def qdump__Utils__Id(d, value): + val = value.extractPointer() + if True: + if d.isMsvcTarget(): + name = d.nameForCoreId(val).address() + else: + name = d.parseAndEvaluate("Utils::nameForId(0x%x)" % val).pointer() + d.putSimpleCharArray(name) + else: + d.putValue(val) + d.putPlainChildren(value) + + +def qdump__Utils__Key(d, value): + d.putByteArrayValue(value["data"]) + d.putBetterType(value.type) + + +def qdump__Debugger__Internal__GdbMi(d, value): + val = d.encodeString(value["m_name"]) + "3a002000" \ + + d.encodeString(value["m_data"]) + d.putValue(val, "utf16") + d.putPlainChildren(value) + + +def qdump__Debugger__Internal__DisassemblerLine(d, value): + d.putByteArrayValue(value["m_data"]) + d.putPlainChildren(value) + + +def qdump__Debugger__Internal__WatchData(d, value): + d.putStringValue(value["iname"]) + d.putPlainChildren(value) + + +def qdump__Debugger__Internal__WatchItem(d, value): + d.putStringValue(value["iname"]) + d.putPlainChildren(value) + + +def qdump__Debugger__Internal__BreakpointModelId(d, value): + d.putValue("%s.%s" % (value["m_majorPart"].integer(), value["m_minorPart"].integer())) + d.putPlainChildren(value) + + +def qdump__Debugger__Internal__ThreadId(d, value): + d.putValue("%s" % value["m_id"]) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__ByteArrayRef(d, value): + d.putSimpleCharArray(value["m_start"], value["m_length"]) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Identifier(d, value): + try: + d.putSimpleCharArray(value["_chars"], value["_size"]) + except: + pass + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Symbol(d, value): + dumpLiteral(d, value["_name"]) + d.putBetterType(value.type) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Class(d, value): + qdump__CPlusPlus__Symbol(d, value) + + +def kindName(d, value): + e = value.integer() + if e: + kindType = d.lookupType("CPlusPlus::Kind") + return kindType.tdata.enumDisplay(e, value.address(), '%d')[11:] + else: + return '' + + +def qdump__CPlusPlus__IntegerType(d, value): + d.putValue(kindName(d, value["_kind"])) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__FullySpecifiedType(d, value): + type = value["_type"] + typeName = stripTypeName(type) + if typeName == "CPlusPlus::NamedType": + dumpLiteral(d, type["_name"]) + elif typeName == "CPlusPlus::PointerType": + d.putValue(d.hexencode(extractPointerType(d, type)), "latin1") + d.putPlainChildren(value) + + +def qdump__CPlusPlus__NamedType(d, value): + dumpLiteral(d, value["_name"]) + d.putBetterType(value.type) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__PointerType(d, value): + d.putValue(d.hexencode(extractPointerType(d, value)), "latin1") + d.putPlainChildren(value) + + +def qdump__CPlusPlus__TemplateNameId(d, value): + dumpLiteral(d, value) + d.putBetterType(value.type) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__QualifiedNameId(d, value): + dumpLiteral(d, value) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Literal(d, value): + dumpLiteral(d, value) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__StringLiteral(d, value): + d.putSimpleCharArray(value["_chars"], value["_size"]) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Internal__Value(d, value): + d.putValue(value["l"]) + d.putPlainChildren(value) + + +def qdump__Utils__FilePath(d, value): + data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value) + elided, enc = d.encodeStringHelper(data, d.displayStringLimit) + # enc is concatenated path + scheme + host + if scheme_len: + scheme_pos = path_len * 4 + host_pos = scheme_pos + scheme_len * 4 + path_enc = enc[0 : path_len * 4] + scheme_enc = enc[scheme_pos : scheme_pos + scheme_len * 4] + host_enc = enc[host_pos : host_pos + host_len * 4] + slash = "2F00" + dot = "2E00" + colon = "3A00" + val = scheme_enc + colon + slash + slash + host_enc + if not path_enc.startswith(slash): + val += slash + dot + slash + val += path_enc + else: + val = enc + d.putValue(val, "utf16", elided=elided) + d.putPlainChildren(value) + + +def qdump__Utils__FileName(d, value): + qdump__Utils__FilePath(d, value) + + +def qdump__Utils__ElfSection(d, value): + d.putByteArrayValue(value["name"]) + d.putPlainChildren(value) + + +def qdump__Utils__Port(d, value): + d.putValue(d.extractInt(value)) + d.putPlainChildren(value) + + + +def x_qdump__Utils__Environment(d, value): + qdump__Utils__NameValueDictionary(d, value) + + +def qdump__Utils__DictKey(d, value): + d.putStringValue(value["name"]) + + +def x_qdump__Utils__NameValueDictionary(d, value): + dptr = d.extractPointer(value) + if d.qtVersion() >= 0x60000: + if dptr == 0: + d.putItemCount(0) + return + m = value['d']['d']['m'] + d.putItem(m) + d.putBetterType('Utils::NameValueDictionary') + else: # Qt5 + (ref, n) = d.split('ii', dptr) + d.check(0 <= n and n <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + + d.putItemCount(n) + if d.isExpanded(): + if n > 10000: + n = 10000 + + typeCode = 'ppp@{%s}@{%s}' % ("Utils::DictKey", "@QPair<@QString,bool>") + + def helper(node): + (p, left, right, padding1, key, padding2, value) = d.split(typeCode, node) + if left: + for res in helper(left): + yield res + yield (key["name"], value) + if right: + for res in helper(right): + yield res + + with Children(d, n): + for (pair, i) in zip(helper(dptr + 8), range(n)): + d.putPairItem(i, pair, 'key', 'value') + + +def qdump__Utf8String(d, value): + d.putByteArrayValue(value['byteArray']) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Token(d, value): + k = value["f"]["kind"] + e = k.lvalue + type = kindName(d, k) + try: + if e == 6: + type = readLiteral(d, value["identifier"]) + " (%s)" % type + elif e >= 7 and e <= 23: + type = readLiteral(d, value["literal"]) + " (%s)" % type + except: + pass + d.putValue(type) + d.putPlainChildren(value) + + +def qdump__CPlusPlus__Internal__PPToken(d, value): + data, size, alloc = d.qArrayData(value["m_src"]) + length = value["f"]["utf16chars"].integer() + offset = value["utf16charOffset"].integer() + #DumperBase.warn("size: %s, alloc: %s, offset: %s, length: %s, data: %s" + # % (size, alloc, offset, length, data)) + d.putValue(d.readMemory(data + offset, min(100, length)), "latin1") + d.putPlainChildren(value) + + +def qdump__ProString(d, value): + try: + s = value["m_string"] + data, size, alloc = d.stringData(s) + data += 2 * value["m_offset"].integer() + size = value["m_length"].integer() + s = d.readMemory(data, 2 * size) + d.putValue(s, "utf16") + except: + d.putEmptyValue() + d.putPlainChildren(value) + + +def qdump__ProKey(d, value): + qdump__ProString(d, value) + d.putBetterType(value.type) + + +def qdump__Core__GeneratedFile(d, value): + d.putStringValue(value["m_d"]["d"]["path"]) + d.putPlainChildren(value) + +#def qdump__ProjectExplorer__Node(d, value): +# d.putStringValue(value["m_filePath"]) +# d.putPlainChildren(value) +# +#def qdump__ProjectExplorer__FolderNode(d, value): +# d.putStringValue(value["m_displayName"]) +# d.putPlainChildren(value) + +# Broke when moving to unique_ptr +#def qdump__ProjectExplorer__ToolChain(d, value): +# d.putStringValue(value["d"]["m_displayName"]) +# d.putPlainChildren(value) + +# Broke when moving to unique_ptr +#def qdump__ProjectExplorer__Kit(d, value): +# d.putStringValue(value["d"]["m_unexpandedDisplayName"]) +# d.putPlainChildren(value) + + +def qdump__ProjectExplorer__ProjectNode(d, value): + qdump__ProjectExplorer__FolderNode(d, value) + + +def qdump__CMakeProjectManager__Internal__CMakeProjectNode(d, value): + qdump__ProjectExplorer__FolderNode(d, value) + + +def qdump__QmakeProjectManager__QmakePriFileNode(d, value): + qdump__ProjectExplorer__FolderNode(d, value) + + +def qdump__QmakeProjectManager__QmakeProFileNode(d, value): + qdump__ProjectExplorer__FolderNode(d, value) diff --git a/share/qtcreator/debugger/python2/dumper.py b/share/qtcreator/debugger/python2/dumper.py new file mode 100644 index 00000000000..1fd42848d4e --- /dev/null +++ b/share/qtcreator/debugger/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="",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('') + 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 + 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 + 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 '' + 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 propertyNames; + # - QList propertyValues; + # - QVector runningTimers; + # - QList > 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 userData; only #ifndef QT_NO_USERDATA + # - QList propertyNames; + # - QList propertyValues; + # - QVector runningTimers; + # - QList > 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 '' + + 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}', 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 = '' + 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 = '' + 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 'servicenamemsglenmsg' 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 '' + + 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) diff --git a/share/qtcreator/debugger/python2/gdbbridge.py b/share/qtcreator/debugger/python2/gdbbridge.py new file mode 100644 index 00000000000..d61160eed3b --- /dev/null +++ b/share/qtcreator/debugger/python2/gdbbridge.py @@ -0,0 +1,1624 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +try: + import __builtin__ +except: + import builtins + +import gdb +import os +import os.path +import re +import sys +import struct +import tempfile + +from dumper import DumperBase, Children, toInteger, TopLevelItem +from utils import TypeCode +from gdbtracepoint import * + + +####################################################################### +# +# Infrastructure +# +####################################################################### + + +def safePrint(output): + try: + print(output) + except: + out = '' + for c in output: + cc = ord(c) + if cc > 127: + out += '\\\\%d' % cc + elif cc < 0: + out += '\\\\%d' % (cc + 256) + else: + out += c + print(out) + + +def registerCommand(name, func): + + class Command(gdb.Command): + def __init__(self): + super(Command, self).__init__(name, gdb.COMMAND_OBSCURE) + + def invoke(self, args, from_tty): + safePrint(func(args)) + + Command() + + +####################################################################### +# +# Convenience +# +####################################################################### + +# For CLI dumper use, see README.txt +class PPCommand(gdb.Command): + def __init__(self): + super(PPCommand, self).__init__('pp', gdb.COMMAND_OBSCURE) + + def invoke(self, args, from_tty): + print(theCliDumper.fetchVariable(args)) + + +PPCommand() + +# Just convenience for 'python print gdb.parse_and_eval(...)' + + +class PPPCommand(gdb.Command): + def __init__(self): + super(PPPCommand, self).__init__('ppp', gdb.COMMAND_OBSCURE) + + def invoke(self, args, from_tty): + print(gdb.parse_and_eval(args)) + + +PPPCommand() + + +def scanStack(p, n): + p = int(p) + r = [] + for i in range(n): + f = gdb.parse_and_eval('{void*}%s' % p) + m = gdb.execute('info symbol %s' % f, to_string=True) + if not m.startswith('No symbol matches'): + r.append(m) + p += f.type.sizeof + return r + + +class ScanStackCommand(gdb.Command): + def __init__(self): + super(ScanStackCommand, self).__init__('scanStack', gdb.COMMAND_OBSCURE) + + def invoke(self, args, from_tty): + if len(args) == 0: + args = 20 + safePrint(scanStack(gdb.parse_and_eval('$sp'), int(args))) + + +ScanStackCommand() + + +####################################################################### +# +# Import plain gdb pretty printers +# +####################################################################### + +class PlainDumper(): + def __init__(self, printer): + self.printer = printer + self.typeCache = {} + + def __call__(self, d, value): + if value.nativeValue is None: + # warn('PlainDumper(gdb): value.nativeValue is missing (%s)'%value) + nativeType = theDumper.lookupNativeType(value.type.name) + nativeTypePointer = nativeType.pointer() + nativePointer = gdb.Value(value.laddress) + value.nativeValue = nativePointer.cast(nativeTypePointer).dereference() + try: + printer = self.printer.gen_printer(value.nativeValue) + except: + printer = self.printer.invoke(value.nativeValue) + d.putType(value.nativeValue.type.name) + val = printer.to_string() + if isinstance(val, str): + # encode and avoid extra quotes ('"') at beginning and end + d.putValue(d.hexencode(val), 'utf8:1:0') + elif sys.version_info[0] <= 2 and isinstance(val, unicode): + d.putValue(val) + elif val is not None: # Assuming LazyString + d.putCharArrayValue(val.address, val.length, + val.type.target().sizeof) + + lister = getattr(printer, 'children', None) + if lister is None: + d.putNumChild(0) + else: + if d.isExpanded(): + children = lister() + with Children(d): + i = 0 + for (name, child) in children: + d.putSubItem(name, d.fromNativeValue(child)) + i += 1 + if i > 1000: + break + d.putNumChild(1) + + +def importPlainDumpers(args): + if args == 'off': + theDumper.usePlainDumpers = False + try: + gdb.execute('disable pretty-printer .* .*') + except: + # Might occur in non-ASCII directories + DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS') + else: + theDumper.usePlainDumpers = True + theDumper.importPlainDumpers() + + +registerCommand('importPlainDumpers', importPlainDumpers) + + +####################################################################### +# +# The Dumper Class +# +####################################################################### + + +class Dumper(DumperBase): + + def __init__(self): + DumperBase.__init__(self) + + # whether to load plain dumpers for objfiles + self.usePlainDumpers = False + + # These values will be kept between calls to 'fetchVariables'. + self.isGdb = True + self.typeCache = {} + self.interpreterBreakpointResolvers = [] + + def prepare(self, args): + self.output = [] + self.setVariableFetchingOptions(args) + + def fromFrameValue(self, nativeValue): + #DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address) + val = nativeValue + if self.useDynamicType: + try: + val = nativeValue.cast(nativeValue.dynamic_type) + except: + pass + return self.fromNativeValue(val) + + def nativeValueType(self, nativeValue): + return self.fromNativeType(nativeValue.type) + + def fromNativeValue(self, nativeValue): + #DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue) + self.check(isinstance(nativeValue, gdb.Value)) + nativeType = nativeValue.type + code = nativeType.code + if code == gdb.TYPE_CODE_REF: + targetType = self.fromNativeType(nativeType.target().unqualified()) + val = self.createReferenceValue(toInteger(nativeValue.address), targetType) + val.nativeValue = nativeValue + #DumperBase.warn('CREATED REF: %s' % val) + return val + if code == gdb.TYPE_CODE_PTR: + try: + nativeTargetValue = nativeValue.dereference() + except: + nativeTargetValue = None + targetType = self.fromNativeType(nativeType.target().unqualified()) + val = self.createPointerValue(toInteger(nativeValue), targetType) + # The nativeValue is needed in case of multiple inheritance, see + # QTCREATORBUG-17823. Using it triggers nativeValueDereferencePointer() + # later which + # is surprisingly expensive. + val.nativeValue = nativeValue + #DumperBase.warn('CREATED PTR 1: %s' % val) + if nativeValue.address is not None: + val.laddress = toInteger(nativeValue.address) + #DumperBase.warn('CREATED PTR 2: %s' % val) + return val + if code == gdb.TYPE_CODE_TYPEDEF: + targetType = nativeType.strip_typedefs().unqualified() + #DumperBase.warn('TARGET TYPE: %s' % targetType) + if targetType.code == gdb.TYPE_CODE_ARRAY: + val = self.Value(self) + else: + try: + # Cast may fail for arrays, for typedefs to __uint128_t with + # gdb.error: That operation is not available on integers + # of more than 8 bytes. + # See test for Bug5799, QTCREATORBUG-18450. + val = self.fromNativeValue(nativeValue.cast(targetType)) + except: + val = self.Value(self) + #DumperBase.warn('CREATED TYPEDEF: %s' % val) + else: + val = self.Value(self) + + val.nativeValue = nativeValue + if nativeValue.address is not None: + val.laddress = toInteger(nativeValue.address) + else: + size = nativeType.sizeof + chars = self.lookupNativeType('unsigned char') + y = nativeValue.cast(chars.array(0, int(nativeType.sizeof - 1))) + buf = bytearray(struct.pack('x' * size)) + for i in range(size): + try: + buf[i] = int(y[i]) + except: + pass + val.ldata = bytes(buf) + + val._type = self.fromNativeType(nativeType) + val.lIsInScope = not nativeValue.is_optimized_out + code = nativeType.code + if code == gdb.TYPE_CODE_ENUM: + val.ldisplay = str(nativeValue) + intval = int(nativeValue) + if val.ldisplay != intval: + val.ldisplay += ' (%s)' % intval + elif code == gdb.TYPE_CODE_COMPLEX: + val.ldisplay = str(nativeValue) + elif code in [gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_INT]: + try: + # extract int presentation from native value and remember it + val.lvalue = int(nativeValue) + except: + # GDB only support converting integers of max. 64 bits to Python int as of now + pass + #elif code == gdb.TYPE_CODE_ARRAY: + # val.type.ltarget = nativeValue[0].type.unqualified() + return val + + def ptrSize(self): + result = gdb.lookup_type('void').pointer().sizeof + self.ptrSize = lambda: result + return result + + def fromNativeType(self, nativeType): + self.check(isinstance(nativeType, gdb.Type)) + code = nativeType.code + #DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType) + nativeType = nativeType.unqualified() + + if code == gdb.TYPE_CODE_PTR: + #DumperBase.warn('PTR') + targetType = self.fromNativeType(nativeType.target().unqualified()) + return self.createPointerType(targetType) + + if code == gdb.TYPE_CODE_REF: + #DumperBase.warn('REF') + targetType = self.fromNativeType(nativeType.target().unqualified()) + return self.createReferenceType(targetType) + + if hasattr(gdb, "TYPE_CODE_RVALUE_REF"): + if code == gdb.TYPE_CODE_RVALUE_REF: + #DumperBase.warn('RVALUEREF') + targetType = self.fromNativeType(nativeType.target()) + return self.createRValueReferenceType(targetType) + + if code == gdb.TYPE_CODE_ARRAY: + #DumperBase.warn('ARRAY') + nativeTargetType = nativeType.target().unqualified() + targetType = self.fromNativeType(nativeTargetType) + if nativeType.sizeof == 0: + # QTCREATORBUG-23998, note that nativeType.name == None here, + # whereas str(nativeType) returns sth like 'QObject [5]' + count = self.arrayItemCountFromTypeName(str(nativeType), 1) + else: + count = nativeType.sizeof // nativeTargetType.sizeof + return self.createArrayType(targetType, count) + + if code == gdb.TYPE_CODE_TYPEDEF: + #DumperBase.warn('TYPEDEF') + nativeTargetType = nativeType.unqualified() + while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF: + nativeTargetType = nativeTargetType.strip_typedefs().unqualified() + targetType = self.fromNativeType(nativeTargetType) + return self.createTypedefedType(targetType, str(nativeType), + self.nativeTypeId(nativeType)) + + if code == gdb.TYPE_CODE_ERROR: + self.warn('Type error: %s' % nativeType) + return self.Type(self, '') + + typeId = self.nativeTypeId(nativeType) + res = self.typeData.get(typeId, None) + if res is None: + tdata = self.TypeData(self, typeId) + tdata.name = str(nativeType) + tdata.lbitsize = nativeType.sizeof * 8 + tdata.code = { + #gdb.TYPE_CODE_TYPEDEF : TypeCode.Typedef, # Handled above. + gdb.TYPE_CODE_METHOD: TypeCode.Function, + gdb.TYPE_CODE_VOID: TypeCode.Void, + gdb.TYPE_CODE_FUNC: TypeCode.Function, + gdb.TYPE_CODE_METHODPTR: TypeCode.Function, + gdb.TYPE_CODE_MEMBERPTR: TypeCode.Function, + #gdb.TYPE_CODE_PTR : TypeCode.Pointer, # Handled above. + #gdb.TYPE_CODE_REF : TypeCode.Reference, # Handled above. + gdb.TYPE_CODE_BOOL: TypeCode.Integral, + gdb.TYPE_CODE_CHAR: TypeCode.Integral, + gdb.TYPE_CODE_INT: TypeCode.Integral, + gdb.TYPE_CODE_FLT: TypeCode.Float, + gdb.TYPE_CODE_ENUM: TypeCode.Enum, + #gdb.TYPE_CODE_ARRAY : TypeCode.Array, + gdb.TYPE_CODE_STRUCT: TypeCode.Struct, + gdb.TYPE_CODE_UNION: TypeCode.Struct, + gdb.TYPE_CODE_COMPLEX: TypeCode.Complex, + gdb.TYPE_CODE_STRING: TypeCode.FortranString, + }[code] + if tdata.code == TypeCode.Enum: + tdata.enumDisplay = lambda intval, addr, form: \ + self.nativeTypeEnumDisplay(nativeType, intval, form) + if tdata.code == TypeCode.Struct: + tdata.lalignment = lambda: \ + self.nativeStructAlignment(nativeType) + tdata.lfields = lambda value: \ + self.listMembers(value, nativeType) + tdata.templateArguments = lambda: \ + self.listTemplateParameters(nativeType) + # warn('CREATE TYPE: %s' % typeId) + #else: + # warn('REUSE TYPE: %s' % typeId) + return self.Type(self, typeId) + + def listTemplateParameters(self, nativeType): + targs = [] + pos = 0 + while True: + try: + targ = nativeType.template_argument(pos) + except: + break + if isinstance(targ, gdb.Type): + targs.append(self.fromNativeType(targ.unqualified())) + elif isinstance(targ, gdb.Value): + targs.append(self.fromNativeValue(targ).value()) + else: + raise RuntimeError('UNKNOWN TEMPLATE PARAMETER') + pos += 1 + targs2 = self.listTemplateParametersManually(str(nativeType)) + return targs if len(targs) >= len(targs2) else targs2 + + def nativeTypeEnumDisplay(self, nativeType, intval, form): + try: + enumerators = [] + for field in nativeType.fields(): + # If we found an exact match, return it immediately + if field.enumval == intval: + return field.name + ' (' + (form % intval) + ')' + enumerators.append((field.name, field.enumval)) + + # No match was found, try to return as flags + enumerators.sort(key=lambda x: x[1]) + flags = [] + v = intval + found = False + for (name, value) in enumerators: + if v & value != 0: + flags.append(name) + v = v & ~value + found = True + if not found or v != 0: + # Leftover value + flags.append('unknown: %d' % v) + return '(' + " | ".join(flags) + ') (' + (form % intval) + ')' + except: + pass + return form % intval + + def nativeTypeId(self, nativeType): + if nativeType and (nativeType.code == gdb.TYPE_CODE_TYPEDEF): + return '%s{%s}' % (nativeType, nativeType.strip_typedefs()) + name = str(nativeType) + if len(name) == 0: + c = '0' + elif name == 'union {...}': + c = 'u' + elif name.endswith('{...}'): + c = 's' + else: + return name + typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type)) + for f in nativeType.fields()]) + return typeId + + def nativeStructAlignment(self, nativeType): + #DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name) + def handleItem(nativeFieldType, align): + a = self.fromNativeType(nativeFieldType).alignment() + return a if a > align else align + align = 1 + for f in nativeType.fields(): + align = handleItem(f.type, align) + return align + + #except: + # # Happens in the BoostList dumper for a 'const bool' + # # item named 'constant_time_size'. There isn't anything we can do + # # in this case. + # pass + + #yield value.extractField(field) + + def memberFromNativeFieldAndValue(self, nativeField, nativeValue, fieldName, value): + nativeMember = self.nativeMemberFromField(nativeValue, nativeField) + if nativeMember is None: + val = self.Value(self) + val.name = fieldName + val._type = self.fromNativeType(nativeField.type) + val.lIsInScope = False + return val + val = self.fromNativeValue(nativeMember) + nativeFieldType = nativeField.type.unqualified() + if nativeField.bitsize: + val.lvalue = int(nativeMember) + val.laddress = None + fieldType = self.fromNativeType(nativeFieldType) + val._type = self.createBitfieldType(fieldType, nativeField.bitsize) + val.isBaseClass = nativeField.is_base_class + val.name = fieldName + return val + + def nativeMemberFromField(self, nativeValue, nativeField): + if nativeField.is_base_class: + return nativeValue.cast(nativeField.type) + try: + return nativeValue[nativeField] + except: + pass + try: + return nativeValue[nativeField.name] + except: + pass + return None + + def listMembers(self, value, nativeType): + nativeValue = value.nativeValue + + anonNumber = 0 + + #DumperBase.warn('LISTING FIELDS FOR %s' % nativeType) + for nativeField in nativeType.fields(): + fieldName = nativeField.name + # Something without a name. + # Anonymous union? We need a dummy name to distinguish + # multiple anonymous unions in the struct. + # Since GDB commit b5b08fb4 anonymous structs get also reported + # with a 'None' name. + if fieldName is None or len(fieldName) == 0: + # Something without a name. + # Anonymous union? We need a dummy name to distinguish + # multiple anonymous unions in the struct. + anonNumber += 1 + fieldName = '#%s' % anonNumber + #DumperBase.warn('FIELD: %s' % fieldName) + # hasattr(nativeField, 'bitpos') == False indicates a static field, + # but if we have access to a nativeValue .fromNativeField will + # also succeed. We essentially skip only static members from + # artificial values, like array members constructed from address. + if hasattr(nativeField, 'bitpos') or nativeValue is not None: + yield self.fromNativeField(nativeField, nativeValue, fieldName) + + def fromNativeField(self, nativeField, nativeValue, fieldName): + nativeFieldType = nativeField.type.unqualified() + #DumperBase.warn(' TYPE: %s' % nativeFieldType) + #DumperBase.warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType)) + + if hasattr(nativeField, 'bitpos'): + bitpos = nativeField.bitpos + else: + bitpos = 0 + + if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0: + bitsize = nativeField.bitsize + else: + bitsize = 8 * nativeFieldType.sizeof + + fieldType = self.fromNativeType(nativeFieldType) + if bitsize != nativeFieldType.sizeof * 8: + fieldType = self.createBitfieldType(fieldType, bitsize) + else: + fieldType = fieldType + + if nativeValue is None: + extractor = None + else: + extractor = lambda value, \ + capturedNativeField = nativeField, \ + capturedNativeValue = nativeValue, \ + capturedFieldName = fieldName: \ + self.memberFromNativeFieldAndValue(capturedNativeField, + capturedNativeValue, + capturedFieldName, + value) + + #DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos)) + return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class, + bitsize=bitsize, bitpos=bitpos, type=fieldType, + extractor=extractor) + + def listLocals(self, partialVar): + frame = gdb.selected_frame() + + try: + block = frame.block() + #DumperBase.warn('BLOCK: %s ' % block) + except RuntimeError as error: + #DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error) + return [] + except: + self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS') + return [] + + items = [] + shadowed = {} + while True: + if block is None: + self.warn("UNEXPECTED 'None' BLOCK") + break + for symbol in block: + + # Filter out labels etc. + if symbol.is_variable or symbol.is_argument: + name = symbol.print_name + + if name in ('__in_chrg', '__PRETTY_FUNCTION__'): + continue + + if partialVar is not None and partialVar != name: + continue + + # 'NotImplementedError: Symbol type not yet supported in + # Python scripts.' + #DumperBase.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name)) + if self.passExceptions and not self.isTesting: + nativeValue = frame.read_var(name, block) + value = self.fromFrameValue(nativeValue) + value.name = name + #DumperBase.warn('READ 0: %s' % value.stringify()) + items.append(value) + continue + + try: + # Same as above, but for production. + nativeValue = frame.read_var(name, block) + value = self.fromFrameValue(nativeValue) + value.name = name + #DumperBase.warn('READ 1: %s' % value.stringify()) + items.append(value) + continue + except: + pass + + try: + #DumperBase.warn('READ 2: %s' % item.value) + value = self.fromFrameValue(frame.read_var(name)) + value.name = name + items.append(value) + continue + except: + # RuntimeError: happens for + # void foo() { std::string s; std::wstring w; } + # ValueError: happens for (as of 2010/11/4) + # a local struct as found e.g. in + # gcc sources in gcc.c, int execute() + pass + + try: + #DumperBase.warn('READ 3: %s %s' % (name, item.value)) + #DumperBase.warn('ITEM 3: %s' % item.value) + value = self.fromFrameValue(gdb.parse_and_eval(name)) + value.name = name + items.append(value) + except: + # Can happen in inlined code (see last line of + # RowPainter::paintChars(): 'RuntimeError: + # No symbol '__val' in current context.\n' + pass + + # The outermost block in a function has the function member + # FIXME: check whether this is guaranteed. + if block.function is not None: + break + + block = block.superblock + + return items + + def reportToken(self, args): + pass + + # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit + # due to misaligned %ebx in SSE calls (qstring.cpp:findChar) + # This seems to be fixed in 7.9 (or earlier) + def canCallLocale(self): + return self.ptrSize() == 8 + + def fetchVariables(self, args): + self.resetStats() + self.prepare(args) + + self.isBigEndian = gdb.execute('show endian', to_string=True).find('big endian') > 0 + self.packCode = '>' if self.isBigEndian else '<' + + (ok, res) = self.tryFetchInterpreterVariables(args) + if ok: + safePrint(res) + return + + self.put('data=[') + + partialVar = args.get('partialvar', '') + isPartial = len(partialVar) > 0 + partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None + + variables = self.listLocals(partialName) + #DumperBase.warn('VARIABLES: %s' % variables) + + # Take care of the return value of the last function call. + if len(self.resultVarName) > 0: + try: + value = self.parseAndEvaluate(self.resultVarName) + value.name = self.resultVarName + value.iname = 'return.' + self.resultVarName + variables.append(value) + except: + # Don't bother. It's only supplementary information anyway. + pass + + self.handleLocals(variables) + self.handleWatches(args) + + self.put('],typeinfo=[') + for name in self.typesToReport.keys(): + typeobj = self.typesToReport[name] + # Happens e.g. for '(anonymous namespace)::InsertDefOperation' + #if not typeobj is None: + # self.put('{name="%s",size="%s"}' % (self.hexencode(name), typeobj.sizeof)) + self.put(']') + self.typesToReport = {} + + if self.forceQtNamespace: + self.qtNamespaceToReport = self.qtNamespace() + + if self.qtNamespaceToReport: + self.put(',qtnamespace="%s"' % self.qtNamespaceToReport) + self.qtNamespaceToReport = None + + self.put(',partial="%d"' % isPartial) + self.put(',counts=%s' % self.counts) + self.put(',timings=%s' % self.timings) + self.reportResult(''.join(self.output), args) + + def parseAndEvaluate(self, exp): + val = self.nativeParseAndEvaluate(exp) + return None if val is None else self.fromNativeValue(val) + + def nativeParseAndEvaluate(self, exp): + #DumperBase.warn('EVALUATE "%s"' % exp) + try: + val = gdb.parse_and_eval(exp) + return val + except RuntimeError as error: + if self.passExceptions: + self.warn("Cannot evaluate '%s': %s" % (exp, error)) + return None + + def callHelper(self, rettype, value, function, args): + if self.isWindowsTarget(): + raise Exception("gdb crashes when calling functions on Windows") + # args is a tuple. + arg = '' + for i in range(len(args)): + if i: + arg += ',' + a = args[i] + if (':' in a) and not ("'" in a): + arg = "'%s'" % a + else: + arg += a + + #DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg)) + typeName = value.type.name + if typeName.find(':') >= 0: + typeName = "'" + typeName + "'" + # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912 + #exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg) + addr = value.address() + if addr is None: + addr = self.pokeValue(value) + #DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr)) + exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg) + #DumperBase.warn('CALL: %s' % exp) + result = gdb.parse_and_eval(exp) + #DumperBase.warn(' -> %s' % result) + res = self.fromNativeValue(result) + if value.address() is None: + self.releaseValue(addr) + return res + + def makeExpression(self, value): + typename = '::' + value.type.name + #DumperBase.warn(' TYPE: %s' % typename) + exp = '(*(%s*)(0x%x))' % (typename, value.address()) + #DumperBase.warn(' EXP: %s' % exp) + return exp + + def makeStdString(init): + # Works only for small allocators, but they are usually empty. + gdb.execute('set $d=(std::string*)calloc(sizeof(std::string), 2)') + gdb.execute('call($d->basic_string("' + init + + '",*(std::allocator*)(1+$d)))') + value = gdb.parse_and_eval('$d').dereference() + return value + + def pokeValue(self, value): + # Allocates inferior memory and copies the contents of value. + # Returns a pointer to the copy. + # Avoid malloc symbol clash with QVector + size = value.type.size() + data = value.data() + h = self.hexencode(data) + #DumperBase.warn('DATA: %s' % h) + string = ''.join('\\x' + h[2 * i:2 * i + 2] for i in range(size)) + exp = '(%s*)memcpy(calloc(%d, 1), "%s", %d)' \ + % (value.type.name, size, string, size) + #DumperBase.warn('EXP: %s' % exp) + res = gdb.parse_and_eval(exp) + #DumperBase.warn('RES: %s' % res) + return toInteger(res) + + def releaseValue(self, address): + gdb.parse_and_eval('free(0x%x)' % address) + + def setValue(self, address, typename, value): + cmd = 'set {%s}%s=%s' % (typename, address, value) + gdb.execute(cmd) + + def setValues(self, address, typename, values): + cmd = 'set {%s[%s]}%s={%s}' \ + % (typename, len(values), address, ','.join(map(str, values))) + gdb.execute(cmd) + + def selectedInferior(self): + try: + # gdb.Inferior is new in gdb 7.2 + self.cachedInferior = gdb.selected_inferior() + except: + # Pre gdb 7.4. Right now we don't have more than one inferior anyway. + self.cachedInferior = gdb.inferiors()[0] + + # Memoize result. + self.selectedInferior = lambda: self.cachedInferior + return self.cachedInferior + + def readRawMemory(self, address, size): + #DumperBase.warn('READ: %s FROM 0x%x' % (size, address)) + if address == 0 or size == 0: + return bytes() + res = self.selectedInferior().read_memory(address, size) + return res + + def findStaticMetaObject(self, type): + symbolName = type.name + '::staticMetaObject' + symbol = gdb.lookup_global_symbol(symbolName, gdb.SYMBOL_VAR_DOMAIN) + if not symbol: + return 0 + try: + # Older GDB ~7.4 don't have gdb.Symbol.value() + return toInteger(symbol.value().address) + except: + pass + + address = gdb.parse_and_eval("&'%s'" % symbolName) + return toInteger(address) + + def isArmArchitecture(self): + return 'arm' in gdb.TARGET_CONFIG.lower() + + def isQnxTarget(self): + return 'qnx' in gdb.TARGET_CONFIG.lower() + + def isWindowsTarget(self): + # We get i686-w64-mingw32 + return 'mingw' in gdb.TARGET_CONFIG.lower() + + def isMsvcTarget(self): + return False + + def prettySymbolByAddress(self, address): + try: + return str(gdb.parse_and_eval('(void(*))0x%x' % address)) + except: + return '0x%x' % address + + def qtVersionString(self): + try: + return str(gdb.lookup_symbol('qVersion')[0].value()()) + except: + pass + try: + ns = self.qtNamespace() + return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns)) + except: + pass + return None + + def qtVersion(self): + try: + # Only available with Qt 5.3+ + qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16) + self.qtVersion = lambda: qtversion + return qtversion + except: + pass + + try: + version = self.qtVersionString() + (major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.') + qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch) + self.qtVersion = lambda: qtversion + return qtversion + except: + # Use fallback until we have a better answer. + return self.fallbackQtVersion + + def createSpecialBreakpoints(self, args): + self.specialBreakpoints = [] + + def newSpecial(spec): + # GDB < 8.1 does not have the 'qualified' parameter here, + # GDB >= 8.1 applies some generous pattern matching, hitting + # e.g. also Foo::abort() when asking for '::abort' + class Pre81SpecialBreakpoint(gdb.Breakpoint): + def __init__(self, spec): + super(Pre81SpecialBreakpoint, self).__init__(spec, + gdb.BP_BREAKPOINT, internal=True) + self.spec = spec + + def stop(self): + print("Breakpoint on '%s' hit." % self.spec) + return True + + class SpecialBreakpoint(gdb.Breakpoint): + def __init__(self, spec): + super(SpecialBreakpoint, self).__init__(spec, + gdb.BP_BREAKPOINT, + internal=True, + qualified=True) + self.spec = spec + + def stop(self): + print("Breakpoint on '%s' hit." % self.spec) + return True + + try: + return SpecialBreakpoint(spec) + except: + return Pre81SpecialBreakpoint(spec) + + # FIXME: ns is accessed too early. gdb.Breakpoint() has no + # 'rbreak' replacement, and breakpoints created with + # 'gdb.execute('rbreak...') cannot be made invisible. + # So let's ignore the existing of namespaced builds for this + # fringe feature here for now. + ns = self.qtNamespace() + if args.get('breakonabort', 0): + self.specialBreakpoints.append(newSpecial('abort')) + + if args.get('breakonwarning', 0): + self.specialBreakpoints.append(newSpecial(ns + 'qWarning')) + self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::warning')) + + if args.get('breakonfatal', 0): + self.specialBreakpoints.append(newSpecial(ns + 'qFatal')) + self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::fatal')) + + #def threadname(self, maximalStackDepth, objectPrivateType): + # e = gdb.selected_frame() + # out = '' + # ns = self.qtNamespace() + # while True: + # maximalStackDepth -= 1 + # if maximalStackDepth < 0: + # break + # e = e.older() + # if e == None or e.name() == None: + # break + # if e.name() in (ns + 'QThreadPrivate::start', '_ZN14QThreadPrivate5startEPv@4'): + # try: + # thrptr = e.read_var('thr').dereference() + # d_ptr = thrptr['d_ptr']['d'].cast(objectPrivateType).dereference() + # try: + # objectName = d_ptr['objectName'] + # except: # Qt 5 + # p = d_ptr['extraData'] + # if not self.isNull(p): + # objectName = p.dereference()['objectName'] + # if not objectName is None: + # (data, size, alloc) = self.stringData(objectName) + # if size > 0: + # s = self.readMemory(data, 2 * size) + # + # thread = gdb.selected_thread() + # inner = '{valueencoded="uf16:2:0",id="' + # inner += str(thread.num) + '",value="' + # inner += s + # #inner += self.encodeString(objectName) + # inner += '"},' + # + # out += inner + # except: + # pass + # return out + + def threadnames(self, maximalStackDepth): + # FIXME: This needs a proper implementation for MinGW, and only there. + # Linux, Mac and QNX mirror the objectName() to the underlying threads, + # so we get the names already as part of the -thread-info output. + return '[]' + #out = '[' + #oldthread = gdb.selected_thread() + #if oldthread: + # try: + # objectPrivateType = gdb.lookup_type(ns + 'QObjectPrivate').pointer() + # inferior = self.selectedInferior() + # for thread in inferior.threads(): + # thread.switch() + # out += self.threadname(maximalStackDepth, objectPrivateType) + # except: + # pass + # oldthread.switch() + #return out + ']' + + def importPlainDumper(self, printer): + name = printer.name.replace('::', '__') + self.qqDumpers[name] = PlainDumper(printer) + self.qqFormats[name] = '' + + def importPlainDumpersForObj(self, obj): + for printers in obj.pretty_printers + gdb.pretty_printers: + if hasattr(printers, "subprinters"): + for printer in printers.subprinters: + self.importPlainDumper(printer) + else: + self.warn('Loading a printer without the subprinters attribute not supported.') + + def importPlainDumpers(self): + for obj in gdb.objfiles(): + self.importPlainDumpersForObj(obj) + + def qtNamespace(self): + # This function is replaced by handleQtCoreLoaded() + return '' + + def findSymbol(self, symbolName): + try: + return toInteger(gdb.parse_and_eval("(size_t)&'%s'" % symbolName)) + except: + return 0 + + def handleNewObjectFile(self, objfile): + name = objfile.filename + if self.isWindowsTarget(): + qtCoreMatch = re.match(r'.*Qt[56]?Core[^/.]*d?\.dll', name) + else: + qtCoreMatch = re.match(r'.*/libQt[56]?Core[^/.]*\.so', name) + + if qtCoreMatch is not None: + self.addDebugLibs(objfile) + self.handleQtCoreLoaded(objfile) + + if self.usePlainDumpers: + self.importPlainDumpersForObj(objfile) + + def addDebugLibs(self, objfile): + # The directory where separate debug symbols are searched for + # is "/usr/lib/debug". + try: + cooked = gdb.execute('show debug-file-directory', to_string=True) + clean = cooked.split('"')[1] + newdir = '/'.join(objfile.filename.split('/')[:-1]) + gdb.execute('set debug-file-directory %s:%s' % (clean, newdir)) + except: + pass + + def handleQtCoreLoaded(self, objfile): + fd, tmppath = tempfile.mkstemp() + os.close(fd) + try: + cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath) + symbols = gdb.execute(cmd, to_string=True) + except: + try: + # command syntax depends on gdb version - below is gdb < 8 + cmd = 'maint print msymbols %s "%s"' % (tmppath, objfile.filename) + symbols = gdb.execute(cmd, to_string=True) + except: + pass + ns = '' + with open(tmppath) as f: + for line in f: + if line.find('msgHandlerGrabbed ') >= 0: + # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE + # section .tbss Myns::msgHandlerGrabbed qlogging.cpp + ns = re.split(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ', line)[2] + if len(ns): + ns += '::' + break + if line.find('currentThreadData ') >= 0: + # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE + # section .tbss UU::currentThreadData qthread_unix.cpp\\n + ns = re.split(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ', line)[2] + if len(ns): + ns += '::' + break + os.remove(tmppath) + + lenns = len(ns) + strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else '' + + if lenns: + # This might be wrong, but we can't do better: We found + # a libQt5Core and could not extract a namespace. + # The best guess is that there isn't any. + self.qtNamespaceToReport = ns + self.qtNamespace = lambda: ns + + sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns + else: + sym = '_ZN7QObject11customEventEP6QEvent' + self.qtCustomEventFunc = self.findSymbol(sym) + + sym += '@plt' + self.qtCustomEventPltFunc = self.findSymbol(sym) + + sym = '_ZNK%s7QObject8propertyEPKc' % strns + if not self.isWindowsTarget(): # prevent calling the property function on windows + self.qtPropertyFunc = self.findSymbol(sym) + + def assignValue(self, args): + typeName = self.hexdecode(args['type']) + expr = self.hexdecode(args['expr']) + value = self.hexdecode(args['value']) + simpleType = int(args['simpleType']) + ns = self.qtNamespace() + if typeName.startswith(ns): + typeName = typeName[len(ns):] + typeName = typeName.replace('::', '__') + pos = typeName.find('<') + if pos != -1: + typeName = typeName[0:pos] + if typeName in self.qqEditable and not simpleType: + #self.qqEditable[typeName](self, expr, value) + expr = self.parseAndEvaluate(expr) + self.qqEditable[typeName](self, expr, value) + else: + cmd = 'set variable (%s)=%s' % (expr, value) + gdb.execute(cmd) + + def appendSolibSearchPath(self, args): + new = list(map(self.hexdecode, args['path'])) + old = [gdb.parameter('solib-search-path')] + joined = os.pathsep.join([item for item in old + new if item != '']) + gdb.execute('set solib-search-path %s' % joined) + + def watchPoint(self, args): + self.reportToken(args) + ns = self.qtNamespace() + lenns = len(ns) + strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else '' + sym = '_ZN%s12QApplication8widgetAtEii' % strns + expr = '%s(%s,%s)' % (sym, args['x'], args['y']) + res = self.parseAndEvaluate(expr) + p = 0 if res is None else res.pointer() + n = ("'%sQWidget'" % ns) if lenns else 'QWidget' + self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args) + + def nativeValueDereferencePointer(self, value): + # This is actually pretty expensive, up to 100ms. + deref = value.nativeValue.dereference() + if self.useDynamicType: + deref = deref.cast(deref.dynamic_type) + return self.fromNativeValue(deref) + + def nativeValueDereferenceReference(self, value): + nativeValue = value.nativeValue + return self.fromNativeValue(nativeValue.cast(nativeValue.type.target())) + + def nativeDynamicTypeName(self, address, baseType): + # Needed for Gdb13393 test. + nativeType = self.lookupNativeType(baseType.name) + if nativeType is None: + return None + nativeTypePointer = nativeType.pointer() + nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference() + val = nativeValue.cast(nativeValue.dynamic_type) + return str(val.type) + #try: + # vtbl = gdb.execute('info symbol {%s*}0x%x' % (baseType.name, address), to_string = True) + #except: + # return None + #pos1 = vtbl.find('vtable ') + #if pos1 == -1: + # return None + #pos1 += 11 + #pos2 = vtbl.find(' +', pos1) + #if pos2 == -1: + # return None + #return vtbl[pos1 : pos2] + + def nativeDynamicType(self, address, baseType): + # Needed for Gdb13393 test. + nativeType = self.lookupNativeType(baseType.name) + if nativeType is None: + return baseType + nativeTypePointer = nativeType.pointer() + nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference() + return self.fromNativeType(nativeValue.dynamic_type) + + def enumExpression(self, enumType, enumValue): + return self.qtNamespace() + 'Qt::' + enumValue + + def lookupNativeType(self, typeName): + nativeType = self.lookupNativeTypeHelper(typeName) + if nativeType is not None: + self.check(isinstance(nativeType, gdb.Type)) + return nativeType + + def lookupNativeTypeHelper(self, typeName): + typeobj = self.typeCache.get(typeName) + #DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj)) + if typeobj is not None: + return typeobj + + if typeName == 'void': + typeobj = gdb.lookup_type(typeName) + self.typeCache[typeName] = typeobj + self.typesToReport[typeName] = typeobj + return typeobj + + #try: + # typeobj = gdb.parse_and_eval('{%s}&main' % typeName).typeobj + # if not typeobj is None: + # self.typeCache[typeName] = typeobj + # self.typesToReport[typeName] = typeobj + # return typeobj + #except: + # pass + + # See http://sourceware.org/bugzilla/show_bug.cgi?id=13269 + # gcc produces '{anonymous}', gdb '(anonymous namespace)' + # '' has been seen too. The only thing gdb + # understands when reading things back is '(anonymous namespace)' + if typeName.find('{anonymous}') != -1: + ts = typeName + ts = ts.replace('{anonymous}', '(anonymous namespace)') + typeobj = self.lookupNativeType(ts) + if typeobj is not None: + self.typeCache[typeName] = typeobj + self.typesToReport[typeName] = typeobj + return typeobj + + #DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj)) + + # This part should only trigger for + # gdb 7.1 for types with namespace separators. + # And anonymous namespaces. + + ts = typeName + while True: + if ts.startswith('class '): + ts = ts[6:] + elif ts.startswith('struct '): + ts = ts[7:] + elif ts.startswith('const '): + ts = ts[6:] + elif ts.startswith('volatile '): + ts = ts[9:] + elif ts.startswith('enum '): + ts = ts[5:] + elif ts.endswith(' const'): + ts = ts[:-6] + elif ts.endswith(' volatile'): + ts = ts[:-9] + elif ts.endswith('*const'): + ts = ts[:-5] + elif ts.endswith('*volatile'): + ts = ts[:-8] + else: + break + + if ts.endswith('*'): + typeobj = self.lookupNativeType(ts[0:-1]) + if typeobj is not None: + typeobj = typeobj.pointer() + self.typeCache[typeName] = typeobj + self.typesToReport[typeName] = typeobj + return typeobj + + try: + #DumperBase.warn("LOOKING UP 1 '%s'" % ts) + typeobj = gdb.lookup_type(ts) + except RuntimeError as error: + #DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error)) + # See http://sourceware.org/bugzilla/show_bug.cgi?id=11912 + exp = "(class '%s'*)0" % ts + try: + typeobj = self.parse_and_eval(exp).type.target() + #DumperBase.warn("LOOKING UP 3 '%s'" % typeobj) + except: + # Can throw 'RuntimeError: No type named class Foo.' + pass + except: + #DumperBase.warn("LOOKING UP '%s' FAILED" % ts) + pass + + if typeobj is not None: + #DumperBase.warn('CACHING: %s' % typeobj) + self.typeCache[typeName] = typeobj + self.typesToReport[typeName] = typeobj + + # This could still be None as gdb.lookup_type('char[3]') generates + # 'RuntimeError: No type named char[3]' + #self.typeCache[typeName] = typeobj + #self.typesToReport[typeName] = typeobj + return typeobj + + def doContinue(self): + gdb.execute('continue') + + def fetchStack(self, args): + + def fromNativePath(string): + return string.replace('\\', '/') + + extraQml = int(args.get('extraqml', '0')) + limit = int(args['limit']) + if limit <= 0: + limit = 10000 + + self.prepare(args) + self.output = [] + + i = 0 + if extraQml: + frame = gdb.newest_frame() + ns = self.qtNamespace() + needle = self.qtNamespace() + 'QV4::ExecutionEngine' + pats = [ + '{0}qt_v4StackTraceForEngine((void*)0x{1:x})', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)', + ] + done = False + while i < limit and frame and not done: + block = None + try: + block = frame.block() + except: + pass + if block is not None: + for symbol in block: + if symbol.is_variable or symbol.is_argument: + value = symbol.value(frame) + typeobj = value.type + if typeobj.code == gdb.TYPE_CODE_PTR: + dereftype = typeobj.target().unqualified() + if dereftype.name == needle: + addr = toInteger(value) + res = None + for pat in pats: + try: + expr = pat.format(ns, addr) + res = str(gdb.parse_and_eval(expr)) + break + except: + continue + + if res is None: + done = True + break + + pos = res.find('"stack=[') + if pos != -1: + res = res[pos + 8:-2] + res = res.replace('\\\"', '\"') + res = res.replace('func=', 'function=') + self.put(res) + done = True + break + frame = frame.older() + i += 1 + + frame = gdb.newest_frame() + self.currentCallContext = None + self.output = [] + self.put('stack={frames=[') + while i < limit and frame: + name = frame.name() + functionName = '??' if name is None else name + fileName = '' + objfile = '' + symtab = '' + pc = frame.pc() + sal = frame.find_sal() + line = -1 + if sal: + line = sal.line + symtab = sal.symtab + if symtab is not None: + objfile = fromNativePath(symtab.objfile.filename) + fullname = symtab.fullname() + if fullname is None: + fileName = '' + else: + fileName = fromNativePath(fullname) + + if self.nativeMixed and functionName == 'qt_qmlDebugMessageAvailable': + interpreterStack = self.extractInterpreterStack() + #print('EXTRACTED INTEPRETER STACK: %s' % interpreterStack) + for interpreterFrame in interpreterStack.get('frames', []): + function = interpreterFrame.get('function', '') + fileName = interpreterFrame.get('file', '') + language = interpreterFrame.get('language', '') + lineNumber = interpreterFrame.get('line', 0) + context = interpreterFrame.get('context', 0) + + self.put(('frame={function="%s",file="%s",' + 'line="%s",language="%s",context="%s"}') + % (function, self.hexencode(fileName), lineNumber, language, context)) + + if False and self.isInternalInterpreterFrame(functionName): + frame = frame.older() + self.put(('frame={address="0x%x",function="%s",' + 'file="%s",line="%s",' + 'module="%s",language="c",usable="0"}') % + (pc, functionName, fileName, line, objfile)) + i += 1 + frame = frame.older() + continue + + self.put(('frame={level="%s",address="0x%x",function="%s",' + 'file="%s",line="%s",module="%s",language="c"}') % + (i, pc, functionName, fileName, line, objfile)) + + try: + # This may fail with something like + # gdb.error: DW_FORM_addr_index used without .debug_addr section + #[in module /data/dev/qt-6/qtbase/lib/libQt6Widgets.so.6] + frame = frame.older() + except: + break + i += 1 + self.put(']}') + self.reportResult(self.takeOutput(), args) + + def createResolvePendingBreakpointsHookBreakpoint(self, args): + class Resolver(gdb.Breakpoint): + def __init__(self, dumper, args): + self.dumper = dumper + self.args = args + spec = 'qt_qmlDebugConnectorOpen' + super(Resolver, self).\ + __init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False) + + def stop(self): + self.dumper.resolvePendingInterpreterBreakpoint(args) + self.enabled = False + return False + + self.interpreterBreakpointResolvers.append(Resolver(self, args)) + + def exitGdb(self, _): + gdb.execute('quit') + + def reportResult(self, result, args): + print('result={token="%s",%s}' % (args.get("token", 0), result)) + + def profile1(self, args): + '''Internal profiling''' + import cProfile + tempDir = tempfile.gettempdir() + '/bbprof' + cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir) + import pstats + pstats.Stats(tempDir).sort_stats('time').print_stats() + + def profile2(self, args): + import timeit + print(timeit.repeat('theDumper.fetchVariables(%s)' % args, + 'from __main__ import theDumper', number=10)) + + def tracepointModified(self, tp): + self.tpExpressions = {} + self.tpExpressionWarnings = [] + s = self.resultToMi(tp.dicts()) + def handler(): + print("tracepointmodified=%s" % s) + gdb.post_event(handler) + + def tracepointHit(self, tp, result): + expressions = '{' + ','.join(["%s=%s" % (k,v) for k,v in self.tpExpressions.items()]) + '}' + warnings = [] + if 'warning' in result.keys(): + warnings.append(result.pop('warning')) + warnings += self.tpExpressionWarnings + r = self.resultToMi(result) + w = self.resultToMi(warnings) + def handler(): + print("tracepointhit={result=%s,expressions=%s,warnings=%s}" % (r, expressions, w)) + gdb.post_event(handler) + + def tracepointExpression(self, tp, expression, value, args): + key = "x" + str(len(self.tpExpressions)) + if (isinstance(value, gdb.Value)): + try: + val = self.fromNativeValue(value) + self.prepare(args) + with TopLevelItem(self, expression): + self.putItem(val) + self.tpExpressions[key] = self.output + except Exception as e: + self.tpExpressions[key] = '""' + self.tpExpressionWarnings.append(str(e)) + elif (isinstance(value, Exception)): + self.tpExpressions[key] = '""' + self.tpExpressionWarnings.append(str(value)) + else: + self.tpExpressions[key] = '""' + self.tpExpressionWarnings.append('Unknown expression value type') + return key + + def createTracepoint(self, args): + """ + Creates a tracepoint + """ + tp = GDBTracepoint.create(args, + onModified=self.tracepointModified, + onHit=self.tracepointHit, + onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args)) + self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args) + +class CliDumper(Dumper): + def __init__(self): + Dumper.__init__(self) + self.childrenPrefix = '[' + self.chidrenSuffix = '] ' + self.indent = 0 + self.isCli = True + self.setupDumpers({}) + + def put(self, line): + if self.output: + if self.output[-1].endswith('\n'): + self.output[-1] = self.output[-1][0:-1] + self.output.append(line) + + def putNumChild(self, numchild): + pass + + def putOriginalAddress(self, address): + pass + + def fetchVariable(self, line): + # HACK: Currently, the response to the QtCore loading is completely + # eaten by theDumper, so copy the results here. Better would be + # some shared component. + self.qtCustomEventFunc = theDumper.qtCustomEventFunc + self.qtCustomEventPltFunc = theDumper.qtCustomEventPltFunc + self.qtPropertyFunc = theDumper.qtPropertyFunc + + names = line.split(' ') + name = names[0] + + toExpand = set() + for n in names: + while n: + toExpand.add(n) + try: + n = n[0:n.rindex('.')] + except ValueError: + break + + args = {} + args['fancy'] = 1 + args['passexceptions'] = 1 + args['autoderef'] = 1 + args['qobjectnames'] = 1 + args['varlist'] = name + args['expanded'] = toExpand + self.expandableINames = set() + self.prepare(args) + + self.output = [] + self.put(name + ' = ') + value = self.parseAndEvaluate(name) + with TopLevelItem(self, name): + self.putItem(value) + + if not self.expandableINames: + self.put('\n\nNo drill down available.\n') + return self.takeOutput() + + pattern = ' pp ' + name + ' ' + '%s' + return (self.takeOutput() + + '\n\nDrill down:\n ' + + '\n '.join(pattern % x for x in self.expandableINames) + + '\n') + + +# Global instances. +theDumper = Dumper() +theCliDumper = CliDumper() + + +###################################################################### +# +# ThreadNames Command +# +####################################################################### + +def threadnames(arg): + return theDumper.threadnames(int(arg)) + + +registerCommand('threadnames', threadnames) + +####################################################################### +# +# Native Mixed +# +####################################################################### + + +class InterpreterMessageBreakpoint(gdb.Breakpoint): + def __init__(self): + spec = 'qt_qmlDebugMessageAvailable' + super(InterpreterMessageBreakpoint, self).\ + __init__(spec, gdb.BP_BREAKPOINT, internal=True) + + def stop(self): + print('Interpreter event received.') + return theDumper.handleInterpreterMessage() + + +####################################################################### +# +# Shared objects +# +####################################################################### + +def new_objfile_handler(event): + return theDumper.handleNewObjectFile(event.new_objfile) + + +gdb.events.new_objfile.connect(new_objfile_handler) + + +#InterpreterMessageBreakpoint() diff --git a/share/qtcreator/debugger/python2/gdbtracepoint.py b/share/qtcreator/debugger/python2/gdbtracepoint.py new file mode 100644 index 00000000000..871132ac48d --- /dev/null +++ b/share/qtcreator/debugger/python2/gdbtracepoint.py @@ -0,0 +1,377 @@ +# Copyright (C) 2021 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import gdb +import sys +import time + +# for ProcessName capture +try: + import psutil +except: + psutil = None + +# Caps types +Address, \ +Caller, \ +Callstack, \ +FilePos, \ +Function, \ +Pid, \ +ProcessName, \ +Tick, \ +Tid, \ +ThreadName, \ +Expression, \ + = range(0, 11) + +class GDBTracepoint(gdb.Breakpoint): + """ + Python Breakpoint extension for "tracepoints", breakpoints that do not stop the inferior + """ + + @staticmethod + def create(args, onModified, onHit, onExpression): + """ + Static creator function + """ + tp_kwargs = {} + if 'temporary' in args.keys(): + tp_kwargs['temporary'] = args['temporary'] + spec = args['spec'] + tp = GDBTracepoint(spec, **tp_kwargs) + tp.onModified = onModified + tp.onHit = onHit + tp.onExpression = onExpression + if 'ignore_count' in args.keys(): + tp.ignore_count = args['ignore_count'] + if 'enabled' in args.keys(): + tp.enabled = args['enabled'] + if 'thread' in args.keys(): + tp.thread = args['thread'] + if 'condition' in args.keys(): + tp.condition = args['condition'] + if 'caps' in args.keys(): + for ce in args['caps']: + tp.addCaps(ce[0], ce[1]) + return tp + + def __init__(self, spec, **kwargs): + """ + Constructor + """ + kwargs['internal'] = True + super(GDBTracepoint, self).__init__(spec, **kwargs) + self.caps = [] + + _hexSize = 8 if sys.maxsize > 2**32 else 4 + _hasMonotonicTime = False if sys.version_info[0] <= 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 3) else True + + def dicts(self): + """ + Returns dictionareis for mi representation + """ + results = [] + result = {} + result['number'] = str(self.number) + result['enabled'] = 'y' if self.enabled else 'n' + result['type'] = 'pseudo_tracepoint' + result['disp'] = 'del' if self.temporary else 'keep' + result['times'] = str(self.hit_count) + result['original-location'] = self.location + try: + d = gdb.decode_line(self.location) + if d[1] is None: + result['addr'] = '' + result['pending'] = self.location + results.append(result) + else: + if len(d[1]) > 1: + result['addr'] = '' + results.append(result) + for i, sl in enumerate(d[1]): + result_ = {} + result_['number'] = result['number'] + "." + str(i + 1) + result_['enabled'] = 'y' if self.enabled else 'n' + if sl.pc is None: + result_['addr'] = '' + else: + result_['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2) + if sl.symtab and sl.symtab.is_valid(): + func = self._getFunctionFromAddr(sl.pc) + if func: + result_['func'] = func.print_name + result_['file'] = sl.symtab.filename + result_['fullname'] = sl.symtab.fullname() + result_['line'] = sl.line + results.append(result_) + else: + sl = d[1][0] + if sl.pc is None: + result['addr'] = '' + else: + result['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2) + if sl.symtab and sl.symtab.is_valid(): + func = self._getFunctionFromAddr(sl.pc) + if func: + result['func'] = func.print_name + result['file'] = sl.symtab.filename + result['fullname'] = sl.symtab.fullname() + result['line'] = sl.line + results.append(result) + except Exception as e: + import traceback + traceback.print_exc() + result['addr'] = '' + result['pending'] = self.location + results.append(result) + return results + + def addCaps(self, capsType, expression=None): + """ + Adds capture expressions for a tracepoint + + :param caps_type: Type of capture + :param expression: Expression for Expression caps type + """ + if capsType != Expression: + expression = None + else: + if expression is None: + expression = '' + self.caps.append((self.capsMap[capsType], expression)) + + def stop(self): + """ + Overridden stop function, this evaluates conditions and captures data from the inferior + + :return: Always False + """ + try: + self.onModified(self) + result = {} + result['number'] = self.number + try: + if self.condition: + try: + result = gdb.parse_and_eval(self.condition) + if result.type.code == gdb.TYPE_CODE_BOOL and str(result) == 'false': + return False + except: + pass + if self.ignore_count > 0: + return False + if self.thread and gdb.selected_thread().global_num != self.thread: + return False + except Exception as e: + result['warning'] = str(e) + self.onHit(self, result) + return False + if len(self.caps) > 0: + caps = [] + try: + for func, expr in self.caps: + if expr is None: + caps.append(func(self)) + else: + caps.append(func(self, expr)) + except Exception as e: + result['warning'] = str(e) + self.onHit(self, result) + return False + result['caps'] = caps + self.onHit(self, result) + return False + except: + # Always return false, regardless... + return False + + def _getFunctionFromAddr(self, addr): + try: + block = gdb.block_for_pc(addr) + while block and not block.function: + block = block.superblock + if block is None: + return None + return block.function + except: + return None + + def _getAddress(self): + """ + Capture function for Address + """ + try: + frame = gdb.selected_frame() + if not (frame is None) and (frame.is_valid()): + return '{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2) + except Exception as e: + return str(e) + return '' + + def _getCaller(self): + """ + Capture function for Caller + """ + try: + frame = gdb.selected_frame() + if not (frame is None) and (frame.is_valid()): + frame = frame.older() + if not (frame is None) and (frame.is_valid()): + name = frame.name() + if name is None: + return '' + return name + except Exception as e: + return str(e) + return '' + + def _getCallstack(self): + """ + Capture function for Callstack + """ + try: + frames = [] + frame = gdb.selected_frame() + if (frame is None) or (not frame.is_valid()): + frames.append('') + return str(frames) + while not (frame is None): + func = frame.function() + if func is None: + frames.append('{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2)) + else: + sl = frame.find_sal() + if sl is None: + frames.append(func.symtab.filename) + else: + frames.append(func.symtab.filename + ':' + str(sl.line)) + frame = frame.older() + return frames + except Exception as e: + return str(e) + + def _getFilePos(self): + """ + Capture function for FilePos + """ + try: + frame = gdb.selected_frame() + if (frame is None) or (not frame.is_valid()): + return '' + sl = frame.find_sal() + if sl is None: + return '' + return sl.symtab.filename + ':' + str(sl.line) + except Exception as e: + return str(e) + + def _getFunction(self): + """ + Capture function for Function + """ + try: + frame = gdb.selected_frame() + if not (frame is None): + return str(frame.name()) + except Exception as e: + return str(e) + return '' + + def _getPid(self): + """ + Capture function for Pid + """ + try: + thread = gdb.selected_thread() + if not (thread is None): + (pid, lwpid, tid) = thread.ptid + return str(pid) + except Exception as e: + return str(e) + return '' + + def _getProcessName(slef): + """ + Capture for ProcessName + """ + # gdb does not expose process name, neither does (standard) python + # You can use for example psutil, but it might not be present. + # Default to name of thread with ID 1 + inf = gdb.selected_inferior() + if psutil is None: + try: + if inf is None: + return '' + threads = filter(lambda t: t.num == 1, list(inf.threads())) + if len(threads) < 1: + return '' + thread = threads[0] + # use thread name + return thread.name + except Exception as e: + return str(e) + else: + return psutil.Process(inf.pid).name() + + def _getTick(self): + """ + Capture function for Tick + """ + if self._hasMonotonicTime: + return str(int(time.monotonic() * 1000)) + else: + return '' + + def _getTid(self): + """ + Capture function for Tid + """ + try: + thread = gdb.selected_thread() + if not (thread is None): + (pid, lwpid, tid) = thread.ptid + if tid == 0: + return str(lwpid) + else: + return str(tid) + except Exception as e: + return str(e) + return '' + + def _getThreadName(self): + """ + Capture function for ThreadName + """ + try: + thread = gdb.selected_thread() + if not (thread is None): + return str(thread.name) + except Exception as e: + return str(e) + return '' + + def _getExpression(self, expression): + """ + Capture function for Expression + + :param expr: The expression to evaluate + """ + try: + value = gdb.parse_and_eval(expression) + if value: + return self.onExpression(self, expression, value) + except Exception as e: + return self.onExpression(self, expression, e) + + capsMap = {Address: _getAddress, + Caller: _getCaller, + Callstack: _getCallstack, + FilePos: _getFilePos, + Function: _getFunction, + Pid: _getPid, + ProcessName: _getProcessName, + Tid: _getTid, + Tick: _getTick, + ThreadName: _getThreadName, + Expression: _getExpression} diff --git a/share/qtcreator/debugger/python2/libcpp_stdtypes.py b/share/qtcreator/debugger/python2/libcpp_stdtypes.py new file mode 100644 index 00000000000..44b3a235ffd --- /dev/null +++ b/share/qtcreator/debugger/python2/libcpp_stdtypes.py @@ -0,0 +1,527 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from stdtypes import qdump__std__array, qdump__std__complex, qdump__std__once_flag, qdump__std__unique_ptr, qdumpHelper__std__deque__libcxx, qdumpHelper__std__vector__libcxx +from utils import DisplayFormat +from dumper import Children, DumperBase + + +def qform__std____1__array(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____1__array(d, value): + qdump__std__array(d, value) + + +def qdump__std____1__complex(d, value): + qdump__std__complex(d, value) + + +def qdump__std____1__deque(d, value): + qdumpHelper__std__deque__libcxx(d, value) + + +def qdump__std____1__list(d, value): + if value.type.size() == 3 * d.ptrSize(): + # C++11 only. + (dummy1, dummy2, size) = value.split("ppp") + d.putItemCount(size) + else: + # Need to count manually. + p = d.extractPointer(value) + head = value.address() + size = 0 + while head != p and size < 1001: + size += 1 + p = d.extractPointer(p) + d.putItemCount(size, 1000) + + if d.isExpanded(): + (prev, p) = value.split("pp") + innerType = value.type[0] + typeCode = "pp{%s}" % innerType.name + with Children(d, size, maxNumChild=1000, childType=innerType): + for i in d.childRange(): + (prev, p, val) = d.split(typeCode, p) + d.putSubItem(i, val) + + +def qdump__std____1__set(d, value): + (proxy, head, size) = value.split("ppp") + + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.putItemCount(size) + + if d.isExpanded(): + valueType = value.type[0] + + def in_order_traversal(node): + (left, right, parent, color, pad, data) = d.split("pppB@{%s}" % (valueType.name), node) + + if left: + for res in in_order_traversal(left): + yield res + + yield data + + if right: + for res in in_order_traversal(right): + yield res + + with Children(d, size, maxNumChild=1000): + for (i, data) in zip(d.childRange(), in_order_traversal(head)): + d.putSubItem(i, data) + + +def qdump__std____1__multiset(d, value): + qdump__std____1__set(d, value) + + +def qform__std____1__map(): + return [DisplayFormat.CompactMap] + + +def qdump__std____1__map(d, value): + try: + (proxy, head, size) = value.split("ppp") + d.check(0 <= size and size <= 100 * 1000 * 1000) + + # Sometimes there is extra data at the front. Don't know why at the moment. + except RuntimeError: + (junk, proxy, head, size) = value.split("pppp") + d.check(0 <= size and size <= 100 * 1000 * 1000) + + d.putItemCount(size) + + if d.isExpanded(): + keyType = value.type[0] + valueType = value.type[1] + pairType = value.type[3][0] + + def in_order_traversal(node): + (left, right, parent, color, pad, pair) = d.split("pppB@{%s}" % (pairType.name), node) + + if left: + for res in in_order_traversal(left): + yield res + + yield pair.split("{%s}@{%s}" % (keyType.name, valueType.name))[::2] + + if right: + for res in in_order_traversal(right): + yield res + + with Children(d, size, maxNumChild=1000): + for (i, pair) in zip(d.childRange(), in_order_traversal(head)): + d.putPairItem(i, pair) + + +def qform__std____1__multimap(): + return [DisplayFormat.CompactMap] + + +def qdump__std____1__multimap(d, value): + qdump__std____1__map(d, value) + + +def qdump__std____1__map__iterator(d, value): + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + node = value['__i_']['__ptr_'].dereference()['__value_']['__cc'] + d.putSubItem('first', node['first']) + d.putSubItem('second', node['second']) + + +def qdump__std____1__map__const_iterator(d, value): + qdump__std____1__map__iterator(d, value) + + +def qdump__std____1__set__iterator(d, value): + d.putEmptyValue() + d.putExpandable() + if value.type.name.endswith("::iterator"): + treeTypeName = value.type.name[:-len("::iterator")] + elif value.type.name.endswith("::const_iterator"): + treeTypeName = value.type.name[:-len("::const_iterator")] + treeType = d.lookupType(treeTypeName) + keyType = treeType[0] + if d.isExpanded(): + with Children(d): + node = value['__ptr_'].dereference()['__value_'] + node = node.cast(keyType) + d.putSubItem('value', node) + + +def qdump__std____1__set_const_iterator(d, value): + qdump__std____1__set__iterator(d, value) + + +def qdump__std____1__stack(d, value): + d.putItem(value["c"]) + d.putBetterType(value.type) + + +def GetChildMemberWithName(value, name): + members = value.members(True) + + for member in members: + if member.name == name: + return member + return None + + +def GetIndexOfChildWithName(value, name): + members = value.members(True) + + for i, member in enumerate(members): + if member.name == name: + return i + return None + + +class StringLayout: + CSD = 0 + DSC = 1 + + +def std_1_string_dumper_v2(d, value): + charType = value['__l']['__data_'].dereference().type + + R = GetChildMemberWithName(value, "__r_") + if not R: + raise Exception("Could not find __r_") + + # __r_ is a compressed_pair of the actual data and the allocator. The data we + # want is in the first base class. + R_Base_SP = R[0] + + if not R_Base_SP: + raise Exception("Could not find R_Base_SP") + + Rep_Sp = GetChildMemberWithName(R_Base_SP, "__value_") + + if not Rep_Sp: + raise Exception("Could not find __value_") + + # Our layout seems a little different + Rep_Sp = Rep_Sp[0] + + if not Rep_Sp: + raise Exception("Could not find Rep_Sp") + + L = GetChildMemberWithName(Rep_Sp, "__l") + + if not L: + raise Exception("Could not find __l") + + layout = StringLayout.CSD + if GetIndexOfChildWithName(L, "__data_") == 0: + layout = StringLayout.DSC + + short_mode = False + using_bitmasks = True + size = 0 + size_mode_value = 0 + + Short_Sp = GetChildMemberWithName(Rep_Sp, "__s") + if not Short_Sp: + raise Exception("Could not find __s") + + Is_Long = GetChildMemberWithName(Short_Sp, "__is_long_") + Size_Sp = GetChildMemberWithName(Short_Sp, "__size_") + if not Size_Sp: + raise Exception("Could not find __size_") + + if Is_Long: + using_bitmasks = False + short_mode = Is_Long.integer() == 0 + size = Size_Sp.integer() + else: + size_mode_value = Size_Sp.integer() + mode_mask = 1 + if layout == StringLayout.DSC: + mode_mask = 0x80 + short_mode = (size_mode_value & mode_mask) == 0 + + if short_mode: + Location_Sp = GetChildMemberWithName(Short_Sp, "__data_") + + if using_bitmasks: + size = ((size_mode_value >> 1) % 256) + if layout == StringLayout.DSC: + size = size_mode_value + + # The string is most likely not initialized yet + if size > 100 or not Location_Sp: + raise Exception("Probably not initialized yet") + + d.putCharArrayHelper(d.extractPointer(Location_Sp), size, + charType, d.currentItemFormat()) + return + + Location_Sp = GetChildMemberWithName(L, "__data_") + Size_Vo = GetChildMemberWithName(L, "__size_") + Capacity_Vo = GetChildMemberWithName(L, "__cap_") + + if not Location_Sp or not Size_Vo or not Capacity_Vo: + raise Exception("Could not find Location_Sp, Size_Vo or Capacity_Vo") + + size = Size_Vo.integer() + capacity = Capacity_Vo.integer() + if not using_bitmasks and layout == StringLayout.CSD: + capacity *= 2 + if capacity < size: + raise Exception("Capacity is less than size") + + d.putCharArrayHelper(d.extractPointer(Location_Sp), size, + charType, d.currentItemFormat()) + + +def std_1_string_dumper_v1(d, value): + charType = value['__l']['__data_'].dereference().type + D = None + + if d.isLldb: + D = value[0][0][0][0] + elif d.isGdb: + D = value["__r_"].members(True)[0][0][0] + else: + raise Exception("Unknown debugger (neither gdb nor lldb)") + + layoutDecider = D[0][0] + if not layoutDecider: + raise Exception("Could not find layoutDecider") + + size = 0 + size_mode_value = 0 + short_mode = False + libcxx_version = 14 + + layoutModeIsDSC = layoutDecider.name == '__data_' + if (layoutModeIsDSC): + size_mode = D[1][1][0] + if not size_mode: + raise Exception("Could not find size_mode") + if not size_mode.name == '__size_': + size_mode = D[1][1][1] + if not size_mode: + raise Exception("Could not find size_mode") + + size_mode_value = size_mode.integer() + short_mode = ((size_mode_value & 0x80) == 0) + else: + size_mode = D[1][0][0] + if not size_mode: + raise Exception("Could not find size_mode") + + if size_mode.name == '__is_long_': + libcxx_version = 15 + short_mode = (size_mode.integer() == 0) + + size_mode = D[1][0][1] + size_mode_value = size_mode.integer() + else: + size_mode_value = size_mode.integer() + short_mode = ((size_mode_value & 1) == 0) + + if short_mode: + s = D[1] + + if not s: + raise Exception("Could not find s") + + if libcxx_version == 14: + location_sp = s[0] if layoutModeIsDSC else s[1] + size = size_mode_value if layoutModeIsDSC else ((size_mode_value >> 1) % 256) + elif libcxx_version == 15: + location_sp = s[0] if layoutModeIsDSC else s[2] + size = size_mode_value + + else: + l = D[0] + if not l: + raise Exception("Could not find l") + + # we can use the layout_decider object as the data pointer + location_sp = layoutDecider if layoutModeIsDSC else l[2] + size_vo = l[1] + if not size_vo or not location_sp: + raise Exception("Could not find size_vo or location_sp") + size = size_vo.integer() + + if short_mode and location_sp: + d.putCharArrayHelper(d.extractPointer(location_sp), size, + charType, d.currentItemFormat()) + else: + d.putCharArrayHelper(location_sp.integer(), + size, charType, d.currentItemFormat()) + + return + +def qdump__std____1__string(d, value): + try: + std_1_string_dumper_v2(d, value) + except Exception as eV2: + try: + std_1_string_dumper_v1(d, value) + except Exception as eV1: + d.putValue("Could not parse: %s, %s" % (eV1, eV2)) + + +def qdump__std____1__wstring(d, value): + try: + std_1_string_dumper_v2(d, value) + except Exception as eV2: + try: + std_1_string_dumper_v1(d, value) + except Exception as eV1: + d.putValue("Could not parse: %s, %s" % (eV1, eV2)) + + +def qdump__std____1__basic_string(d, value): + innerType = value.type[0].name + if innerType in ("char", "char8_t", "char16_t"): + qdump__std____1__string(d, value) + elif innerType in ("wchar_t", "char32_t"): + qdump__std____1__wstring(d, value) + else: + d.warn("UNKNOWN INNER TYPE %s" % innerType) + + +def qdump__std____1__shared_ptr(d, value): + i = value["__ptr_"] + if i.pointer() == 0: + d.putValue("(null)") + else: + d.putItem(i.dereference()) + d.putBetterType(value.type) + + +def qdump__std____1__weak_ptr(d, value): + return qdump__std____1__shared_ptr(d, value) + + +def qdump__std____1__unique_ptr(d, value): + if value.type.size() == d.ptrSize(): + p = d.extractPointer(value) + else: + _, p = value.split("pp"); # For custom deleters. + if p == 0: + d.putValue("(null)") + else: + try: + d.putItem(value["__value_"]) + d.putValue(d.currentValue.value, d.currentValue.encoding) + except: + d.putItem(d.createValue(p, value.type[0])) + d.putBetterType(value.type) + + +def qform__std____1__unordered_map(): + return [DisplayFormat.CompactMap] + + +def qdump__std____1__unordered_map(d, value): + (size, _) = value["__table_"]["__p2_"].split("pp") + d.putItemCount(size) + + keyType = value.type[0] + valueType = value.type[1] + pairType = value.type[4][0] + + if d.isExpanded(): + curr = value["__table_"]["__p1_"].split("pp")[0] + + def traverse_list(node): + while node: + (next_, _, pad, pair) = d.split("pp@{%s}" % (pairType.name), node) + yield pair.split("{%s}@{%s}" % (keyType.name, valueType.name))[::2] + node = next_ + + with Children(d, size, childType=value.type[0], maxNumChild=1000): + for (i, value) in zip(d.childRange(), traverse_list(curr)): + d.putPairItem(i, value, 'key', 'value') + + +def qdump__std____1__unordered_set(d, value): + (size, _) = value["__table_"]["__p2_"].split("pp") + d.putItemCount(size) + + valueType = value.type[0] + + if d.isExpanded(): + curr = value["__table_"]["__p1_"].split("pp")[0] + + def traverse_list(node): + while node: + (next_, _, pad, val) = d.split("pp@{%s}" % (valueType.name), node) + yield val + node = next_ + + with Children(d, size, childType=value.type[0], maxNumChild=1000): + for (i, value) in zip(d.childRange(), traverse_list(curr)): + d.putSubItem(i, value) + + +def qdump__std____1__unordered_multiset(d, value): + qdump__std____1__unordered_set(d, value) + + +def qform__std____1__valarray(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____1__valarray(d, value): + innerType = value.type[0] + (begin, end) = value.split('pp') + size = int((end - begin) / innerType.size()) + d.putItemCount(size) + d.putPlotData(begin, size, innerType) + + +def qform__std____1__vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____1__vector(d, value): + qdumpHelper__std__vector__libcxx(d, value) + + +def qdump__std____1__once_flag(d, value): + qdump__std__once_flag(d, value) + + +def qdump__std____1__variant(d, value): + index = value['__impl']['__index'] + index_num = int(index) + value_type = d.templateArgument(value.type, index_num) + d.putValue("<%s:%s>" % (index_num, value_type.name)) + + d.putNumChild(2) + if d.isExpanded(): + with Children(d): + d.putSubItem("index", index) + d.putSubItem("value", value.cast(value_type)) + + +def qdump__std____1__optional(d, value): + if value['__engaged_'].integer() == 0: + d.putSpecialValue("empty") + else: + d.putItem(value['#1']['__val_']) + + +def qdump__std____1__tuple(d, value): + values = [] + for member in value['__base_'].members(False): + values.append(member['__value_']) + d.putItemCount(len(values)) + d.putNumChild(len(values)) + if d.isExpanded(): + with Children(d): + count = 0 + for internal_value in values: + d.putSubItem("[%i]" % count, internal_value) + count += 1 diff --git a/share/qtcreator/debugger/python2/lldbbridge.py b/share/qtcreator/debugger/python2/lldbbridge.py new file mode 100644 index 00000000000..4d9b74b2ea2 --- /dev/null +++ b/share/qtcreator/debugger/python2/lldbbridge.py @@ -0,0 +1,2505 @@ +# 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 inspect +import os +import platform +import re +import sys +import threading +import time +import lldb +import utils +from utils import DebuggerStartMode, BreakpointType, TypeCode, LogChannel + +from contextlib import contextmanager + +sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) + +# Simplify development of this module by reloading deps +if 'dumper' in sys.modules: + if sys.version_info[0] >= 3: + if sys.version_info[1] > 3: + from importlib import reload + else: + def reload(m): print('Unsupported Python version - not reloading %s' % str(m)) + reload(sys.modules['dumper']) + +from dumper import DumperBase, SubItem, Children, TopLevelItem + +####################################################################### +# +# Helpers +# +####################################################################### + +qqWatchpointOffset = 10000 +_c_str_trans = None + +if sys.version_info[0] >= 3: + _c_str_trans = str.maketrans({"\n": "\\n", '"':'\\"', "\\":"\\\\"}) + +def toCString(s): + if _c_str_trans is not None: + return str(s).translate(_c_str_trans) + else: + return str(s).replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"') + +def fileNameAsString(file): + return toCString(file) if file.IsValid() else '' + + +def check(exp): + if not exp: + raise RuntimeError('Check failed') + + +class Dumper(DumperBase): + def __init__(self, debugger=None): + DumperBase.__init__(self) + lldb.theDumper = self + + self.isLldb = True + self.typeCache = {} + + self.outputLock = threading.Lock() + + if debugger: + # Re-use existing debugger + self.debugger = debugger + else: + self.debugger = lldb.SBDebugger.Create() + #self.debugger.SetLoggingCallback(loggingCallback) + #def loggingCallback(args): + # s = args.strip() + # s = s.replace('"', "'") + # sys.stdout.write('log="%s"@\n' % s) + #Same as: self.debugger.HandleCommand('log enable lldb dyld step') + #self.debugger.EnableLog('lldb', ['dyld', 'step', 'process', 'state', + # 'thread', 'events', + # 'communication', 'unwind', 'commands']) + #self.debugger.EnableLog('lldb', ['all']) + self.debugger.Initialize() + self.debugger.SetAsync(True) + self.debugger.HandleCommand('settings set auto-confirm on') + + # FIXME: warn('DISABLING DEFAULT FORMATTERS') + # It doesn't work at all with 179.5 and we have some bad + # interaction in 300 + # if not hasattr(lldb.SBType, 'GetCanonicalType'): # 'Test' for 179.5 + #self.debugger.HandleCommand('type category delete gnu-libstdc++') + #self.debugger.HandleCommand('type category delete libcxx') + #self.debugger.HandleCommand('type category delete default') + self.debugger.DeleteCategory('gnu-libstdc++') + self.debugger.DeleteCategory('libcxx') + self.debugger.DeleteCategory('default') + self.debugger.DeleteCategory('cplusplus') + #for i in range(self.debugger.GetNumCategories()): + # self.debugger.GetCategoryAtIndex(i).SetEnabled(False) + + self.process = None + self.target = None + self.fakeAddress_ = None + self.fakeLAddress_ = None + self.eventState = lldb.eStateInvalid + + self.executable_ = None + self.symbolFile_ = None + self.startMode_ = None + self.processArgs_ = None + self.attachPid_ = None + self.dyldImageSuffix = None + self.dyldLibraryPath = None + self.dyldFrameworkPath = None + + self.isShuttingDown_ = False + self.isInterrupting_ = False + self.interpreterBreakpointResolvers = [] + + DumperBase.warn = Dumper.warn_impl + self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString()) + + @staticmethod + def warn_impl(message): + if message[-1:] == '\n': + message += '\n' + print('@\nbridgemessage={msg="%s",channel="%s"}\n@' + % (message.replace('"', '$'), LogChannel.AppError)) + + def fromNativeFrameValue(self, nativeValue): + return self.fromNativeValue(nativeValue) + + def fromNativeValue(self, nativeValue): + self.check(isinstance(nativeValue, lldb.SBValue)) + nativeType = nativeValue.GetType() + typeName = nativeType.GetName() + code = nativeType.GetTypeClass() + + # Display the result of GetSummary() for Core Foundation string + # and string-like types. + summary = None + if self.useFancy: + if (typeName.startswith('CF') + or typeName.startswith('__CF') + or typeName.startswith('NS') + or typeName.startswith('__NSCF')): + if code == lldb.eTypeClassPointer: + summary = nativeValue.Dereference().GetSummary() + elif code == lldb.eTypeClassReference: + summary = nativeValue.Dereference().GetSummary() + else: + summary = nativeValue.GetSummary() + + nativeValue.SetPreferSyntheticValue(False) + + if code == lldb.eTypeClassReference: + nativeTargetType = nativeType.GetDereferencedType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + targetType = self.fromNativeType(nativeTargetType) + val = self.createReferenceValue(nativeValue.GetValueAsUnsigned(), targetType) + val.laddress = nativeValue.AddressOf().GetValueAsUnsigned() + #DumperBase.warn('CREATED REF: %s' % val) + elif code == lldb.eTypeClassPointer: + nativeTargetType = nativeType.GetPointeeType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + targetType = self.fromNativeType(nativeTargetType) + val = self.createPointerValue(nativeValue.GetValueAsUnsigned(), targetType) + #DumperBase.warn('CREATED PTR 1: %s' % val) + val.laddress = nativeValue.AddressOf().GetValueAsUnsigned() + #DumperBase.warn('CREATED PTR 2: %s' % val) + elif code == lldb.eTypeClassTypedef: + nativeTargetType = nativeType.GetUnqualifiedType() + if hasattr(nativeTargetType, 'GetCanonicalType'): + nativeTargetType = nativeTargetType.GetCanonicalType() + val = self.fromNativeValue(nativeValue.Cast(nativeTargetType)) + val._type = self.fromNativeType(nativeType) + #DumperBase.warn('CREATED TYPEDEF: %s' % val) + else: + val = self.Value(self) + address = nativeValue.GetLoadAddress() + if address is not None: + val.laddress = address + if True: + data = nativeValue.GetData() + error = lldb.SBError() + size = nativeValue.GetType().GetByteSize() + if size > 1: + # 0 happens regularly e.g. for cross-shared-object types. + # 1 happens on Linux e.g. for QObject uses outside of QtCore. + try: + val.ldata = data.ReadRawData(error, 0, size) + except: + pass + + val._type = self.fromNativeType(nativeType) + + if code == lldb.eTypeClassEnumeration: + intval = nativeValue.GetValueAsSigned() + display = str(nativeValue).split(' = ') + if len(display) == 2: + verbose = display[1] + if '|' in verbose and not verbose.startswith('('): + verbose = '(' + verbose + ')' + else: + verbose = intval + val.ldisplay = '%s (%d)' % (verbose, intval) + elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat): + val.ldisplay = str(nativeValue.GetValue()) + #elif code == lldb.eTypeClassArray: + # if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x + # val.type.ltarget = self.fromNativeType(nativeType.GetArrayElementType()) + # else: + # fields = nativeType.get_fields_array() + # if len(fields): + # val.type.ltarget = self.fromNativeType(fields[0]) + #elif code == lldb.eTypeClassVector: + # val.type.ltarget = self.fromNativeType(nativeType.GetVectorElementType()) + + val.summary = summary + val.lIsInScope = nativeValue.IsInScope() + val.name = nativeValue.GetName() + return val + + def nativeStructAlignment(self, nativeType): + def handleItem(nativeFieldType, align): + a = self.fromNativeType(nativeFieldType).alignment() + return a if a > align else align + align = 1 + for i in range(nativeType.GetNumberOfDirectBaseClasses()): + base = nativeType.GetDirectBaseClassAtIndex(i) + align = handleItem(base.GetType(), align) + for i in range(nativeType.GetNumberOfFields()): + child = nativeType.GetFieldAtIndex(i) + align = handleItem(child.GetType(), align) + return align + + def listMembers(self, value, nativeType): + #DumperBase.warn("ADDR: 0x%x" % self.fakeAddress_) + if value.laddress: + fakeAddress = lldb.SBAddress(value.laddress, self.target) + fakeLAddress = value.laddress + else: + fakeAddress = self.fakeAddress_ + fakeLAddress = self.fakeLAddress_ + + fakeValue = self.target.CreateValueFromAddress('x', fakeAddress, nativeType) + fakeValue.SetPreferSyntheticValue(False) + + baseNames = {} + for i in range(nativeType.GetNumberOfDirectBaseClasses()): + base = nativeType.GetDirectBaseClassAtIndex(i) + baseNames[base.GetName()] = i + + fieldBits = {} + for f in nativeType.get_fields_array(): + bitsize = f.GetBitfieldSizeInBits() + if bitsize == 0: + bitsize = f.GetType().GetByteSize() * 8 + bitpos = f.GetOffsetInBits() + fieldBits[f.name] = (bitsize, bitpos, f.IsBitfield()) + + # Normal members and non-empty base classes. + anonNumber = 0 + for i in range(fakeValue.GetNumChildren()): + nativeField = fakeValue.GetChildAtIndex(i) + nativeField.SetPreferSyntheticValue(False) + + fieldName = nativeField.GetName() + nativeFieldType = nativeField.GetType() + + if fieldName in fieldBits: + (fieldBitsize, fieldBitpos, isBitfield) = fieldBits[fieldName] + else: + fieldBitsize = nativeFieldType.GetByteSize() * 8 + fieldBitpos = None + isBitfield = False + + if isBitfield: # Bit fields + fieldType = self.createBitfieldType( + self.createType(self.typeName(nativeFieldType)), fieldBitsize) + yield self.Field(self, name=fieldName, type=fieldType, + bitsize=fieldBitsize, bitpos=fieldBitpos) + + elif fieldName is None: # Anon members + anonNumber += 1 + fieldName = '#%s' % anonNumber + fakeMember = fakeValue.GetChildAtIndex(i) + fakeMemberAddress = fakeMember.GetLoadAddress() + offset = fakeMemberAddress - fakeLAddress + yield self.Field(self, name=fieldName, type=self.fromNativeType(nativeFieldType), + bitsize=fieldBitsize, bitpos=8 * offset) + + elif fieldName in baseNames: # Simple bases + member = self.fromNativeValue(fakeValue.GetChildAtIndex(i)) + member.isBaseClass = True + yield member + + else: # Normal named members + member = self.fromNativeValue(fakeValue.GetChildAtIndex(i)) + member.name = nativeField.GetName() + yield member + + # Empty bases are not covered above. + for i in range(nativeType.GetNumberOfDirectBaseClasses()): + fieldObj = nativeType.GetDirectBaseClassAtIndex(i) + fieldType = fieldObj.GetType() + if fieldType.GetNumberOfFields() == 0: + if fieldType.GetNumberOfDirectBaseClasses() == 0: + member = self.Value(self) + fieldName = fieldObj.GetName() + member._type = self.fromNativeType(fieldType) + member.name = fieldName + member.fields = [] + if False: + # This would be correct if we came here only for + # truly empty base classes. Alas, we don't, see below. + member.ldata = bytes() + member.lbitsize = fieldType.GetByteSize() * 8 + else: + # This is a hack. LLDB 3.8 reports declared but not defined + # types as having no fields and(!) size == 1. At least + # for the common case of a single base class we can + # fake the contents by using the whole derived object's + # data as base class data. + data = fakeValue.GetData() + size = nativeType.GetByteSize() + member.lbitsize = size * 8 + error = lldb.SBError() + member.laddress = value.laddress + member.ldata = data.ReadRawData(error, 0, size) + member.isBaseClass = True + member.ltype = self.fromNativeType(fieldType) + member.name = fieldName + yield member + + def ptrSize(self): + result = self.target.GetAddressByteSize() + self.ptrSize = lambda: result + return result + + def fromNativeType(self, nativeType): + self.check(isinstance(nativeType, lldb.SBType)) + code = nativeType.GetTypeClass() + + # eTypeClassInvalid = (0u), + # eTypeClassArray = (1u << 0), + # eTypeClassBlockPointer = (1u << 1), + # eTypeClassBuiltin = (1u << 2), + # eTypeClassClass = (1u << 3), + # eTypeClassComplexFloat = (1u << 4), + # eTypeClassComplexInteger = (1u << 5), + # eTypeClassEnumeration = (1u << 6), + # eTypeClassFunction = (1u << 7), + # eTypeClassMemberPointer = (1u << 8), + # eTypeClassObjCObject = (1u << 9), + # eTypeClassObjCInterface = (1u << 10), + # eTypeClassObjCObjectPointer = (1u << 11), + # eTypeClassPointer = (1u << 12), + # eTypeClassReference = (1u << 13), + # eTypeClassStruct = (1u << 14), + # eTypeClassTypedef = (1u << 15), + # eTypeClassUnion = (1u << 16), + # eTypeClassVector = (1u << 17), + # // Define the last type class as the MSBit of a 32 bit value + # eTypeClassOther = (1u << 31), + # // Define a mask that can be used for any type when finding types + # eTypeClassAny = (0xffffffffu) + + #DumperBase.warn('CURRENT: %s' % self.typeData.keys()) + #DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType.GetName()) + if code == lldb.eTypeClassInvalid: + return None + + if code == lldb.eTypeClassBuiltin: + nativeType = nativeType.GetUnqualifiedType() + + if code == lldb.eTypeClassPointer: + #DumperBase.warn('PTR') + nativeTargetType = nativeType.GetPointeeType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + #DumperBase.warn('PTR: %s' % nativeTargetType.name) + return self.createPointerType(self.fromNativeType(nativeTargetType)) + + if code == lldb.eTypeClassReference: + #DumperBase.warn('REF') + nativeTargetType = nativeType.GetDereferencedType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + #DumperBase.warn('REF: %s' % nativeTargetType.name) + return self.createReferenceType(self.fromNativeType(nativeTargetType)) + + if code == lldb.eTypeClassTypedef: + #DumperBase.warn('TYPEDEF') + nativeTargetType = nativeType.GetUnqualifiedType() + if hasattr(nativeTargetType, 'GetCanonicalType'): + nativeTargetType = nativeTargetType.GetCanonicalType() + targetType = self.fromNativeType(nativeTargetType) + return self.createTypedefedType(targetType, nativeType.GetName(), + self.nativeTypeId(nativeType)) + + nativeType = nativeType.GetUnqualifiedType() + typeName = self.typeName(nativeType) + + if code in (lldb.eTypeClassArray, lldb.eTypeClassVector): + #DumperBase.warn('ARRAY: %s' % nativeType.GetName()) + if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x + nativeTargetType = nativeType.GetArrayElementType() + if not nativeTargetType.IsValid(): + if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x + #DumperBase.warn('BAD: %s ' % nativeTargetType.get_fields_array()) + nativeTargetType = nativeType.GetVectorElementType() + count = nativeType.GetByteSize() // nativeTargetType.GetByteSize() + targetTypeName = nativeTargetType.GetName() + if targetTypeName.startswith('(anon'): + typeName = nativeType.GetName() + pos1 = typeName.rfind('[') + targetTypeName = typeName[0:pos1].strip() + #DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName) + targetType = self.fromNativeType(nativeTargetType) + targetType.tdata = targetType.tdata.copy() + targetType.tdata.name = targetTypeName + return self.createArrayType(targetType, count) + if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x + nativeTargetType = nativeType.GetVectorElementType() + count = nativeType.GetByteSize() // nativeTargetType.GetByteSize() + targetType = self.fromNativeType(nativeTargetType) + return self.createArrayType(targetType, count) + return self.createType(nativeType.GetName()) + + typeId = self.nativeTypeId(nativeType) + res = self.typeData.get(typeId, None) + if res is None: + # # This strips typedefs for pointers. We don't want that. + # typeobj.nativeType = nativeType.GetUnqualifiedType() + tdata = self.TypeData(self, typeId) + tdata.name = typeName + tdata.lbitsize = nativeType.GetByteSize() * 8 + if code == lldb.eTypeClassBuiltin: + if utils.isFloatingPointTypeName(typeName): + tdata.code = TypeCode.Float + elif utils.isIntegralTypeName(typeName): + tdata.code = TypeCode.Integral + elif typeName in ('__int128', 'unsigned __int128'): + tdata.code = TypeCode.Integral + elif typeName == 'void': + tdata.code = TypeCode.Void + elif typeName == 'wchar_t': + tdata.code = TypeCode.Integral + elif typeName in ("char16_t", "char32_t", "char8_t"): + tdata.code = TypeCode.Integral + else: + self.warn('UNKNOWN TYPE KEY: %s: %s' % (typeName, code)) + elif code == lldb.eTypeClassEnumeration: + tdata.code = TypeCode.Enum + tdata.enumDisplay = lambda intval, addr, form: \ + self.nativeTypeEnumDisplay(nativeType, intval, form) + elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat): + tdata.code = TypeCode.Complex + elif code in (lldb.eTypeClassClass, lldb.eTypeClassStruct, lldb.eTypeClassUnion): + tdata.code = TypeCode.Struct + tdata.lalignment = lambda: \ + self.nativeStructAlignment(nativeType) + tdata.lfields = lambda value: \ + self.listMembers(value, nativeType) + tdata.templateArguments = lambda: \ + self.listTemplateParametersHelper(nativeType) + elif code == lldb.eTypeClassFunction: + tdata.code = TypeCode.Function + elif code == lldb.eTypeClassMemberPointer: + tdata.code = TypeCode.MemberPointer + # warn('CREATE TYPE: %s' % typeId) + #else: + # warn('REUSE TYPE: %s' % typeId) + return self.Type(self, typeId) + + def listTemplateParametersHelper(self, nativeType): + stringArgs = self.listTemplateParameters(nativeType.GetName()) + n = nativeType.GetNumberOfTemplateArguments() + if n != len(stringArgs): + # Something wrong in the debug info. + # Should work in theory, doesn't work in practice. + # Items like std::allocator report 0 + # for nativeType.GetNumberOfTemplateArguments() with LLDB 3.8 + return stringArgs + + targs = [] + for i in range(nativeType.GetNumberOfTemplateArguments()): + kind = nativeType.GetTemplateArgumentKind(i) + # eTemplateArgumentKindNull = 0, + # eTemplateArgumentKindType, + # eTemplateArgumentKindDeclaration, + # eTemplateArgumentKindIntegral, + # eTemplateArgumentKindTemplate, + # eTemplateArgumentKindTemplateExpansion, + # eTemplateArgumentKindExpression, + # eTemplateArgumentKindPack + if kind == lldb.eTemplateArgumentKindType: + innerType = nativeType.GetTemplateArgumentType( + i).GetUnqualifiedType().GetCanonicalType() + targs.append(self.fromNativeType(innerType)) + #elif kind == lldb.eTemplateArgumentKindIntegral: + # innerType = nativeType.GetTemplateArgumentType(i).GetUnqualifiedType().GetCanonicalType() + # #DumperBase.warn('INNER TYP: %s' % innerType) + # basicType = innerType.GetBasicType() + # #DumperBase.warn('IBASIC TYP: %s' % basicType) + # inner = self.extractTemplateArgument(nativeType.GetName(), i) + # exp = '(%s)%s' % (innerType.GetName(), inner) + # #DumperBase.warn('EXP : %s' % exp) + # val = self.nativeParseAndEvaluate('(%s)%s' % (innerType.GetName(), inner)) + # # Clang writes 'int' and '0xfffffff' into the debug info + # # LLDB manages to read a value of 0xfffffff... + # #if basicType == lldb.eBasicTypeInt: + # value = val.GetValueAsUnsigned() + # if value >= 0x8000000: + # value -= 0x100000000 + # #DumperBase.warn('KIND: %s' % kind) + # targs.append(value) + else: + #DumperBase.warn('UNHANDLED TEMPLATE TYPE : %s' % kind) + targs.append(stringArgs[i]) # Best we can do. + #DumperBase.warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs])) + return targs + + def typeName(self, nativeType): + # Don't use GetDisplayTypeName since LLDB removed the inline namespace __1 + # https://reviews.llvm.org/D74478 + return nativeType.GetName() + + def nativeTypeId(self, nativeType): + if nativeType and (nativeType.GetTypeClass() == lldb.eTypeClassTypedef): + nativeTargetType = nativeType.GetUnqualifiedType() + if hasattr(nativeTargetType, 'GetCanonicalType'): + nativeTargetType = nativeTargetType.GetCanonicalType() + return '%s{%s}' % (nativeType.name, nativeTargetType.name) + name = self.typeName(nativeType) + if name is None or len(name) == 0: + c = '0' + elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassStruct: + c = 's' + elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassUnion: + c = 'u' + else: + return name + fields = nativeType.get_fields_array() + typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.GetType())) for f in fields]) + #DumperBase.warn('NATIVE TYPE ID FOR %s IS %s' % (name, typeId)) + return typeId + + def nativeTypeEnumDisplay(self, nativeType, intval, form): + if hasattr(nativeType, 'get_enum_members_array'): + enumerators = [] + flags = [] + found = False + for enumMember in nativeType.get_enum_members_array(): + # Even when asking for signed we get unsigned with LLDB 3.8. + value = enumMember.GetValueAsSigned() + name = nativeType.GetName().split('::') + name[-1] = enumMember.GetName() + if value == intval: + return '::'.join(name) + ' (' + (form % intval) + ')' + enumerators.append(('::'.join(name), value)) + + given = intval + for (name, value) in enumerators: + if value & given != 0: + flags.append(name) + given = given & ~value + found = True + + if not found or given != 0: + flags.append('unknown: %d' % given) + + return '(' + ' | '.join(flags) + ') (' + (form % intval) + ')' + return form % intval + + def nativeDynamicTypeName(self, address, baseType): + return None # FIXME: Seems sufficient, no idea why. + addr = self.target.ResolveLoadAddress(address) + ctx = self.target.ResolveSymbolContextForAddress(addr, 0) + sym = ctx.GetSymbol() + return sym.GetName() + + def stateName(self, s): + try: + # See db.StateType + return ( + 'invalid', + 'unloaded', # Process is object is valid, but not currently loaded + 'connected', # Process is connected to remote debug services, + # but not launched or attached to anything yet + 'attaching', # Process is currently trying to attach + 'launching', # Process is in the process of launching + 'stopped', # Process or thread is stopped and can be examined. + 'running', # Process or thread is running and can't be examined. + 'stepping', # Process or thread is in the process of stepping + # and can not be examined. + 'crashed', # Process or thread has crashed and can be examined. + 'detached', # Process has been detached and can't be examined. + 'exited', # Process has exited and can't be examined. + 'suspended' # Process or thread is in a suspended state as far + )[s] + except: + return 'unknown(%s)' % s + + def stopReason(self, s): + try: + return ( + 'invalid', + 'none', + 'trace', + 'breakpoint', + 'watchpoint', + 'signal', + 'exception', + 'exec', + 'plancomplete', + 'threadexiting', + 'instrumentation', + )[s] + except: + return 'unknown(%s)' % s + + def enumExpression(self, enumType, enumValue): + ns = self.qtNamespace() + return ns + 'Qt::' + enumType + '(' \ + + ns + 'Qt::' + enumType + '::' + enumValue + ')' + + def callHelper(self, rettype, value, func, args): + # args is a tuple. + arg = ','.join(args) + #DumperBase.warn('PRECALL: %s -> %s(%s)' % (value.address(), func, arg)) + typename = value.type.name + exp = '((%s*)0x%x)->%s(%s)' % (typename, value.address(), func, arg) + #DumperBase.warn('CALL: %s' % exp) + result = self.currentContextValue.CreateValueFromExpression('', exp) + #DumperBase.warn(' -> %s' % result) + return self.fromNativeValue(result) + + def pokeValue(self, typeName, *args): + thread = self.currentThread() + frame = thread.GetFrameAtIndex(0) + inner = ','.join(args) + value = frame.EvaluateExpression(typeName + '{' + inner + '}') + #DumperBase.warn(' TYPE: %s' % value.type) + #DumperBase.warn(' ADDR: 0x%x' % value.address) + #DumperBase.warn(' VALUE: %s' % value) + return value + + def nativeParseAndEvaluate(self, exp): + thread = self.currentThread() + frame = thread.GetFrameAtIndex(0) + val = frame.EvaluateExpression(exp) + #options = lldb.SBExpressionOptions() + #val = self.target.EvaluateExpression(exp, options) + err = val.GetError() + if err.Fail(): + #DumperBase.warn('FAILING TO EVAL: %s' % exp) + return None + #DumperBase.warn('NO ERROR.') + #DumperBase.warn('EVAL: %s -> %s' % (exp, val.IsValid())) + return val + + def parseAndEvaluate(self, exp): + val = self.nativeParseAndEvaluate(exp) + return None if val is None else self.fromNativeValue(val) + + def isWindowsTarget(self): + return 'windows' in self.target.triple + + def isQnxTarget(self): + return False + + def isArmArchitecture(self): + return 'arm' in self.target.triple + + def isMsvcTarget(self): + return 'msvc' in self.target.triple + + def prettySymbolByAddress(self, address): + try: + result = lldb.SBCommandReturnObject() + # Cast the address to a function pointer to get the name and location of the function. + expression = 'po (void (*)()){}' + self.debugger.GetCommandInterpreter().HandleCommand(expression.format(address), result) + output = '' + if result.Succeeded(): + output = result.GetOutput().strip() + if output: + return output + except: + pass + return '0x%x' % address + + def fetchInternalFunctions(self): + funcs = self.target.FindFunctions('QObject::customEvent') + if len(funcs): + symbol = funcs[0].GetSymbol() + self.qtCustomEventFunc = symbol.GetStartAddress().GetLoadAddress(self.target) + + funcs = self.target.FindFunctions('QObject::property') + if len(funcs): + symbol = funcs[0].GetSymbol() + self.qtPropertyFunc = symbol.GetStartAddress().GetLoadAddress(self.target) + + def fetchQtVersionAndNamespace(self): + for func in self.target.FindFunctions('qVersion'): + name = func.GetSymbol().GetName() + if name.endswith('()'): + name = name[:-2] + if name.count(':') > 2: + continue + + qtNamespace = name[:name.find('qVersion')] + self.qtNamespace = lambda: qtNamespace + + options = lldb.SBExpressionOptions() + res = self.target.EvaluateExpression(name + '()', options) + + if not res.IsValid() or not res.GetType().IsPointerType(): + exp = '((const char*())%s)()' % name + res = self.target.EvaluateExpression(exp, options) + + if not res.IsValid() or not res.GetType().IsPointerType(): + exp = '((const char*())_Z8qVersionv)()' + res = self.target.EvaluateExpression(exp, options) + + if not res.IsValid() or not res.GetType().IsPointerType(): + continue + + version = str(res) + if version.count('.') != 2: + continue + + version.replace("'", '"') # Both seem possible + version = version[version.find('"') + 1:version.rfind('"')] + + (major, minor, patch) = version.split('.') + qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch) + self.qtVersion = lambda: qtVersion + + return (qtNamespace, qtVersion) + + try: + versionValue = self.target.EvaluateExpression('qtHookData[2]') + if versionValue.IsValid(): + return ('', versionValue.unsigned) + except: + pass + + return ('', self.fallbackQtVersion) + + def qtVersionAndNamespace(self): + qtVersionAndNamespace = None + try: + qtVersionAndNamespace = self.fetchQtVersionAndNamespace() + self.report("Detected Qt Version: 0x%0x (namespace='%s')" % + (qtVersionAndNamespace[1], qtVersionAndNamespace[0] or "no namespace")) + except Exception as e: + DumperBase.warn('[lldb] Error detecting Qt version: %s' % e) + + try: + self.fetchInternalFunctions() + self.report('Found function QObject::property: 0x%0x' % self.qtPropertyFunc) + self.report('Found function QObject::customEvent: 0x%0x' % self.qtCustomEventFunc) + except Exception as e: + DumperBase.warn('[lldb] Error fetching internal Qt functions: %s' % e) + + # Cache version information by overriding this function. + self.qtVersionAndNamespace = lambda: qtVersionAndNamespace + return qtVersionAndNamespace + + def qtNamespace(self): + return self.qtVersionAndNamespace()[0] + + def qtVersion(self): + return self.qtVersionAndNamespace()[1] + + def handleCommand(self, command): + result = lldb.SBCommandReturnObject() + self.debugger.GetCommandInterpreter().HandleCommand(command, result) + success = result.Succeeded() + if success: + self.report('output="%s"' % toCString(result.GetOutput())) + else: + self.report('error="%s"' % toCString(result.GetError())) + + def canonicalTypeName(self, name): + return re.sub('\\bconst\\b', '', name).replace(' ', '') + + def removeTypePrefix(self, name): + return re.sub('^(struct|class|union|enum|typedef) ', '', name) + + def lookupNativeType(self, name): + #DumperBase.warn('LOOKUP TYPE NAME: %s' % name) + + typeobj = self.typeCache.get(name) + if typeobj is not None: + #DumperBase.warn('CACHED: %s' % name) + return typeobj + typeobj = self.target.FindFirstType(name) + if typeobj.IsValid(): + #DumperBase.warn('VALID FIRST : %s' % typeobj) + self.typeCache[name] = typeobj + return typeobj + + # FindFirstType has a bug (in lldb) that if there are two types with the same base name + # but different scope name (e.g. inside different classes) and the searched for type name + # would be returned as the second result in a call to FindTypes, FindFirstType would return + # an empty result. + # Therefore an additional call to FindTypes is done as a fallback. + # Note that specifying a prefix like enum or typedef or class will make the call fail to + # find the type, thus the prefix is stripped. + nonPrefixedName = self.canonicalTypeName(self.removeTypePrefix(name)) + if re.match(r'^.+\(.*\)', nonPrefixedName) is not None: + return lldb.SBType() + + typeobjlist = self.target.FindTypes(nonPrefixedName) + if typeobjlist.IsValid(): + for typeobj in typeobjlist: + n = self.canonicalTypeName(self.removeTypePrefix(typeobj.GetName())) + if n == nonPrefixedName: + #DumperBase.warn('FOUND TYPE USING FindTypes : %s' % typeobj) + self.typeCache[name] = typeobj + return typeobj + if name.endswith('*'): + #DumperBase.warn('RECURSE PTR') + typeobj = self.lookupNativeType(name[:-1].strip()) + if typeobj is not None: + #DumperBase.warn('RECURSE RESULT X: %s' % typeobj) + self.fromNativeType(typeobj.GetPointerType()) + #DumperBase.warn('RECURSE RESULT: %s' % typeobj.GetPointerType()) + return typeobj.GetPointerType() + + #typeobj = self.target.FindFirstType(name[:-1].strip()) + #if typeobj.IsValid(): + # self.typeCache[name] = typeobj.GetPointerType() + # return typeobj.GetPointerType() + + if name.endswith(' const'): + #DumperBase.warn('LOOKUP END CONST') + typeobj = self.lookupNativeType(name[:-6]) + if typeobj is not None: + return typeobj + + if name.startswith('const '): + #DumperBase.warn('LOOKUP START CONST') + typeobj = self.lookupNativeType(name[6:]) + if typeobj is not None: + return typeobj + + # For QMetaType based typenames we have to re-format the type name. + # Converts "T>"" to "T >" since FindFirstType + # expects it that way. + name = name.replace(',', ', ').replace('>>', '> >') + typeobj = self.target.FindFirstType(name) + if typeobj.IsValid(): + self.typeCache[name] = typeobj + return typeobj + + return lldb.SBType() + + def setupInferior(self, args): + """ Set up SBTarget instance """ + + error = lldb.SBError() + + self.executable_ = args['executable'] + self.startMode_ = args.get('startmode', 1) + self.breakOnMain_ = args.get('breakonmain', 0) + self.useTerminal_ = args.get('useterminal', 0) + self.firstStop_ = True + pargs = self.hexdecode(args.get('processargs', '')) + self.processArgs_ = pargs.split('\0') if len(pargs) else [] + self.environment_ = args.get('environment', []) + self.environment_ = list(map(lambda x: self.hexdecode(x), self.environment_)) + self.attachPid_ = args.get('attachpid', 0) + self.sysRoot_ = args.get('sysroot', '') + self.remoteChannel_ = args.get('remotechannel', '') + self.platform_ = args.get('platform', '') + self.nativeMixed = int(args.get('nativemixed', 0)) + self.symbolFile_ = args['symbolfile']; + self.workingDirectory_ = args.get('workingdirectory', '') + if self.workingDirectory_ == '': + try: + self.workingDirectory_ = os.getcwd() + except: # Could have been deleted in the mean time. + pass + + if self.platform_: + self.debugger.SetCurrentPlatform(self.platform_) + # sysroot has to be set *after* the platform + if self.sysRoot_: + self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_) + + # There seems to be some kind of unexpected behavior, or bug in LLDB + # such that target.Attach(attachInfo, error) below does not create + # a valid process if this symbolFile here is valid. + if self.startMode_ == DebuggerStartMode.AttachExternal: + self.symbolFile_ = '' + + self.target = self.debugger.CreateTarget( + self.symbolFile_, None, self.platform_, True, error) + + if not error.Success(): + self.report(self.describeError(error)) + self.reportState('enginerunfailed') + return + + broadcaster = self.target.GetBroadcaster() + listener = self.debugger.GetListener() + broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) + listener.StartListeningForEvents(broadcaster, lldb.SBProcess.eBroadcastBitStateChanged) + broadcaster.AddListener(listener, lldb.SBTarget.eBroadcastBitBreakpointChanged) + listener.StartListeningForEvents( + broadcaster, lldb.SBTarget.eBroadcastBitBreakpointChanged) + + if self.nativeMixed: + self.interpreterEventBreakpoint = \ + self.target.BreakpointCreateByName('qt_qmlDebugMessageAvailable') + + state = 1 if self.target.IsValid() else 0 + self.reportResult('success="%s",msg="%s",exe="%s"' + % (state, toCString(error), toCString(self.executable_)), args) + + def runEngine(self, args): + """ Set up SBProcess instance """ + + error = lldb.SBError() + + if self.startMode_ == DebuggerStartMode.AttachExternal: + attach_info = lldb.SBAttachInfo(self.attachPid_) + if self.breakOnMain_: + self.createBreakpointAtMain() + self.process = self.target.Attach(attach_info, error) + if not error.Success(): + self.reportState('enginerunfailed') + else: + self.report('pid="%s"' % self.process.GetProcessID()) + self.reportState('enginerunandinferiorstopok') + + elif (self.startMode_ == DebuggerStartMode.AttachToRemoteServer + and self.platform_ == 'remote-android'): + + connect_options = lldb.SBPlatformConnectOptions(self.remoteChannel_) + res = self.target.GetPlatform().ConnectRemote(connect_options) + + DumperBase.warn("CONNECT: %s %s platform: %s %s" % (res, + self.remoteChannel_, + self.target.GetPlatform().GetName(), + self.target.GetPlatform().IsConnected())) + if not res.Success(): + self.report(self.describeError(res)) + self.reportState('enginerunfailed') + return + + attach_info = lldb.SBAttachInfo(self.attachPid_) + self.process = self.target.Attach(attach_info, error) + if not error.Success(): + self.report(self.describeError(error)) + self.reportState('enginerunfailed') + else: + self.report('pid="%s"' % self.process.GetProcessID()) + self.reportState('enginerunandinferiorstopok') + + elif (self.startMode_ == DebuggerStartMode.AttachToRemoteServer + or self.startMode_ == DebuggerStartMode.AttachToRemoteProcess): + if self.platform_ == 'remote-ios': + self.process = self.target.ConnectRemote( + self.debugger.GetListener(), + self.remoteChannel_, None, error) + else: + if self.platform_ == "remote-macosx": + self.report("Connecting to remote target: connect://%s" % self.remoteChannel_) + self.process = self.target.ConnectRemote( + self.debugger.GetListener(), + "connect://" + self.remoteChannel_, None, error) + + if not error.Success(): + self.report("Failed to connect to remote target: %s" % error.GetCString()) + self.reportState('enginerunfailed') + return + + if self.breakOnMain_: + self.createBreakpointAtMain() + + DumperBase.warn("PROCESS: %s (%s)" % (self.process, error.Success() and "Success" or error.GetCString())) + elif self.platform_ == "remote-linux": + self.report("Connecting to remote target: connect://%s" % self.remoteChannel_) + + platform = self.target.GetPlatform() + url = "connect://" + self.remoteChannel_ + conOptions = lldb.SBPlatformConnectOptions(url) + error = platform.ConnectRemote(conOptions) + + if not error.Success(): + self.report("Failed to connect to remote target (%s): %s" % (url, error.GetCString())) + self.reportState('enginerunfailed') + return + + f = lldb.SBFileSpec() + f.SetFilename(self.executable_) + launchInfo = lldb.SBLaunchInfo(self.processArgs_) + launchInfo.SetWorkingDirectory(self.workingDirectory_) + launchInfo.SetWorkingDirectory('/tmp') + launchInfo.SetEnvironmentEntries(self.environment_, False) + launchInfo.SetExecutableFile(f, True) + self.process = self.target.Launch(launchInfo, error) + + if not error.Success(): + self.report("Failed to launch remote target: %s" % (error.GetCString())) + self.reportState('enginerunfailed') + return + else: + self.report("Process has launched.") + + if self.breakOnMain_: + self.createBreakpointAtMain() + + else: + self.report("Unsupported platform: %s" % self.platform_) + self.reportState('enginerunfailed') + return + + if not error.Success(): + self.report(self.describeError(error)) + self.reportState('enginerunfailed') + return + + # Even if it stops it seems that LLDB assumes it is running + # and later detects that it did stop after all, so it is be + # better to mirror that and wait for the spontaneous stop. + self.reportState('enginerunandinferiorrunok') + + elif self.startMode_ == DebuggerStartMode.AttachCore: + coreFile = args.get('coreFile', '') + self.process = self.target.LoadCore(coreFile) + if self.process.IsValid(): + self.reportState('enginerunokandinferiorunrunnable') + else: + self.reportState('enginerunfailed') + else: + launchInfo = lldb.SBLaunchInfo(self.processArgs_) + launchInfo.SetWorkingDirectory(self.workingDirectory_) + launchInfo.SetEnvironmentEntries(self.environment_, False) + if self.breakOnMain_: + self.createBreakpointAtMain() + self.process = self.target.Launch(launchInfo, error) + if not error.Success(): + self.report(self.describeError(error)) + self.reportState('enginerunfailed') + return + self.report('pid="%s"' % self.process.GetProcessID()) + self.reportState('enginerunandinferiorrunok') + + s = threading.Thread(target=self.loop, args=[]) + s.start() + + def loop(self): + event = lldb.SBEvent() + #broadcaster = self.target.GetBroadcaster() + listener = self.debugger.GetListener() + + while True: + while listener.GetNextEvent(event): + self.handleEvent(event) + time.sleep(0.25) + + #if listener.WaitForEventForBroadcaster(0, broadcaster, event): + # self.handleEvent(event) + + def describeError(self, error): + desc = lldb.SBStream() + error.GetDescription(desc) + result = 'success="%d",' % int(error.Success()) + result += 'error={type="%s"' % error.GetType() + if error.GetType(): + result += ',status="%s"' % error.GetCString() + result += ',code="%s"' % error.GetError() + result += ',desc="%s"}' % toCString(desc.GetData()) + return result + + def describeStatus(self, status): + return 'status="%s",' % toCString(status) + + def describeLocation(self, frame): + if int(frame.pc) == 0xffffffffffffffff: + return '' + fileName = fileNameAsString(frame.line_entry.file) + function = frame.GetFunctionName() + line = frame.line_entry.line + return 'location={file="%s",line="%s",address="%s",function="%s"}' \ + % (fileName, line, frame.pc, function) + + def currentThread(self): + return None if self.process is None else self.process.GetSelectedThread() + + def currentFrame(self): + thread = self.currentThread() + return None if thread is None else thread.GetSelectedFrame() + + def firstStoppedThread(self): + for i in range(0, self.process.GetNumThreads()): + thread = self.process.GetThreadAtIndex(i) + reason = thread.GetStopReason() + if (reason == lldb.eStopReasonBreakpoint or + reason == lldb.eStopReasonException or + reason == lldb.eStopReasonPlanComplete or + reason == lldb.eStopReasonSignal or + reason == lldb.eStopReasonWatchpoint): + return thread + return None + + def fetchThreads(self, args): + result = 'threads=[' + for i in range(0, self.process.GetNumThreads()): + thread = self.process.GetThreadAtIndex(i) + if thread.is_stopped: + state = 'stopped' + elif thread.is_suspended: + state = 'suspended' + else: + state = 'unknown' + reason = thread.GetStopReason() + result += '{id="%d"' % thread.GetThreadID() + result += ',index="%s"' % i + result += ',details="%s"' % toCString(thread.GetQueueName()) + result += ',stop-reason="%s"' % self.stopReason(thread.GetStopReason()) + result += ',state="%s"' % state + result += ',name="%s"' % toCString(thread.GetName()) + result += ',frame={' + frame = thread.GetFrameAtIndex(0) + result += 'pc="0x%x"' % frame.pc + result += ',addr="0x%x"' % frame.pc + result += ',fp="0x%x"' % frame.fp + result += ',func="%s"' % frame.GetFunctionName() + result += ',line="%s"' % frame.line_entry.line + result += ',fullname="%s"' % fileNameAsString(frame.line_entry.file) + result += ',file="%s"' % fileNameAsString(frame.line_entry.file) + result += '}},' + + result += '],current-thread-id="%s"' % self.currentThread().id + self.reportResult(result, args) + + def firstUsableFrame(self, thread): + for i in range(10): + frame = thread.GetFrameAtIndex(i) + lineEntry = frame.GetLineEntry() + line = lineEntry.GetLine() + if line != 0: + return i + return None + + def fetchStack(self, args): + if not self.process: + self.reportResult('msg="No process"', args) + return + thread = self.currentThread() + if not thread: + self.reportResult('msg="No thread"', args) + return + + isNativeMixed = int(args.get('nativemixed', 0)) + extraQml = int(args.get('extraqml', '0')) + + limit = args.get('stacklimit', -1) + (n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False) + self.currentCallContext = None + result = 'stack={current-thread="%d"' % thread.GetThreadID() + result += ',frames=[' + + ii = 0 + if extraQml: + ns = self.qtNamespace() + needle = self.qtNamespace() + 'QV4::ExecutionEngine' + pats = [ + '{0}qt_v4StackTraceForEngine((void*)0x{1:x})', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())', + '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)', + ] + done = False + while ii < n and not done: + res = None + frame = thread.GetFrameAtIndex(ii) + if not frame.IsValid(): + break + for variable in frame.GetVariables(True, True, False, True): + if not variable.GetType().IsPointerType(): + continue + derefvar = variable.Dereference() + if derefvar.GetType().GetName() != needle: + continue + addr = derefvar.GetLoadAddress() + for pat in pats: + exp = pat.format(ns, addr) + val = frame.EvaluateExpression(exp) + err = val.GetError() + res = str(val) + if err.Fail(): + continue + pos = res.find('"stack=[') + if pos == -1: + continue + res = res[pos + 8:-2] + res = res.replace('\\\"', '\"') + res = res.replace('func=', 'function=') + result += res + done = True + break + ii += 1 + # if we have not found a qml stack do not omit original stack + if not done: + DumperBase.warn("Failed to fetch qml stack - you need Qt debug information") + ii = 0 + + for i in range(n - ii): + frame = thread.GetFrameAtIndex(i) + if not frame.IsValid(): + isLimited = False + break + + lineEntry = frame.GetLineEntry() + lineNumber = lineEntry.GetLine() + + pc = frame.GetPC() + level = frame.idx + addr = frame.GetPCAddress().GetLoadAddress(self.target) + + functionName = frame.GetFunctionName() + module = frame.GetModule() + + if isNativeMixed and functionName == '::qt_qmlDebugMessageAvailable()': + interpreterStack = self.extractInterpreterStack() + for interpreterFrame in interpreterStack.get('frames', []): + function = interpreterFrame.get('function', '') + fileName = toCString(interpreterFrame.get('file', '')) + language = interpreterFrame.get('language', '') + lineNumber = interpreterFrame.get('line', 0) + context = interpreterFrame.get('context', 0) + result += ('frame={function="%s",file="%s",' + 'line="%s",language="%s",context="%s"}' + % (function, fileName, lineNumber, language, context)) + + fileName = fileNameAsString(lineEntry.file) + result += '{pc="0x%x"' % pc + result += ',level="%d"' % level + result += ',address="0x%x"' % addr + result += ',function="%s"' % functionName + result += ',line="%d"' % lineNumber + result += ',module="%s"' % toCString(module) + result += ',file="%s"},' % fileName + result += ']' + result += ',hasmore="%d"' % isLimited + result += ',limit="%d"' % limit + result += '}' + self.reportResult(result, args) + + def reportResult(self, result, args): + self.report('result={token="%s",%s}' % (args.get("token", 0), result)) + + def reportToken(self, args): + if "token" in args: + # Unusual syntax intended, to support the double-click in left + # logview pane feature. + self.report('token(\"%s\")' % args["token"]) + + def reportBreakpointUpdate(self, bp): + self.report('breakpointmodified={%s}' % self.describeBreakpoint(bp)) + + def readRawMemory(self, address, size): + if size == 0: + return bytes() + error = lldb.SBError() + #DumperBase.warn("READ: %s %s" % (address, size)) + res = self.process.ReadMemory(address, size, error) + if res is None or len(res) != size: + # Using code in e.g. readToFirstZero relies on exceptions. + raise RuntimeError("Unreadable %s bytes at 0x%x" % (size, address)) + return res + + def findStaticMetaObject(self, type): + symbolName = self.mangleName(type.name + '::staticMetaObject') + symbol = self.target.FindFirstGlobalVariable(symbolName) + return symbol.AddressOf().GetValueAsUnsigned() if symbol.IsValid() else 0 + + def findSymbol(self, symbolName): + return self.target.FindFirstGlobalVariable(symbolName) + + def warn(self, msg): + self.put('{name="%s",value="",type="",numchild="0"},' % toCString(msg)) + + def fetchVariables(self, args): + (ok, res) = self.tryFetchInterpreterVariables(args) + if ok: + self.reportResult(res, args) + return + + self.setVariableFetchingOptions(args) + + # Reset certain caches whenever a step over / into / continue + # happens. + # FIXME: Caches are currently also cleared if currently + # selected frame is changed, that shouldn't happen. + if not self.partialVariable: + self.resetPerStepCaches() + + frame = self.currentFrame() + if frame is None: + self.reportResult('error="No frame"', args) + return + + self.output = [] + isPartial = len(self.partialVariable) > 0 + + self.currentIName = 'local' + self.put('data=[') + + with SubItem(self, '[statics]'): + self.put('iname="%s",' % self.currentIName) + self.putEmptyValue() + self.putExpandable() + if self.isExpanded(): + with Children(self): + statics = frame.GetVariables(False, False, True, False) + if len(statics): + for i in range(len(statics)): + staticVar = statics[i] + staticVar.SetPreferSyntheticValue(False) + typename = staticVar.GetType().GetName() + name = staticVar.GetName() + with SubItem(self, i): + self.put('name="%s",' % name) + self.put('iname="%s",' % self.currentIName) + self.putItem(self.fromNativeValue(staticVar)) + else: + with SubItem(self, "None"): + self.putEmptyValue() + + # FIXME: Implement shortcut for partial updates. + #if isPartial: + # values = [frame.FindVariable(partialVariable)] + #else: + if True: + values = list(frame.GetVariables(True, True, False, True)) + values.reverse() # To get shadowed vars numbered backwards. + + variables = [] + for val in values: + val.SetPreferSyntheticValue(False) + if not val.IsValid(): + continue + self.currentContextValue = val + name = val.GetName() + if name is None: + # This can happen for unnamed function parameters with + # default values: void foo(int = 0) + continue + value = self.fromNativeFrameValue(val) + variables.append(value) + + self.handleLocals(variables) + self.handleWatches(args) + + self.put('],partial="%d"' % isPartial) + self.reportResult(self.takeOutput(), args) + + + def fetchRegisters(self, args=None): + if not self.process: + self.reportResult('process="none",registers=[]', args) + return + + frame = self.currentFrame() + if not frame or not frame.IsValid(): + self.reportResult('frame="none",registers=[]', args) + return + + result = 'registers=[' + for group in frame.GetRegisters(): + for reg in group: + data = reg.GetData() + if data.GetByteOrder() == lldb.eByteOrderLittle: + value = ''.join(["%02x" % x for x in reversed(data.uint8s)]) + else: + value = ''.join(["%02x" % x for x in data.uint8s]) + result += '{name="%s"' % reg.GetName() + result += ',value="0x%s"' % value + result += ',size="%s"' % reg.GetByteSize() + result += ',type="%s"},' % reg.GetType() + result += ']' + self.reportResult(result, args) + + + def setRegister(self, args): + name = args["name"] + value = args["value"] + result = lldb.SBCommandReturnObject() + interp = self.debugger.GetCommandInterpreter() + interp.HandleCommand("register write %s %s" % (name, value), result) + success = result.Succeeded() + if success: + self.reportResult('output="%s"' % toCString(result.GetOutput()), args) + return + # Try again with register write xmm0 "{0x00 ... 0x02}" syntax: + vec = ' '.join(["0x" + value[i:i + 2] for i in range(2, len(value), 2)]) + success = interp.HandleCommand('register write %s "{%s}"' % (name, vec), result) + if success: + self.reportResult('output="%s"' % toCString(result.GetOutput()), args) + else: + self.reportResult('error="%s"' % toCString(result.GetError()), args) + + def report(self, stuff): + with self.outputLock: + sys.stdout.write("@\n" + stuff + "@\n") + sys.stdout.flush() + + def reportState(self, state): + self.report('state="%s"' % state) + + def interruptInferior(self, args): + if self.process is None: + self.reportResult('status="No process to interrupt",success="0"', args) + else: + self.isInterrupting_ = True + error = self.process.Stop() + self.reportResult(self.describeError(error), args) + + def detachInferior(self, args): + if self.process is None: + self.reportResult('status="No process to detach from."', args) + else: + error = self.process.Detach() + self.reportResult(self.describeError(error), args) + + def continueInferior(self, args): + if self.process is None: + self.reportResult('status="No process to continue."', args) + else: + # Can fail when attaching to GDBserver. + error = self.process.Continue() + self.reportResult(self.describeError(error), args) + + def quitDebugger(self, args): + self.reportState("inferiorshutdownrequested") + self.process.Kill() + self.reportResult('', args) + + def handleBreakpointEvent(self, event): + eventType = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event) + # handle only the resolved locations for now.. + if eventType & lldb.eBreakpointEventTypeLocationsResolved: + bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event) + if bp is not None: + self.reportBreakpointUpdate(bp) + + def handleEvent(self, event): + if lldb.SBBreakpoint.EventIsBreakpointEvent(event): + self.handleBreakpointEvent(event) + return + if not lldb.SBProcess.EventIsProcessEvent(event): + self.warn("UNEXPECTED event (%s)" % event.GetType()) + return + + out = lldb.SBStream() + event.GetDescription(out) + #DumperBase.warn("EVENT: %s" % event) + eventType = event.GetType() + msg = lldb.SBEvent.GetCStringFromEvent(event) + flavor = event.GetDataFlavor() + state = lldb.SBProcess.GetStateFromEvent(event) + bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event) + skipEventReporting = eventType in ( + lldb.SBProcess.eBroadcastBitSTDOUT, lldb.SBProcess.eBroadcastBitSTDERR) + self.report('event={type="%s",data="%s",msg="%s",flavor="%s",state="%s",bp="%s"}' + % (eventType, toCString(out.GetData()), + toCString(msg), flavor, self.stateName(state), bp)) + + if state == lldb.eStateExited: + self.eventState = state + if not self.isShuttingDown_: + self.reportState("inferiorexited") + self.report('exited={status="%d",desc="%s"}' + % (self.process.GetExitStatus(), + toCString(self.process.GetExitDescription()))) + elif state != self.eventState and not skipEventReporting: + self.eventState = state + if state == lldb.eStateStopped: + stoppedThread = self.firstStoppedThread() + if stoppedThread: + #self.report("STOPPED THREAD: %s" % stoppedThread) + frame = stoppedThread.GetFrameAtIndex(0) + #self.report("FRAME: %s" % frame) + function = frame.GetFunction() + functionName = function.GetName() + if functionName == "::qt_qmlDebugConnectorOpen()": + self.report("RESOLVER HIT") + for resolver in self.interpreterBreakpointResolvers: + resolver() + self.report("AUTO-CONTINUE AFTER RESOLVING") + self.reportState("inferiorstopok") + self.process.Continue() + return + if functionName == "::qt_qmlDebugMessageAvailable()": + self.report("ASYNC MESSAGE FROM SERVICE") + res = self.handleInterpreterMessage() + if not res: + self.report("EVENT NEEDS NO STOP") + self.reportState("stopped") + self.process.Continue() + return + if self.isInterrupting_: + self.isInterrupting_ = False + self.reportState("inferiorstopok") + else: + self.reportState("stopped") + if self.firstStop_: + self.firstStop_ = False + if self.useTerminal_ or self.platform_ == "remote-macosx": + # When using a terminal or remote debugging macosx apps, + # the process will be interrupted on startup. + # We therefore need to continue it here. + self.process.Continue() + else: + self.reportState(self.stateName(state)) + + if eventType == lldb.SBProcess.eBroadcastBitStateChanged: # 1 + state = self.process.GetState() + if state == lldb.eStateStopped: + stoppedThread = self.firstStoppedThread() + if stoppedThread: + self.process.SetSelectedThread(stoppedThread) + elif eventType == lldb.SBProcess.eBroadcastBitInterrupt: # 2 + pass + elif eventType == lldb.SBProcess.eBroadcastBitSTDOUT: + self.handleInferiorOutput(self.process.GetSTDOUT, "stdout") + elif eventType == lldb.SBProcess.eBroadcastBitSTDERR: + self.handleInferiorOutput(self.process.GetSTDERR, "stderr") + elif eventType == lldb.SBProcess.eBroadcastBitProfileData: + pass + + def handleInferiorOutput(self, proc, channel): + while True: + msg = proc(1024) + if msg == None or len(msg) == 0: + break + self.report('output={channel="%s",data="%s"}' % (channel, self.hexencode(msg))) + + def describeBreakpoint(self, bp): + isWatch = isinstance(bp, lldb.SBWatchpoint) + if isWatch: + result = 'lldbid="%s"' % (qqWatchpointOffset + bp.GetID()) + else: + result = 'lldbid="%s"' % bp.GetID() + result += ',valid="%d"' % (1 if bp.IsValid() else 0) + result += ',hitcount="%d"' % bp.GetHitCount() + if bp.IsValid(): + if isinstance(bp, lldb.SBBreakpoint): + result += ',threadid="%d"' % bp.GetThreadID() + result += ',oneshot="%d"' % (1 if bp.IsOneShot() else 0) + cond = bp.GetCondition() + result += ',condition="%s"' % self.hexencode("" if cond is None else cond) + result += ',enabled="%d"' % (1 if bp.IsEnabled() else 0) + result += ',valid="%d"' % (1 if bp.IsValid() else 0) + result += ',ignorecount="%d"' % bp.GetIgnoreCount() + if bp.IsValid() and isinstance(bp, lldb.SBBreakpoint): + result += ',locations=[' + lineEntry = None + for i in range(bp.GetNumLocations()): + loc = bp.GetLocationAtIndex(i) + addr = loc.GetAddress() + lineEntry = addr.GetLineEntry() + result += '{locid="%d"' % loc.GetID() + result += ',function="%s"' % addr.GetFunction().GetName() + result += ',enabled="%d"' % (1 if loc.IsEnabled() else 0) + result += ',resolved="%d"' % (1 if loc.IsResolved() else 0) + result += ',valid="%d"' % (1 if loc.IsValid() else 0) + result += ',ignorecount="%d"' % loc.GetIgnoreCount() + result += ',file="%s"' % toCString(lineEntry.GetFileSpec()) + result += ',line="%d"' % lineEntry.GetLine() + result += ',addr="%s"},' % addr.GetFileAddress() + result += ']' + if lineEntry is not None: + result += ',file="%s"' % toCString(lineEntry.GetFileSpec()) + result += ',line="%d"' % lineEntry.GetLine() + return result + + def createBreakpointAtMain(self): + return self.target.BreakpointCreateByName( + 'main', self.target.GetExecutable().GetFilename()) + + def insertBreakpoint(self, args): + bpType = args['type'] + if bpType == BreakpointType.BreakpointByFileAndLine: + fileName = args['file'] + if fileName.endswith('.js') or fileName.endswith('.qml'): + self.insertInterpreterBreakpoint(args) + return + + extra = '' + more = True + if bpType == BreakpointType.BreakpointByFileAndLine: + bp = self.target.BreakpointCreateByLocation( + str(args['file']), int(args['line'])) + elif bpType == BreakpointType.BreakpointByFunction: + bp = self.target.BreakpointCreateByName(args['function']) + elif bpType == BreakpointType.BreakpointByAddress: + bp = self.target.BreakpointCreateByAddress(args['address']) + elif bpType == BreakpointType.BreakpointAtMain: + bp = self.createBreakpointAtMain() + elif bpType == BreakpointType.BreakpointAtThrow: + bp = self.target.BreakpointCreateForException( + lldb.eLanguageTypeC_plus_plus, False, True) + elif bpType == BreakpointType.BreakpointAtCatch: + bp = self.target.BreakpointCreateForException( + lldb.eLanguageTypeC_plus_plus, True, False) + elif bpType == BreakpointType.WatchpointAtAddress: + error = lldb.SBError() + # This might yield bp.IsValid() == False and + # error.desc == 'process is not alive'. + bp = self.target.WatchAddress(args['address'], 4, False, True, error) + extra = self.describeError(error) + elif bpType == BreakpointType.WatchpointAtExpression: + # FIXME: Top level-only for now. + try: + frame = self.currentFrame() + value = frame.FindVariable(args['expression']) + error = lldb.SBError() + bp = self.target.WatchAddress(value.GetLoadAddress(), + value.GetByteSize(), False, True, error) + except: + bp = self.target.BreakpointCreateByName(None) + else: + # This leaves the unhandled breakpoint in a (harmless) + # 'pending' state. + bp = self.target.BreakpointCreateByName(None) + more = False + + if more and bp.IsValid(): + bp.SetIgnoreCount(int(args['ignorecount'])) + bp.SetCondition(self.hexdecode(args['condition'])) + bp.SetEnabled(bool(args['enabled'])) + bp.SetScriptCallbackBody('\n'.join([ + 'def foo(frame = frame, bp_loc = bp_loc, dict = internal_dict):', + ' ' + self.hexdecode(args['command']).replace('\n', '\n '), + 'from cStringIO import StringIO', + 'origout = sys.stdout', + 'sys.stdout = StringIO()', + 'result = foo()', + 'd = lldb.theDumper', + 'output = d.hexencode(sys.stdout.getvalue())', + 'sys.stdout = origout', + 'd.report("output={channel=\"stderr\",data=\" + output + \"}")', + 'sys.stdout.flush()', + 'if result is False:', + ' d.reportState("continueafternextstop")', + 'return True' + ])) + if isinstance(bp, lldb.SBBreakpoint): + bp.SetOneShot(bool(args['oneshot'])) + self.reportResult(self.describeBreakpoint(bp) + extra, args) + + def changeBreakpoint(self, args): + lldbId = int(args['lldbid']) + if lldbId > qqWatchpointOffset: + bp = self.target.FindWatchpointByID(lldbId) + else: + bp = self.target.FindBreakpointByID(lldbId) + if bp.IsValid(): + bp.SetIgnoreCount(int(args['ignorecount'])) + bp.SetCondition(self.hexdecode(args['condition'])) + bp.SetEnabled(bool(args['enabled'])) + if isinstance(bp, lldb.SBBreakpoint): + bp.SetOneShot(bool(args['oneshot'])) + self.reportResult(self.describeBreakpoint(bp), args) + + def enableSubbreakpoint(self, args): + lldbId = int(args['lldbid']) + locId = int(args['locid']) + bp = self.target.FindBreakpointByID(lldbId) + res = False + enabled = False + if bp.IsValid(): + loc = bp.FindLocationByID(locId) + if loc.IsValid(): + loc.SetEnabled(bool(args['enabled'])) + enabled = loc.IsEnabled() + res = True + self.reportResult('success="%d",enabled="%d",locid="%d"' + % (int(res), int(enabled), locId), args) + + def removeBreakpoint(self, args): + lldbId = int(args['lldbid']) + if lldbId > qqWatchpointOffset: + res = self.target.DeleteWatchpoint(lldbId - qqWatchpointOffset) + res = self.target.BreakpointDelete(lldbId) + self.reportResult('success="%d"' % int(res), args) + + def fetchModules(self, args): + result = 'modules=[' + for i in range(self.target.GetNumModules()): + module = self.target.GetModuleAtIndex(i) + result += '{file="%s"' % toCString(module.file.fullpath) + result += ',name="%s"' % toCString(module.file.basename) + result += ',addrsize="%d"' % module.addr_size + result += ',triple="%s"' % module.triple + #result += ',sections={' + #for section in module.sections: + # result += '[name="%s"' % section.name + # result += ',addr="%s"' % section.addr + # result += ',size="%d"],' % section.size + #result += '}' + result += '},' + result += ']' + self.reportResult(result, args) + + def fetchSymbols(self, args): + moduleName = args['module'] + #file = lldb.SBFileSpec(moduleName) + #module = self.target.FindModule(file) + for i in range(self.target.GetNumModules()): + module = self.target.GetModuleAtIndex(i) + if module.file.fullpath == moduleName: + break + result = 'symbols={valid="%s"' % module.IsValid() + result += ',sections="%s"' % module.GetNumSections() + result += ',symbols=[' + for symbol in module.symbols: + startAddress = symbol.GetStartAddress().GetLoadAddress(self.target) + endAddress = symbol.GetEndAddress().GetLoadAddress(self.target) + result += '{type="%s"' % symbol.GetType() + result += ',name="%s"' % symbol.GetName() + result += ',address="0x%x"' % startAddress + result += ',demangled="%s"' % symbol.GetMangledName() + result += ',size="%d"' % (endAddress - startAddress) + result += '},' + result += ']}' + self.reportResult(result, args) + + def executeNext(self, args): + self.currentThread().StepOver() + self.reportResult('', args) + + def executeNextI(self, args): + self.currentThread().StepInstruction(True) + self.reportResult('', args) + + def executeStep(self, args): + self.currentThread().StepInto() + self.reportResult('', args) + + def shutdownInferior(self, args): + self.isShuttingDown_ = True + if self.process is not None: + state = self.process.GetState() + if state == lldb.eStateStopped: + self.process.Kill() + self.reportState('inferiorshutdownfinished') + self.reportResult('', args) + + def quit(self, args): + self.reportState('engineshutdownfinished') + self.process.Kill() + self.reportResult('', args) + + def executeStepI(self, args): + self.currentThread().StepInstruction(False) + self.reportResult('', args) + + def executeStepOut(self, args={}): + self.currentThread().StepOut() + self.reportResult('', args) + + def executeRunToLocation(self, args): + self.reportToken(args) + addr = args.get('address', 0) + if addr: + # Does not seem to hit anything on Linux: + # self.currentThread().RunToAddress(addr) + bp = self.target.BreakpointCreateByAddress(addr) + if bp.GetNumLocations() == 0: + self.target.BreakpointDelete(bp.GetID()) + self.reportResult(self.describeStatus('No target location found.') + + self.describeLocation(frame), args) + return + bp.SetOneShot(True) + self.reportResult('', args) + self.process.Continue() + else: + frame = self.currentFrame() + file = args['file'] + line = int(args['line']) + error = self.currentThread().StepOverUntil(frame, lldb.SBFileSpec(file), line) + self.reportResult(self.describeError(error), args) + self.reportState('running') + self.reportState('stopped') + + def executeJumpToLocation(self, args): + self.reportToken(args) + frame = self.currentFrame() + if not frame: + self.reportResult(self.describeStatus('No frame available.'), args) + return + addr = args.get('address', 0) + if addr: + bp = self.target.BreakpointCreateByAddress(addr) + else: + bp = self.target.BreakpointCreateByLocation( + str(args['file']), int(args['line'])) + if bp.GetNumLocations() == 0: + self.target.BreakpointDelete(bp.GetID()) + status = 'No target location found.' + else: + loc = bp.GetLocationAtIndex(0) + self.target.BreakpointDelete(bp.GetID()) + res = frame.SetPC(loc.GetLoadAddress()) + status = 'Jumped.' if res else 'Cannot jump.' + self.report(self.describeLocation(frame)) + self.reportResult(self.describeStatus(status), args) + + def breakList(self): + result = lldb.SBCommandReturnObject() + self.debugger.GetCommandInterpreter().HandleCommand('break list', result) + self.report('success="%d",output="%s",error="%s"' + % (result.Succeeded(), toCString(result.GetOutput()), + toCString(result.GetError()))) + + def activateFrame(self, args): + self.reportToken(args) + frame = max(0, int(args['index'])) # Can be -1 in all-asm stacks + self.currentThread().SetSelectedFrame(frame) + self.reportResult('', args) + + def selectThread(self, args): + self.reportToken(args) + self.process.SetSelectedThreadByID(int(args['id'])) + self.reportResult('', args) + + def fetchFullBacktrace(self, args): + command = 'thread backtrace all' + result = lldb.SBCommandReturnObject() + self.debugger.GetCommandInterpreter().HandleCommand(command, result) + self.reportResult('fulltrace="%s"' % self.hexencode(result.GetOutput()), args) + + def executeDebuggerCommand(self, args): + self.reportToken(args) + result = lldb.SBCommandReturnObject() + command = args['command'] + self.debugger.GetCommandInterpreter().HandleCommand(command, result) + success = result.Succeeded() + output = toCString(result.GetOutput()) + error = toCString(str(result.GetError())) + self.report('success="%d",output="%s",error="%s"' % (success, output, error)) + + def executeRoundtrip(self, args): + self.reportResult('', args) + + def fetchDisassembler(self, args): + functionName = args.get('function', '') + flavor = args.get('flavor', '') + function = None + if len(functionName): + functions = self.target.FindFunctions(functionName).functions + if len(functions): + function = functions[0] + if function: + base = function.GetStartAddress().GetLoadAddress(self.target) + instructions = function.GetInstructions(self.target) + else: + base = args.get('address', 0) + if int(base) == 0xffffffffffffffff: + self.warn('INVALID DISASSEMBLER BASE') + return + addr = lldb.SBAddress(base, self.target) + instructions = self.target.ReadInstructions(addr, 100) + + currentFile = None + currentLine = None + hunks = dict() + sources = dict() + result = 'lines=[' + for insn in instructions: + comment = insn.GetComment(self.target) + addr = insn.GetAddress() + loadAddr = addr.GetLoadAddress(self.target) + lineEntry = addr.GetLineEntry() + if lineEntry: + lineNumber = lineEntry.GetLine() + fileName = str(lineEntry.GetFileSpec()) + if lineNumber != currentLine or fileName != currentFile: + currentLine = lineNumber + currentFile = fileName + key = '%s:%s' % (fileName, lineNumber) + hunk = hunks.get(key, 0) + 1 + hunks[key] = hunk + source = sources.get(fileName, None) + if source is None: + try: + with open(fileName, 'r') as f: + source = f.read().splitlines() + sources[fileName] = source + except IOError as error: + # With lldb-3.8 files like /data/dev/creator-3.6/tests/ + # auto/debugger/qt_tst_dumpers_StdVector_bfNWZa/main.cpp + # with non-existent directories appear. + self.warn('FILE: %s ERROR: %s' % (fileName, error)) + source = '' + result += '{line="%d"' % lineNumber + result += ',file="%s"' % toCString(fileName) + if 0 < lineNumber and lineNumber <= len(source): + result += ',hexdata="%s"' % self.hexencode(source[lineNumber - 1]) + result += ',hunk="%s"}' % hunk + result += '{address="%s"' % loadAddr + result += ',data="%s %s"' % (insn.GetMnemonic(self.target), + insn.GetOperands(self.target)) + result += ',function="%s"' % functionName + rawData = insn.GetData(self.target).uint8s + result += ',rawdata="%s"' % ' '.join(["%02x" % x for x in rawData]) + if comment: + result += ',comment="%s"' % self.hexencode(comment) + result += ',offset="%s"}' % (loadAddr - base) + self.reportResult(result + ']', args) + + def fetchMemory(self, args): + address = args['address'] + length = args['length'] + error = lldb.SBError() + contents = self.process.ReadMemory(address, length, error) + result = 'address="%s",' % address + result += self.describeError(error) + result += ',contents="%s"' % self.hexencode(contents) + self.reportResult(result, args) + + def findValueByExpression(self, exp): + # FIXME: Top level-only for now. + frame = self.currentFrame() + value = frame.FindVariable(exp) + return value + + def setValue(self, address, typename, value): + sbtype = self.lookupNativeType(typename) + error = lldb.SBError() + sbaddr = lldb.SBAddress(address, self.target) + sbvalue = self.target.CreateValueFromAddress('x', sbaddr, sbtype) + sbvalue.SetValueFromCString(str(value), error) + + def setValues(self, address, typename, values): + sbtype = self.lookupNativeType(typename) + sizeof = sbtype.GetByteSize() + error = lldb.SBError() + for i in range(len(values)): + sbaddr = lldb.SBAddress(address + i * sizeof, self.target) + sbvalue = self.target.CreateValueFromAddress('x', sbaddr, sbtype) + sbvalue.SetValueFromCString(str(values[i]), error) + + def assignValue(self, args): + self.reportToken(args) + error = lldb.SBError() + expr = self.hexdecode(args['expr']) + value = self.hexdecode(args['value']) + simpleType = int(args['simpleType']) + lhs = self.findValueByExpression(expr) + typeName = lhs.GetType().GetName() + typeName = typeName.replace('::', '__') + pos = typeName.find('<') + if pos != -1: + typeName = typeName[0:pos] + if typeName in self.qqEditable and not simpleType: + expr = self.parseAndEvaluate(expr) + self.qqEditable[typeName](self, expr, value) + else: + self.parseAndEvaluate(expr + '=' + value) + self.reportResult(self.describeError(error), args) + + def watchPoint(self, args): + self.reportToken(args) + ns = self.qtNamespace() + lenns = len(ns) + funcs = self.target.FindGlobalFunctions('.*QApplication::widgetAt', 2, 1) + func = funcs[1] + addr = func.GetFunction().GetStartAddress().GetLoadAddress(self.target) + expr = '((void*(*)(int,int))0x%x)' % addr + #expr = '%sQApplication::widgetAt(%s,%s)' % (ns, args['x'], args['y']) + res = self.parseAndEvaluate(expr) + p = 0 if res is None else res.pointer() + n = ns + 'QWidget' + self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args) + + def createResolvePendingBreakpointsHookBreakpoint(self, args): + bp = self.target.BreakpointCreateByName('qt_qmlDebugConnectorOpen') + bp.SetOneShot(True) + self.interpreterBreakpointResolvers.append( + lambda: self.resolvePendingInterpreterBreakpoint(args)) + + +# Used in dumper auto test. +class Tester(Dumper): + def __init__(self, binary, args): + Dumper.__init__(self) + lldb.theDumper = self + self.loadDumpers({'token': 1}) + error = lldb.SBError() + self.target = self.debugger.CreateTarget(binary, None, None, True, error) + + if error.GetType(): + self.warn('ERROR: %s' % error) + return + + s = threading.Thread(target=self.testLoop, args=(args,)) + s.start() + s.join(30) + + def testLoop(self, args): + # Disable intermediate reporting. + savedReport = self.report + self.report = lambda stuff: 0 + + error = lldb.SBError() + launchInfo = lldb.SBLaunchInfo([]) + launchInfo.SetWorkingDirectory(os.getcwd()) + environmentList = [key + '=' + value for key, value in os.environ.items()] + launchInfo.SetEnvironmentEntries(environmentList, False) + + self.process = self.target.Launch(launchInfo, error) + if error.GetType(): + self.warn('ERROR: %s' % error) + + event = lldb.SBEvent() + listener = self.debugger.GetListener() + while True: + state = self.process.GetState() + if listener.WaitForEvent(100, event): + #DumperBase.warn('EVENT: %s' % event) + state = lldb.SBProcess.GetStateFromEvent(event) + if state == lldb.eStateExited: # 10 + break + if state == lldb.eStateStopped: # 5 + stoppedThread = None + for i in range(0, self.process.GetNumThreads()): + thread = self.process.GetThreadAtIndex(i) + reason = thread.GetStopReason() + #DumperBase.warn('THREAD: %s REASON: %s' % (thread, reason)) + if (reason == lldb.eStopReasonBreakpoint or + reason == lldb.eStopReasonException or + reason == lldb.eStopReasonSignal): + stoppedThread = thread + + if stoppedThread: + # This seems highly fragile and depending on the 'No-ops' in the + # event handling above. + frame = stoppedThread.GetFrameAtIndex(0) + line = frame.line_entry.line + if line != 0: + self.report = savedReport + self.process.SetSelectedThread(stoppedThread) + self.fakeAddress_ = frame.GetPC() + self.fakeLAddress_ = frame.GetPCAddress() + self.fetchVariables(args) + #self.describeLocation(frame) + self.report('@NS@%s@' % self.qtNamespace()) + #self.report('ENV=%s' % os.environ.items()) + #self.report('DUMPER=%s' % self.qqDumpers) + break + + else: + self.warn('TIMEOUT') + self.warn('Cannot determined stopped thread') + + lldb.SBDebugger.Destroy(self.debugger) + +if 'QT_CREATOR_LLDB_PROCESS' in os.environ: + # Initialize Qt Creator dumper + try: + theDumper = Dumper() + except Exception as error: + print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error)) + +# ------------------------------ For use in LLDB ------------------------------ + +debug = print if 'QT_LLDB_SUMMARY_PROVIDER_DEBUG' in os.environ \ + else lambda *a, **k: None + +debug(f"Loading lldbbridge.py from {__file__}") + +class LogMixin(): + @staticmethod + def log(message='', log_caller=False, frame=1, args=''): + if log_caller: + message = ": " + message if len(message) else '' + # FIXME: Compute based on first frame not in this class? + frame = sys._getframe(frame) + fn = frame.f_code.co_name + localz = frame.f_locals + instance = str(localz["self"]) + "." if 'self' in localz else '' + message = "%s%s(%s)%s" % (instance, fn, args, message) + debug(message) + + @staticmethod + def log_fn(arg_str=''): + LogMixin.log(log_caller=True, frame=2, args=arg_str) + + +class DummyDebugger(object): + def __getattr__(self, attr): + raise AttributeError("Debugger should not be needed to create summaries") + + +class SummaryDumper(Dumper, LogMixin): + _instance = None + _lock = threading.RLock() + _type_caches = {} + + @staticmethod + def initialize(): + SummaryDumper._instance = SummaryDumper() + return SummaryDumper._instance + + @staticmethod + @contextmanager + def shared(valobj): + SummaryDumper._lock.acquire() + dumper = SummaryDumper._instance + dumper.target = valobj.target + dumper.process = valobj.process + debugger_id = dumper.target.debugger.GetID() + dumper.typeCache = SummaryDumper._type_caches.get(debugger_id, {}) + yield dumper + SummaryDumper._type_caches[debugger_id] = dumper.typeCache + SummaryDumper._lock.release() + + @staticmethod + def warn(message): + print("Qt summary warning: %s" % message) + + @staticmethod + def showException(message, exType, exValue, exTraceback): + # FIXME: Store for later and report back in summary + SummaryDumper.log("Exception during dumping: %s" % exValue) + + def __init__(self): + DumperBase.warn = staticmethod(SummaryDumper.warn) + DumperBase.showException = staticmethod(SummaryDumper.showException) + + Dumper.__init__(self, DummyDebugger()) + + self.setVariableFetchingOptions({ + 'fancy': True, + 'qobjectnames': True, + 'passexceptions': True + }) + + self.dumpermodules = ['qttypes'] + self.loadDumpers({}) + self.output = [] + + def report(self, stuff): + return # Don't mess up lldb output + + def dump_summary(self, valobj, expanded=False): + from pygdbmi import gdbmiparser + + value = self.fromNativeValue(valobj) + + # Expand variable if we need synthetic children + oldExpanded = self.expandedINames + self.expandedINames = {value.name: 100} if expanded else {} + + savedOutput = self.output + self.output = [] + with TopLevelItem(self, value.name): + self.putItem(value) + + # FIXME: Hook into putField, etc to build up object instead of parsing MI + response = gdbmiparser.parse_response("^ok,summary=%s" % self.takeOutput()) + + self.output = savedOutput + self.expandedINames = oldExpanded + + summary = response["payload"]["summary"] + + #print value, " --> ", + #pprint(summary) + + return summary + + +class SummaryProvider(LogMixin): + + DEFAULT_SUMMARY = '' + VOID_PTR_TYPE = None + + @staticmethod + def provide_summary(valobj, internal_dict, options=None): + if __name__ not in internal_dict: + # When disabling the import of the Qt summary providers, the + # summary functions are still registered with LLDB, and we will + # get callbacks to provide summaries, but at this point the child + # providers are not active, so instead of providing half-baked and + # confusing summaries we opt to unload all the summaries. + SummaryDumper.log("Module '%s' was unloaded, removing Qt category" % __name__) + lldb.debugger.HandleCommand('type category delete Qt') + return SummaryProvider.DEFAULT_SUMMARY + + # FIXME: It would be nice to cache the providers, so that if a + # synthetic child provider has already been created for a valobj, + # we can re-use that when providing summary for the synthetic child + # parent, but we lack a consistent cache key for that to work. + + provider = SummaryProvider(valobj) + provider.update() + + return provider.get_summary(options) + + def __init__(self, valobj, expand=False): + # Prevent recursive evaluation during logging, etc + valobj.SetPreferSyntheticValue(False) + + self.log_fn(valobj.path) + + self.valobj = valobj + self.expand = expand + + if not SummaryProvider.VOID_PTR_TYPE: + with SummaryDumper.shared(self.valobj) as dumper: + SummaryProvider.VOID_PTR_TYPE = dumper.lookupNativeType('void*') + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self.key())) + + def key(self): + if not hasattr(self, 'valobj'): + return None + return self.valobj.path + + def update(self): + self.log_fn() + with SummaryDumper.shared(self.valobj) as dumper: + self.summary = dumper.dump_summary(self.valobj, self.expand) + + def get_summary(self, options): + self.log_fn() + + summary = self.summary + if not summary: + return '' + + encoding = summary.get("valueencoded") + summaryValue = summary["value"] + + # FIXME: Share between Creator and LLDB in the python code + + if encoding: + special_encodings = { + "empty": "", + "minimumitemcount": "" % summaryValue, + "undefined": "", + "null": "", + "itemcount": "<%s items>" % summaryValue, + "notaccessible": "", + "optimizedout": "", + "nullreference": "", + "emptystructure": "", + "uninitialized": "", + "invalid": "", + "notcallable": "", + "outofscope": "" + } + if encoding in special_encodings: + return special_encodings[encoding] + + text_encodings = [ + 'latin1', + # 'local8bit', + 'utf8', + 'utf16', + # 'ucs4' + ] + + if encoding in text_encodings: + try: + decodedValue = Dumper.hexdecode(summaryValue, encoding) + # LLDB expects UTF-8 for python 2 + if sys.version_info[0] < 3: + return "\"%s\"" % (decodedValue.encode('utf8')) + return '"' + decodedValue + '"' + except: + return "" % (summaryValue, encoding, sys.exc_info()[1]) + + # FIXME: Support these + other_encodings = [ + 'int', + 'uint', + 'float', + 'juliandate', + 'juliandateandmillisecondssincemidnight', + 'millisecondssincemidnight', + 'ipv6addressandhexscopeid', + 'datetimeinternal'] + + summaryValue += " " % encoding + + if self.valobj.value: + # If we've resolved a pointer that matches what LLDB natively chose, + # then use that instead of printing the values twice. FIXME, ideally + # we'd have the same pointer format as LLDB uses natively. + if re.sub('^0x0*', '', self.valobj.value) == re.sub('^0x0*', '', summaryValue): + return SummaryProvider.DEFAULT_SUMMARY + + return summaryValue.strip() + + +class SyntheticChildrenProvider(SummaryProvider): + def __init__(self, valobj, dict): + SummaryProvider.__init__(self, valobj, expand=True) + self.summary = None + self.synthetic_children = [] + + def num_native_children(self): + return self.valobj.GetNumChildren() + + def num_children(self): + self.log("native: %d synthetic: %d" % + (self.num_native_children(), len(self.synthetic_children)), True) + return self._num_children() + + def _num_children(self): + return self.num_native_children() + len(self.synthetic_children) + + def has_children(self): + return self._num_children() > 0 + + def get_child_index(self, name): + # FIXME: Do we ever need this to return something? + self.log_fn(name) + return None + + def get_child_at_index(self, index): + self.log_fn(index) + + if index < 0 or index > self._num_children(): + return None + if not self.valobj.IsValid() or not self.summary: + return None + + if index < self.num_native_children(): + # Built-in children + value = self.valobj.GetChildAtIndex(index) + else: + # Synthetic children + index -= self.num_native_children() + child = self.synthetic_children[index] + name = child.get('name', "[%s]" % index) + value = self.create_value(child, name) + + return value + + def create_value(self, child, name=''): + child_type = child.get('type', self.summary.get('childtype')) + + value = None + if child_type: + if 'address' in child: + value_type = None + with SummaryDumper.shared(self.valobj) as dumper: + value_type = dumper.lookupNativeType(child_type) + if not value_type or not value_type.IsValid(): + return None + address = int(child['address'], 16) + + # Create as void* so that the value is fully initialized before + # we trigger our own summary/child providers recursively. + value = self.valobj.synthetic_child_from_address( + name, address, SummaryProvider.VOID_PTR_TYPE).Cast(value_type) + else: + self.log("Don't know how to create value for child %s" % child) + # FIXME: Figure out if we ever will hit this case, and deal with it + #value = self.valobj.synthetic_child_from_expression(name, + # "(%s)(%s)" % (child_type, child['value'])) + else: + # FIXME: Find a good way to deal with synthetic values + self.log("Don't know how to create value for child %s" % child) + + # FIXME: Handle value type or value errors consistently + return value + + def update(self): + SummaryProvider.update(self) + + self.synthetic_children = [] + if 'children' not in self.summary: + return + + dereference_child = None + for child in self.summary['children']: + if not child or not isinstance(child, dict): + continue + + # Skip base classes, they are built-in children + # FIXME: Is there a better check than 'iname'? + if 'iname' in child: + continue + + name = child.get('name') + if name: + if name == '*': + # No use for this unless the built in children failed to resolve + dereference_child = child + continue + + if name.startswith('['): + # Skip pure synthetic children until we can deal with them + continue + + if name.startswith('#'): + # Skip anonymous unions, lookupNativeType doesn't handle them (yet), + # so it triggers the slow lookup path, and the union is provided as + # a native child anyways. + continue + + # Skip native children + if self.valobj.GetChildMemberWithName(name): + continue + + self.synthetic_children.append(child) + + # Xcode will sometimes fail to show the children of pointers in + # the debugger UI, even if dereferencing the pointer works fine. + # We fall back to the special child reported by the Qt dumper. + if not self.valobj.GetNumChildren() and dereference_child: + self.valobj = self.create_value(dereference_child) + self.update() + +def ensure_gdbmiparser(): + try: + from pygdbmi import gdbmiparser + return True + except ImportError: + try: + if not 'QT_LLDB_SUMMARY_PROVIDER_NO_AUTO_INSTALL' in os.environ: + print("Required module 'pygdbmi' not installed. Installing automatically...") + import subprocess + python3 = os.path.join(sys.exec_prefix, 'bin', 'python3') + process = subprocess.run([python3, '-m', 'pip', + '--disable-pip-version-check', + 'install', '--user', 'pygdbmi' ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + print(process.stdout.decode('utf-8').strip()) + process.check_returncode() + from importlib import invalidate_caches + invalidate_caches() + from pygdbmi import gdbmiparser + return True + except Exception as e: + print(e) + + print("Qt summary provider requires the pygdbmi module. Please install\n" \ + "manually using '/usr/bin/pip3 install pygdbmi', and restart Xcode.") + return False + + +def __lldb_init_module(debugger, internal_dict): + # Module is being imported in an LLDB session + if 'QT_CREATOR_LLDB_PROCESS' in os.environ: + # Let Qt Creator take care of its own dumper + return + + debug("Initializing module with", debugger) + + if not ensure_gdbmiparser(): + return + + if not __name__ == 'qt': + # Make available under global 'qt' name for consistency, + # and so we can refer to SyntheticChildrenProvider below. + internal_dict['qt'] = internal_dict[__name__] + + dumper = SummaryDumper.initialize() + + type_category = 'Qt' + + # Concrete types + summary_function = "%s.%s.%s" % (__name__, 'SummaryProvider', 'provide_summary') + types = map(lambda x: x.replace('__', '::'), dumper.qqDumpers) + debugger.HandleCommand("type summary add -F %s -w %s %s" + % (summary_function, type_category, ' '.join(types))) + + regex_types = list(map(lambda x: "'" + x + "'", dumper.qqDumpersEx.keys())) + if regex_types: + debugger.HandleCommand("type summary add -x -F %s -w %s %s" + % (summary_function, type_category, ' '.join(regex_types))) + + # Global catch-all for Qt classes + debugger.HandleCommand("type summary add -x '^Q.*$' -F %s -w %s" + % (summary_function, type_category)) + + # Named summary ('frame variable foo --summary Qt') + debugger.HandleCommand("type summary add --name Qt -F %s -w %s" + % (summary_function, type_category)) + + # Synthetic children + debugger.HandleCommand("type synthetic add -x '^Q.*$' -l %s -w %s" + % ("qt.SyntheticChildrenProvider", type_category)) + + debugger.HandleCommand('type category enable %s' % type_category) diff --git a/share/qtcreator/debugger/python2/misctypes.py b/share/qtcreator/debugger/python2/misctypes.py new file mode 100644 index 00000000000..9099f19a2f7 --- /dev/null +++ b/share/qtcreator/debugger/python2/misctypes.py @@ -0,0 +1,588 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from dumper import Children, SubItem +from utils import TypeCode, DisplayFormat +import re + +####################################################################### +# +# SSE +# +####################################################################### + + +def qdump____m128(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 4, d.lookupType('float')) + + +def qdump____m256(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 8, d.lookupType('float')) + + +def qdump____m512(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 16, d.lookupType('float')) + + +def qdump____m128d(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 2, d.lookupType('double')) + + +def qdump____m256d(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 4, d.lookupType('double')) + + +def qdump____m512d(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + d.putArrayData(value.address(), 8, d.lookupType('double')) + + +def qdump____m128i(d, value): + data = d.hexencode(value.data(16)) + d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 32, 4))) + d.putExpandable() + if d.isExpanded(): + with Children(d): + addr = value.address() + d.putArrayItem('uint8x16', addr, 16, 'unsigned char') + d.putArrayItem('uint16x8', addr, 8, 'unsigned short') + d.putArrayItem('uint32x4', addr, 4, 'unsigned int') + d.putArrayItem('uint64x2', addr, 2, 'unsigned long long') + + +def qdump____m256i(d, value): + data = d.hexencode(value.data(32)) + d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 64, 4))) + d.putExpandable() + if d.isExpanded(): + with Children(d): + addr = value.address() + d.putArrayItem('uint8x32', addr, 32, 'unsigned char') + d.putArrayItem('uint16x16', addr, 16, 'unsigned short') + d.putArrayItem('uint32x8', addr, 8, 'unsigned int') + d.putArrayItem('uint64x4', addr, 4, 'unsigned long long') + + +def qdump____m512i(d, value): + data = d.hexencode(value.data(64)) + d.putValue(':'.join('%04x' % int(data[i:i + 4], 16) for i in range(0, 64, 4)) + + ', ' + ':'.join('%04x' % int(data[i:i + 4], 16) for i in range(64, 128, 4))) + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putArrayItem('uint32x16', value.address(), 16, 'unsigned int') + d.putArrayItem('uint64x8', value.address(), 8, 'unsigned long long') + +####################################################################### +# +# GSL +# +####################################################################### + + +def qform__std__array(): + return [DisplayFormat.ArrayPlot] + + +def qdump__gsl__span(d, value): + size, pointer = value.split('pp') + d.putItemCount(size) + if d.isExpanded(): + d.putPlotData(pointer, size, value.type[0]) + + +def qdump__gsl__byte(d, value): + d.putValue(value.integer()) + +####################################################################### +# +# Eigen +# +####################################################################### + +#def qform__Eigen__Matrix(): +# return 'Transposed' + + +def qdump__Eigen__Matrix(d, value): + innerType = value.type[0] + argRow = value.type[1] + argCol = value.type[2] + options = value.type[3] + rowMajor = (int(options) & 0x1) + # The magic dimension value is -1 in Eigen3, but 10000 in Eigen2. + # 10000 x 10000 matrices are rare, vectors of dim 10000 less so. + # So 'fix' only the matrix case: + if argCol == 10000 and argRow == 10000: + argCol = -1 + argRow = -1 + if argCol != -1 and argRow != -1: + nrows = argRow + ncols = argCol + p = value.address() + else: + storage = value['m_storage'] + nrows = storage['m_rows'].integer() if argRow == -1 else argRow + ncols = storage['m_cols'].integer() if argCol == -1 else argCol + p = storage['m_data'].pointer() + innerSize = innerType.size() + d.putValue('(%s x %s), %s' % (nrows, ncols, ['ColumnMajor', 'RowMajor'][rowMajor])) + d.putField('keeporder', '1') + d.putNumChild(nrows * ncols) + + limit = 10000 + nncols = min(ncols, limit) + nnrows = min(nrows, limit * limit / nncols) + if d.isExpanded(): + #format = d.currentItemFormat() # format == 1 is 'Transposed' + with Children(d, nrows * ncols, childType=innerType): + if ncols == 1 or nrows == 1: + for i in range(0, min(nrows * ncols, 10000)): + d.putSubItem(i, d.createValue(p + i * innerSize, innerType)) + elif rowMajor == 1: + s = 0 + for i in range(0, nnrows): + for j in range(0, nncols): + v = d.createValue(p + (i * ncols + j) * innerSize, innerType) + d.putNamedSubItem(s, v, '[%d,%d]' % (i, j)) + s = s + 1 + else: + s = 0 + for j in range(0, nncols): + for i in range(0, nnrows): + v = d.createValue(p + (i + j * nrows) * innerSize, innerType) + d.putNamedSubItem(s, v, '[%d,%d]' % (i, j)) + s = s + 1 + + +####################################################################### +# +# Nim +# +####################################################################### + +def qdump__NimStringDesc(d, value): + size, reserved = value.split('pp') + data = value.address() + 2 * d.ptrSize() + d.putBetterType('string') + d.putCharArrayHelper(data, size, d.createType('char'), 'utf8') + + +def qdump__NimGenericSequence__(d, value, regex=r'^TY[\d]+$'): + code = value.type.stripTypedefs().code + if code == TypeCode.Struct: + size, reserved = d.split('pp', value) + data = value.address() + 2 * d.ptrSize() + typeobj = value['data'].type.dereference() + d.putItemCount(size) + d.putArrayData(data, size, typeobj) + d.putBetterType('%s (%s[%s])' % (value.type.name, typeobj.name, size)) + else: + d.putEmptyValue() + d.putPlainChildren(value) + + +def qdump__TNimNode(d, value): + name = value['name'].pointer() + d.putSimpleCharArray(name) if name != 0 else d.putEmptyValue() + if d.isExpanded(): + with Children(d): + sons = value['sons'].pointer() + size = value['len'].integer() + for i in range(size): + val = d.createValue(d.extractPointer(sons + i * d.ptrSize()), value.type) + with SubItem(d, '[%d]' % i): + d.putItem(val) + with SubItem(d, '[raw]'): + d.putPlainChildren(value) + + +####################################################################### +# +# D +# +####################################################################### + +def cleanDType(type): + return str(type).replace('uns long long', 'string') + + +def qdump_Array(d, value): + n = value['length'] + p = value['ptr'] + t = cleanDType(value.type)[7:] + d.putType('%s[%d]' % (t, n)) + if t == 'char': + d.putValue(encodeCharArray(p, 100), 'local8bit') + else: + d.putEmptyValue() + d.putNumChild(n) + innerType = p.type + if d.isExpanded(): + with Children(d, n, childType=innerType): + for i in range(0, n): + d.putSubItem(i, p.dereference()) + p = p + 1 + + +def qdump_AArray(d, value): + #n = value['length'] + # This ends up as _AArray__ with a single .ptr + # member of type void *. Not much that can be done here. + p = value['ptr'] + t = cleanDType(value.type)[8:] + d.putType('%s]' % t.replace('_', '[')) + d.putEmptyValue() + if d.isExpanded(): + with Children(d, 1): + d.putSubItem('ptr', p) + + +####################################################################### +# +# MPI +# +####################################################################### + +if False: + + def qdump__tree_entry(d, value): + d.putValue('len: %s, offset: %s, type: %s' % + (value['blocklength'], value['offset'], value['type'])) + + def qdump__tree(d, value): + count = value['count'] + entries = value['entries'] + base = value['base'].pointer() + d.putItemCount(count) + if d.isExpanded(): + with Children(d): + with SubItem(d, 'tree'): + d.putEmptyValue() + d.putNoType() + if d.isExpanded(): + with Children(d): + for i in range(count): + d.putSubItem(Item(entries[i], iname)) + with SubItem(d, 'data'): + d.putEmptyValue() + d.putNoType() + if d.isExpanded(): + with Children(d): + for i in range(count): + with SubItem(d, i): + entry = entries[i] + mpitype = str(entry['type']) + d.putType(mpitype) + length = int(entry['blocklength']) + offset = int(entry['offset']) + d.putValue('%s items at %s' % (length, offset)) + if mpitype == 'MPI_INT': + innerType = 'int' + elif mpitype == 'MPI_CHAR': + innerType = 'char' + elif mpitype == 'MPI_DOUBLE': + innerType = 'double' + else: + length = 0 + d.putNumChild(length) + if d.isExpanded(): + with Children(d): + t = d.lookupType(innerType).pointer() + p = (base + offset).cast(t) + for j in range(length): + d.putSubItem(j, p.dereference()) + + +####################################################################### +# +# KDSoap +# +####################################################################### + +def qdump__KDSoapValue1(d, value): + inner = value['d']['d'].dereference() + d.putStringValue(inner['m_name']) + d.putPlainChildren(inner) + + +def qdump__KDSoapValue(d, value): + p = (value.cast(d.lookupType('char*')) + 4).dereference().cast(d.lookupType('QString')) + d.putStringValue(p) + d.putPlainChildren(value['d']['d'].dereference()) + + +####################################################################### +# +# Webkit +# +####################################################################### + +def qdump__WTF__String(d, value): + # WTF::String -> WTF::RefPtr -> WTF::StringImpl* + data = value['m_impl']['m_ptr'] + d.checkPointer(data) + + stringLength = int(data['m_length']) + d.check(0 <= stringLength and stringLength <= 100000000) + + # WTF::StringImpl* -> WTF::StringImpl -> sizeof(WTF::StringImpl) + offsetToData = data.type.target().size() + bufferPtr = data.pointer() + offsetToData + + is8Bit = data['m_is8Bit'] + charSize = 1 + if not is8Bit: + charSize = 2 + + d.putCharArrayHelper(bufferPtr, stringLength, charSize) + + +####################################################################### +# +# Python +# +####################################################################### + +def get_python_interpreter_major_version(d): + key = 'python_interpreter_major_version' + if key in d.generalCache: + return d.generalCache[key] + + e = "(char*)Py_GetVersion()" + result = d.nativeParseAndEvaluate(e) + result_str = str(result) + matches = re.search(r'(\d+?)\.(\d+?)\.(\d+?)', result_str) + if matches: + result_str = matches.group(1) + d.generalCache[key] = result_str + return result_str + + +def is_python_3(d): + return get_python_interpreter_major_version(d) == '3' + + +def repr_cache_decorator(namespace): + def real_decorator(func_to_decorate): + def wrapper(d, address): + if namespace in d.perStepCache and address in d.perStepCache[namespace]: + return d.perStepCache[namespace][address] + + if namespace not in d.perStepCache: + d.perStepCache[namespace] = {} + + if address == 0: + result_str = d.perStepCache[namespace][address] = "" + return result_str + + result = func_to_decorate(d, address) + d.perStepCache[namespace][address] = result + return result + return wrapper + return real_decorator + + +@repr_cache_decorator('py_object') +def get_py_object_repr_helper(d, address): + # The code below is a long way to evaluate: + # ((PyBytesObject *)PyUnicode_AsEncodedString(PyObject_Repr( + # (PyObject*){}), \"utf-8\", \"backslashreplace\"))->ob_sval" + # But with proper object cleanup. + + e_decref = "Py_DecRef((PyObject *){})" + + e = "PyObject_Repr((PyObject*){})" + repr_object_value = d.parseAndEvaluate(e.format(address)) + repr_object_address = d.fromPointerData(repr_object_value.ldata)[0] + + if is_python_3(d): + # Try to get a UTF-8 encoded string from the repr object. + e = "PyUnicode_AsEncodedString((PyObject*){}, \"utf-8\", \"backslashreplace\")" + string_object_value = d.parseAndEvaluate(e.format(repr_object_address)) + string_object_address = d.fromPointerData(string_object_value.ldata)[0] + + e = "(char*)(((PyBytesObject *){})->ob_sval)" + result = d.nativeParseAndEvaluate(e.format(string_object_address)) + + # It's important to stringify the result before any other evaluations happen. + result_str = str(result) + + # Clean up. + d.nativeParseAndEvaluate(e_decref.format(string_object_address)) + + else: + # Retrieve non-unicode string. + e = "(char*)(PyString_AsString((PyObject*){}))" + result = d.nativeParseAndEvaluate(e.format(repr_object_address)) + + # It's important to stringify the result before any other evaluations happen. + result_str = str(result) + + # Do some string stripping. + # FIXME when using cdb engine. + matches = re.search(r'.+?"(.+)"$', result_str) + if matches: + result_str = matches.group(1) + + # Clean up. + d.nativeParseAndEvaluate(e_decref.format(repr_object_address)) + + return result_str + + +@repr_cache_decorator('py_object_type') +def get_py_object_type(d, object_address): + e = "((PyObject *){})->ob_type" + type_value = d.parseAndEvaluate(e.format(object_address)) + type_address = d.fromPointerData(type_value.ldata)[0] + type_repr = get_py_object_repr_helper(d, type_address) + return type_repr + + +@repr_cache_decorator('py_object_meta_type') +def get_py_object_meta_type(d, object_address): + # The python3 object layout has a few more indirections. + if is_python_3(d): + e = "((PyObject *){})->ob_type->ob_base->ob_base->ob_type" + else: + e = "((PyObject *){})->ob_type->ob_type" + type_value = d.parseAndEvaluate(e.format(object_address)) + type_address = d.fromPointerData(type_value.ldata)[0] + type_repr = get_py_object_repr_helper(d, type_address) + return type_repr + + +@repr_cache_decorator('py_object_base_class') +def get_py_object_base_class(d, object_address): + e = "((PyObject *){})->ob_type->tp_base" + base_value = d.parseAndEvaluate(e.format(object_address)) + base_address = d.fromPointerData(base_value.ldata)[0] + base_repr = get_py_object_repr_helper(d, base_address) + return base_repr + + +def get_py_object_repr(d, value): + address = value.address() + repr_available = False + try: + result = get_py_object_repr_helper(d, address) + d.putValue(d.hexencode(result), encoding='utf8') + repr_available = True + except: + d.putEmptyValue() + + def sub_item(name, functor, address): + with SubItem(d, '[{}]'.format(name)): + sub_value = functor(d, address) + d.putValue(d.hexencode(sub_value), encoding='utf8') + + d.putExpandable() + if d.isExpanded(): + with Children(d): + if repr_available: + sub_item('class', get_py_object_type, address) + sub_item('super class', get_py_object_base_class, address) + sub_item('meta type', get_py_object_meta_type, address) + d.putFields(value) + + +def qdump__PyTypeObject(d, value): + get_py_object_repr(d, value) + + +def qdump___typeobject(d, value): + get_py_object_repr(d, value) + + +def qdump__PyObject(d, value): + get_py_object_repr(d, value) + + +def qdump__PyVarObject(d, value): + get_py_object_repr(d, value) + + +####################################################################### +# +# Internal test +# +####################################################################### + +def qdump__QtcDumperTest_FieldAccessByIndex(d, value): + d.putValue(value["d"][2].integer()) + + +def qdump__QtcDumperTest_PointerArray(d, value): + foos = value["foos"] + d.putItemCount(10) + if d.isExpanded(): + with Children(d, 10): + for i in d.childRange(): + d.putSubItem(i, foos[i]) + + +def qdump__QtcDumperTest_BufArray(d, value): + maxItems = 1000 + buffer = value['buffer'] + count = int(value['count']) + objsize = int(value['objSize']) + valueType = value.type.templateArgument(0) + d.putItemCount(count, maxItems) + d.putNumChild(count) + if d.isExpanded(): + with Children(d, count, maxNumChild=maxItems, childType=valueType): + for i in d.childRange(): + d.putSubItem(i, (buffer + (i * objsize)).dereference().cast(valueType)) + + +def qdump__QtcDumperTest_List__NodeX(d, value): + typename = value.type.unqualified().name + pos0 = typename.find('<') + pos1 = typename.find('>') + tName = typename[pos0 + 1:pos1] + d.putBetterType('QtcDumperTest_List<' + tName + '>::Node') + d.putExpandable() + if d.isExpanded(): + obj_type = d.lookupType(tName) + with Children(d): + d.putSubItem("this", value.cast(obj_type)) + d.putFields(value) + #d.putPlainChildren(value) + + +def qdump__QtcDumperTest_List(d, value): + innerType = value.type[0] + d.putExpandable() + p = value['root'] + if d.isExpanded(): + with Children(d): + d.putSubItem("[p]", p) + d.putSubItem("[root]", value["root"].cast(innerType)) + d.putFields(value) + #d.putPlainChildren(value) + + +def qdump__QtcDumperTest_String(d, value): + with Children(d): + first = d.hexdecode(d.putSubItem('first', value['first']).value) + second = d.hexdecode(d.putSubItem('second', value['second']).value) + third = d.hexdecode(d.putSubItem('third', value['third']).value)[:-1] + d.putValue(first + ', ' + second + ', ' + third) diff --git a/share/qtcreator/debugger/python2/opencvtypes.py b/share/qtcreator/debugger/python2/opencvtypes.py new file mode 100644 index 00000000000..5cfd60c5d25 --- /dev/null +++ b/share/qtcreator/debugger/python2/opencvtypes.py @@ -0,0 +1,44 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from dumper import Children, SubItem +from utils import TypeCode, DisplayFormat + + +def qdump__cv__Size_(d, value): + d.putValue('(%s, %s)' % (value[0].display(), value[1].display())) + d.putPlainChildren(value) + + +def qform__cv__Mat(): + return [DisplayFormat.Separate] + + +def qdump__cv__Mat(d, value): + (flag, dims, rows, cols, data, refcount, datastart, dataend, + datalimit, allocator, size, stepp) \ + = value.split('iiiipppppppp') + steps = d.split('p' * dims, stepp) + innerSize = 0 if dims == 0 else steps[dims - 1] + if dims != 2: + d.putEmptyValue() + d.putPlainChildren(value) + return + + if d.currentItemFormat() == DisplayFormat.Separate: + rs = steps[0] * innerSize + cs = cols * innerSize + dform = 'arraydata:separate:int:%d::2:%d:%d' % (innerSize, cols, rows) + out = ''.join(d.readMemory(data + i * rs, cs) for i in range(rows)) + d.putDisplay(dform, out) + + d.putValue('(%s x %s)' % (rows, cols)) + if d.isExpanded(): + with Children(d): + innerType = d.createType(TypeCode.Integral, innerSize) + for i in range(rows): + for j in range(cols): + with SubItem(d, None): + d.putName('[%d,%d]' % (i, j)) + addr = data + (i * steps[0] + j) * innerSize + d.putItem(d.createValue(addr, innerType)) diff --git a/share/qtcreator/debugger/python2/pdbbridge.py b/share/qtcreator/debugger/python2/pdbbridge.py new file mode 100644 index 00000000000..228f4c8c1fc --- /dev/null +++ b/share/qtcreator/debugger/python2/pdbbridge.py @@ -0,0 +1,1726 @@ +# 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 re +import sys +import code +import base64 +import linecache +import signal +import string +import inspect +import traceback +import fnmatch +import platform + + +class QuitException(Exception): + pass + + +class QtcInternalBreakpoint(): + """Breakpoint class. + Breakpoints are indexed by number through bpbynumber and by + the file,line tuple using bplist. The former points to a + single instance of class Breakpoint. The latter points to a + list of such instances since there may be more than one + breakpoint per line. + """ + + next = 1 # Next bp to be assigned + bplist = {} # indexed by (file, lineno) tuple + bpbynumber = [None] # Each entry is None or an instance of Bpt + # index 0 is unused, except for marking an + # effective break .... see effective() + + def __init__(self, filepath, line, temporary=False, cond=None, funcname=None): + self.funcname = funcname + # Needed if funcname is not None. + self.func_first_executable_line = None + self.file = filepath # This better be in canonical form! + self.line = line + self.temporary = temporary + self.cond = cond + self.enabled = True + self.ignore = 0 + self.hits = 0 + self.number = QtcInternalBreakpoint.next + QtcInternalBreakpoint.next += 1 + # Build the two lists + self.bpbynumber.append(self) + if (filepath, line) in self.bplist: + self.bplist[filepath, line].append(self) + else: + self.bplist[filepath, line] = [self] + + def deleteMe(self): + index = (self.file, self.line) + self.bpbynumber[self.number] = None # No longer in list + self.bplist[index].remove(self) + if not self.bplist[index]: + # No more bp for this f:l combo + del self.bplist[index] + + def enable(self): + self.enabled = True + + def disable(self): + self.enabled = False + + def __str__(self): + return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line) + + +def checkfuncname(b, frame): + """Check whether we should break here because of `b.funcname`.""" + if not b.funcname: + # Breakpoint was set via line number. + if b.line != frame.f_lineno: + # Breakpoint was set at a line with a def statement and the function + # defined is called: don't break. + return False + return True + + # Breakpoint set via function name. + + if frame.f_code.co_name != b.funcname: + # It's not a function call, but rather execution of def statement. + return False + + # We are in the right frame. + if not b.func_first_executable_line: + # The function is entered for the 1st time. + b.func_first_executable_line = frame.f_lineno + + if b.func_first_executable_line != frame.f_lineno: + # But we are not at the first line number: don't break. + return False + return True + + +# Determines if there is an effective (active) breakpoint at this +# line of code. Returns breakpoint number or 0 if none +def effective(filename, line, frame): + """Determine which breakpoint for this file:line is to be acted upon. + + Called only if we know there is a bpt at this + location. Returns breakpoint that was triggered and a flag + that indicates if it is ok to delete a temporary bp. + + """ + possibles = QtcInternalBreakpoint.bplist[filename, line] + for b in possibles: + if not b.enabled: + continue + if not checkfuncname(b, frame): + continue + # Count every hit when bp is enabled + b.hits += 1 + if not b.cond: + # If unconditional, and ignoring go on to next, else break + if b.ignore > 0: + b.ignore -= 1 + continue + else: + # breakpoint and marker that it's ok to delete if temporary + return (b, True) + else: + # Conditional bp. + # Ignore count applies only to those bpt hits where the + # condition evaluates to true. + try: + val = eval(b.cond, frame.f_globals, frame.f_locals) + if val: + if b.ignore > 0: + b.ignore -= 1 + # continue + else: + return (b, True) + # else: + # continue + except: + # if eval fails, most conservative thing is to stop on + # breakpoint regardless of ignore count. Don't delete + # temporary, as another hint to user. + return (b, False) + return (None, None) + + +# __all__ = ['QtcInternalDumper'] + + +def find_function(funcname, filename): + cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname)) + try: + fp = open(filename) + except OSError: + return None + # consumer of this info expects the first line to be 1 + with fp: + for lineno, line in enumerate(fp, start=1): + if cre.match(line): + return funcname, filename, lineno + return None + + +class _rstr(str): + """String that doesn't quote its repr.""" + + def __repr__(self): + return self + + +class QtcInternalDumper(): + identchars = string.ascii_letters + string.digits + '_' + lastcmd = '' + use_rawinput = 1 + + def __init__(self, stdin=None, stdout=None): + self.skip = [] + self.breaks = {} + self.fncache = {} + self.frame_returning = None + + nosigint = False + + if stdin is not None: + self.stdin = stdin + else: + self.stdin = sys.stdin + if stdout is not None: + self.stdout = stdout + else: + self.stdout = sys.stdout + self.cmdqueue = [] + + if stdout: + self.use_rawinput = 0 + self.aliases = {} + self.mainpyfile = '' + self._wait_for_mainpyfile = False + # Try to load readline if it exists + try: + import readline + # remove some common file name delimiters + readline.set_completer_delims(' \t\n`@#$%^&*()=+[{]}\\|;:\'",<>?') + except ImportError: + pass + self.allow_kbdint = False + self.nosigint = nosigint + + self.commands = {} # associates a command list to breakpoint numbers + self.botframe = None + self.currentbp = -1 + self.stopframe = None + self.returnframe = None + self.quitting = False + self.stoplineno = 0 + self.mainpyfile = '' + self._user_requested_quit = False + self.stack = [] + self.curindex = 0 + self.curframe = None + self.curframe_locals = {} + self.typeformats = {} + self.formats = {} + self.output = '' + self.expandedINames = [] + + # Hex decoding operating on str, return str. + @staticmethod + def hexdecode(s): + if sys.version_info[0] == 2: + return s.decode('hex') + return bytes.fromhex(s).decode('utf8') + + # Hex encoding operating on str or bytes, return str. + @staticmethod + def hexencode(s): + if sys.version_info[0] == 2: + return s.encode('hex') + if isinstance(s, __builtins__.str): + s = s.encode('utf8') + return base64.b16encode(s).decode('utf8') + + @staticmethod + def cleanAddress(addr): + if addr is None: + return '' + h = hex(addr) + return '0x%x' % (int(h, 16) if sys.version_info[0] >= 3 else long(h, 16)) + + def canonic(self, filename): + if filename == '<' + filename[1:-1] + '>': + return filename + canonic = self.fncache.get(filename) + if not canonic: + canonic = os.path.abspath(filename) + canonic = os.path.normcase(canonic) + if platform.system() in ('Microsoft', 'Windows'): + canonic = canonic.replace('\\', '/') + self.fncache[filename] = canonic + return canonic + + def reset(self): + linecache.checkcache() + self.botframe = None + self._set_stopinfo(None, None) + self.forget() + + def trace_dispatch(self, frame, event, arg): + if self.quitting: + return None + if event == 'line': + return self.dispatch_line(frame) + if event == 'call': + return self.dispatch_call(frame) + if event == 'return': + return self.dispatch_return(frame, arg) + if event == 'exception': + return self.dispatch_exception(frame, arg) + if event == 'c_call': + return self.trace_dispatch + if event == 'c_exception': + return self.trace_dispatch + if event == 'c_return': + return self.trace_dispatch + print('Bdb.dispatch: unknown debugging event:', repr(event)) + return self.trace_dispatch + + def dispatch_line(self, frame): + if self.stop_here(frame) or self.break_here(frame): + self.user_line(frame) + if self.quitting: + raise QuitException + return self.trace_dispatch + + def dispatch_call(self, frame): + if self.botframe is None: + # First call of dispatch since reset() + self.botframe = frame.f_back # (CT) Note that this may also be None! + return self.trace_dispatch + if not (self.stop_here(frame) or self.break_anywhere(frame)): + # No need to trace this function + return None + # Ignore call events in generator except when stepping. + if self.stopframe and frame.f_code.co_flags & inspect.CO_GENERATOR: + return self.trace_dispatch + self.user_call(frame) + if self.quitting: + raise QuitException + return self.trace_dispatch + + def dispatch_return(self, frame, arg): + if self.stop_here(frame) or frame == self.returnframe: + # Ignore return events in generator except when stepping. + if self.stopframe and frame.f_code.co_flags & inspect.CO_GENERATOR: + return self.trace_dispatch + try: + self.frame_returning = frame + self.user_return(frame, arg) + finally: + self.frame_returning = None + if self.quitting: + raise QuitException + # The user issued a 'next' or 'until' command. + if self.stopframe is frame and self.stoplineno != -1: + self._set_stopinfo(None, None) + return self.trace_dispatch + + def dispatch_exception(self, frame, arg): + if self.stop_here(frame): + # When stepping with next/until/return in a generator frame, skip + # the internal StopIteration exception (with no traceback) + # triggered by a subiterator run with the 'yield from' statement. + if not (frame.f_code.co_flags & inspect.CO_GENERATOR + and arg[0] is StopIteration and arg[2] is None): + self.user_exception(frame, arg) + if self.quitting: + raise QuitException + # Stop at the StopIteration or GeneratorExit exception when the user + # has set stopframe in a generator by issuing a return command, or a + # next/until command at the last statement in the generator before the + # exception. + elif (self.stopframe and frame is not self.stopframe + and self.stopframe.f_code.co_flags & inspect.CO_GENERATOR + and arg[0] in (StopIteration, GeneratorExit)): + self.user_exception(frame, arg) + if self.quitting: + raise QuitException + + return self.trace_dispatch + + # Normally derived classes don't override the following + # methods, but they may if they want to redefine the + # definition of stopping and breakpoints. + + def is_skipped_module(self, module_name): + for pattern in self.skip: + if fnmatch.fnmatch(module_name, pattern): + return True + return False + + def stop_here(self, frame): + # (CT) stopframe may now also be None, see dispatch_call. + # (CT) the former test for None is therefore removed from here. + if self.skip and \ + self.is_skipped_module(frame.f_globals.get('__name__')): + return False + if frame is self.stopframe: + if self.stoplineno == -1: + return False + return frame.f_lineno >= self.stoplineno + if not self.stopframe: + return True + return False + + def break_here(self, frame): + filename = self.canonic(frame.f_code.co_filename) + if filename not in self.breaks: + return False + lineno = frame.f_lineno + if lineno not in self.breaks[filename]: + # The line itself has no breakpoint, but maybe the line is the + # first line of a function with breakpoint set by function name. + lineno = frame.f_code.co_firstlineno + if lineno not in self.breaks[filename]: + return False + + # flag says ok to delete temp. bp + (bp, flag) = effective(filename, lineno, frame) + if bp: + self.currentbp = bp.number + if (flag and bp.temporary): + self.do_clear(__builtins__.str(bp.number)) + return True + else: + return False + + def break_anywhere(self, frame): + return self.canonic(frame.f_code.co_filename) in self.breaks + + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): + self.stopframe = stopframe + self.returnframe = returnframe + self.quitting = False + # stoplineno >= 0 means: stop at line >= the stoplineno + # stoplineno -1 means: don't stop at all + self.stoplineno = stoplineno + + # Derived classes and clients can call the following methods + # to affect the stepping state. + + def set_step(self): + """Stop after one line of code.""" + # Issue #13183: pdb skips frames after hitting a breakpoint and running + # step commands. + # Restore the trace function in the caller (that may not have been set + # for performance reasons) when returning from the current frame. + if self.frame_returning: + caller_frame = self.frame_returning.f_back + if caller_frame and not caller_frame.f_trace: + caller_frame.f_trace = self.trace_dispatch + self._set_stopinfo(None, None) + + def set_trace(self, frame=None): + """Start debugging from `frame`. + + If frame is not specified, debugging starts from caller's frame. + """ + if frame is None: + frame = sys._getframe().f_back + self.reset() + while frame: + frame.f_trace = self.trace_dispatch + self.botframe = frame + frame = frame.f_back + self.set_step() + sys.settrace(self.trace_dispatch) + + def set_continue(self): + # Don't stop except at breakpoints or when finished + self._set_stopinfo(self.botframe, None, -1) + if not self.breaks: + # no breakpoints; run without debugger overhead + sys.settrace(None) + frame = sys._getframe().f_back + while frame and frame is not self.botframe: + del frame.f_trace + frame = frame.f_back + + def set_quit(self): + self.stopframe = self.botframe + self.returnframe = None + self.quitting = True + sys.settrace(None) + + # Derived classes and clients can call the following methods + # to manipulate breakpoints. These methods return an + # error message if something went wrong, None if all is well. + # Set_break prints out the breakpoint line and file:lineno. + # Call self.get_*break*() to see the breakpoints or better + # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). + + def set_break(self, filename, lineno, temporary=False, cond=None, + funcname=None): + filename = self.canonic(filename) + line = linecache.getline(filename, lineno) + if not line: + return 'Line %s:%d does not exist' % (filename, lineno) + lines = self.breaks.setdefault(filename, []) + if lineno not in lines: + lines.append(lineno) + QtcInternalBreakpoint(filename, lineno, temporary, cond, funcname) + return None + + def _prune_breaks(self, filename, lineno): + if (filename, lineno) not in QtcInternalBreakpoint.bplist: + self.breaks[filename].remove(lineno) + if not self.breaks[filename]: + del self.breaks[filename] + + def clear_break(self, filename, lineno): + filename = self.canonic(filename) + if filename not in self.breaks: + return 'There are no breakpoints in %s' % filename + if lineno not in self.breaks[filename]: + return 'There is no breakpoint at %s:%d' % (filename, lineno) + # If there's only one bp in the list for that file,line + # pair, then remove the breaks entry + for bp in QtcInternalBreakpoint.bplist[filename, lineno][:]: + bp.deleteMe() + self._prune_breaks(filename, lineno) + return None + + def clear_bpbynumber(self, arg): + try: + bp = self.get_bpbynumber(arg) + except ValueError as err: + return __builtins__.str(err) + bp.deleteMe() + self._prune_breaks(bp.file, bp.line) + return None + + def clear_all_file_breaks(self, filename): + filename = self.canonic(filename) + if filename not in self.breaks: + return + for line in self.breaks[filename]: + blist = QtcInternalBreakpoint.bplist[filename, line] + for bp in blist: + bp.deleteMe() + del self.breaks[filename] + + def clear_all_breaks(self): + if not self.breaks: + return + for bp in QtcInternalBreakpoint.bpbynumber: + if bp: + bp.deleteMe() + self.breaks = {} + + @staticmethod + def get_bpbynumber(arg): + if not arg: + raise ValueError('Breakpoint number expected') + try: + number = int(arg) + except ValueError: + raise ValueError('Non-numeric breakpoint number %s' % arg) + try: + bp = QtcInternalBreakpoint.bpbynumber[number] + except IndexError: + raise ValueError('Breakpoint number %d out of range' % number) + if bp is None: + raise ValueError('Breakpoint %d already deleted' % number) + return bp + + def get_break(self, filename, lineno): + filename = self.canonic(filename) + return filename in self.breaks and \ + lineno in self.breaks[filename] + + def get_breaks(self, filename, lineno): + filename = self.canonic(filename) + return QtcInternalBreakpoint.bplist.get((filename, lineno), []) + + def get_file_breaks(self, filename): + filename = self.canonic(filename) + return self.breaks.get(filename, []) + + def get_all_breaks(self): + return self.breaks + + # Derived classes and clients can call the following method + # to get a data structure representing a stack trace. + + def get_stack(self, frame, tb): + stack = [] + if tb and tb.tb_frame is frame: + tb = tb.tb_next + while frame is not None: + stack.append((frame, frame.f_lineno)) + if frame is self.botframe: + break + frame = frame.f_back + stack.reverse() + i = max(0, __builtins__.len(stack) - 1) + while tb is not None: + stack.append((tb.tb_frame, tb.tb_lineno)) + tb = tb.tb_next + if frame is None: + i = max(0, __builtins__.len(stack) - 1) + return stack, i + + # The following methods can be called by clients to use + # a debugger to debug a statement or an expression. + # Both can be given as a string, or a code object. + + def run(self, cmd, pyGlobals=None, pyLocals=None): + if pyGlobals is None: + import __main__ + pyGlobals = __main__.__dict__ + if pyLocals is None: + pyLocals = pyGlobals + self.reset() + if isinstance(cmd, __builtins__.str): + cmd = compile(cmd, '', 'exec') + sys.settrace(self.trace_dispatch) + try: + exec(cmd, pyGlobals, pyLocals) + except QuitException: + pass + finally: + self.quitting = True + sys.settrace(None) + + def runeval(self, expr, pyGlobals=None, pyLocals=None): + if pyGlobals is None: + import __main__ + pyGlobals = __main__.__dict__ + if pyLocals is None: + pyLocals = pyGlobals + self.reset() + sys.settrace(self.trace_dispatch) + try: + return eval(expr, pyGlobals, pyLocals) + except QuitException: + pass + finally: + self.quitting = True + sys.settrace(None) + + # This method is more useful to debug a single function call. + def runcall(self, func, *args, **kwds): + self.reset() + sys.settrace(self.trace_dispatch) + res = None + try: + res = func(*args, **kwds) + except QuitException: + pass + finally: + self.quitting = True + sys.settrace(None) + return res + + def cmdloop(self): + """Repeatedly accept input, parse an initial prefix + off the received input, and dispatch to action methods, passing them + the remainder of the line as argument. + """ + try: + stop = None + while not stop: + if self.cmdqueue: + line = self.cmdqueue.pop(0) + else: + if self.use_rawinput: + try: + if sys.version_info[0] == 2: + line = raw_input('') + else: + line = input('') + except EOFError: + line = 'EOF' + else: + self.stdout.write('') + self.stdout.flush() + line = self.stdin.readline() + if not line: + line = 'EOF' + else: + line = line.rstrip('\r\n') + print('LINE: %s' % line) + stop = self.onecmd(line) + finally: + pass + + def parseline(self, line): + """Parse the line into a command name and a string containing + the arguments. Returns a tuple containing (command, args, line). + 'command' and 'args' may be None if the line couldn't be parsed. + """ + line = line.strip() + if not line: + return None, None, line + elif line[0] == '?': + line = 'help ' + line[1:] + elif line[0] == '!': + if hasattr(self, 'do_shell'): + line = 'shell ' + line[1:] + else: + return None, None, line + i, length = 0, __builtins__.len(line) + while i < length and line[i] in self.identchars: + i = i + 1 + cmd, arg = line[:i], line[i:].strip() + return cmd, arg, line + + def onecmd(self, line): + """Interpret the argument as though it had been typed in response + to the prompt. + + The return value is a flag indicating whether interpretation of + commands by the interpreter should stop. + """ + line = __builtins__.str(line) + print('LINE 0: %s' % line) + cmd, arg, line = self.parseline(line) + print('LINE 1: %s' % line) + if cmd is None: + return self.default(line) + self.lastcmd = line + if line == 'EOF': + self.lastcmd = '' + if cmd == '': + return self.default(line) + else: + func = getattr(self, 'do_' + cmd, None) + if func: + return func(arg) + return self.default(line) + + def runit(self): + print('DIR: %s' % dir()) + if sys.argv[0] == '-c': + sys.argv = sys.argv[2:] + else: + sys.argv = sys.argv[1:] + print('ARGV: %s' % sys.argv) + mainpyfile = sys.argv[0] # Get script filename + sys.path.append(os.path.dirname(mainpyfile)) + print('MAIN: %s' % mainpyfile) + + while True: + try: + # The script has to run in __main__ namespace (or imports from + # __main__ will break). + # + # So we clear up the __main__ and set several special variables + # (this gets rid of pdb's globals and cleans old variables on restarts). + + import __main__ + # __main__.__dict__.clear() + __main__.__dict__.update({'__name__': '__main__', + '__file__': mainpyfile, + #'__builtins__': __builtins__, + }) + + # When bdb sets tracing, a number of call and line events happens + # BEFORE debugger even reaches user's code (and the exact sequence of + # events depends on python version). So we take special measures to + # avoid stopping before we reach the main script (see user_line and + # user_call for details). + self._wait_for_mainpyfile = True + self.mainpyfile = self.canonic(mainpyfile) + self._user_requested_quit = False + with open(mainpyfile, 'rb') as fp: + statement = "exec(compile(%r, %r, 'exec'))" % \ + (fp.read(), self.mainpyfile) + self.run(statement) + + if self._user_requested_quit: + break + print('The program finished') + sys.exit(0) + except SystemExit: + # In most cases SystemExit does not warrant a post-mortem session. + print('The program exited via sys.exit(). Exit status:') + print(sys.exc_info()[1]) + t = sys.exc_info()[2] + print('Post-mortem debugging is finished - ending debug session.') + sys.exit(0) + + except: + traceback.print_exc() + print('Uncaught exception. Entering post mortem debugging') + t = sys.exc_info()[2] + self.curframe_locals['__exception__'] = t + print('Post mortem debugger finished - ending debug session.') + sys.exit(0) + + def sigint_handler(self, signum, frame): + if self.allow_kbdint: + raise KeyboardInterrupt + self.report('state="stopped"') + self.set_step() + self.set_trace(frame) + # restore previous signal handler + signal.signal(signal.SIGINT, self._previous_sigint_handler) + + def forget(self): + self.stack = [] + self.curindex = 0 + self.curframe = None + + def setup(self, frame, tb): + self.forget() + self.stack, self.curindex = self.get_stack(frame, tb) + self.curframe = self.stack[self.curindex][0] + # The f_locals dictionary is updated from the actual frame + # locals whenever the .f_locals accessor is called, so we + # cache it here to ensure that modifications are not overwritten. + self.curframe_locals = self.curframe.f_locals + + def user_call(self, frame): + """This method is called when there is the remote possibility + that we ever need to stop in this function.""" + if self._wait_for_mainpyfile: + return + if self.stop_here(frame): + self.message('--Call--') + self.interaction(frame, None) + + def user_line(self, frame): + """This function is called when we stop or break at this line.""" + if self._wait_for_mainpyfile: + if (self.mainpyfile != self.canonic(frame.f_code.co_filename) + or frame.f_lineno <= 0): + return + self._wait_for_mainpyfile = False + if self.bp_commands(frame): + self.interaction(frame, None) + + def bp_commands(self, frame): + """Call every command that was set for the current active breakpoint + (if there is one). + + Returns True if the normal interaction function must be called, + False otherwise.""" + # self.currentbp is set in break_here if a breakpoint was hit + if getattr(self, 'currentbp', False) and self.currentbp in self.commands: + currentbp = self.currentbp + self.currentbp = 0 + lastcmd_back = self.lastcmd + self.setup(frame, None) + for line in self.commands[currentbp]: + self.onecmd(line) + self.lastcmd = lastcmd_back + self.forget() + return False + return True + + def user_return(self, frame, return_value): + """This function is called when a return trap is set here.""" + if self._wait_for_mainpyfile: + return + frame.f_locals['__return__'] = return_value + self.message('--Return--') + self.interaction(frame, None) + + def user_exception(self, frame, exc_info): + """This function is called if an exception occurs, + but only if we are to stop at or just below this level.""" + if self._wait_for_mainpyfile: + return + exc_type, exc_value, exc_traceback = exc_info + frame.f_locals['__exception__'] = exc_type, exc_value + + # An 'Internal StopIteration' exception is an exception debug event + # issued by the interpreter when handling a subgenerator run with + # 'yield from' or a generator controled by a for loop. No exception has + # actually occurred in this case. The debugger uses this debug event to + # stop when the debuggee is returning from such generators. + prefix = 'Internal ' if (not exc_traceback + and exc_type is StopIteration) else '' + self.message('%s%s' % (prefix, + traceback.format_exception_only(exc_type, exc_value)[-1].strip())) + self.interaction(frame, exc_traceback) + + def interaction(self, frame, tb): + if self.setup(frame, tb): + # no interaction desired at this time (happens if .pdbrc contains + # a command like 'continue') + self.forget() + return + + frame, lineNumber = self.stack[self.curindex] + fileName = self.canonic(frame.f_code.co_filename) + self.report('location={file="%s",line="%s"}' % (fileName, lineNumber)) + + while True: + try: + # keyboard interrupts allow for an easy way to cancel + # the current command, so allow them during interactive input + self.allow_kbdint = True + self.cmdloop() + self.allow_kbdint = False + break + except KeyboardInterrupt: + self.message('--KeyboardInterrupt--') + self.forget() + + def displayhook(self, obj): + """Custom displayhook for the exec in default(), which prevents + assignment of the _ variable in the builtins. + """ + # reproduce the behavior of the standard displayhook, not printing None + if obj is not None: + self.message(repr(obj)) + + def default(self, line): + if line[:1] == '!': + line = line[1:] + pyLocals = self.curframe_locals + pyGlobals = self.curframe.f_globals + try: + code = compile(line + '\n', '', 'single') + save_stdout = sys.stdout + save_stdin = sys.stdin + save_displayhook = sys.displayhook + try: + sys.stdin = self.stdin + sys.stdout = self.stdout + sys.displayhook = self.displayhook + exec(code, pyGlobals, pyLocals) + finally: + sys.stdout = save_stdout + sys.stdin = save_stdin + sys.displayhook = save_displayhook + except: + exc_info = sys.exc_info()[:2] + self.error(traceback.format_exception_only(*exc_info)[-1].strip()) + + @staticmethod + def message(msg): + print(msg) + + @staticmethod + def error(msg): + # print('***'+ msg) + pass + + def do_break(self, arg, temporary=0): + """b(reak) [ ([filename:]lineno | function) [, condition] ] + Without argument, list all breaks. + + With a line number argument, set a break at this line in the + current file. With a function name, set a break at the first + executable line of that function. If a second argument is + present, it is a string specifying an expression which must + evaluate to true before the breakpoint is honored. + + The line number may be prefixed with a filename and a colon, + to specify a breakpoint in another file (probably one that + hasn't been loaded yet). The file is searched for on + sys.path; the .py suffix may be omitted. + """ + if not arg: + if self.breaks: # There's at least one + self.message('Num Type Disp Enb Where') + for bp in QtcInternalBreakpoint.bpbynumber: + if bp: + self.message(bp.bpformat()) + return + # parse arguments; comma has lowest precedence + # and cannot occur in filename + filename = None + lineno = None + cond = None + comma = arg.find(',') + if comma > 0: + # parse stuff after comma: 'condition' + cond = arg[comma + 1:].lstrip() + arg = arg[:comma].rstrip() + # parse stuff before comma: [filename:]lineno | function + colon = arg.rfind(':') + funcname = None + if colon >= 0: + filename = arg[:colon].rstrip() + f = self.lookupmodule(filename) + if not f: + self.error('%r not found from sys.path' % filename) + return + else: + filename = f + arg = arg[colon + 1:].lstrip() + try: + lineno = int(arg) + except ValueError: + self.error('Bad lineno: %s' % arg) + return + else: + # no colon; can be lineno or function + try: + lineno = int(arg) + except ValueError: + try: + func = eval(arg, + self.curframe.f_globals, + self.curframe_locals) + except: + func = arg + try: + if hasattr(func, '__func__'): + func = func.__func__ + code = func.__code__ + # use co_name to identify the bkpt (function names + # could be aliased, but co_name is invariant) + funcname = code.co_name + lineno = code.co_firstlineno + filename = code.co_filename + except: + # last thing to try + (ok, filename, ln) = self.lineinfo(arg) + if not ok: + self.error('The specified object %r is not a function ' + 'or was not found along sys.path.' % arg) + return + funcname = ok # ok contains a function name + lineno = int(ln) + if not filename: + filename = self.defaultFile() + # Check for reasonable breakpoint + line = self.checkline(filename, lineno) + if line: + # now set the break point + err = self.set_break(filename, line, temporary, cond, funcname) + if err: + self.error(err) + else: + bp = self.get_breaks(filename, line)[-1] + self.message('Breakpoint %d at %s:%d' % + (bp.number, bp.file, bp.line)) + + # To be overridden in derived debuggers + def defaultFile(self): + """Produce a reasonable default.""" + filename = self.curframe.f_code.co_filename + if filename == '' and self.mainpyfile: + filename = self.mainpyfile + return filename + + def do_tbreak(self, arg): + """tbreak [ ([filename:]lineno | function) [, condition] ] + Same arguments as break, but sets a temporary breakpoint: it + is automatically deleted when first hit. + """ + self.do_break(arg, 1) + + def lineinfo(self, identifier): + failed = (None, None, None) + # Input is identifier, may be in single quotes + idstring = identifier.split("'") + if __builtins__.len(idstring) == 1: + # not in single quotes + tmp_id = idstring[0].strip() + elif __builtins__.len(idstring) == 3: + # quoted + tmp_id = idstring[1].strip() + else: + return failed + if tmp_id == '': + return failed + parts = tmp_id.split('.') + # Protection for derived debuggers + if parts[0] == 'self': + del parts[0] + if not parts: + return failed + # Best first guess at file to look at + fname = self.defaultFile() + if __builtins__.len(parts) == 1: + item = parts[0] + else: + # More than one part. + # First is module, second is method/class + f = self.lookupmodule(parts[0]) + if f: + fname = f + item = parts[1] + answer = find_function(item, fname) + return answer or failed + + def checkline(self, filename, lineno): + """Check whether specified line seems to be executable. + + Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank + line or EOF). Warning: testing is not comprehensive. + """ + # this method should be callable before starting debugging, so default + # to "no globals" if there is no current frame + globs = self.curframe.f_globals if hasattr(self, 'curframe') else None + line = linecache.getline(filename, lineno, globs) + if not line: + self.message('End of file') + return 0 + line = line.strip() + # Don't allow setting breakpoint at a blank line + if (not line or (line[0] == '#') or + (line[:3] == '"""') or line[:3] == "'''"): + self.error('Blank or comment') + return 0 + return lineno + + def do_enable(self, arg): + """enable bpnumber [bpnumber ...] + Enables the breakpoints given as a space separated list of + breakpoint numbers. + """ + args = arg.split() + for i in args: + try: + bp = self.get_bpbynumber(i) + except ValueError as err: + self.error(err) + else: + bp.enable() + self.message('Enabled %s' % bp) + + def do_disable(self, arg): + """disable bpnumber [bpnumber ...] + Disables the breakpoints given as a space separated list of + breakpoint numbers. Disabling a breakpoint means it cannot + cause the program to stop execution, but unlike clearing a + breakpoint, it remains in the list of breakpoints and can be + (re-)enabled. + """ + args = arg.split() + for i in args: + try: + bp = self.get_bpbynumber(i) + except ValueError as err: + self.error(err) + else: + bp.disable() + self.message('Disabled %s' % bp) + + def do_condition(self, arg): + """condition bpnumber [condition] + Set a new condition for the breakpoint, an expression which + must evaluate to true before the breakpoint is honored. If + condition is absent, any existing condition is removed; i.e., + the breakpoint is made unconditional. + """ + args = arg.split(' ', 1) + try: + cond = args[1] + except IndexError: + cond = None + try: + bp = self.get_bpbynumber(args[0].strip()) + except IndexError: + self.error('Breakpoint number expected') + except ValueError as err: + self.error(err) + else: + bp.cond = cond + if not cond: + self.message('Breakpoint %d is now unconditional.' % bp.number) + else: + self.message('New condition set for breakpoint %d.' % bp.number) + + def do_ignore(self, arg): + """ignore bpnumber [count] + Set the ignore count for the given breakpoint number. If + count is omitted, the ignore count is set to 0. A breakpoint + becomes active when the ignore count is zero. When non-zero, + the count is decremented each time the breakpoint is reached + and the breakpoint is not disabled and any associated + condition evaluates to true. + """ + args = arg.split() + try: + count = int(args[1].strip()) + except: + count = 0 + try: + bp = self.get_bpbynumber(args[0].strip()) + except IndexError: + self.error('Breakpoint number expected') + except ValueError as err: + self.error(err) + else: + bp.ignore = count + if count > 0: + if count > 1: + countstr = '%d crossings' % count + else: + countstr = '1 crossing' + self.message('Will ignore next %s of breakpoint %d.' % + (countstr, bp.number)) + else: + self.message('Will stop next time breakpoint %d is reached.' + % bp.number) + + def do_clear(self, arg): + """cl(ear) filename:lineno\ncl(ear) [bpnumber [bpnumber...]] + With a space separated list of breakpoint numbers, clear + those breakpoints. Without argument, clear all breaks (but + first ask confirmation). With a filename:lineno argument, + clear all breaks at that line in that file. + """ + if not arg: + try: + reply = input('Clear all breaks? ') + except EOFError: + reply = 'no' + reply = reply.strip().lower() + if reply in ('y', 'yes'): + bplist = [bp for bp in QtcInternalBreakpoint.bpbynumber if bp] + self.clear_all_breaks() + for bp in bplist: + self.message('Deleted %s' % bp) + return + if ':' in arg: + # Make sure it works for 'clear C:\foo\bar.py:12' + i = arg.rfind(':') + filename = arg[:i] + arg = arg[i + 1:] + try: + lineno = int(arg) + except ValueError: + err = 'Invalid line number (%s)' % arg + else: + bplist = self.get_breaks(filename, lineno) + err = self.clear_break(filename, lineno) + if err: + self.error(err) + else: + for bp in bplist: + self.message('Deleted %s' % bp) + return + numberlist = arg.split() + for i in numberlist: + try: + bp = self.get_bpbynumber(i) + except ValueError as err: + self.error(err) + else: + self.clear_bpbynumber(i) + self.message('Deleted %s' % bp) + + def do_until(self, arg): + """until [lineno] + Without argument, continue execution until the line with a + number greater than the current one is reached. With a line + number, continue execution until a line with a number greater + or equal to that is reached. In both cases, also stop when + the current frame returns. + """ + if arg: + try: + lineno = int(arg) + except ValueError: + self.error('Error in argument: %r' % arg) + return 0 + if lineno <= self.curframe.f_lineno: + self.error('"until" line number is smaller than current ' + 'line number') + return 0 + else: + lineno = None + lineno = self.curframe.f_lineno + 1 + self._set_stopinfo(self.curframe, self.curframe, lineno) + + return 1 + + def do_step(self, arg): + self.set_step() + return 1 + + def do_next(self, arg): + self._set_stopinfo(self.curframe, None) + return 1 + + def do_return(self, arg): + if self.curframe.f_code.co_flags & inspect.CO_GENERATOR: + self._set_stopinfo(self.curframe, None, -1) + else: + self._set_stopinfo(self.curframe.f_back, self.curframe) + return 1 + + def do_continue(self, arg): + """continue + Continue execution, only stop when a breakpoint is encountered. + """ + if not self.nosigint: + try: + self._previous_sigint_handler = \ + signal.signal(signal.SIGINT, self.sigint_handler) + except ValueError: + # ValueError happens when do_continue() is invoked from + # a non-main thread in which case we just continue without + # SIGINT set. Would printing a message here (once) make + # sense? + pass + self.set_continue() + return 1 + + def do_jump(self, arg): + """jump lineno + Set the next line that will be executed. Only available in + the bottom-most frame. This lets you jump back and execute + code again, or jump forward to skip code that you don't want + to run. + + It should be noted that not all jumps are allowed -- for + instance it is not possible to jump into the middle of a + for loop or out of a finally clause. + """ + if self.curindex + 1 != __builtins__.len(self.stack): + self.error('You can only jump within the bottom frame') + return + try: + arg = int(arg) + except ValueError: + self.error("The 'jump' command requires a line number") + else: + try: + # Do the jump, fix up our copy of the stack, and display the + # new position + self.curframe.f_lineno = arg + self.stack[self.curindex] = self.stack[self.curindex][0], arg + except ValueError as e: + self.error('Jump failed: %s' % e) + + def do_debug(self, arg): + """debug code + Enter a recursive debugger that steps through the code + argument (which is an arbitrary expression or statement to be + executed in the current environment). + """ + sys.settrace(None) + pyGlobals = self.curframe.f_globals + pyLocals = self.curframe_locals + p = QtcInternalDumper(self.stdin, self.stdout) + self.message('ENTERING RECURSIVE DEBUGGER') + sys.call_tracing(p.run, (arg, pyGlobals, pyLocals)) + self.message('LEAVING RECURSIVE DEBUGGER') + sys.settrace(self.trace_dispatch) + self.lastcmd = p.lastcmd + + def do_quit(self, arg): + """q(uit)\nexit + Quit from the debugger. The program being executed is aborted. + """ + self._user_requested_quit = True + self.set_quit() + return 1 + + def do_EOF(self, arg): + """EOF + Handles the receipt of EOF as a command. + """ + self.message('') + self._user_requested_quit = True + self.set_quit() + return 1 + + def do_args(self, arg): + """a(rgs) + Print the argument list of the current function. + """ + co = self.curframe.f_code + loc = self.curframe_locals + n = co.co_argcount + if co.co_flags & 4: + n = n + 1 + if co.co_flags & 8: + n = n + 1 + for i in range(n): + name = co.co_varnames[i] + if name in loc: + self.message('%s = %r' % (name, loc[name])) + else: + self.message('%s = *** undefined ***' % (name,)) + + def do_retval(self, arg): + """retval + Print the return value for the last return of a function. + """ + if '__return__' in self.curframe_locals: + self.message(repr(self.curframe_locals['__return__'])) + else: + self.error('Not yet returned!') + + def _getval(self, arg): + try: + return eval(arg, self.curframe.f_globals, self.curframe_locals) + except: + exc_info = sys.exc_info()[:2] + self.error(traceback.format_exception_only(*exc_info)[-1].strip()) + raise + + def _getval_except(self, arg, frame=None): + try: + if frame is None: + return eval(arg, self.curframe.f_globals, self.curframe_locals) + return eval(arg, frame.f_globals, frame.f_locals) + except: + exc_info = sys.exc_info()[:2] + err = traceback.format_exception_only(*exc_info)[-1].strip() + return _rstr('** raised %s **' % err) + + def do_whatis(self, arg): + """whatis arg + Print the type of the argument. + """ + try: + value = self._getval(arg) + except: + # _getval() already printed the error + return + code = None + # Is it a function? + try: + code = value.__code__ + except Exception: + pass + if code: + self.message('Function %s' % code.co_name) + return + # Is it an instance method? + try: + code = value.__func__.__code__ + except Exception: + pass + if code: + self.message('Method %s' % code.co_name) + return + # Is it a class? + if value.__class__ is type: + self.message('Class %s.%s' % (value.__module__, value.__name__)) + return + # None of the above... + self.message(__builtins__.type(value)) + + def do_interact(self, arg): + """interact + + Start an interactive interpreter whose global namespace + contains all the (global and local) names found in the current scope. + """ + ns = self.curframe.f_globals.copy() + ns.update(self.curframe_locals) + code.interact('*interactive*', local=ns) + + def lookupmodule(self, filename): + """Helper function for break/clear parsing -- may be overridden. + + lookupmodule() translates (possibly incomplete) file or module name + into an absolute file name. + """ + if os.path.isabs(filename) and os.path.exists(filename): + return filename + f = os.path.join(sys.path[0], filename) + if os.path.exists(f) and self.canonic(f) == self.mainpyfile: + return f + ext = os.path.splitext(filename)[1] + if ext == '': + filename = filename + '.py' + if os.path.isabs(filename): + return filename + for dirname in sys.path: + while os.path.islink(dirname): + dirname = os.readlink(dirname) + fullname = os.path.join(dirname, filename) + if os.path.exists(fullname): + return fullname + return None + + def evaluateTooltip(self, args): + self.updateData(args) + + def updateData(self, args): + self.expandedINames = args.get('expanded', {}) + self.typeformats = args.get('typeformats', {}) + self.formats = args.get('formats', {}) + self.output = '' + + frameNr = args.get('frame', 0) + if frameNr == -1: + frameNr = 0 + + frame_lineno = self.stack[-1 - frameNr] + frame = frame_lineno[0] + + self.output += 'data={' + for var in frame.f_locals.keys(): + if var in ('__file__', '__name__', '__package__', '__spec__', + '__doc__', '__loader__', '__cached__', '__the_dumper__', + '__annotations__', 'QtcInternalBreakpoint', 'QtcInternalDumper'): + continue + value = frame.f_locals[var] + # this applies only for anonymous arguments + # e.g. def dummy(var, (width, height), var2) would create an anonymous local var + # named '.1' for (width, height) as this is the second argument + if var.startswith('.'): + var = '@arg' + var[1:] + self.dumpValue(value, var, 'local.%s' % var) + + for watcher in args.get('watchers', []): + iname = watcher['iname'] + exp = self.hexdecode(watcher['exp']) + exp = __builtins__.str(exp).strip() + escapedExp = self.hexencode(exp) + self.put('{') + self.putField('iname', iname) + self.putField('wname', escapedExp) + try: + res = eval(exp, {}, frame.f_locals) + self.putValue(res) + except: + self.putValue('') + self.put('}') + # self.dumpValue(eval(value), escapedExp, iname) + + self.output += '}' + self.output += '{frame="%s"}' % frameNr + self.flushOutput() + + def flushOutput(self): + sys.stdout.write('@\n' + self.output + '@\n') + sys.stdout.flush() + self.output = '' + + def put(self, value): + # sys.stdout.write(value) + self.output += value + + def putField(self, name, value): + self.put('%s="%s",' % (name, value)) + + def putItemCount(self, count): + self.put('value="<%s items>",numchild="%s",' % (count, count)) + + @staticmethod + def cleanType(typename): + t = __builtins__.str(typename) + if t.startswith(""): + t = t[7:-2] + if t.startswith(""): + t = t[8:-2] + return t + + def putType(self, typeName, priority=0): + self.putField('type', QtcInternalDumper.cleanType(typeName)) + + def putNumChild(self, numchild): + self.put('numchild="%s",' % numchild) + + def putValue(self, value, encoding=None, priority=0): + self.putField('value', value) + + def putName(self, name): + self.put('name="%s",' % name) + + def isExpanded(self, iname): + # DumperBase.warn('IS EXPANDED: %s in %s' % (iname, self.expandedINames)) + if iname.startswith('None'): + raise "Illegal iname '%s'" % iname + # DumperBase.warn(' --> %s' % (iname in self.expandedINames)) + return iname in self.expandedINames + + def isExpandedIName(self, iname): + return iname in self.expandedINames + + def itemFormat(self, item): + form = self.formats.get(__builtins__.str(QtcInternalDumper.cleanAddress(item.value.address))) + if form is None: + form = self.typeformats.get(__builtins__.str(item.value.type)) + return form + + def dumpValue(self, value, name, iname): + t = __builtins__.type(value) + tt = QtcInternalDumper.cleanType(t) + valueStr = __builtins__.str(value) + if tt == 'module' or tt == 'function': + return + if valueStr.startswith("= 3: + vals = value.items() + else: + vals = value.iteritems() + for (k, v) in vals: + self.put('{') + self.putType(' ') + self.putValue('%s: %s' % (k, v)) + if self.isExpanded(iname): + self.put('children=[') + self.dumpValue(k, 'key', '%s.%d.k' % (iname, i)) + self.dumpValue(v, 'value', '%s.%d.v' % (iname, i)) + self.put(']') + self.put('},') + i += 1 + self.put(']') + elif tt == 'class': + pass + elif tt == 'module': + pass + elif tt == 'function': + pass + elif valueStr.startswith(' 1: + self.putValue('@' + v[p + 11:-1]) + self.putType(v[1:p]) + else: + p = v.find(' instance at ') + if p > 1: + self.putValue('@' + v[p + 13:-1]) + self.putType(v[1:p]) + else: + self.putType(tt) + self.putValue(v) + if self.isExpanded(iname): + self.put('children=[') + for child in dir(value): + if child in ('__dict__', '__doc__', '__module__'): + continue + attr = getattr(value, child) + if callable(attr): + continue + try: + self.dumpValue(attr, child, '%s.%s' % (iname, child)) + except: + pass + self.put('],') + self.put('},') + + def warn(self, msg): + self.putField('warning', msg) + + def listModules(self, args): + self.put('modules=[') + for name in sys.modules: + self.put('{') + self.putName(name) + self.putValue(sys.modules[name]) + self.put('},') + self.put(']') + self.flushOutput() + + def listSymbols(self, args): + moduleName = args['module'] + module = sys.modules.get(moduleName, None) + self.put("symbols={module='%s',symbols='%s'}" + % (module, dir(module) if module else [])) + self.flushOutput() + + def assignValue(self, args): + exp = args['expression'] + value = args['value'] + cmd = '%s=%s' % (exp, value) + eval(cmd, {}) + self.put('CMD: "%s"' % cmd) + self.flushOutput() + + def stackListFrames(self, args): + # result = 'stack={current-thread="%s"' % thread.GetThreadID() + result = 'stack={current-thread="%s"' % 1 + + result += ',frames=[' + try: + level = 0 + frames = __builtins__.list(reversed(self.stack)) + frames = frames[:-2] # Drop "pdbbridge" and "" levels + for frame_lineno in frames: + frame, lineno = frame_lineno + filename = self.canonic(frame.f_code.co_filename) + level += 1 + result += '{' + result += 'file="%s",' % filename + result += 'line="%s",' % lineno + result += 'level="%s",' % level + result += '}' + except KeyboardInterrupt: + pass + result += ']' + + # result += ',hasmore="%d"' % isLimited + # result += ',limit="%d"' % limit + result += '}' + self.report(result) + + @staticmethod + def report(stuff): + sys.stdout.write('@\n' + stuff + '@\n') + sys.stdout.flush() + + +def qdebug(cmd, args): + global __the_dumper__ + method = getattr(__the_dumper__, cmd) + method(args) + + +__the_dumper__ = QtcInternalDumper() +__the_dumper__.runit() diff --git a/share/qtcreator/debugger/python2/personaltypes.py b/share/qtcreator/debugger/python2/personaltypes.py new file mode 100644 index 00000000000..291922b8fa2 --- /dev/null +++ b/share/qtcreator/debugger/python2/personaltypes.py @@ -0,0 +1,40 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +# This is a place to add your own dumpers for testing purposes. +# Any contents here will be picked up by GDB, LLDB, and CDB based +# debugging in Qt Creator automatically. + +# NOTE: This file will get overwritten when updating Qt Creator. +# +# To add dumpers that don't get overwritten, copy this file here +# to a safe location outside the Qt Creator installation and +# make this location known to Qt Creator using the Debugger > +# Locals & Expressions > Extra Debugging Helpers setting. + +# Example to display a simple type +# template struct MapNode +# { +# U key; +# V data; +# } +# +# def qdump__MapNode(d, value): +# d.putValue("This is the value column contents") +# d.putExpandable() +# if d.isExpanded(): +# with Children(d): +# # Compact simple case. +# d.putSubItem("key", value["key"]) +# # Same effect, with more customization possibilities. +# with SubItem(d, "data") +# d.putItem("data", value["data"]) + +# Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html +# for more details or look at qttypes.py, stdtypes.py, boosttypes.py +# for more complex examples. + +from dumper import Children, SubItem, UnnamedSubItem, DumperBase +from utils import DisplayFormat, TypeCode + +######################## Your code below ####################### diff --git a/share/qtcreator/debugger/python2/qttypes.py b/share/qtcreator/debugger/python2/qttypes.py new file mode 100644 index 00000000000..b08f60a9bb3 --- /dev/null +++ b/share/qtcreator/debugger/python2/qttypes.py @@ -0,0 +1,3674 @@ +# 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 platform +import struct +import re +from dumper import Children, SubItem, UnnamedSubItem, toInteger, DumperBase +from utils import DisplayFormat, TypeCode + + +def qdump__QAtomicInt(d, value): + d.putValue(value.integer()) + + +def qdump__QBasicAtomicInt(d, value): + d.putValue(value.integer()) + + +def qdump__QAtomicPointer(d, value): + d.putItem(value.cast(value.type[0].pointer())) + d.putBetterType(value.type) + + +def qform__QByteArray(): + return [DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String, + DisplayFormat.Utf8String, DisplayFormat.SeparateUtf8String] + + +def qedit__QByteArray(d, value, data): + d.call('void', value, 'resize', str(len(data))) + (base, size, alloc) = d.stringData(value) + d.setValues(base, 'char', [ord(c) for c in data]) + + +def qdump__QByteArray(d, value): + if d.qtVersion() >= 0x60000: + dd, data, size = value.split('ppi') + if dd: + _, _, alloc = d.split('iii', dd) + else: # fromRawData + alloc = size + else: + data, size, alloc = d.qArrayData(value) + + d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000)) + if size > 0: + d.putExpandable() + + elided, shown = d.computeLimit(size, d.displayStringLimit) + p = d.readMemory(data, shown) + + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Automatic or displayFormat == DisplayFormat.Latin1String: + d.putValue(p, 'latin1', elided=elided) + elif displayFormat == DisplayFormat.SeparateLatin1String: + d.putValue(p, 'latin1', elided=elided) + d.putDisplay('latin1:separate', d.encodeByteArray(value, limit=100000)) + elif displayFormat == DisplayFormat.Utf8String: + d.putValue(p, 'utf8', elided=elided) + elif displayFormat == DisplayFormat.SeparateUtf8String: + d.putValue(p, 'utf8', elided=elided) + d.putDisplay('utf8:separate', d.encodeByteArray(value, limit=100000)) + if d.isExpanded(): + d.putArrayData(data, size, d.charType()) + + +#def qdump__QArrayData(d, value): +# data, size, alloc = d.qArrayDataHelper(value.address()) +# d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000)) +# d.putValue(d.readMemory(data, size), 'latin1') +# d.putPlainChildren(value) + + +#def qdump__QByteArrayData(d, value): +# qdump__QArrayData(d, value) + + +def qdump__QBitArray(d, value): + if d.qtVersion() >= 0x60000: + _, data, basize = value.split('ppi') + else: + data, basize, _ = d.qArrayData(value['d']) + unused = d.extractByte(data) if data else 0 + size = basize * 8 - unused + d.putItemCount(size) + if d.isExpanded(): + with Children(d, size, maxNumChild=10000): + for i in d.childRange(): + q = data + 1 + int(i / 8) + with SubItem(d, i): + d.putValue((int(d.extractPointer(q)) >> (i % 8)) & 1) + d.putType('bool') + + +def qdump__QChar(d, value): + d.putValue(d.extractUShort(value)) + + +def qform_X_QAbstractItemModel(): + return [DisplayFormat.Simple, DisplayFormat.Enhanced] + + +def qdump_X_QAbstractItemModel(d, value): + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Simple: + d.putPlainChildren(value) + return + #displayFormat == Enhanced: + # Create a default-constructed QModelIndex on the stack. + try: + ri = d.pokeValue(d.qtNamespace() + 'QModelIndex', '-1, -1, 0, 0') + this_ = d.makeExpression(value) + ri_ = d.makeExpression(ri) + rowCount = int(d.parseAndEvaluate('%s.rowCount(%s)' % (this_, ri_))) + columnCount = int(d.parseAndEvaluate('%s.columnCount(%s)' % (this_, ri_))) + except: + d.putPlainChildren(value) + return + d.putValue('%d x %d' % (rowCount, columnCount)) + d.putNumChild(rowCount * columnCount) + if d.isExpanded(): + with Children(d, numChild=rowCount * columnCount, childType=ri.type): + i = 0 + for row in range(rowCount): + for column in range(columnCount): + with SubItem(d, i): + d.putName('[%s, %s]' % (row, column)) + mi = d.parseAndEvaluate('%s.index(%d,%d,%s)' + % (this_, row, column, ri_)) + d.putItem(mi) + i = i + 1 + #gdb.execute('call free($ri)') + + +def qform_X_QModelIndex(): + return [DisplayFormat.Simple, DisplayFormat.Enhanced] + + +def qdump_X_QModelIndex(d, value): + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Simple: + d.putPlainChildren(value) + return + r = value['r'] + c = value['c'] + try: + p = value['p'] + except: + p = value['i'] + m = value['m'] + if m.pointer() == 0 or r < 0 or c < 0: + d.putValue('(invalid)') + d.putPlainChildren(value) + return + + mm = m.dereference() + mm = mm.cast(mm.type.unqualified()) + ns = d.qtNamespace() + try: + mi = d.pokeValue(ns + 'QModelIndex', '%s,%s,%s,%s' % (r, c, p, m)) + mm_ = d.makeExpression(mm) + mi_ = d.makeExpression(mi) + rowCount = int(d.parseAndEvaluate('%s.rowCount(%s)' % (mm_, mi_))) + columnCount = int(d.parseAndEvaluate('%s.columnCount(%s)' % (mm_, mi_))) + except: + d.putPlainChildren(value) + return + + try: + # Access DisplayRole as value + val = d.parseAndEvaluate('%s.data(%s, 0)' % (mm_, mi_)) + v = val['d']['data']['ptr'] + d.putStringValue(d.pokeValue(ns + 'QString', v)) + except: + d.putValue('') + + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putFields(value, False) + i = 0 + for row in range(rowCount): + for column in range(columnCount): + with UnnamedSubItem(d, i): + d.putName('[%s, %s]' % (row, column)) + mi2 = d.parseAndEvaluate('%s.index(%d,%d,%s)' + % (mm_, row, column, mi_)) + d.putItem(mi2) + i = i + 1 + d.putCallItem('parent', '@QModelIndex', value, 'parent') + #gdb.execute('call free($mi)') + + +def qdump__Qt__ItemDataRole(d, value): + d.putEnumValue(value.integer(), { + 0: "Qt::DisplayRole", + 1: "Qt::DecorationRole", + 2: "Qt::EditRole", + 3: "Qt::ToolTipRole", + 4: "Qt::StatusTipRole", + 5: "Qt::WhatsThisRole", + 6: "Qt::FontRole", + 7: "Qt::TextAlignmentRole", + # obsolete: 8 : "Qt::BackgroundColorRole", + 8: "Qt::BackgroundRole", + # obsolete: 9 : "Qt::TextColorRole", + 9: "Qt::ForegroundRole", + 10: "Qt::CheckStateRole", + 11: "Qt::AccessibleTextRole", + 12: "Qt::AccessibleDescriptionRole", + 13: "Qt::SizeHintRole", + 14: "Qt::InitialSortOrderRole", + # 27-31 Qt4 ItemDataRoles + 27: "Qt::DisplayPropertyRole", + 28: "Qt::DecorationPropertyRole", + 29: "Qt::ToolTipPropertyRole", + 30: "Qt::StatusTipPropertyRole", + 31: "Qt::WhatsThisPropertyRole", + 0x100: "Qt::UserRole" + }) + + +def qdump__QStandardItemData(d, value): + d.createType('@Qt::ItemDataRole') # warm up cache + role, pad, val = value.split('{@Qt::ItemDataRole}@{@QVariant}') + d.putPairContents(role.value(), (role, val), 'role', 'value') + + +def qdump__QStandardItem(d, value): + d.createType('@QStandardItemData') # warm up cache + d.createType('@QStandardItem') + d.createType('@QStandardItem*') + + vtable, dptr = value.split('pp') + if d.qtVersion() >= 0x060000: + model, parent, values, children, rows, cols, item = \ + d.split('pp{@QList<@QStandardItemData>}{@QList<@QStandardItem *>}IIp', dptr) + else: + # There used to be a virtual destructor that got removed in + # 88b6abcebf29b455438 on Apr 18 17:01:22 2017 + if d.qtVersion() < 0x050900 and not d.isMsvcTarget(): + dptr += d.ptrSize(); + model, parent, values, children, rows, cols, item = \ + d.split('pp{@QVector<@QStandardItemData>}{@QVector<@QStandardItem *>}IIp', dptr) + + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putSubItem('[model]', d.createValue(model, '@QStandardItemModel')) + d.putSubItem('[values]', values) + d.putSubItem('[children]', children) + + +def qdump__QDate(d, value): + jd = value.pointer() + if not jd: + d.putValue('(invalid)') + return + d.putValue(jd, 'juliandate') + d.putExpandable() + if d.isExpanded(): + with Children(d): + if d.canCallLocale(): + d.putCallItem('toString', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'TextDate')) + d.putCallItem('(ISO)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'ISODate')) + if d.qtVersion() < 0x060000: + d.putCallItem('(SystemLocale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'SystemLocaleDate')) + d.putCallItem('(Locale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'LocaleDate')) + d.putFields(value) + + +def qdump__QTime(d, value): + mds = value.split('i')[0] + if mds == -1: + d.putValue('(invalid)') + return + d.putValue(mds, 'millisecondssincemidnight') + if d.isExpanded(): + with Children(d): + d.putCallItem('toString', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'TextDate')) + d.putCallItem('(ISO)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'ISODate')) + if d.canCallLocale() and d.qtVersion() < 0x060000: + d.putCallItem('(SystemLocale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'SystemLocaleDate')) + d.putCallItem('(Locale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'LocaleDate')) + d.putFields(value) + + +def qdump__QTimeZone(d, value): + base = d.extractPointer(value) + if base == 0: + d.putValue('(null)') + return + idAddr = base + 2 * d.ptrSize() # [QSharedData] + [vptr] + d.putByteArrayValue(idAddr) + d.putPlainChildren(value['d']) + + +def qdump__QDateTime(d, value): + qtVersion = d.qtVersion() + isValid = False + # This relies on the Qt4/Qt5 internal structure layout: + # {sharedref(4), ... + base = d.extractPointer(value) + is32bit = d.ptrSize() == 4 + if qtVersion >= 0x050200: + tiVersion = d.qtTypeInfoVersion() + #DumperBase.warn('TI VERSION: %s' % tiVersion) + if tiVersion is None: + tiVersion = 4 + if tiVersion > 10: + status = d.extractByte(value) + #DumperBase.warn('STATUS: %s' % status) + if status & 0x01: + # Short data + msecs = d.extractUInt64(value) >> 8 + spec = (status & 0x30) >> 4 + offsetFromUtc = 0 + timeZone = 0 + isValid = status & 0x08 + else: + dptr = d.extractPointer(value) + (_, status, msecs, offsetFromUtc, _, timeZone) = d.split('iIqII{QTimeZone}', dptr) + spec = (status & 0x30) >> 4 + isValid = True + tzD = d.extractPointer(timeZone) + if tzD == 0: + timeZone = 'UTC' + else: + idAddr = tzD + 2 * d.ptrSize() + tzBa = d.encodeByteArray(idAddr, limit=100) + timeZone = tzBa + d.putValue( + '%s/%s/%s/%s/%s/%s' % + (msecs, + spec, + offsetFromUtc, + timeZone, + status, + tiVersion), + 'datetimeinternal') + else: + if d.isWindowsTarget(): + msecsOffset = 8 + specOffset = 16 + offsetFromUtcOffset = 20 + timeZoneOffset = 24 + statusOffset = 28 if is32bit else 32 + else: + msecsOffset = 4 if is32bit else 8 + specOffset = 12 if is32bit else 16 + offsetFromUtcOffset = 16 if is32bit else 20 + timeZoneOffset = 20 if is32bit else 24 + statusOffset = 24 if is32bit else 32 + status = d.extractInt(base + statusOffset) + if int(status & 0x0c == 0x0c): # ValidDate and ValidTime + isValid = True + msecs = d.extractInt64(base + msecsOffset) + spec = d.extractInt(base + specOffset) + offset = d.extractInt(base + offsetFromUtcOffset) + tzp = d.extractPointer(base + timeZoneOffset) + if tzp == 0: + tz = '' + else: + idBase = tzp + 2 * d.ptrSize() # [QSharedData] + [vptr] + tz = d.encodeByteArray(idBase, limit=100) + d.putValue('%s/%s/%s/%s/%s/%s' % (msecs, spec, offset, tz, status, 0), + 'datetimeinternal') + else: + # This relies on the Qt4/Qt5 internal structure layout: + # {sharedref(4), date(8), time(4+x)} + # QDateTimePrivate: + # - QAtomicInt ref; (padded on 64 bit) + # - [QDate date;] + # - - uint jd in Qt 4, qint64 in Qt 5.0 and Qt 5.1; padded on 64 bit + # - [QTime time;] + # - - uint mds; + # - Spec spec; + dateSize = 8 if qtVersion >= 0x050000 else 4 # Qt5: qint64, Qt4 uint + # 4 byte padding after 4 byte QAtomicInt if we are on 64 bit and QDate is 64 bit + refPlusPadding = 8 if qtVersion >= 0x050000 and d.ptrSize() == 8 else 4 + dateBase = base + refPlusPadding + timeBase = dateBase + dateSize + mds = d.extractInt(timeBase) + isValid = mds > 0 + if isValid: + jd = d.extractInt(dateBase) + d.putValue('%s/%s' % (jd, mds), 'juliandateandmillisecondssincemidnight') + + if not isValid: + d.putValue('(invalid)') + return + + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putCallItem('toTime_t', 'unsigned int', value, 'toTime_t') + if d.canCallLocale(): + d.putCallItem('toString', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'TextDate')) + d.putCallItem('(ISO)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'ISODate')) + d.putCallItem('toUTC', '@QDateTime', value, 'toTimeSpec', + d.enumExpression('TimeSpec', 'UTC')) + if d.qtVersion() < 0x060000: + d.putCallItem('(SystemLocale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'SystemLocaleDate')) + d.putCallItem('(Locale)', '@QString', value, 'toString', + d.enumExpression('DateFormat', 'LocaleDate')) + d.putCallItem('toLocalTime', '@QDateTime', value, 'toTimeSpec', + d.enumExpression('TimeSpec', 'LocalTime')) + d.putFields(value) + + +def qdump__QDir(d, value): + d.putExpandable() + privAddress = d.extractPointer(value) + bit32 = d.ptrSize() == 4 + + # change fc3942114da adds FileCache + # QStringList nameFilters; + # QDir::SortFlags sort; + # QDir::Filters filters; + # std::unique_ptr fileEngine; + # QFileSystemEntry dirEntry; + # struct FileCache + # { + # QMutex mutex; + # QStringList files; + # QFileInfoList fileInfos; + # std::atomic fileListsInitialized = false; + # QFileSystemEntry absoluteDirEntry; + # QFileSystemMetaData metaData; + # }; + # mutable FileCache fileCache; + + + # Change 9fc0965 reorders members again. + # bool fileListsInitialized + # QStringList files + # QFileInfoList fileInfos + # QStringList nameFilters + # QDir::SortFlags sort + # QDir::Filters filters + + # Before 9fc0965: + # QDirPrivate: + # QAtomicInt ref + # QStringList nameFilters; + # QDir::SortFlags sort; + # QDir::Filters filters; + # // qt3support: + # QChar filterSepChar; + # bool matchAllDirs; + # // end qt3support + # QScopedPointer fileEngine; + # bool fileListsInitialized; + # QStringList files; + # QFileInfoList fileInfos; + # QFileSystemEntry dirEntry; + # QFileSystemEntry absoluteDirEntry; + + # QFileSystemEntry: + # QString m_filePath + # QByteArray m_nativeFilePath + # qint16 m_lastSeparator + # qint16 m_firstDotInFileName + # qint16 m_lastDotInFileName + # + 2 byte padding + fileSystemEntrySize = 2 * d.ptrSize() + 8 + + if d.qtVersion() >= 0x060600: + case = 3 + elif d.qtVersion() >= 0x060000: + case = 2 + elif d.qtVersion() >= 0x050300: + case = 1 + elif d.qtVersion() < 0x050200: + case = 0 + else: + # Try to distinguish bool vs QStringList at the first item + # after the (padded) refcount. If it looks like a bool assume + # this is after 9fc0965. This is not safe. + firstValue = d.extractInt(privAddress + d.ptrSize()) + case = 1 if firstValue == 0 or firstValue == 1 else 0 + + if case == 3: + if bit32: + dirEntryOffset = 24 + fileCacheOffset = 52 + filesOffset = fileCacheOffset + 4 + fileInfosOffset = fileCacheOffset + 16 + absoluteDirEntryOffset = fileCacheOffset + 32 + else: + dirEntryOffset = 48 + fileCacheOffset = 104 + filesOffset = fileCacheOffset + 8 + fileInfosOffset = fileCacheOffset + 32 + absoluteDirEntryOffset = fileCacheOffset + 64 + elif case == 2: + if bit32: + filesOffset = 4 + fileInfosOffset = 16 + dirEntryOffset = 40 + absoluteDirEntryOffset = 72 + else: + filesOffset = 8 + fileInfosOffset = 32 + dirEntryOffset = 96 + absoluteDirEntryOffset = 152 + elif case == 1: + if bit32: + filesOffset = 4 + fileInfosOffset = 8 + dirEntryOffset = 32 + absoluteDirEntryOffset = 48 + else: + filesOffset = 8 + fileInfosOffset = 16 + dirEntryOffset = 48 + absoluteDirEntryOffset = 72 + else: + # Assume this is before 9fc0965. + qt3support = d.isQt3Support() + qt3SupportAddition = d.ptrSize() if qt3support else 0 + filesOffset = (24 if bit32 else 40) + qt3SupportAddition + fileInfosOffset = filesOffset + d.ptrSize() + dirEntryOffset = fileInfosOffset + d.ptrSize() + absoluteDirEntryOffset = dirEntryOffset + fileSystemEntrySize + + d.putStringValue(privAddress + dirEntryOffset) + if d.isExpanded(): + with Children(d): + if not d.isMsvcTarget(): + ns = d.qtNamespace() + try: + d.call('int', value, 'count') # Fill cache. + except: + pass + + #d.putCallItem('absolutePath', '@QString', value, 'absolutePath') + #d.putCallItem('canonicalPath', '@QString', value, 'canonicalPath') + with SubItem(d, 'absolutePath'): + typ = d.lookupType(ns + 'QString') + d.putItem(d.createValue(privAddress + absoluteDirEntryOffset, typ)) + with SubItem(d, 'entryInfoList'): + typ = d.lookupType(ns + 'QFileInfo') + qdumpHelper_QList(d, privAddress + fileInfosOffset, typ) + with SubItem(d, 'entryList'): + typ = d.lookupType(ns + 'QStringList') + d.putItem(d.createValue(privAddress + filesOffset, typ)) + d.putFields(value) + + +def qdump__QEvent(d, value): + d.putExpandable() + if d.isExpanded(): + with Children(d): + # Add a sub-item with the event type. + with SubItem(d, '[type]'): + (vtable, privateD, t, flags) = value.split("pp{short}{short}") + event_type_name = d.qtNamespace() + "QEvent::Type" + type_value = t.cast(event_type_name) + d.putValue(type_value.displayEnum('0x%04x', bitsize=16)) + d.putType(event_type_name) + + # Show the rest of the class fields as usual. + d.putFields(value) + + +def qdump__QKeyEvent(d, value): + # QEvent fields + # virtual table pointer + # QEventPrivate *d; + # ushort t; + # ushort posted : 1; + # ushort spont : 1; + # ushort m_accept : 1; + # ushort reserved : 13; + # QInputEvent fields + # Qt::KeyboardModifiers modState; + # ulong ts; + # QKeyEvent fields + # QString txt; + # int k; + # quint32 nScanCode; + # quint32 nVirtualKey; + # quint32 nModifiers; <- nativeModifiers + # ushort c; + # ushort autor:1; + # ushort reserved:15; + (vtable, privateD, t, flags, modState, ts, txt, k, scanCode, + virtualKey, modifiers, + c, autor) = value.split("ppHHiQ{@QString}{int}IIIHH") + + #d.putStringValue(txt) + #data = d.encodeString(txt) + key_txt_utf8 = d.encodeStringUtf8(txt) + + k_type_name = d.qtNamespace() + "Qt::Key" + k_cast_to_enum_value = k.cast(k_type_name) + k_name = k_cast_to_enum_value.displayEnum(bitsize=32) + matches = re.search(r'Key_(\w+)', k_name) + if matches: + k_name = matches.group(1) + + if t == 6: + key_event_type = "Pressed" + elif t == 7: + key_event_type = "Released" + else: + key_event_type = "" + + data = "" + + if key_event_type: + data += "{} ".format(key_event_type) + + # Try to use the name of the enum value, otherwise the value + # of txt in QKeyEvent. + if k_name: + data += "'{}'".format(k_name) + elif key_txt_utf8: + data += "'{}'".format(key_txt_utf8) + else: + data += "" + + k_int = k.integer() + data += " (key:{} vKey:{}".format(k_int, virtualKey) + + modifier_list = [] + modifier_list.append(("Shift", 0x02000000)) + modifier_list.append(("Control", 0x04000000)) + modifier_list.append(("Alt", 0x08000000)) + modifier_list.append(("Meta", 0x10000000)) + # modifier_map.append(("KeyPad", 0x20000000)) Is this useful? + modifier_list.append(("Grp", 0x40000000)) + + modifiers = [] + for modifier_name, mask in modifier_list: + if modState & mask: + modifiers.append(modifier_name) + + if modifiers: + data += " mods:" + "+".join(modifiers) + + data += ")" + + d.putValue(d.hexencode(data), 'utf8') + + d.putExpandable() + if d.isExpanded(): + with Children(d): + # Add a sub-item with the enum name and value. + with SubItem(d, '[{}]'.format(k_type_name)): + k_cast_to_enum_value = k.cast(k_type_name) + d.putValue(k_cast_to_enum_value.displayEnum('0x%04x', bitsize=32)) + d.putType(k_type_name) + + # Show the rest of the class fields as usual. + d.putFields(value, dumpBase=True) + + +def qdump__QKeySequence(d, value): + dd = d.extractPointer(value) + _, k0, k1, k2, k3 = d.split('iiiii', dd) + d.putValue("(0x%x, 0x%x, 0x%x, 0x%x)" % (k0, k1, k2, k3)); + d.putPlainChildren(value) + + +def qdump__QFile(d, value): + # 9fc0965 and a373ffcd change the layout of the private structure + qtVersion = d.qtVersion() + is32bit = d.ptrSize() == 4 + # FIXME: values 0 are wrong. As the file name is the + # only direct member of QFilePrivate, the offsets are + # equal to sizeof(QFileDevicePrivate), the base class. + if qtVersion >= 0x060300 and d.qtTypeInfoVersion() >= 22: + if d.isWindowsTarget(): + if d.isMsvcTarget(): + offset = 0 if is32bit else 424 + else: + offset = 0 if is32bit else 424 + else: + offset = 300 if is32bit else 424 + elif qtVersion >= 0x060000 and d.qtTypeInfoVersion() >= 20: + if d.isWindowsTarget(): + if d.isMsvcTarget(): + offset = 0 if is32bit else 304 + else: + offset = 0 if is32bit else 304 + else: + offset = 196 if is32bit else 304 + elif qtVersion >= 0x050600 and d.qtTypeInfoVersion() >= 17: + # Some QRingBuffer member got removed in 8f92baf5c9 + if d.isWindowsTarget(): + if d.isMsvcTarget(): + offset = 164 if is32bit else 224 + else: + offset = 160 if is32bit else 224 + else: + offset = 156 if is32bit else 224 + elif qtVersion >= 0x050700: + if d.isWindowsTarget(): + if d.isMsvcTarget(): + offset = 176 if is32bit else 248 + else: + offset = 172 if is32bit else 248 + else: + offset = 168 if is32bit else 248 + elif qtVersion >= 0x050600: + if d.isWindowsTarget(): + if d.isMsvcTarget(): + offset = 184 if is32bit else 248 + else: + offset = 180 if is32bit else 248 + else: + offset = 168 if is32bit else 248 + elif qtVersion >= 0x050500: + if d.isWindowsTarget(): + offset = 164 if is32bit else 248 + else: + offset = 164 if is32bit else 248 + elif qtVersion >= 0x050400: + if d.isWindowsTarget(): + offset = 188 if is32bit else 272 + else: + offset = 180 if is32bit else 272 + elif qtVersion > 0x050200: + if d.isWindowsTarget(): + offset = 180 if is32bit else 272 + else: + offset = 176 if is32bit else 272 + elif qtVersion >= 0x050000: + offset = 176 if is32bit else 280 + else: + if d.isWindowsTarget(): + offset = 144 if is32bit else 232 + else: + offset = 140 if is32bit else 232 + vtable, privAddress = value.split('pp') + fileNameAddress = privAddress + offset + d.putStringValue(fileNameAddress) + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putCallItem('exists', 'bool', value, 'exists') + d.putFields(value) + + +def qdump__QFileInfo(d, value): + privAddress = d.extractPointer(value) + #bit32 = d.ptrSize() == 4 + #qt5 = d.qtVersion() >= 0x050000 + #try: + # d.putStringValue(value['d_ptr']['d'].dereference()['fileNames'][3]) + #except: + # d.putPlainChildren(value) + # return + filePathAddress = privAddress + d.ptrSize() + d.putStringValue(filePathAddress) + d.putExpandable() + if d.isExpanded(): + ns = d.qtNamespace() + with Children(d): + stype = '@QString' + d.putCallItem('absolutePath', stype, value, 'absolutePath') + d.putCallItem('absoluteFilePath', stype, value, 'absoluteFilePath') + d.putCallItem('canonicalPath', stype, value, 'canonicalPath') + d.putCallItem('canonicalFilePath', stype, value, 'canonicalFilePath') + d.putCallItem('completeBaseName', stype, value, 'completeBaseName') + d.putCallItem('completeSuffix', stype, value, 'completeSuffix') + d.putCallItem('baseName', stype, value, 'baseName') + if platform.system() == 'Darwin': + d.putCallItem('isBundle', stype, value, 'isBundle') + d.putCallItem('bundleName', stype, value, 'bundleName') + d.putCallItem('fileName', stype, value, 'fileName') + d.putCallItem('filePath', stype, value, 'filePath') + # Crashes gdb (archer-tromey-python, at dad6b53fe) + #d.putCallItem('group', value, 'group') + #d.putCallItem('owner', value, 'owner') + d.putCallItem('path', stype, value, 'path') + + d.putCallItem('groupid', 'unsigned int', value, 'groupId') + d.putCallItem('ownerid', 'unsigned int', value, 'ownerId') + + #QFile::Permissions permissions () const + try: + perms = d.call('int', value, 'permissions') + except: + perms = None + + if perms is None: + with SubItem(d, 'permissions'): + d.putSpecialValue('notcallable') + d.putType(ns + 'QFile::Permissions') + else: + with SubItem(d, 'permissions'): + d.putEmptyValue() + d.putType(ns + 'QFile::Permissions') + d.putExpandable() + if d.isExpanded(): + with Children(d, 10): + perms = perms['i'] + d.putBoolItem('ReadOwner', perms & 0x4000) + d.putBoolItem('WriteOwner', perms & 0x2000) + d.putBoolItem('ExeOwner', perms & 0x1000) + d.putBoolItem('ReadUser', perms & 0x0400) + d.putBoolItem('WriteUser', perms & 0x0200) + d.putBoolItem('ExeUser', perms & 0x0100) + d.putBoolItem('ReadGroup', perms & 0x0040) + d.putBoolItem('WriteGroup', perms & 0x0020) + d.putBoolItem('ExeGroup', perms & 0x0010) + d.putBoolItem('ReadOther', perms & 0x0004) + d.putBoolItem('WriteOther', perms & 0x0002) + d.putBoolItem('ExeOther', perms & 0x0001) + + #QDir absoluteDir () const + #QDir dir () const + d.putCallItem('caching', 'bool', value, 'caching') + d.putCallItem('exists', 'bool', value, 'exists') + d.putCallItem('isAbsolute', 'bool', value, 'isAbsolute') + d.putCallItem('isDir', 'bool', value, 'isDir') + d.putCallItem('isExecutable', 'bool', value, 'isExecutable') + d.putCallItem('isFile', 'bool', value, 'isFile') + d.putCallItem('isHidden', 'bool', value, 'isHidden') + d.putCallItem('isReadable', 'bool', value, 'isReadable') + d.putCallItem('isRelative', 'bool', value, 'isRelative') + d.putCallItem('isRoot', 'bool', value, 'isRoot') + d.putCallItem('isSymLink', 'bool', value, 'isSymLink') + d.putCallItem('isWritable', 'bool', value, 'isWritable') + d.putCallItem('created', 'bool', value, 'created') + d.putCallItem('lastModified', 'bool', value, 'lastModified') + d.putCallItem('lastRead', 'bool', value, 'lastRead') + d.putFields(value) + + +def qdump__QFixed(d, value): + v = value.split('i')[0] + d.putValue('%s/64 = %s' % (v, v / 64.0)) + + +def qform__QFiniteStack(): + return [DisplayFormat.ArrayPlot] + + +def qdump__QFiniteStack(d, value): + array, alloc, size = value.split('pii') + d.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000) + d.putItemCount(size) + d.putPlotData(array, size, value.type[0]) + + +def qdump__QFlags(d, value): + i = value.split('{int}')[0] + enumType = value.type[0] + v = i.cast(enumType.name) + d.putValue(v.displayEnum('0x%04x', bitsize=32)) + + +def qform__QHash(): + return [DisplayFormat.CompactMap] + + +def qdump__QHash(d, value): + qdumpHelper_QHash(d, value, value.type[0], value.type[1]) + + +def qdump__QVariantHash(d, value): + qdumpHelper_QHash(d, value, d.createType('@QString'), d.createType('@QVariant')) + + +def qdumpHelper_QHash(d, value, keyType, valueType): + if d.qtVersion() >= 0x60000: + qdumpHelper_QHash_6(d, value, keyType, valueType) + else: + qdumpHelper_QHash_5(d, value, keyType, valueType) + +def qdumpHelper_QHash_5(d, value, keyType, valueType): + def hashDataFirstNode(): + b = buckets + n = numBuckets + while n: + n -= 1 + bb = d.extractPointer(b) + if bb != dptr: + return bb + b += ptrSize + return dptr + + def hashDataNextNode(node): + (nextp, h) = d.split('pI', node) + if d.extractPointer(nextp): + return nextp + start = (h % numBuckets) + 1 + b = buckets + start * ptrSize + n = numBuckets - start + while n: + n -= 1 + bb = d.extractPointer(b) + if bb != nextp: + return bb + b += ptrSize + return nextp + + ptrSize = d.ptrSize() + dptr = d.extractPointer(value) + (fakeNext, buckets, ref, size, nodeSize, userNumBits, numBits, numBuckets) = \ + d.split('ppiiihhi', dptr) + + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + + d.putItemCount(size) + if d.isExpanded(): + isShort = d.qtVersion() < 0x050000 and keyType.name == 'int' + with Children(d, size): + node = hashDataFirstNode() + for i in d.childRange(): + if isShort: + typeCode = 'P{%s}@{%s}' % (keyType.name, valueType.name) + (pnext, key, padding2, val) = d.split(typeCode, node) + else: + typeCode = 'Pi@{%s}@{%s}' % (keyType.name, valueType.name) + (pnext, hashval, padding1, key, padding2, val) = d.split(typeCode, node) + d.putPairItem(i, (key, val), 'key', 'value') + node = hashDataNextNode(node) + + +def qdumpHelper_QHash_6(d, value, keyType, valueType): + dptr = d.extractPointer(value) + if dptr == 0: + d.putItemCount(0) + return + + ref, _, size, buckets, seed, spans = d.split('i@qqqp', dptr) + + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + #d.putValue("%d 0x%x 0x%x 0x%x" % (ref, size, buckets, seed)); + d.putItemCount(size) + + if d.isExpanded(): + type_code = '{%s}@{%s}' % (keyType.name, valueType.name) + _, entry_size, _ = d.describeStruct(type_code) + with Children(d, size): + span_size = 128 + 2 * d.ptrSize() # Including tail padding. + nspans = int((buckets + 127) / 128) + count = 0 + for b in range(nspans): + span = spans + b * span_size + offsets, entries, allocated, next_free = d.split('128spbb', span) + #with SubItem(d, 'span %d' % b): + # d.putValue('span: 0x%x %s alloc: %s next: %s' + # % (span, d.hexencode(offsets), allocated, next_free)) + entry_pos = 0 + for i in range(128): + offset = offsets[i] + #with SubItem(d, 'offset %i' % i): + # d.putValue('i: %s off: %s' % (i, offset)) + if offset != 255: # Entry is used + entry = entries + offset * entry_size + key, _, val = d.split(type_code, entry) + #with SubItem(d, 'count %d entry %d' % (count, i)): + # d.putValue('i: %s entry: 0x%x' % (i, entry)) + d.putPairItem(count, (key, val), 'key', 'value') + count += 1 + entry_pos += 1 + #with SubItem(d, 'total'): + # d.putValue('total: %s item size: %s' % (count, entry_size)) + + +def qform__QHashNode(): + return [DisplayFormat.CompactMap] + + +def qdump__QHashNode(d, value): + d.putPairItem(None, value) + + +def qHashIteratorHelper(d, value): + typeName = value.type.name + hashTypeName = typeName[0:typeName.rfind('::')] + hashType = d.lookupType(hashTypeName) + keyType = hashType[0] + valueType = hashType[1] + d.putExpandable() + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + node = d.extractPointer(value) + isShort = d.qtVersion() < 0x050000 and keyType.name == 'int' + if isShort: + typeCode = 'P{%s}@{%s}' % (keyType.name, valueType.name) + (pnext, key, padding2, val) = d.split(typeCode, node) + else: + typeCode = 'Pi@{%s}@{%s}' % (keyType.name, valueType.name) + (pnext, hashval, padding1, key, padding2, val) = d.split(typeCode, node) + d.putSubItem('key', key) + d.putSubItem('value', val) + + +def qdump__QHash__const_iterator(d, value): + qHashIteratorHelper(d, value) + + +def qdump__QHash__iterator(d, value): + qHashIteratorHelper(d, value) + + +def qdump__QHostAddress(d, value): + dd = d.extractPointer(value) + qtVersion = d.qtVersion() + tiVersion = d.qtTypeInfoVersion() + #DumperBase.warn('QT: %x, TI: %s' % (qtVersion, tiVersion)) + mayNeedParse = True + if tiVersion is not None: + if tiVersion >= 16: + # After a6cdfacf + p, scopeId, a6, a4, protocol = d.split('p{@QString}16s{@quint32}B', dd) + mayNeedParse = False + elif tiVersion >= 5: + # Branch 5.8.0 at f70b4a13 TI: 15 + # Branch 5.7.0 at b6cf0418 TI: 5 + (ipString, scopeId, a6, a4, protocol, isParsed) \ + = d.split('{@QString}{@QString}16s{@quint32}B{bool}', dd) + else: + (ipString, scopeId, a4, pad, a6, protocol, isParsed) \ + = d.split('{@QString}{@QString}{@quint32}I16sI{bool}', dd) + elif qtVersion >= 0x050600: # 5.6.0 at f3aabb42 + if d.ptrSize() == 8 or d.isWindowsTarget(): + (ipString, scopeId, a4, pad, a6, protocol, isParsed) \ + = d.split('{@QString}{@QString}{@quint32}I16sI{bool}', dd) + else: + (ipString, scopeId, a4, a6, protocol, isParsed) \ + = d.split('{@QString}{@QString}{@quint32}16sI{bool}', dd) + elif qtVersion >= 0x050000: # 5.2.0 at 62feb088 + (ipString, scopeId, a4, a6, protocol, isParsed) \ + = d.split('{@QString}{@QString}{@quint32}16sI{bool}', dd) + else: # 4.8.7 at b05d05f + (a4, a6, protocol, pad, ipString, isParsed, pad, scopeId) \ + = d.split('{@quint32}16sB@{@QString}{bool}@{@QString}', dd) + + if mayNeedParse: + ipStringData, ipStringSize, ipStringAlloc = d.stringData(ipString) + if mayNeedParse and isParsed.integer() and ipStringSize > 0: + d.putStringValue(ipString) + else: + # value.d.d->protocol: + # QAbstractSocket::IPv4Protocol = 0 + # QAbstractSocket::IPv6Protocol = 1 + if protocol == 1: + # value.d.d->a6 + data = d.hexencode(a6) + address = ':'.join('%x' % int(data[i:i + 4], 16) for i in range(0, 32, 4)) + d.putValue(address) + elif protocol == 0: + # value.d.d->a + a = a4.integer() + a, n4 = divmod(a, 256) + a, n3 = divmod(a, 256) + a, n2 = divmod(a, 256) + a, n1 = divmod(a, 256) + d.putValue('%d.%d.%d.%d' % (n1, n2, n3, n4)) + else: + d.putValue('' % protocol) + + d.putExpandable() + if d.isExpanded(): + with Children(d): + if mayNeedParse: + d.putSubItem('ipString', ipString) + d.putSubItem('isParsed', isParsed) + d.putSubItem('scopeId', scopeId) + d.putSubItem('a', a4) + + +def qdump__QIPv6Address(d, value): + raw = d.split('16s', value)[0] + data = d.hexencode(raw) + d.putValue(':'.join('%x' % int(data[i:i + 4], 16) for i in range(0, 32, 4))) + d.putArrayData(value.address(), 16, d.lookupType('unsigned char')) + + +def qform__QList(): + return [DisplayFormat.DirectQListStorage, DisplayFormat.IndirectQListStorage] + + +def qdump__QList(d, value): + return qdumpHelper_QList(d, value, d.createType(value.type[0])) + + +def qdump__QVariantList(d, value): + qdumpHelper_QList(d, value, d.createType('@QVariant')) + + +def qdumpHelper_QList(d, value, innerType): + data, size = d.listData(value, check=True) + d.putItemCount(size) + + if d.qtVersion() >= 0x60000: + d.putPlotData(data, size, innerType) + return + + if d.isExpanded(): + innerSize = innerType.size() + stepSize = d.ptrSize() + # The exact condition here is: + # QTypeInfo::isLarge || QTypeInfo::isStatic + # but this data is available neither in the compiled binary nor + # in the frontend. + # So as first approximation only do the 'isLarge' check: + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.DirectQListStorage: + isInternal = True + elif displayFormat == DisplayFormat.IndirectQListStorage: + isInternal = False + else: + isInternal = innerSize <= stepSize and innerType.isMovableType() + if isInternal: + if innerSize == stepSize: + d.putArrayData(data, size, innerType) + else: + with Children(d, size, childType=innerType): + for i in d.childRange(): + p = d.createValue(data + i * stepSize, innerType) + d.putSubItem(i, p) + else: + # about 0.5s / 1000 items + with Children(d, size, maxNumChild=2000, childType=innerType): + for i in d.childRange(): + p = d.extractPointer(data + i * stepSize) + x = d.createValue(p, innerType) + d.putSubItem(i, x) + + +def qform__QImage(): + return [DisplayFormat.Simple, DisplayFormat.Separate] + + +def qdump__QImage(d, value): + if d.qtVersion() >= 0x060000: + vtbl, painters, image_data = value.split('ppp') + elif d.qtVersion() >= 0x050000: + vtbl, painters, reserved, image_data = value.split('pppp') + else: + vtbl, painters, image_data = value.split('ppp') + + if image_data == 0: + d.putValue('(invalid)') + return + + ref, width, height = d.split('iii', image_data) + d.putValue('(%dx%d)' % (width, height)) + + d.putExpandable() + if d.isExpanded(): + if d.qtVersion() < 0x060000: + (ref, width, height, depth, nbytes, pad, devicePixelRatio, colorTable, + bits, iformat) = d.split('iiiii@dppi', image_data) + else: + (ref, width, height, depth, nbytes, pad, devicePixelRatio, _, _, _, + bits, iformat) = d.split('iiiii@dppppi', image_data) + with Children(d): + d.putIntItem('width', width) + d.putIntItem('height', height) + d.putIntItem('nbytes', nbytes) + d.putIntItem('format', iformat) + with SubItem(d, 'data'): + d.putValue('0x%x' % bits) + d.putType('void *') + + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Separate: + d.putDisplay('imagedata:separate', '%08x%08x%08x%08x' % (width, height, nbytes, iformat) + + d.readMemory(bits, nbytes)) + + +def qdump__QLinkedList(d, value): + dd = d.extractPointer(value) + ptrSize = d.ptrSize() + n = d.extractInt(dd + 4 + 2 * ptrSize) + ref = d.extractInt(dd + 2 * ptrSize) + d.check(0 <= n and n <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref <= 1000) + d.putItemCount(n) + if d.isExpanded(): + innerType = value.type[0] + with Children(d, n, maxNumChild=1000, childType=innerType): + pp = d.extractPointer(dd) + for i in d.childRange(): + d.putSubItem(i, d.createValue(pp + 2 * ptrSize, innerType)) + pp = d.extractPointer(pp) + + +qqLocalesCount = None + + +def qdump__QLocale(d, value): + # Check for uninitialized 'index' variable. Retrieve size of + # QLocale data array from variable in qlocale.cpp. + # Default is 368 in Qt 4.8, 438 in Qt 5.0.1, the last one + # being 'System'. + #global qqLocalesCount + #if qqLocalesCount is None: + # #try: + # qqLocalesCount = int(value(ns + 'locale_data_size')) + # #except: + # qqLocalesCount = 438 + #try: + # index = int(value['p']['index']) + #except: + # try: + # index = int(value['d']['d']['m_index']) + # except: + # index = int(value['d']['d']['m_data']...) + #d.check(index >= 0) + #d.check(index <= qqLocalesCount) + if d.qtVersion() < 0x50000: + d.putStringValue(d.call('const char *', value, 'name')) + d.putPlainChildren(value) + return + + ns = d.qtNamespace() + dd = value.extractPointer() + (data, ref, numberOptions) = d.split('pi4s', dd) + (languageId, scriptId, countryId, + decimal, group, listt, percent, zero, + minus, plus, exponential) \ + = d.split('2s{short}2s' + + '{@QChar}{@QChar}{short}{@QChar}{@QChar}' + + '{@QChar}{@QChar}{@QChar}', data) + try: + d.putStringValue(d.call('const char *', value, 'name')) + except: + pass + d.putExpandable() + if d.isExpanded(): + with Children(d): + prefix = ns + 'QLocale::' + d.putSubItem('country', d.createValue(countryId, prefix + 'Country')) + d.putSubItem('language', d.createValue(languageId, prefix + 'Language')) + d.putSubItem('numberOptions', d.createValue(numberOptions, prefix + 'NumberOptions')) + d.putSubItem('decimalPoint', decimal) + d.putSubItem('exponential', exponential) + d.putSubItem('percent', percent) + d.putSubItem('zeroDigit', zero) + d.putSubItem('groupSeparator', group) + d.putSubItem('negativeSign', minus) + d.putSubItem('positiveSign', plus) + d.putCallItem('measurementSystem', '@QLocale::MeasurementSystem', + value, 'measurementSystem') + d.putCallItem('timeFormat_(short)', '@QString', + value, 'timeFormat', ns + 'QLocale::ShortFormat') + d.putCallItem('timeFormat_(long)', '@QString', + value, 'timeFormat', ns + 'QLocale::LongFormat') + d.putFields(value) + + +def qdump__QMapNode(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putSubItem('key', value['key']) + d.putSubItem('value', value['value']) + + +def qdumpHelper_Qt4_QMap(d, value, keyType, valueType): + dd = value.extractPointer() + (dummy, it, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, + ref, toplevel, n) = d.split('p' * 13 + 'iii', dd) + d.check(0 <= n and n <= 100 * 1000 * 1000) + d.checkRef(ref) + d.putItemCount(n) + if d.isExpanded(): + if n > 10000: + n = 10000 + + typeCode = '{%s}@{%s}' % (keyType.name, valueType.name) + pp, payloadSize, fields = d.describeStruct(typeCode) + + with Children(d, n): + for i in d.childRange(): + key, pad, value = d.split(typeCode, it - payloadSize) + d.putPairItem(i, (key, value), 'key', 'value') + dummy, it = d.split('Pp', it) + + +def qdumpHelper_Qt5_QMap(d, value, keyType, valueType): + dptr = d.extractPointer(value) + (ref, n) = d.split('ii', dptr) + d.check(0 <= n and n <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + + d.putItemCount(n) + if d.isExpanded(): + if n > 10000: + n = 10000 + + typeCode = 'ppp@{%s}@{%s}' % (keyType.name, valueType.name) + + def helper(node): + (p, left, right, padding1, key, padding2, value) = d.split(typeCode, node) + if left: + for res in helper(left): + yield res + yield (key, value) + if right: + for res in helper(right): + yield res + + with Children(d, n): + for (pair, i) in zip(helper(dptr + 8), range(n)): + d.putPairItem(i, pair, 'key', 'value') + + +def qdumpHelper_Qt6_QMap(d, value, keyType, valueType): + d_ptr = d.extractPointer(value) + if d_ptr == 0: + d.putItemCount(0) + return + m = value['d']['d']['m'] + d.putItem(m) + d.putBetterType('@QMap<%s, %s>' % (keyType.name, valueType.name)) + + +def qform__QMap(): + return [DisplayFormat.CompactMap] + + +def qdump__QMap(d, value): + qdumpHelper_QMap(d, value, value.type[0], value.type[1]) + + +def qdumpHelper_QMap(d, value, keyType, valueType): + if d.qtVersion() >= 0x60000: + qdumpHelper_Qt6_QMap(d, value, keyType, valueType) + elif d.qtVersion() >= 0x50000: + qdumpHelper_Qt5_QMap(d, value, keyType, valueType) + else: + qdumpHelper_Qt4_QMap(d, value, keyType, valueType) + + +def qform__QMultiMap(): + return [DisplayFormat.CompactMap] + + +def qdumpHelper_Qt6_QMultiMap(d, value, keyType, valueType): + d_ptr = d.extractPointer(value) + if d_ptr == 0: + d.putItemCount(0) + return + m = value['d']['d']['m'] + d.putItem(m) + d.putBetterType('@QMultiMap<%s, %s>' % (keyType.name, valueType.name)) + +def qdump__QMultiMap(d, value): + if d.qtVersion() >= 0x60000: + qdumpHelper_Qt6_QMultiMap(d, value, value.type[0], value.type[1]) + else: + qdump__QMap(d, value) + + +def qform__QVariantMap(): + return [DisplayFormat.CompactMap] + + +def qdump__QVariantMap(d, value): + qdumpHelper_QMap(d, value, d.createType('@QString'), d.createType('@QVariant')) + d.putBetterType('@QVariantMap') + + +def qdump__QMetaMethod(d, value): + d.putQMetaStuff(value, 'QMetaMethod') + + +def qdump__QMetaEnum(d, value): + d.putQMetaStuff(value, 'QMetaEnum') + + +def qdump__QMetaProperty(d, value): + d.putQMetaStuff(value, 'QMetaProperty') + + +def qdump__QMetaClassInfo(d, value): + d.putQMetaStuff(value, 'QMetaClassInfo') + + +def qdump__QMetaObject(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putQObjectGutsHelper(0, 0, -1, value.address(), 'QMetaObject') + d.putMembersItem(value) + + +if False: + def qdump__QObjectPrivate__ConnectionList(d, value): + d.putExpandable() + if d.isExpanded(): + i = 0 + with Children(d): + first, last = value.split('pp') + currentConnection = first + connectionType = d.createType('@QObjectPrivate::Connection') + while currentConnection and currentConnection != last: + sender, receiver, slotObj, nextConnectionList, nextp, prev = \ + d.split('pppppp', currentConnection) + d.putSubItem(i, d.createValue(currentConnection, connectionType)) + currentConnection = nextp + i += 1 + d.putFields(value) + d.putItemCount(i) + else: + d.putSpecialValue('minimumitemcount', 0) + + +def qdump__QPair(d, value): + typeCode = '{%s}@{%s}' % (value.type[0].name, value.type[1].name) + first, pad, second = value.split(typeCode) + with Children(d): + key = d.putSubItem('first', first) + value = d.putSubItem('second', second) + key = key.value if key.encoding is None else "..." + value = value.value if value.encoding is None else "..." + d.putValue('(%s, %s)' % (key, value)) + + +def qdump__QProcEnvKey(d, value): + d.putByteArrayValue(value) + d.putPlainChildren(value) + + +def qdump__QPixmap(d, value): + if d.qtVersion() >= 0x060000: + vtbl, painters, data = value.split('ppp') + elif d.qtVersion() >= 0x050000: + vtbl, painters, reserved, data = s = d.split('pppp', value) + else: + vtbl, painters, data = value.split('ppp') + + if data == 0: + d.putValue('(invalid)') + else: + _, width, height = d.split('pii', data) + d.putValue('(%dx%d)' % (width, height)) + + d.putPlainChildren(value) + + +def qdump__QMargins(d, value): + d.putValue('left:%s, top:%s, right:%s, bottom:%s' % (value.split('iiii'))) + d.putPlainChildren(value) + + +def qdump__QPoint(d, value): + d.putValue('(%s, %s)' % (value.split('ii'))) + d.putPlainChildren(value) + + +def qdump__QPointF(d, value): + d.putValue('(%s, %s)' % (value.split('dd'))) + d.putPlainChildren(value) + + +def qdump__QRect(d, value): + def pp(l): return ('+' if l >= 0 else '') + str(l) + (x1, y1, x2, y2) = d.split('iiii', value) + d.putValue('%sx%s%s%s' % (x2 - x1 + 1, y2 - y1 + 1, pp(x1), pp(y1))) + d.putPlainChildren(value) + + +def qdump__QRectF(d, value): + def pp(l): return ('+' if l >= 0 else '') + str(l) + (x, y, w, h) = value.split('dddd') + d.putValue('%sx%s%s%s' % (w, h, pp(x), pp(y))) + d.putPlainChildren(value) + + +def qdump__QRegExp(d, value): + # value.priv.engineKey.pattern + privAddress = d.extractPointer(value) + (eng, pattern) = d.split('p{@QString}', privAddress) + d.putStringValue(pattern) + d.putExpandable() + if d.isExpanded(): + with Children(d): + try: + d.call('void', value, 'capturedTexts') # Warm up internal cache. + except: + # Might fail (LLDB, Core files, ...), still cache might be warm. + pass + (patternSyntax, caseSensitive, minimal, pad, t, captures) \ + = d.split('{int}{int}B@{@QString}{@QStringList}', privAddress + 2 * d.ptrSize()) + d.putSubItem('syntax', patternSyntax.cast(d.qtNamespace() + 'QRegExp::PatternSyntax')) + d.putSubItem('captures', captures) + + +def qdump__QRegion(d, value): + d_ptr = d.extractPointer(value) + if d_ptr == 0: + d.putSpecialValue('empty') + else: + if d.qtVersion() >= 0x060000: + ref, _, rgn = d.split('i@p', d_ptr) + numRects, innerArea, rects, extents, innerRect = \ + d.split('ii{@QList<@QRect>}{@QRect}{@QRect}', rgn) + elif d.qtVersion() >= 0x050400: # Padding removed in ee324e4ed + ref, _, rgn = d.split('i@p', d_ptr) + numRects, innerArea, rects, extents, innerRect = \ + d.split('ii{@QVector<@QRect>}{@QRect}{@QRect}', rgn) + elif d.qtVersion() >= 0x050000: + ref, _, rgn = d.split('i@p', d_ptr) + numRects, _, rects, extents, innerRect, innerArea = \ + d.split('i@{@QVector<@QRect>}{@QRect}{@QRect}i', rgn) + else: + if d.isWindowsTarget(): + ref, _, rgn = d.split('i@p', d_ptr) + else: + ref, _, xrgn, xrectangles, rgn = d.split('i@ppp', d_ptr) + if rgn == 0: + numRects = 0 + else: + numRects, _, rects, extents, innerRect, innerArea = \ + d.split('i@{@QVector<@QRect>}{@QRect}{@QRect}i', rgn) + + d.putItemCount(numRects) + if d.isExpanded(): + with Children(d): + d.putIntItem('numRects', numRects) + d.putIntItem('innerArea', innerArea) + d.putSubItem('extents', extents) + d.putSubItem('innerRect', innerRect) + d.putSubItem('rects', rects) + + +def qdump__QScopedPointer(d, value): + if value.pointer() == 0: + d.putValue('(null)') + else: + d.putItem(value['d']) + d.putValue(d.currentValue.value, d.currentValue.encoding) + typeName = value.type.name + if value.type[1].name == d.qtNamespace() + 'QScopedPointerDeleter<%s>' % value.type[0].name: + typeName = '@QScopedPointer<%s>' % value.type[0].name + d.putBetterType(typeName) + + +def qdump__QSet(d, value): + if d.qtVersion() >= 0x060000: + qdumpHelper_QSet6(d, value) + else: + qdumpHelper_QSet45(d, value) + +def qdumpHelper_QSet6(d, value): + dptr = d.extractPointer(value) + if dptr == 0: + d.putItemCount(0) + return + + ref, _, size, buckets, seed, spans = d.split('i@qqqp', dptr) + + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + d.putItemCount(size) + + if d.isExpanded(): + value_type = value.type[0] + value_size = value_type.size() + with Children(d, size): + span_size = 128 + 2 * d.ptrSize() # Including tail padding. + nspans = int((buckets + 127) / 128) + count = 0 + for b in range(nspans): + span = spans + b * span_size + offsets, entries, allocated, next_free = d.split('128spbb', span) + for i in range(128): + offset = offsets[i] + if offset != 255: # Entry is used + entry = entries + offset * value_size + d.putSubItem(count, d.createValue(entry, value_type)) + count += 1 + + +def qdumpHelper_QSet45(d, value): + def hashDataFirstNode(): + b = buckets + n = numBuckets + while n: + n -= 1 + bb = d.extractPointer(b) + if bb != dptr: + return bb + b += ptrSize + return dptr + + def hashDataNextNode(node): + (nextp, h) = d.split('pI', node) + if d.extractPointer(nextp): + return nextp + start = (h % numBuckets) + 1 + b = buckets + start * ptrSize + n = numBuckets - start + while n: + n -= 1 + bb = d.extractPointer(b) + if bb != nextp: + return bb + b += ptrSize + return nextp + + ptrSize = d.ptrSize() + dptr = d.extractPointer(value) + (fakeNext, buckets, ref, size, nodeSize, userNumBits, numBits, numBuckets) = \ + d.split('ppiiihhi', dptr) + + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) + + d.putItemCount(size) + if d.isExpanded(): + keyType = value.type[0] + isShort = d.qtVersion() < 0x050000 and keyType.name == 'int' + with Children(d, size, childType=keyType): + node = hashDataFirstNode() + for i in d.childRange(): + if isShort: + typeCode = 'P{%s}' % keyType.name + (pnext, key) = d.split(typeCode, node) + else: + typeCode = 'Pi@{%s}' % keyType.name + (pnext, hashval, padding1, key) = d.split(typeCode, node) + with SubItem(d, i): + d.putItem(key) + node = hashDataNextNode(node) + + +def qdump__QSharedData(d, value): + d.putValue('ref: %s' % value.to('i')) + + +def qdump__QSharedDataPointer(d, value): + d_ptr = value['d'] + if d_ptr.pointer() == 0: + d.putValue('(null)') + else: + # This replaces the pointer by the pointee, making the + # pointer transparent. + try: + innerType = value.type[0] + except: + d.putValue(d_ptr) + d.putPlainChildren(value) + return + d.putBetterType(d.currentType) + d.putItem(d_ptr.dereference()) + + +def qdump__QSize(d, value): + d.putValue('(%s, %s)' % value.split('ii')) + d.putPlainChildren(value) + + +def qdump__QSizeF(d, value): + d.putValue('(%s, %s)' % value.split('dd')) + d.putPlainChildren(value) + + +def qdump__QSizePolicy__Policy(d, value): + d.putEnumValue(value.integer(), { + 0: 'QSizePolicy::Fixed', + 1: 'QSizePolicy::GrowFlag', + 2: 'QSizePolicy::ExpandFlag', + 3: 'QSizePolicy::MinimumExpanding (GrowFlag|ExpandFlag)', + 4: 'QSizePolicy::ShrinkFlag', + 5: 'QSizePolicy::Preferred (GrowFlag|ShrinkFlag)', + 7: 'QSizePolicy::Expanding (GrowFlag|ShrinkFlag|ExpandFlag)', + 8: 'QSizePolicy::IgnoreFlag', + 13: 'QSizePolicy::Ignored (ShrinkFlag|GrowFlag|IgnoreFlag)', + }) + + +def qdump__QSizePolicy(d, value): + bits = value.integer() + d.putEmptyValue(-99) + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putIntItem('horStretch', (bits >> 0) & 0xff) + d.putIntItem('verStretch', (bits >> 8) & 0xff) + d.putEnumItem('horPolicy', (bits >> 16) & 0xf, "@QSizePolicy::Policy") + d.putEnumItem('verPolicy', (bits >> 20) & 0xf, "@QSizePolicy::Policy") + + +def qform__QStack(): + return [DisplayFormat.ArrayPlot] + + +def qdump__QStack(d, value): + if d.qtVersion() >= 0x60000: + qdump__QList(d, value) + else: + qdump__QVector(d, value) + + +def qdump__QPolygonF(d, value): + data, size = d.vectorData(value) + d.putItemCount(size) + d.putPlotData(data, size, d.createType('@QPointF')) + + +def qdump__QPolygon(d, value): + data, size = d.vectorData(value) + d.putItemCount(size) + d.putPlotData(data, size, d.createType('@QPoint')) + + +def qdump__QGraphicsPolygonItem(d, value): + (vtbl, dptr) = value.split('pp') + if d.qtVersion() >= 0x060000: + if d.ptrSize() == 8: + offset = 424 # sizeof(QGraphicsPolygonItemPrivate), the base + else: + # Chicken out. Not worth maintaining. + d.putPlainChildren(value) + return + else: + if d.ptrSize() == 8: + offset = 384 + elif d.isWindowsTarget(): + offset = 328 if d.isMsvcTarget() else 320 + else: + offset = 308 + data, size = d.vectorData(dptr + offset) + d.putItemCount(size) + d.putPlotData(data, size, d.createType('@QPointF')) + + +def qedit__QString(d, value, data): + d.call('void', value, 'resize', str(len(data))) + (base, size, alloc) = d.stringData(value) + d.setValues(base, 'short', [ord(c) for c in data]) + + +def qform__QString(): + return [DisplayFormat.Simple, DisplayFormat.Separate] + + +def qdump__QString(d, value): + d.putStringValue(value) + data, size, _ = d.stringData(value) + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Separate: + d.putDisplay('utf16:separate', d.encodeString(value, limit=100000)) + if (size > 0): + d.putExpandable() + if d.isExpanded(): + d.putArrayData(data, size, d.createType('@QChar')) + + +def qdump__QSettingsKey(d, value): + qdump__QString(d, value) + d.putBetterType(value.type) + + +def qdump__QStaticStringData(d, value): + size = value.type[0] + (ref, size, alloc, pad, offset, data) = value.split('iii@p%ss' % (2 * size)) + d.putValue(d.hexencode(data), 'utf16') + d.putPlainChildren(value) + + +def qdump__QTypedArrayData(d, value): + if value.type[0].name == 'unsigned short': + qdump__QStringData(d, value) +# else: +# qdump__QArrayData(d, value) + + +def qdump__QStringData(d, value): + (ref, size, alloc, pad, offset) = value.split('III@p') + elided, shown = d.computeLimit(size, d.displayStringLimit) + data = d.readMemory(value.address() + offset, shown * 2) + d.putValue(data, 'utf16', elided=elided) + d.putPlainChildren(value) + + +def qdump__QAnyStringView(d, value): + data, size = value.split('pp') + bits = d.ptrSize() * 8 - 2 + tag = size >> bits + size = size & (2**bits - 1) + elided, shown = d.computeLimit(size, d.displayStringLimit) + if tag == 0: + mem = d.readMemory(data, shown) + d.putValue(mem, 'utf8', elided=elided) + elif tag == 1: + mem = d.readMemory(data, shown) + d.putValue(mem, 'latin1', elided=elided) + elif tag == 2: + mem = d.readMemory(data, shown * 2) + d.putValue(mem, 'utf16', elided=elided) + else: + d.putSpecialValue('empty') + d.putPlainChildren(value) + + +def qform__QStringView(): + return [DisplayFormat.Simple, DisplayFormat.Separate] + + +def qdump__QStringView(d, value): + data = value['m_data'] + idata = data.integer() + if idata == 0: + d.putValue('(null)') + return + size = value['m_size'] + isize = size.integer() + elided, shown = d.computeLimit(isize, d.displayStringLimit) + mem = d.readMemory(idata, shown * 2) + d.putValue(mem, 'utf16', elided=elided) + if d.currentItemFormat() == DisplayFormat.Separate: + d.putDisplay('utf16:separate', mem) + d.putExpandable() + if d.isExpanded(): + d.putArrayData(idata, isize, d.createType('char16_t')) + + +def qdump__QHashedString(d, value): + qdump__QString(d, value) + d.putBetterType(value.type) + + +def qdump__QQmlRefCount(d, value): + d.putItem(value['refCount']) + d.putBetterType(value.type) + + +def qdump__QStringRef(d, value): + (stringptr, pos, size) = value.split('pii') + if stringptr == 0: + d.putValue('(null)') + return + data, ssize, alloc = d.stringData(d.createValue(stringptr, '@QString')) + d.putValue(d.readMemory(data + 2 * pos, 2 * size), 'utf16') + d.putPlainChildren(value) + + +def qdump__QStringList(d, value): + qdumpHelper_QList(d, value, d.createType('@QString')) + d.putBetterType(value.type) + + +def qdump__QTemporaryFile(d, value): + qdump__QFile(d, value) + + +def qdump__QTextCodec(d, value): + name = d.call('const char *', value, 'name') + d.putValue(d.encodeByteArray(name, limit=100), 6) + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putCallItem('name', '@QByteArray', value, 'name') + d.putCallItem('mibEnum', 'int', value, 'mibEnum') + d.putFields(value) + + +def qdump__QTextCursor(d, value): + privAddress = d.extractPointer(value) + if privAddress == 0: + d.putValue('(invalid)') + else: + positionAddress = privAddress + 2 * d.ptrSize() + 8 + d.putValue(d.extractInt(positionAddress)) + d.putExpandable() + if d.isExpanded(): + with Children(d): + positionAddress = privAddress + 2 * d.ptrSize() + 8 + d.putIntItem('position', d.extractInt(positionAddress)) + d.putIntItem('anchor', d.extractInt(positionAddress + 4)) + d.putCallItem('selected', '@QString', value, 'selectedText') + d.putFields(value) + + +def qdump__QTextDocument(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putCallItem('blockCount', 'int', value, 'blockCount') + d.putCallItem('characterCount', 'int', value, 'characterCount') + d.putCallItem('lineCount', 'int', value, 'lineCount') + d.putCallItem('revision', 'int', value, 'revision') + d.putCallItem('toPlainText', '@QString', value, 'toPlainText') + d.putFields(value) + + +def qform__QUrl(): + return [DisplayFormat.Simple, DisplayFormat.Separate] + + +def qdump__QUrl(d, value): + privAddress = d.extractPointer(value) + if not privAddress: + # d == 0 if QUrl was constructed with default constructor + d.putValue('') + return + + if d.qtVersion() < 0x050000: + d.call('void', value, 'port') # Warm up internal cache. + d.call('void', value, 'path') + st = '{@QString}' + ba = '{@QByteArray}' + (ref, dummy, + scheme, userName, password, host, path, # QString + query, # QByteArray + fragment, # QString + encodedOriginal, encodedUserName, encodedPassword, + encodedPath, encodedFragment, # QByteArray + port) \ + = d.split('i@' + st * 5 + ba + st + ba * 5 + 'i', privAddress) + else: + (ref, port, scheme, userName, password, host, path, query, fragment) \ + = d.split('ii' + '{@QString}' * 7, privAddress) + + userNameEnc = d.encodeString(userName) + hostEnc = d.encodeString(host) + elided, pathEnc = d.encodeStringHelper(path, d.displayStringLimit) + url = d.encodeString(scheme) + url += '3a002f002f00' # '://' + if len(userNameEnc): + url += userNameEnc + '4000' # '@' + url += hostEnc + if port >= 0: + url += '3a00' + ''.join(['%02x00' % ord(c) for c in str(port)]) + url += pathEnc + d.putValue(url, 'utf16', elided=elided) + + displayFormat = d.currentItemFormat() + if displayFormat == DisplayFormat.Separate: + d.putDisplay('utf16:separate', url) + + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putIntItem('port', port) + d.putSubItem('scheme', scheme) + d.putSubItem('userName', userName) + d.putSubItem('password', password) + d.putSubItem('host', host) + d.putSubItem('path', path) + d.putSubItem('query', query) + d.putSubItem('fragment', fragment) + d.putFields(value) + + +def qdump__QUuid(d, value): + r = value.split('IHHBBBBBBBB') + d.putValue('{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}' % r) + d.putPlainChildren(value) + + +def qdumpHelper_QVariant_0(d, value): + # QVariant::Invalid + d.putBetterType('@QVariant (invalid)') + d.putValue('(invalid)') + + +def qdumpHelper_QVariant_1(d, value): + # QVariant::Bool + d.putBetterType('@QVariant (bool)') + d.putValue('true' if value.to('b') else 'false') + + +def qdumpHelper_QVariant_2(d, value): + # QVariant::Int + d.putBetterType('@QVariant (int)') + d.putValue(value.to('i')) + + +def qdumpHelper_QVariant_3(d, value): + # uint + d.putBetterType('@QVariant (uint)') + d.putValue(value.to('I')) + + +def qdumpHelper_QVariant_4(d, value): + # qlonglong + d.putBetterType('@QVariant (qlonglong)') + d.putValue(value.to('q')) + + +def qdumpHelper_QVariant_5(d, value): + # qulonglong + d.putBetterType('@QVariant (qulonglong)') + d.putValue(value.to('Q')) + + +def qdumpHelper_QVariant_6(d, value): + # QVariant::Double + d.putBetterType('@QVariant (double)') + d.putValue(value.to('d')) + + +qdumpHelper_QVariants_A = [ + qdumpHelper_QVariant_0, + qdumpHelper_QVariant_1, + qdumpHelper_QVariant_2, + qdumpHelper_QVariant_3, + qdumpHelper_QVariant_4, + qdumpHelper_QVariant_5, + qdumpHelper_QVariant_6 +] + + +qdumpHelper_QVariants_B = [ + 'QChar', # 7 + 'QVariantMap', # 8 + 'QVariantList', # 9 + 'QString', # 10 + 'QStringList', # 11 + 'QByteArray', # 12 + 'QBitArray', # 13 + 'QDate', # 14 + 'QTime', # 15 + 'QDateTime', # 16 + 'QUrl', # 17 + 'QLocale', # 18 + 'QRect', # 19 + 'QRectF', # 20 + 'QSize', # 21 + 'QSizeF', # 22 + 'QLine', # 23 + 'QLineF', # 24 + 'QPoint', # 25 + 'QPointF', # 26 + 'QRegExp', # 27 + 'QVariantHash', # 28 +] + + +def qdumpHelper_QVariant_31(d, value): + # QVariant::VoidStar + d.putBetterType('@QVariant (void *)') + d.putValue('0x%x' % d.extractPointer(value)) + + +def qdumpHelper_QVariant_32(d, value): + # QVariant::Long + d.putBetterType('@QVariant (long)') + if d.ptrSize() == 4: + d.putValue('%s' % d.extractInt(value)) + else: + d.putValue('%s' % d.extractInt64(value)) # sic! + + +def qdumpHelper_QVariant_33(d, value): + # QVariant::Short + d.putBetterType('@QVariant (short)') + d.putValue('%s' % d.extractShort(value)) + + +def qdumpHelper_QVariant_34(d, value): + # QVariant::Char + d.putBetterType('@QVariant (char)') + d.putValue('%s' % d.extractByte(value)) + + +def qdumpHelper_QVariant_35(d, value): + # QVariant::ULong + d.putBetterType('@QVariant (unsigned long)') + if d.ptrSize() == 4: + d.putValue('%s' % d.extractUInt(value)) + else: + d.putValue('%s' % d.extractUInt64(value)) # sic! + + +def qdumpHelper_QVariant_36(d, value): + # QVariant::UShort + d.putBetterType('@QVariant (unsigned short)') + d.putValue('%s' % d.extractUShort(value)) + + +def qdumpHelper_QVariant_37(d, value): + # QVariant::UChar + d.putBetterType('@QVariant (unsigned char)') + d.putValue('%s' % d.extractByte(value)) + + +def qdumpHelper_QVariant_38(d, value): + # QVariant::Float + d.putBetterType('@QVariant (float)') + d.putValue(value.to('f')) + + +qdumpHelper_QVariants_D = [ + qdumpHelper_QVariant_31, + qdumpHelper_QVariant_32, + qdumpHelper_QVariant_33, + qdumpHelper_QVariant_34, + qdumpHelper_QVariant_35, + qdumpHelper_QVariant_36, + qdumpHelper_QVariant_37, + qdumpHelper_QVariant_38 +] + +qdumpHelper_QVariants_E = [ + 'QFont', # 64 + 'QPixmap', # 65 + 'QBrush', # 66 + 'QColor', # 67 + 'QPalette', # 68 + 'QIcon', # 69 + 'QImage', # 70 + 'QPolygon', # 71 + 'QRegion', # 72 + 'QBitmap', # 73 + 'QCursor', # 74 +] + +qdumpHelper_QVariants_F = [ + # Qt 5. In Qt 4 add one. + 'QKeySequence', # 75 + 'QPen', # 76 + 'QTextLength', # 77 + 'QTextFormat', # 78 + 'X', + 'QTransform', # 80 + 'QMatrix4x4', # 81 + 'QVector2D', # 82 + 'QVector3D', # 83 + 'QVector4D', # 84 + 'QQuaternion', # 85 + 'QPolygonF' # 86 +] + + +def qdump__QVariant(d, value): + if d.qtVersion() >= 0x060000: + qdumpHelper__QVariant6(d, value) + else: + qdumpHelper__QVariant45(d, value) + +def qdumpHelper__QVariant6(d, value): + data, typeStuff = d.split('24sp', value) + packedType = typeStuff >> 2 + metaTypeInterface = typeStuff - (typeStuff & 3) + + if metaTypeInterface == 0: + qdumpHelper_QVariant_0(d, value) + return + + revision, alignment, size, flags, variantType, metaObjectPtr, name = \ + d.split('HHIIIpp', metaTypeInterface) + + # Well-known simple type. + if variantType >= 1 and variantType <= 6: + qdumpHelper_QVariants_A[variantType](d, value) + return None + + # Extended Core type (Qt 5+) + if variantType >= 31 and variantType <= 38: + qdumpHelper_QVariants_D[variantType - 31](d, value) + return None + + typeName = d.extractCString(name).decode('utf8') + isShared = bool(typeStuff & 0x1) + + if isShared: + # indirectly stored items (QRectF, ...) + ptr = d.extractPointer(value) + _, data = d.split('8s{%s}' % typeName, ptr) + d.putItem(data) + else: + val = d.createValue(data, typeName) + val.laddress = value.laddress + d.putItem(val) + + d.putBetterType('@QVariant (%s)' % typeName) + + +def qdumpHelper__QVariant45(d, value): + data, typeStuff = d.split('8sI', value) + variantType = typeStuff & 0x3fffffff + isShared = bool(typeStuff & 0x40000000) + + # Well-known simple type. + if variantType <= 6: + qdumpHelper_QVariants_A[variantType](d, value) + return None + + # Extended Core type (Qt 5) + if variantType >= 31 and variantType <= 38 and d.qtVersion() >= 0x050000: + qdumpHelper_QVariants_D[variantType - 31](d, value) + return None + + # Extended Core type (Qt 4) + if variantType >= 128 and variantType <= 135 and d.qtVersion() < 0x050000: + if variantType == 128: + d.putBetterType('@QVariant (void *)') + d.putValue('0x%x' % value.extractPointer()) + else: + if variantType == 135: # Float + blob = value + else: + p = d.extractPointer(value) + blob = d.extractUInt64(p) + qdumpHelper_QVariants_D[variantType - 128](d, blob) + return None + + #DumperBase.warn('TYPE: %s' % variantType) + + if variantType <= 86: + # Known Core or Gui type. + if variantType <= 28: + innert = qdumpHelper_QVariants_B[variantType - 7] + elif variantType <= 74: + innert = qdumpHelper_QVariants_E[variantType - 64] + elif d.qtVersion() < 0x050000: + innert = qdumpHelper_QVariants_F[variantType - 76] + else: + innert = qdumpHelper_QVariants_F[variantType - 75] + + #data = value['d']['data'] + innerType = d.qtNamespace() + innert + + #DumperBase.warn('SHARED: %s' % isShared) + if isShared: + base1 = d.extractPointer(value) + #DumperBase.warn('BASE 1: %s %s' % (base1, innert)) + base = d.extractPointer(base1) + #DumperBase.warn('SIZE 1: %s' % size) + val = d.createValue(base, innerType) + else: + #DumperBase.warn('DIRECT ITEM 1: %s' % innerType) + val = d.createValue(data, innerType) + val.laddress = value.laddress + + d.putEmptyValue(-99) + d.putItem(val) + d.putBetterType('@QVariant (%s)' % innert) + + return innert + + # User types. + ns = d.qtNamespace() + d.putEmptyValue(-99) + d.putType('%sQVariant (%s)' % (ns, variantType)) + d.putExpandable() + if d.isExpanded(): + innerType = None + with Children(d): + try: + p = d.call('const char *', value, 'typeName') + except: + d.putSpecialValue('notcallable') + return None + ptr = p.pointer() + (elided, blob) = d.encodeCArray(ptr, 1, 100) + innerType = d.hexdecode(blob) + + # Prefer namespaced version. + if len(ns) > 0: + if not d.lookupNativeType(ns + innerType) is None: + innerType = ns + innerType + + if isShared: + base1 = d.extractPointer(value) + base = d.extractPointer(base1) + val = d.createValue(base, innerType) + else: + val = d.createValue(data, innerType) + val.laddress = value.laddress + d.putSubItem('data', val) + + if innerType is not None: + d.putBetterType('%sQVariant (%s)' % (ns, innerType)) + return None + + +def qedit__QVector(d, value, data): + values = data.split(',') + d.call('void', value, 'resize', str(len(values))) + base, vsize = d.vectorData(value) + d.setValues(base, value.type[0].name, values) + + +def qform__QVector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__QVector(d, value): + if d.qtVersion() >= 0x060000: + data, size = d.listData(value) + d.putItemCount(size) + d.putPlotData(data, size, d.createType(value.type.ltarget[0])) + # g++ 9.3 does not add the template parameter list to the debug info. + # Fake it for the common case: + if value.type.name == d.qtNamespace() + "QVector": + d.putBetterType(value.type.name + '<' + value.type.ltarget[0].name + '>') + else: + data, size = d.vectorData(value) + d.putItemCount(size) + d.putPlotData(data, size, d.createType(value.type[0])) + + +if False: + def qdump__QObjectConnectionList(d, value): + data, size = d.vectorData(value) + d.putItemCount(size) + d.putPlotData(data, size, d.createType('@QObjectPrivate::ConnectionList')) + + +def qdump__QVarLengthArray(d, value): + if d.qtVersion() >= 0x060000: + cap, size, data = value.split('QQp') + else: + cap, size, data = value.split('iip') + d.check(0 <= size) + d.putItemCount(size) + d.putPlotData(data, size, value.type[0]) + + +def qdump__QSharedPointer(d, value): + qdump_QWeakPointerHelper(d, value, False) + + +def qdump__QWeakPointer(d, value): + qdump_QWeakPointerHelper(d, value, True) + + +def qdump__QPointer(d, value): + # actually, we'd use value['wp'] instead of value, but since we + # only split() on the result and the (sub-)object address is the + # same it does not matter but saves some cycles. + qdump_QWeakPointerHelper(d, value, True, value.type[0]) + + +def qdump_QWeakPointerHelper(d, value, isWeak, innerType=None): + if isWeak: + (d_ptr, val) = value.split('pp') + else: + (val, d_ptr) = value.split('pp') + if d_ptr == 0 and val == 0: + d.putValue('(null)') + return + if d_ptr == 0 or val == 0: + d.putValue('') + return + + if d.qtVersion() >= 0x050000: + (weakref, strongref) = d.split('ii', d_ptr) + else: + (vptr, weakref, strongref) = d.split('pii', d_ptr) + d.check(strongref >= -1) + d.check(strongref <= weakref) + d.check(weakref <= 10 * 1000 * 1000) + + if innerType is None: + innerType = value.type[0] + with Children(d): + dataAddress = value.laddress + if isWeak: + dataAddress = dataAddress + d.ptrSize() + short = d.putSubItem('data', d.createValue(dataAddress, d.createPointerType(innerType))) + d.putIntItem('weakref', weakref) + d.putIntItem('strongref', strongref) + d.putValue(short.value, short.encoding) + + +def qdump__QXmlAttributes__Attribute(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + (qname, uri, localname, val) = value.split('{@QString}' * 4) + d.putSubItem('qname', qname) + d.putSubItem('uri', uri) + d.putSubItem('localname', localname) + d.putSubItem('value', val) + + +def qdump__QXmlAttributes(d, value): + vptr, atts = value.split('p{@QList<@QXmlAttributes::Attribute>}') + _, att_size, _ = d.describeStruct('{@QString}' * 4) + innerType = d.createType('@QXmlAttributes::Attribute', att_size) + qdumpHelper_QList(d, atts, innerType) + + +def qdump__QXmlStreamStringRef(d, value): + s = value['m_string'] + (data, size, alloc) = d.stringData(s) + data += 2 * int(value['m_position']) + size = int(value['m_size']) + s = d.readMemory(data, 2 * size) + d.putValue(s, 'utf16') + d.putPlainChildren(value) + + +def qdump__QXmlStreamAttribute(d, value): + s = value['m_name']['m_string'] + (data, size, alloc) = d.stringData(s) + data += 2 * int(value['m_name']['m_position']) + size = int(value['m_name']['m_size']) + s = d.readMemory(data, 2 * size) + d.putValue(s, 'utf16') + d.putPlainChildren(value) + + +####################################################################### +# +# V4 +# +####################################################################### + +def extractQmlData(d, value): + #if value.type.code == TypeCode.Pointer: + # value = value.dereference() + base = value.split('p')[0] + #mmdata = d.split('Q', base)[0] + #PointerMask = 0xfffffffffffffffd + #vtable = mmdata & PointerMask + #DumperBase.warn('QML DATA: %s' % value.stringify()) + #data = value['data'] + #return #data.cast(d.lookupType(value.type.name.replace('QV4::', 'QV4::Heap::'))) + typeName = value.type.name.replace('QV4::', 'QV4::Heap::') + #DumperBase.warn('TYOE DATA: %s' % typeName) + return d.createValue(base, typeName) + + +def qdump__QV4__Heap__Base(d, value): + mm_data = value.extractPointer() + d.putValue('[%s]' % mm_data) + if d.isExpanded(): + with Children(d): + with SubItem(d, 'vtable'): + d.putItem(d.createValue(mm_data & (~3), d.qtNamespace() + 'QV4::VTable')) + d.putBoolItem('isMarked', mm_data & 1) + d.putBoolItem('inUse', (mm_data & 2) == 0) + with SubItem(d, 'nextFree'): + d.putItem(d.createValue(mm_data & (~3), value.type)) + + +def qdump__QV4__Heap__String(d, value): + # Note: There's also the 'Identifier' case. And the largestSubLength != 0 case. + (baseClass, textOrLeft, idOrRight, subtype, stringHash, largestSub, length, mm) \ + = value.split('QppIIIIp') + textPtr = d.split('{@QStringDataPtr}', textOrLeft)[0] + qdump__QStringData(d, d.createValue(textOrLeft, d.qtNamespace() + 'QStringData')) + if d.isExpanded(): + with Children(d): + d.putFields(value) + + +def qmlPutHeapChildren(d, value): + d.putItem(extractQmlData(d, value)) + + +def qdump__QV4__Object(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QV4__FunctionObject(d, value): + #qmlPutHeapChildren(d, value) + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + d.putFields(value) + d.putSubItem('heap', extractQmlData(d, value)) + d.putCallItem('sourceLocation', '@QQmlSourceLocation', + value, 'sourceLocation') + + +def qdump__QV4__CompilationUnit(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QV4__CallContext(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QV4__ScriptFunction(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QV4__SimpleScriptFunction(d, value): + qdump__QV4__FunctionObject(d, value) + + +def qdump__QV4__ExecutionContext(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QQmlSourceLocation(d, value): + (sourceFile, line, col) = value.split('pHH') + (data, size, alloc) = d.stringData(value) + d.putValue(d.readMemory(data, 2 * size), 'utf16') + d.putField('valuesuffix', ':%s:%s' % (line, col)) + d.putPlainChildren(value) + + +#def qdump__QV4__CallData(d, value): +# argc = value['argc'].integer() +# d.putItemCount(argc) +# if d.isExpanded(): +# with Children(d): +# d.putSubItem('[this]', value['thisObject']) +# for i in range(0, argc): +# d.putSubItem(i, value['args'][i]) +# + +def qdump__QV4__String(d, value): + qmlPutHeapChildren(d, value) + + +def qdump__QV4__Identifier(d, value): + d.putStringValue(value) + d.putPlainChildren(value) + + +def qdump__QV4__PropertyHash(d, value): + data = value.extractPointer() + (ref, alloc, size, numBits, entries) = d.split('iiiip', data) + n = 0 + with Children(d): + for i in range(alloc): + (identifier, index) = d.split('pI', entries + i * 2 * d.ptrSize()) + if identifier != 0: + n += 1 + with SubItem(d): + d.putItem(d, d.createValue(identifier, '@QV4::Identifier')) + d.put('keysuffix', ' %d' % index) + d.putItemCount(n) + d.putPlainChildren(value) + + +def qdump__QV4__InternalClass__Transition(d, value): + identifier = d.createValue(value.extractPointer(), '@QV4::Identifier') + d.putStringValue(identifier) + d.putPlainChildren(value) + + +def qdump__QV4__InternalClassTransition(d, value): + qdump__QV4__InternalClass__Transition(d, value) + + +def qdump__QV4__SharedInternalClassData(d, value): + (ref, alloc, size, pad, data) = value.split('iIIip') + val = d.createValue(data, value.type[0]) + with Children(d): + with SubItem(d, 'data'): + d.putItem(val) + short = d.currentValue + d.putIntItem('size', size) + d.putIntItem('alloc', alloc) + d.putIntItem('refcount', ref) + d.putValue(short.value, short.encoding) + + +def qdump__QV4__IdentifierTable(d, value): + (engine, alloc, size, numBits, pad, entries) = value.split('piiiip') + n = 0 + with Children(d): + for i in range(alloc): + identifierPtr = d.extractPointer(entries + i * d.ptrSize()) + if identifierPtr != 0: + n += 1 + with SubItem(d, None): + d.putItem(d.createValue(identifierPtr, '@QV4::Heap::String')) + d.putItemCount(n) + d.putPlainChildren(value) + + +if False: + # 32 bit. + QV4_Masks_SilentNaNBit = 0x00040000 + QV4_Masks_NaN_Mask = 0x7ff80000 + QV4_Masks_NotDouble_Mask = 0x7ffa0000 + QV4_Masks_Type_Mask = 0xffffc000 + QV4_Masks_Immediate_Mask = QV4_Masks_NotDouble_Mask | 0x00004000 | QV4_Masks_SilentNaNBit + QV4_Masks_IsNullOrUndefined_Mask = QV4_Masks_Immediate_Mask | 0x08000 + QV4_Masks_Tag_Shift = 32 + + QV4_ValueType_Undefined_Type = QV4_Masks_Immediate_Mask | 0x00000 + QV4_ValueType_Null_Type = QV4_Masks_Immediate_Mask | 0x10000 + QV4_ValueType_Boolean_Type = QV4_Masks_Immediate_Mask | 0x08000 + QV4_ValueType_Integer_Type = QV4_Masks_Immediate_Mask | 0x18000 + QV4_ValueType_Managed_Type = QV4_Masks_NotDouble_Mask | 0x00000 | QV4_Masks_SilentNaNBit + QV4_ValueType_Empty_Type = QV4_Masks_NotDouble_Mask | 0x18000 | QV4_Masks_SilentNaNBit + + QV4_ConvertibleToInt = QV4_Masks_Immediate_Mask | 0x1 + + QV4_ValueTypeInternal_Null_Type_Internal = QV4_ValueType_Null_Type | QV4_ConvertibleToInt + QV4_ValueTypeInternal_Boolean_Type_Internal = QV4_ValueType_Boolean_Type | QV4_ConvertibleToInt + QV4_ValueTypeInternal_Integer_Type_Internal = QV4_ValueType_Integer_Type | QV4_ConvertibleToInt + + +def QV4_getValue(d, jsval): # (Dumper, QJSValue *jsval) -> QV4::Value * + dd = d.split('Q', jsval)[0] + if dd & 3: + return 0 + return dd + + +def QV4_getVariant(d, jsval): # (Dumper, QJSValue *jsval) -> QVariant * + dd = d.split('Q', jsval)[0] + if dd & 1: + return dd & ~3 + return 0 + + +def QV4_valueForData(d, jsval): # (Dumper, QJSValue *jsval) -> QV4::Value * + v = QV4_getValue(d, jsval) + if v: + return v + d.warn('Not implemented: VARIANT') + return 0 + + +def QV4_putObjectValue(d, objectPtr): + ns = d.qtNamespace() + base = d.extractPointer(objectPtr) + (inlineMemberOffset, inlineMemberSize, internalClass, prototype, + memberData, arrayData) = d.split('IIpppp', base) + d.putValue('PTR: 0x%x' % objectPtr) + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % objectPtr) + d.putType(' ') + d.putIntItem('inlineMemberOffset', inlineMemberOffset) + d.putIntItem('inlineMemberSize', inlineMemberSize) + d.putIntItem('internalClass', internalClass) + d.putIntItem('prototype', prototype) + d.putPtrItem('memberData', memberData) + d.putPtrItem('arrayData', arrayData) + d.putSubItem('OBJ', d.createValue(objectPtr, ns + 'QV4::Object')) + #d.putFields(value) + + +def qdump__QV4_Object(d, value): + ns = d.qtNamespace() + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + base = d.extractPointer(objectPtr) + (inlineMemberOffset, inlineMemberSize, internalClass, prototype, + memberData, arrayData) = d.split('IIpppp', base) + d.putValue('PTR: 0x%x' % objectPtr) + + +def qdump__QV4__Value(d, value): + if d.ptrSize() == 4: + qdump_32__QV4__Value(d, value) + else: + qdump_64__QV4__Value(d, value) + + +def qdump_32__QV4__Value(d, value): + # QV4_Masks_SilentNaNBit = 0x00040000 + # QV4_Masks_NaN_Mask = 0x7ff80000 + # QV4_Masks_NotDouble_Mask = 0x7ffa0000 + # QV4_Masks_Type_Mask = 0xffffc000 + ns = d.qtNamespace() + v = value.split('Q')[0] + tag = v >> 32 + val = v & 0xffffffff + if (tag & 0x7fff2000) == 0x7fff2000: # Int + d.putValue(val) + d.putBetterType('@QV4::Value (int32)') + elif (tag & 0x7fff4000) == 0x7fff4000: # Bool + d.putValue(val) + d.putBetterType('@QV4::Value (bool)') + elif (tag & 0x7fff0000) == 0x7fff0000: # Null + d.putValue(val) + d.putBetterType('@QV4::Value (null)') + elif (tag & 0x7ffa0000) != 0x7ffa0000: # Double + d.putValue(value.split('d')[0]) + d.putBetterType('@QV4::Value (double)') + elif tag == 0x7ffa0000: + if val == 0: + d.putValue('(undefined)') + d.putBetterType('@QV4::Value (undefined)') + else: + managed = d.createValue(val, ns + 'QV4::Heap::Base') + qdump__QV4__Heap__Base(d, managed) + #d.putValue('[0x%x]' % v) + #d.putPlainChildren(value) + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % v) + d.putType(' ') + with SubItem(d, '[val]'): + d.putValue('[0x%x]' % val) + d.putType(' ') + with SubItem(d, '[tag]'): + d.putValue('[0x%x]' % tag) + d.putType(' ') + #with SubItem(d, '[vtable]'): + # d.putItem(d.createValue(vtable, ns + 'QV4::VTable')) + # d.putType(' '); + d.putFields(value) + + +def qdump_64__QV4__Value(d, value): + dti = d.qtDeclarativeTypeInfoVersion() + new = dti is not None and dti >= 2 + if new: + QV4_NaNEncodeMask = 0xfffc000000000000 + QV4_Masks_Immediate_Mask = 0x00020000 # bit 49 + + QV4_ValueTypeInternal_Empty_Type_Internal = QV4_Masks_Immediate_Mask | 0 + QV4_ConvertibleToInt = QV4_Masks_Immediate_Mask | 0x10000 # bit 48 + QV4_ValueTypeInternal_Null_Type_Internal = QV4_ConvertibleToInt | 0x08000 + QV4_ValueTypeInternal_Boolean_Type_Internal = QV4_ConvertibleToInt | 0x04000 + QV4_ValueTypeInternal_Integer_Type_Internal = QV4_ConvertibleToInt | 0x02000 + + QV4_ValueType_Undefined_Type = 0 # Dummy to make generic code below pass. + + else: + QV4_NaNEncodeMask = 0xffff800000000000 + QV4_Masks_Immediate_Mask = 0x00018000 + + QV4_IsInt32Mask = 0x0002000000000000 + QV4_IsDoubleMask = 0xfffc000000000000 + QV4_IsNumberMask = QV4_IsInt32Mask | QV4_IsDoubleMask + QV4_IsNullOrUndefinedMask = 0x0000800000000000 + QV4_IsNullOrBooleanMask = 0x0001000000000000 + + QV4_Masks_NaN_Mask = 0x7ff80000 + QV4_Masks_Type_Mask = 0xffff8000 + QV4_Masks_IsDouble_Mask = 0xfffc0000 + QV4_Masks_IsNullOrUndefined_Mask = 0x00008000 + QV4_Masks_IsNullOrBoolean_Mask = 0x00010000 + + QV4_ValueType_Undefined_Type = QV4_Masks_IsNullOrUndefined_Mask + QV4_ValueType_Null_Type = QV4_Masks_IsNullOrUndefined_Mask \ + | QV4_Masks_IsNullOrBoolean_Mask + QV4_ValueType_Boolean_Type = QV4_Masks_IsNullOrBoolean_Mask + QV4_ValueType_Integer_Type = 0x20000 | QV4_Masks_IsNullOrBoolean_Mask + QV4_ValueType_Managed_Type = 0 + QV4_ValueType_Empty_Type = QV4_ValueType_Undefined_Type | 0x4000 + + QV4_ValueTypeInternal_Null_Type_Internal = QV4_ValueType_Null_Type + QV4_ValueTypeInternal_Boolean_Type_Internal = QV4_ValueType_Boolean_Type + QV4_ValueTypeInternal_Integer_Type_Internal = QV4_ValueType_Integer_Type + + QV4_PointerMask = 0xfffffffffffffffd + + QV4_Masks_Tag_Shift = 32 + QV4_IsDouble_Shift = 64 - 14 + QV4_IsNumber_Shift = 64 - 15 + QV4_IsConvertibleToInt_Shift = 64 - 16 + QV4_IsManaged_Shift = 64 - 17 + + v = value.split('Q')[0] + tag = v >> QV4_Masks_Tag_Shift + vtable = v & QV4_PointerMask + ns = d.qtNamespace() + if (v >> QV4_IsNumber_Shift) == 1: + d.putBetterType('@QV4::Value (int32)') + vv = v & 0xffffffff + vv = vv if vv < 0x80000000 else -(0x100000000 - vv) + d.putBetterType('@QV4::Value (int32)') + d.putValue('%d' % vv) + elif (v >> QV4_IsDouble_Shift): + d.putBetterType('@QV4::Value (double)') + d.putValue('%0.16x' % (v ^ QV4_NaNEncodeMask), 'float:8') + elif tag == QV4_ValueType_Undefined_Type and not new: + d.putBetterType('@QV4::Value (undefined)') + d.putValue('(undefined)') + elif tag == QV4_ValueTypeInternal_Null_Type_Internal: + d.putBetterType('@QV4::Value (null?)') + d.putValue('(null?)') + elif v == 0: + if new: + d.putBetterType('@QV4::Value (undefined)') + d.putValue('(undefined)') + else: + d.putBetterType('@QV4::Value (null)') + d.putValue('(null)') + #elif ((v >> QV4_IsManaged_Shift) & ~1) == 1: + # d.putBetterType('@QV4::Value (null/undef)') + # d.putValue('(null/undef)') + #elif v & QV4_IsNullOrBooleanMask: + # d.putBetterType('@QV4::Value (null/bool)') + # d.putValue('(null/bool)') + # d.putValue(v & 1) + else: + (parentv, flags, pad, className) = d.split('pIIp', vtable) + #vtable = value['m']['vtable'] + if flags & 2: # isString' + d.putBetterType('@QV4::Value (string)') + qdump__QV4__Heap__String(d, d.createValue(v, ns + 'QV4::Heap::String')) + #d.putStringValue(d.extractPointer(value) + 2 * d.ptrSize()) + #d.putValue('ptr: 0x%x' % d.extractPointer(value)) + return + elif flags & 4: # isObject + d.putBetterType('@QV4::Value (object)') + #QV4_putObjectValue(d, d.extractPointer(value) + 2 * d.ptrSize()) + arrayVTable = d.symbolAddress(ns + 'QV4::ArrayObject::static_vtbl') + #DumperBase.warn('ARRAY VTABLE: 0x%x' % arrayVTable) + d.putExpandable() + d.putItem(d.createValue(d.extractPointer(value) + 2 * d.ptrSize(), ns + 'QV4::Object')) + return + elif flags & 8: # isFunction + d.putBetterType('@QV4::Value (function)') + d.putEmptyValue() + else: + d.putBetterType('@QV4::Value (unknown)') + #d.putValue('[0x%x]' % v) + d.putValue('[0x%x : flag 0x%x : tag 0x%x]' % (v, flags, tag)) + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % v) + d.putType(' ') + with SubItem(d, '[vtable]'): + d.putItem(d.createValue(vtable, ns + 'QV4::VTable')) + d.putType(' ') + d.putFields(value) + + +def qdump__QV__PropertyHashData(d, value): + (ref, alloc, size, numBits, entries) = value.split('IIIIp') + d.putItemCount(size) + if d.isExpanded(): + with Children(d): + d.putFields(value) + + +def qdump__QV__PropertyHash(d, value): + qdump__QV__PropertyHashData(d, d.createValue(d.extractPointer(), value.type.name + 'Data')) + + +def qdump__QV4__Scoped(d, value): + innerType = value.type[0] + d.putItem(d.createValue(value.extractPointer(), innerType)) + #d.putEmptyValue() + #if d.isExpanded(): + # with Children(d): + # d.putSubItem('[]', d.createValue(value.extractPointer(), innerType)) + # d.putFields(value) + + +def qdump__QV4__ScopedString(d, value): + innerType = value.type[0] + qdump__QV4__String(d, d.createValue(value.extractPointer(), innerType)) + + +def qdump__QJSValue(d, value): + if d.ptrSize() == 4: + qdump_32__QJSValue(d, value) + else: + if d.qtVersion() >= 0x60000: + qdump_64__QJSValue_6(d, value) + else: + qdump_64__QJSValue_5(d, value) + + +def qdump_32__QJSValue(d, value): + ns = d.qtNamespace() + dd = value.split('I')[0] + d.putValue('[0x%x]' % dd) + if dd == 0: + d.putValue('(null)') + d.putType(value.type.name + ' (null)') + elif dd & 1: + variant = d.createValue(dd & ~3, ns + 'QVariant') + qdump__QVariant(d, variant) + d.putBetterType(d.currentType.value.replace('QVariant', 'QJSValue', 1)) + elif dd & 3 == 0: + v4value = d.createValue(dd, ns + 'QV4::Value') + qdump_32__QV4__Value(d, v4value) + d.putBetterType(d.currentType.value.replace('QV4::Value', 'QJSValue', 1)) + return + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % dd) + d.putType(' ') + d.putFields(value) + +def qdump_64__QJSValue_6(d, value): + dd = value.split('Q')[0] + if dd == 0: + d.putValue('(undefined)') + d.putType(value.type.name + ' (undefined)') + if d.qtVersion() < 0x60500: + typ = dd >> 47 + if typ == 5: + d.putValue('(null)') + d.putType(value.type.name + ' (null)') + elif typ == 6: + d.putValue('true' if dd & 1 else 'false') + d.putType(value.type.name + ' (bool)') + elif typ == 7: + d.putValue(dd & 0xfffffffff) + d.putType(value.type.name + ' (int)') + elif typ > 7: + val = d.Value(d) + val.ldata = struct.pack('q', dd ^ 0xfffc000000000000) + val._type = d.createType('double') + d.putItem(val) + d.putType(value.type.name + ' (double)') + elif typ <= 3: # Heap + if dd & 1: # String + val = d.Value(d) + val.ldata = struct.pack('q', dd & ~1) + val._type = d.createType('@QString*') + d.putItem(val) + d.putType(value.type.name + ' (QString)') + else: + # FIXME: Arrays, Objects missing. + val = d.split('{@QV4::Managed*}', value)[0] + d.putItem(val) + d.putItemCount(1) + else: + d.putEmptyValue() + d.putItemCount(1) + d.putPlainChildren(value) + return + + else: + typ = dd & 7 + isPointer = typ & 1 + if typ == 0: + d.putValue('(undefined)') + d.putType(value.type.name + ' (undefined)') + elif typ == 2: + d.putValue('(null)') + d.putType(value.type.name + ' (null)') + elif typ == 4: + d.putValue(dd >> 32) + d.putType(value.type.name + ' (int)') + elif typ == 6: + d.putValue('true' if dd >> 32 & 1 else 'false') + d.putType(value.type.name + ' (bool)') + elif isPointer: + pointer = dd >> 3 + pointer = pointer << 3 + val = d.Value(d) + val.ldata = struct.pack('q', pointer) + if typ == 1: + val._type = d.createType('double*') + d.putItem(val) + d.putType(value.type.name + ' (double)') + elif typ == 3: + val._type = d.createType('@QV4::Value*') + d.putItem(val) + d.putType(value.type.name + ' (QV4::Value)') + elif typ == 5: + val._type = d.createType('@QString*') + d.putItem(val) + d.putType(value.type.name + ' (QString)') + + else: + d.putEmptyValue() + d.putItemCount(1) + d.putPlainChildren(value) + return + + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % dd) + d.putType(' ') + d.putFields(value) + + +def qdump_64__QJSValue_5(d, value): + ns = d.qtNamespace() + dd = value.split('Q')[0] + if dd == 0: + d.putValue('(null)') + d.putType(value.type.name + ' (null)') + elif dd & 1: + variant = d.createValue(dd & ~3, ns + 'QVariant') + qdump__QVariant(d, variant) + d.putBetterType(d.currentType.value.replace('QVariant', 'QJSValue', 1)) + else: + d.putEmptyValue() + #qdump__QV4__Value(d, d.createValue(dd, ns + 'QV4::Value')) + #return + if d.isExpanded(): + with Children(d): + with SubItem(d, '[raw]'): + d.putValue('[0x%x]' % dd) + d.putType(' ') + d.putFields(value) + + +def qdump__QQmlBinding(d, value): + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + d.putCallItem('expressionIdentifier', '@QString', + value, 'expressionIdentifier') + d.putFields(value) + + +####################################################################### +# +# Webkit +# +####################################################################### + + +def jstagAsString(tag): + # enum { Int32Tag = 0xffffffff }; + # enum { CellTag = 0xfffffffe }; + # enum { TrueTag = 0xfffffffd }; + # enum { FalseTag = 0xfffffffc }; + # enum { NullTag = 0xfffffffb }; + # enum { UndefinedTag = 0xfffffffa }; + # enum { EmptyValueTag = 0xfffffff9 }; + # enum { DeletedValueTag = 0xfffffff8 }; + if tag == -1: + return 'Int32' + if tag == -2: + return 'Cell' + if tag == -3: + return 'True' + if tag == -4: + return 'Null' + if tag == -5: + return 'Undefined' + if tag == -6: + return 'Empty' + if tag == -7: + return 'Deleted' + return 'Unknown' + + +def qdump__QTJSC__JSValue(d, value): + d.putEmptyValue() + d.putExpandable() + if d.isExpanded(): + with Children(d): + tag = value['u']['asBits']['tag'] + payload = value['u']['asBits']['payload'] + #d.putIntItem('tag', tag) + with SubItem(d, 'tag'): + d.putValue(jstagAsString(int(tag))) + d.putNoType() + + d.putIntItem('payload', int(payload)) + d.putFields(value['u']) + + if tag == -2: + cellType = d.lookupType('QTJSC::JSCell').pointer() + d.putSubItem('cell', payload.cast(cellType)) + + try: + # FIXME: This might not always be a variant. + delegateType = d.lookupType(d.qtNamespace() + 'QScript::QVariantDelegate').pointer() + delegate = scriptObject['d']['delegate'].cast(delegateType) + #d.putSubItem('delegate', delegate) + variant = delegate['m_value'] + d.putSubItem('variant', variant) + except: + pass + + +def qdump__QScriptValue(d, value): + # structure: + # engine QScriptEnginePrivate + # jscValue QTJSC::JSValue + # next QScriptValuePrivate * + # numberValue 5.5987310416280426e-270 myns::qsreal + # prev QScriptValuePrivate * + # ref QBasicAtomicInt + # stringValue QString + # type QScriptValuePrivate::Type: { JavaScriptCore, Number, String } + #d.putEmptyValue() + dd = value['d_ptr']['d'] + ns = d.qtNamespace() + if dd.pointer() == 0: + d.putValue('(invalid)') + return + if int(dd['type']) == 1: # Number + d.putValue(dd['numberValue']) + d.putType('%sQScriptValue (Number)' % ns) + return + if int(dd['type']) == 2: # String + d.putStringValue(dd['stringValue']) + d.putType('%sQScriptValue (String)' % ns) + return + + d.putType('%sQScriptValue (JSCoreValue)' % ns) + x = dd['jscValue']['u'] + tag = x['asBits']['tag'] + payload = x['asBits']['payload'] + #isValid = int(x['asBits']['tag']) != -6 # Empty + #isCell = int(x['asBits']['tag']) == -2 + #DumperBase.warn('IS CELL: %s ' % isCell) + #isObject = False + #className = 'UNKNOWN NAME' + #if isCell: + # # isCell() && asCell()->isObject(); + # # in cell: m_structure->typeInfo().type() == ObjectType; + # cellType = d.lookupType('QTJSC::JSCell').pointer() + # cell = payload.cast(cellType).dereference() + # dtype = 'NO DYNAMIC TYPE' + # try: + # dtype = cell.dynamic_type + # except: + # pass + # warn('DYNAMIC TYPE: %s' % dtype) + # warn('STATUC %s' % cell.type) + # type = cell['m_structure']['m_typeInfo']['m_type'] + # isObject = int(type) == 7 # ObjectType; + # className = 'UNKNOWN NAME' + #DumperBase.warn('IS OBJECT: %s ' % isObject) + + #inline bool JSCell::inherits(const ClassInfo* info) const + #for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { + # if (ci == info) + # return true; + #return false; + + try: + # This might already fail for 'native' payloads. + scriptObjectType = d.lookupType(ns + 'QScriptObject').pointer() + scriptObject = payload.cast(scriptObjectType) + + # FIXME: This might not always be a variant. + delegateType = d.lookupType(ns + 'QScript::QVariantDelegate').pointer() + delegate = scriptObject['d']['delegate'].cast(delegateType) + #d.putSubItem('delegate', delegate) + + variant = delegate['m_value'] + #d.putSubItem('variant', variant) + t = qdump__QVariant(d, variant) + # Override the 'QVariant (foo)' output + d.putBetterType('@QScriptValue (%s)' % t) + if t != 'JSCoreValue': + return + except: + pass + + # This is a 'native' JSCore type for e.g. QDateTime. + d.putValue('') + d.putExpandable() + if d.isExpanded(): + with Children(d): + d.putSubItem('jscValue', dd['jscValue']) + + +def qdump__QQmlAccessorProperties__Properties(d, value): + size = int(value['count']) + d.putItemCount(size) + if d.isExpanded(): + d.putArrayData(value['properties'], size) + + +# +# QJson +# + +def qdumpHelper_qle_cutBits(value, offset, length): + return (value >> offset) & ((1 << length) - 1) + + +def qdump__QJsonPrivate__qle_bitfield(d, value): + offset = value.type[0] + length = value.type[1] + val = value['val'].integer() + d.putValue('%s' % qdumpHelper_qle_cutBits(val, offset, length)) + + +def qdumpHelper_qle_signedbitfield_value(d, value): + offset = value.type[0] + length = value.type[1] + val = value['val'].integer() + val = (val >> offset) & ((1 << length) - 1) + if val >= (1 << (length - 1)): + val -= (1 << (length - 1)) + return val + + +def qdump__QJsonPrivate__qle_signedbitfield(d, value): + d.putValue('%s' % qdumpHelper_qle_signedbitfield_value(d, value)) + + +def qdump__QJsonPrivate__q_littleendian(d, value): + d.putValue('%s' % value['val'].integer()) + + +def qdumpHelper_QJsonValue(d, data, base, pv): + """ + Parameters are the parameters to the + QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, + const QJsonPrivate::Value& pv) + constructor. We 'inline' the construction here. + + data is passed as pointer integer + base is passed as pointer integer + pv is passed as 32 bit integer. + """ + d.checkIntType(data) + d.checkIntType(base) + d.checkIntType(pv) + + t = qdumpHelper_qle_cutBits(pv, 0, 3) + v = qdumpHelper_qle_cutBits(pv, 5, 27) + latinOrIntValue = qdumpHelper_qle_cutBits(pv, 3, 1) + + if t == 0: + d.putType('QJsonValue (Null)') + d.putValue('Null') + return + if t == 1: + d.putType('QJsonValue (Bool)') + d.putValue('true' if v else 'false') + return + if t == 2: + d.putType('QJsonValue (Number)') + if latinOrIntValue: + w = toInteger(v) + if w >= 0x4000000: + w -= 0x8000000 + d.putValue(w) + else: + data = base + v + f = d.split('d', data)[0] + d.putValue(str(f)) + return + if t == 3: + d.putType('QJsonValue (String)') + data = base + v + if latinOrIntValue: + length = d.extractUShort(data) + d.putValue(d.readMemory(data + 2, length), 'latin1') + else: + length = d.extractUInt(data) + d.putValue(d.readMemory(data + 4, length * 2), 'utf16') + return + if t == 4: + d.putType('QJsonValue (Array)') + qdumpHelper_QJsonArray(d, data, base + v) + return + if t == 5: + d.putType('QJsonValue (Object)') + qdumpHelper_QJsonObject(d, data, base + v) + + +def qdumpHelper_QJsonArray(d, data, array): + """ + Parameters are the parameters to the + QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) + constructor. We 'inline' the construction here. + + array is passed as integer pointer to the QJsonPrivate::Base object. + """ + + if data: + # The 'length' part of the _dummy member: + n = qdumpHelper_qle_cutBits(d.extractUInt(array + 4), 1, 31) + else: + n = 0 + + d.putItemCount(n) + if d.isExpanded(): + with Children(d, maxNumChild=1000): + table = array + d.extractUInt(array + 8) + for i in range(n): + with SubItem(d, i): + qdumpHelper_QJsonValue(d, data, array, d.extractUInt(table + 4 * i)) + + +def qdumpHelper_QJsonObject(d, data, obj): + """ + Parameters are the parameters to the + QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); + constructor. We "inline" the construction here. + + obj is passed as integer pointer to the QJsonPrivate::Base object. + """ + + if data: + # The 'length' part of the _dummy member: + n = qdumpHelper_qle_cutBits(d.extractUInt(obj + 4), 1, 31) + else: + n = 0 + + d.putItemCount(n) + if d.isExpanded(): + with Children(d, maxNumChild=1000): + table = obj + d.extractUInt(obj + 8) + for i in range(n): + with SubItem(d, i): + entryPtr = table + 4 * i # entryAt(i) + entryStart = obj + d.extractUInt(entryPtr) # Entry::value + keyStart = entryStart + 4 # sizeof(QJsonPrivate::Entry) == 4 + val = d.extractInt(entryStart) + key = d.extractInt(keyStart) + isLatinKey = qdumpHelper_qle_cutBits(val, 4, 1) + if isLatinKey: + keyLength = d.extractUShort(keyStart) + d.putField('key', d.readMemory(keyStart + 2, keyLength)) + d.putField('keyencoded', 'latin1') + else: + keyLength = d.extractUInt(keyStart) + d.putField('key', d.readMemory(keyStart + 4, keyLength)) + d.putField('keyencoded', 'utf16') + + qdumpHelper_QJsonValue(d, data, obj, val) + + +def qdump__QJsonValue(d, value): + (data, dd, t) = value.split('QpI') + + if d.qtVersion() >= 0x050f00: + value = d.createProxyValue((data, dd, t, False), 'QCborValue_proxy') + d.putItem(value) + return + + if t == 0: + d.putType('QJsonValue (Null)') + d.putValue('Null') + return + if t == 1: + d.putType('QJsonValue (Bool)') + v = value.split('b') + d.putValue('true' if v else 'false') + return + if t == 2: + d.putType('QJsonValue (Number)') + d.putValue(value.split('d')) + return + if t == 3: + d.putType('QJsonValue (String)') + string = value.split('{@QString}')[0] + elided, base = d.encodeString(string, d.displayStringLimit) + d.putValue(base, 'utf16', elided=elided) + return + if t == 4: + d.putType('QJsonValue (Array)') + qdumpHelper_QJsonArray(d, dd, data) + return + if t == 5: + d.putType('QJsonValue (Object)') + qdumpHelper_QJsonObject(d, dd, data) + return + d.putType('QJsonValue (Undefined)') + d.putEmptyValue() + + +def qdump__QJsonArray(d, value): + if d.qtVersion() >= 0x060000: + dptr = d.extractPointer(value) + if not dptr: + d.putItemCount(0) + else: + qdumpHelper_QCbor_array(d, dptr, False) + elif d.qtVersion() >= 0x050f00: + _, container_ptr = value.split('pp') + qdumpHelper_QCbor_array(d, container_ptr, False) + else: + qdumpHelper_QJsonArray(d, value['d'].pointer(), value['a'].pointer()) + + +def qdump__QJsonObject(d, value): + if d.qtVersion() >= 0x060000: + dptr = d.extractPointer(value) + if not dptr: + d.putItemCount(0) + else: + qdumpHelper_QCbor_map(d, dptr, False) + elif d.qtVersion() >= 0x050f00: + _, container_ptr = value.split('pp') + qdumpHelper_QCbor_map(d, container_ptr, False) + else: + qdumpHelper_QJsonObject(d, value['d'].pointer(), value['o'].pointer()) + + +def qdump__QSqlResultPrivate(d, value): + # QSqlResult *q_ptr; + # QPointer sqldriver; + # int idx; + # QString sql; + # bool active; + # bool isSel; + # QSqlError error; + # bool forwardOnly; + # QSql::NumericalPrecisionPolicy precisionPolicy; + # int bindCount; + # QSqlResult::BindingSyntax binds; + # QString executedQuery; + # QHash types; + # QVector values; + # QHash > indexes; + # QVector holders + vptr, qptr, sqldriver1, sqldriver2, idx, pad, sql, active, isSel, pad, \ + error1, error2, error3, \ + forwardOnly, pad, precisionPolicy, bindCount, \ + binds, executedQuery, types, values, indexes, holders = \ + value.split('ppppi@{@QString}bb@pppb@iiii{@QString}ppp') + + d.putStringValue(sql) + d.putPlainChildren(value) + + +def qdump__QSqlField(d, value): + val, dptr = value.split('{@QVariant}p') + qdump__QVariant(d, val) + d.putBetterType(d.currentType.value.replace('QVariant', 'QSqlField')) + d.putPlainChildren(value) + + +def qdump__QLazilyAllocated(d, value): + p = value.extractPointer() + if p == 0: + d.putValue("(null)") + else: + d.putItem(d.createValue(p, value.type[0])) + d.putBetterType(value.type) + + +def qdump__qfloat16(d, value): + h = value.split('H')[0] + # Stole^H^H^HHeavily inspired by J.F. Sebastian at + # http://math.stackexchange.com/questions/1128204/how-to-convert- + # from-floating-point-binary-to-decimal-in-half-precision16-bits + sign = h >> 15 + exp = (h >> 10) & 0b011111 + fraction = h & (2**10 - 1) + if exp == 0: + if fraction == 0: + res = -0.0 if sign else 0.0 + else: + res = (-1)**sign * fraction / 2**10 * 2**(-14) # subnormal + elif exp == 0b11111: + res = ('-inf' if sign else 'inf') if fraction == 0 else 'nan' + else: + res = (-1)**sign * (1 + 1. * fraction / 2**10) * 2**(exp - 15) + d.putValue(res) + d.putPlainChildren(value) + + + +def qdumpHelper_QCbor_string(d, container_ptr, element_index, is_bytes): + # d.split('i@{@QByteArray::size_type}pp', container_ptr) doesn't work with CDB, + # so be explicit: + data_pos = container_ptr + (2 * d.ptrSize() if d.qtVersion() >= 0x060000 else 8) + elements_pos = data_pos + (3 * d.ptrSize() if d.qtVersion() >= 0x060000 else d.ptrSize()) + elements_data_ptr, elements_size = d.vectorData(elements_pos) + element_at_n_addr = elements_data_ptr + element_index * 16 # sizeof(QtCbor::Element) == 16 + element_value, _, element_flags = d.split('qII', element_at_n_addr) + enc = 'latin1' if is_bytes or (element_flags & 8) else 'utf16' + bytedata, _, _ = d.qArrayData(data_pos) + bytedata += element_value + if d.qtVersion() >= 0x060000: + bytedata_len = d.extractInt64(bytedata) + bytedata_data = bytedata + 8 + else: + bytedata_len = d.extractInt(bytedata) + bytedata_data = bytedata + 4 # sizeof(QtCbor::ByteData) header part + + elided, shown = d.computeLimit(bytedata_len, d.displayStringLimit) + res = d.readMemory(bytedata_data, shown) + d.putValue(res, enc, elided=elided) + + +def qdumpHelper_QCborArray_valueAt(d, container_ptr, elements_data_ptr, idx, bytedata, is_cbor): + element_at_n_addr = elements_data_ptr + idx * 16 # sizeof(QtCbor::Element) == 15 + element_value, element_type, element_flags = d.split('qII', element_at_n_addr) + element_container, _, _ = d.split('pII', element_at_n_addr) + if element_flags & 1: # QtCbor::Element::IsContainer + return d.createProxyValue((-1, element_container, element_type, is_cbor), 'QCborValue_proxy') + if element_flags & 2: # QtCbor::Element::HasByteData + return d.createProxyValue((idx, container_ptr, element_type, is_cbor), 'QCborValue_proxy') + return d.createProxyValue((element_value, 0, element_type, is_cbor), 'QCborValue_proxy') + + +def qdumpHelper_QCbor_array(d, container_ptr, is_cbor): + if not container_ptr: + d.putItemCount(0) + return + # d.split('i@{@QByteArray::size_type}pp', container_ptr) doesn't work with CDB, + # so be explicit: + data_pos = container_ptr + (2 * d.ptrSize() if d.qtVersion() >= 0x060000 else 8) + elements_pos = data_pos + (3 * d.ptrSize() if d.qtVersion() >= 0x060000 else d.ptrSize()) + elements_data_ptr, elements_size = d.vectorData(elements_pos) + d.putItemCount(elements_size) + if d.isExpanded(): + bytedata, _, _ = d.qArrayData(data_pos) + with Children(d, maxNumChild=1000): + for i in range(elements_size): + d.putSubItem(i, qdumpHelper_QCborArray_valueAt(d, container_ptr, elements_data_ptr, i, bytedata, is_cbor)) + + +def qdump__QCborArray(d, value): + container_ptr = value.extractPointer() + qdumpHelper_QCbor_array(d, container_ptr, True) + + +def qdumpHelper_QCbor_map(d, container_ptr, is_cbor): + if not container_ptr: + d.putItemCount(0) + return + # d.split('i@{@QByteArray::size_type}pp', container_ptr) doesn't work with CDB, + # so be explicit: + data_pos = container_ptr + (2 * d.ptrSize() if d.qtVersion() >= 0x060000 else 8) + elements_pos = data_pos + (3 * d.ptrSize() if d.qtVersion() >= 0x060000 else d.ptrSize()) + elements_data_ptr, elements_size = d.vectorData(elements_pos) + elements_size = int(elements_size / 2) + d.putItemCount(elements_size) + if d.isExpanded(): + bytedata, _, _ = d.qArrayDataHelper(data_pos) + with Children(d, maxNumChild=1000): + for i in range(elements_size): + key = qdumpHelper_QCborArray_valueAt(d, container_ptr, elements_data_ptr, 2 * i, bytedata, is_cbor) + val = qdumpHelper_QCborArray_valueAt(d, container_ptr, elements_data_ptr, 2 * i + 1, bytedata, is_cbor) + d.putPairItem(i, (key, val), 'key', 'value') + + +def qdump__QCborMap(d, value): + container_ptr = value.extractPointer() + qdumpHelper_QCbor_map(d, container_ptr, True) + + +def qdump__QCborValue(d, value): + item_data, container_ptr, item_type = value.split('qpi') + d.putItem(d.createProxyValue((item_data, container_ptr, item_type, True), 'QCborValue_proxy')) + d.putPlainChildren(value) + +def qdump__QCborValue_proxy(d, value): + item_data, container_ptr, item_type, is_cbor = value.data() + + def typename(key, is_cbor): + if is_cbor: + return { + 'invalid' : 'QCborValue (Invalid)', + 'int' : 'QCborValue (Integer)', + 'false' : 'QCborValue (False)', + 'true' : 'QCborValue (True)', + 'null' : 'QCborValue (Null)', + 'undefined' : 'QCborValue (Undefined)', + 'double' : 'QCborValue (Double)', + 'bytes' : 'QCborValue (ByteArray)', + 'string' : 'QCborValue (String)', + 'map' : 'QCborValue (Map)', + 'array' : 'QCborValue (Array)' + }.get(key, 'QCborValue (Unknown)') + else: + return { + 'invalid' : 'QJsonValue (Invalid)', + 'int' : 'QJsonValue (Number)', + 'false' : 'QJsonValue (Bool)', + 'true' : 'QJsonValue (Bool)', + 'null' : 'QJsonValue (Null)', + 'undefined' : 'QJsonValue (Undefined)', + 'double' : 'QJsonValue (Number)', + 'bytes' : 'QJsonValue (ByteArray)', + 'string' : 'QJsonValue (String)', + 'map' : 'QJsonValue (Object)', + 'array' : 'QJsonValue (Array)' + }.get(key, 'QJsonValue (Unknown)') + + + if item_type == 0xffffffffffffffff: + d.putType(typename('invalid', is_cbor)) + d.putValue('Invalid') + + elif item_type == 0x00: + d.putType(typename('int', is_cbor)) + d.putValue(item_data) + + elif item_type == 0x100 + 20: + d.putType(typename('false', is_cbor)) + d.putValue('False') + + elif item_type == 0x100 + 21: + d.putType(typename('true', is_cbor)) + d.putValue('True') + + elif item_type == 0x100 + 22: + d.putType(typename('null', is_cbor)) + d.putValue('Null') + + elif item_type == 0x100 + 23: + d.putType(typename('undefined', is_cbor)) + d.putValue('Undefined') + + elif item_type == 0x202: + val = struct.unpack('d', struct.pack('q', item_data)) + d.putType(typename('double', is_cbor)) + d.putValue('%f' % val) + + elif item_type == 0x40: + d.putType(typename('bytes', is_cbor)) + qdumpHelper_QCbor_string(d, container_ptr, item_data, True) + + elif item_type == 0x60: + d.putType(typename('string', is_cbor)) + qdumpHelper_QCbor_string(d, container_ptr, item_data, False) + + elif item_type == 0x80: + d.putType(typename('array', is_cbor)) + qdumpHelper_QCbor_array(d, container_ptr, is_cbor) + + elif item_type == 0xa0: + d.putType(typename('map', is_cbor)) + qdumpHelper_QCbor_map(d, container_ptr, is_cbor) + + elif item_type == 0x10000: + d.putType('QCborValue (DateTime)') + d.putValue('DateTime') + + elif item_type == 0x10020: + d.putType('QCborValue (Url)') + d.putValue('Url') + + elif item_type == 0x10023: + d.putType('QCborValue (RegularExpression)') + d.putValue('RegularExpression') + + elif item_type == 0x10025: + d.putType('QCborValue (Uuid)') + d.putValue('Uuid') + + else: + d.putType('QCborValue (Unknown)') + d.putValue(item_data) + + +def qendian_helper(d, value, code, endian): + data = value.data() + size = len(data) + v = struct.unpack_from(endian + code, data) + d.putValue('%d (%s)' % (v[0], 'LE' if endian == '<' else 'BE')) + d.putNumChild(size) + if d.isExpanded(): + s = struct.unpack_from(endian + 'b' * size, data) + with Children(d): + for i in range(size): + with SubItem(d, '[%d]' % i): + d.putValue(s[i]) + d.putType('byte') + + +def qdump__quint16_le(d, value): + qendian_helper(d, value, 'H', '<') + +def qdump__quint32_le(d, value): + qendian_helper(d, value, 'I', '<') + +def qdump__quint64_le(d, value): + qendian_helper(d, value, 'Q', '<') + +def qdump__quint16_be(d, value): + qendian_helper(d, value, 'H', '>') + +def qdump__quint32_be(d, value): + qendian_helper(d, value, 'I', '>') + +def qdump__quint64_be(d, value): + qendian_helper(d, value, 'Q', '>') diff --git a/share/qtcreator/debugger/python2/stdtypes.py b/share/qtcreator/debugger/python2/stdtypes.py new file mode 100644 index 00000000000..c14812dee1d --- /dev/null +++ b/share/qtcreator/debugger/python2/stdtypes.py @@ -0,0 +1,1115 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +from utils import DisplayFormat +from dumper import Children, SubItem, DumperBase + + +def qform__std__array(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std__array(d, value): + size = value.type[1] + d.putItemCount(size) + if d.isExpanded(): + d.putPlotData(value.address(), size, value.type[0]) + + +def qdump__std__function(d, value): + (ptr, dummy1, manager, invoker) = value.split('pppp') + if manager: + if ptr > 2: + d.putSymbolValue(ptr) + else: + d.putEmptyValue() + d.putBetterType(value.type) + else: + d.putValue('(null)') + d.putPlainChildren(value) + + +def qdump__std__complex(d, value): + innerType = value.type[0] + (real, imag) = value.split('{%s}{%s}' % (innerType.name, innerType.name)) + d.putValue("(%s, %s)" % (real.display(), imag.display())) + d.putExpandable() + if d.isExpanded(): + with Children(d, 2, childType=innerType): + d.putSubItem("real", real) + d.putSubItem("imag", imag) + + +def qdump__std__deque(d, value): + if d.isQnxTarget(): + qdumpHelper__std__deque__qnx(d, value) + elif d.isMsvcTarget(): + qdumpHelper__std__deque__msvc(d, value) + elif value.hasMember("_M_impl"): + qdumpHelper__std__deque__libstdcxx(d, value) + elif value.hasMember("__start_"): + qdumpHelper__std__deque__libcxx(d, value) + elif value.type.size() == 10 * d.ptrSize(): + qdumpHelper__std__deque__libstdcxx(d, value) + elif value.type.size() == 6 * d.ptrSize(): + qdumpHelper__std__deque__libcxx(d, value) + else: + qdumpHelper__std__deque__libstdcxx(d, value) + + +def qdumpHelper__std__deque__libstdcxx(d, value): + innerType = value.type[0] + innerSize = innerType.size() + bufsize = 1 + if innerSize < 512: + bufsize = 512 // innerSize + + (mapptr, mapsize, startCur, startFirst, startLast, startNode, + finishCur, finishFirst, finishLast, finishNode) = value.split("pppppppppp") + + size = bufsize * ((finishNode - startNode) // d.ptrSize() - 1) + size += (finishCur - finishFirst) // innerSize + size += (startLast - startCur) // innerSize + + d.check(0 <= size and size <= 1000 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + with Children(d, size, maxNumChild=2000, childType=innerType): + pcur = startCur + plast = startLast + pnode = startNode + for i in d.childRange(): + d.putSubItem(i, d.createValue(pcur, innerType)) + pcur += innerSize + if pcur == plast: + newnode = pnode + d.ptrSize() + pfirst = d.extractPointer(newnode) + plast = pfirst + bufsize * d.ptrSize() + pcur = pfirst + pnode = newnode + + +def qdumpHelper__std__deque__libcxx(d, value): + mptr, mfirst, mbegin, mend, start, size = value.split("pppptt") + d.check(0 <= size and size <= 1000 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + innerType = value.type[0] + innerSize = innerType.size() + ptrSize = d.ptrSize() + bufsize = (4096 // innerSize) if innerSize < 256 else 16 + with Children(d, size, maxNumChild=2000, childType=innerType): + for i in d.childRange(): + k, j = divmod(start + i, bufsize) + base = d.extractPointer(mfirst + k * ptrSize) + d.putSubItem(i, d.createValue(base + j * innerSize, innerType)) + + +def qdumpHelper__std__deque__qnx(d, value): + innerType = value.type[0] + innerSize = innerType.size() + if innerSize <= 1: + bufsize = 16 + elif innerSize <= 2: + bufsize = 8 + elif innerSize <= 4: + bufsize = 4 + elif innerSize <= 8: + bufsize = 2 + else: + bufsize = 1 + + try: + val = value['_Mypair']['_Myval2'] + except: + val = value + + myoff = val['_Myoff'].integer() + mysize = val['_Mysize'].integer() + mapsize = val['_Mapsize'].integer() + + d.check(0 <= mapsize and mapsize <= 1000 * 1000 * 1000) + d.putItemCount(mysize) + if d.isExpanded(): + with Children(d, mysize, maxNumChild=2000, childType=innerType): + map = val['_Map'] + for i in d.childRange(): + block = myoff / bufsize + offset = myoff - (block * bufsize) + if mapsize <= block: + block -= mapsize + d.putSubItem(i, map[block][offset]) + myoff += 1 + + +def qdumpHelper__std__deque__msvc(d, value): + innerType = value.type[0] + innerSize = innerType.size() + if innerSize <= 1: + bufsize = 16 + elif innerSize <= 2: + bufsize = 8 + elif innerSize <= 4: + bufsize = 4 + elif innerSize <= 8: + bufsize = 2 + else: + bufsize = 1 + + (proxy, map, mapsize, myoff, mysize) = value.split("ppppp") + + d.check(0 <= mapsize and mapsize <= 1000 * 1000 * 1000) + d.putItemCount(mysize) + if d.isExpanded(): + with Children(d, mysize, maxNumChild=2000, childType=innerType): + for i in d.childRange(): + if myoff >= bufsize * mapsize: + myoff = 0 + buf = map + ((myoff // bufsize) * d.ptrSize()) + address = d.extractPointer(buf) + ((myoff % bufsize) * innerSize) + d.putSubItem(i, d.createValue(address, innerType)) + myoff += 1 + + +def qdump__std____debug__deque(d, value): + qdump__std__deque(d, value) + + +def qdump__std__list(d, value): + if d.isQnxTarget() or d.isMsvcTarget(): + qdump__std__list__QNX(d, value) + return + + if value.type.size() == 3 * d.ptrSize(): + # C++11 only. + (dummy1, dummy2, size) = value.split("ppp") + d.putItemCount(size) + else: + # Need to count manually. + p = d.extractPointer(value) + head = value.address() + size = 0 + while head != p and size < 1001: + size += 1 + p = d.extractPointer(p) + d.putItemCount(size, 1000) + + if d.isExpanded(): + p = d.extractPointer(value) + innerType = value.type[0] + with Children(d, size, maxNumChild=1000, childType=innerType): + for i in d.childRange(): + d.putSubItem(i, d.createValue(p + 2 * d.ptrSize(), innerType)) + p = d.extractPointer(p) + + +def qdump__std__list__QNX(d, value): + if d.isDebugBuild is None: + try: + _ = value["_Mypair"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except Exception: + d.isDebugBuild = False + if d.isDebugBuild: + (proxy, head, size) = value.split("ppp") + else: + (head, size) = value.split("pp") + + d.putItemCount(size, 1000) + + if d.isExpanded(): + p = d.extractPointer(head) + innerType = value.type[0] + with Children(d, size, maxNumChild=1000, childType=innerType): + for i in d.childRange(): + d.putSubItem(i, d.createValue(p + 2 * d.ptrSize(), innerType)) + p = d.extractPointer(p) + + +def qdump__std____debug__list(d, value): + qdump__std__list(d, value) + + +def qdump__std____cxx11__list(d, value): + qdump__std__list(d, value) + + +def qform__std__map(): + return [DisplayFormat.CompactMap] + + +def qdump__std__map(d, value): + if d.isQnxTarget() or d.isMsvcTarget(): + qdump_std__map__helper(d, value) + return + + # stuff is actually (color, pad) with 'I@', but we can save cycles/ + (compare, stuff, parent, left, right) = value.split('ppppp') + size = value["_M_t"]["_M_impl"]["_M_node_count"].integer() + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.putItemCount(size) + + if d.isExpanded(): + keyType = value.type[0] + valueType = value.type[1] + with Children(d, size, maxNumChild=1000): + node = value["_M_t"]["_M_impl"]["_M_header"]["_M_left"] + nodeSize = node.dereference().type.size() + typeCode = "@{%s}@{%s}" % (keyType.name, valueType.name) + for i in d.childRange(): + (pad1, key, pad2, value) = d.split(typeCode, node.pointer() + nodeSize) + d.putPairItem(i, (key, value)) + if node["_M_right"].pointer() == 0: + parent = node["_M_parent"] + while True: + if node.pointer() != parent["_M_right"].pointer(): + break + node = parent + parent = parent["_M_parent"] + if node["_M_right"] != parent: + node = parent + else: + node = node["_M_right"] + while True: + if node["_M_left"].pointer() == 0: + break + node = node["_M_left"] + + +def qdump_std__map__helper(d, value): + if d.isDebugBuild is None: + try: + _ = value["_Mypair"]["_Myval2"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except Exception: + d.isDebugBuild = False + if d.isDebugBuild: + (proxy, head, size) = value.split("ppp") + else: + (head, size) = value.split("pp") + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + keyType = value.type[0] + valueType = value.type[1] + pairType = value.type[3][0] + + def helper(node): + (left, parent, right, color, isnil, pad, pair) = d.split( + "pppcc@{%s}" % (pairType.name), node) + if left != head: + for res in helper(left): + yield res + yield pair.split("{%s}@{%s}" % (keyType.name, valueType.name))[::2] + if right != head: + for res in helper(right): + yield res + + (smallest, root) = d.split("pp", head) + with Children(d, size, maxNumChild=1000): + for (pair, i) in zip(helper(root), d.childRange()): + d.putPairItem(i, pair) + + +def qdump__std____debug__map(d, value): + qdump__std__map(d, value) + + +def qdump__std____debug__set(d, value): + qdump__std__set(d, value) + + +def qdump__std__multiset(d, value): + qdump__std__set(d, value) + + +def qdump__std____cxx1998__map(d, value): + qdump__std__map(d, value) + + +def qform__std__multimap(): + return [DisplayFormat.CompactMap] + + +def qdump__std__multimap(d, value): + return qdump__std__map(d, value) + + +def qdumpHelper__std__tree__iterator(d, value, isSet=False): + treeTypeName = None + if value.type.name.endswith("::iterator"): + treeTypeName = value.type.name[:-len("::iterator")] + elif value.type.name.endswith("::const_iterator"): + treeTypeName = value.type.name[:-len("::const_iterator")] + treeType = d.lookupType(treeTypeName) if treeTypeName else value.type[0] + keyType = treeType[0] + valueType = treeType[1] + node = value["_M_node"].dereference() # std::_Rb_tree_node_base + d.putExpandable() + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + if isSet: + typecode = 'pppp@{%s}' % keyType.name + (color, parent, left, right, pad1, key) = d.split(typecode, node) + d.putSubItem("value", key) + else: + typecode = 'pppp@{%s}@{%s}' % (keyType.name, valueType.name) + (color, parent, left, right, pad1, key, pad2, value) = d.split(typecode, node) + d.putSubItem("first", key) + d.putSubItem("second", value) + with SubItem(d, "[node]"): + d.putExpandable() + d.putEmptyValue() + d.putType(" ") + if d.isExpanded(): + with Children(d): + #d.putSubItem("color", color) + nodeType = node.type.pointer() + d.putSubItem("left", d.createValue(left, nodeType)) + d.putSubItem("right", d.createValue(right, nodeType)) + d.putSubItem("parent", d.createValue(parent, nodeType)) + + +def qdump__std___Rb_tree_iterator(d, value): + qdumpHelper__std__tree__iterator(d, value) + + +def qdump__std___Rb_tree_const_iterator(d, value): + qdumpHelper__std__tree__iterator(d, value) + + +def qdump__std__map__iterator(d, value): + qdumpHelper__std__tree__iterator(d, value) + + +def qdump____gnu_debug___Safe_iterator(d, value): + d.putItem(value["_M_current"]) + + +def qdump__std__map__const_iterator(d, value): + qdumpHelper__std__tree__iterator(d, value) + + +def qdump__std__set__iterator(d, value): + qdumpHelper__std__tree__iterator(d, value, True) + + +def qdump__std__set__const_iterator(d, value): + qdumpHelper__std__tree__iterator(d, value, True) + + +def qdump__std____cxx1998__set(d, value): + qdump__std__set(d, value) + + +def qdumpHelper__std__tree__iterator_MSVC(d, value): + d.putExpandable() + d.putEmptyValue() + if d.isExpanded(): + with Children(d): + childType = value.type[0][0][0] + (proxy, nextIter, node) = value.split("ppp") + (left, parent, right, color, isnil, pad, child) = \ + d.split("pppcc@{%s}" % (childType.name), node) + if (childType.name.startswith("std::pair")): + # workaround that values created via split have no members + keyType = childType[0].name + valueType = childType[1].name + d.putPairItem(None, child.split("{%s}@{%s}" % (keyType, valueType))[::2]) + else: + d.putSubItem("value", child) + + +def qdump__std___Tree_const_iterator(d, value): + qdumpHelper__std__tree__iterator_MSVC(d, value) + + +def qdump__std___Tree_iterator(d, value): + qdumpHelper__std__tree__iterator_MSVC(d, value) + + +def qdump__std__set(d, value): + if d.isQnxTarget() or d.isMsvcTarget(): + qdump__std__set__QNX(d, value) + return + + impl = value["_M_t"]["_M_impl"] + size = impl["_M_node_count"].integer() + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + valueType = value.type[0] + node = impl["_M_header"]["_M_left"] + nodeSize = node.dereference().type.size() + typeCode = "@{%s}" % valueType.name + with Children(d, size, maxNumChild=1000, childType=valueType): + for i in d.childRange(): + (pad, val) = d.split(typeCode, node.pointer() + nodeSize) + d.putSubItem(i, val) + if node["_M_right"].pointer() == 0: + parent = node["_M_parent"] + while node.pointer() == parent["_M_right"].pointer(): + node = parent + parent = parent["_M_parent"] + if node["_M_right"] != parent: + node = parent + else: + node = node["_M_right"] + while node["_M_left"].pointer() != 0: + node = node["_M_left"] + + +def qdump__std__set__QNX(d, value): + if d.isDebugBuild is None: + try: + _ = value["_Mypair"]["_Myval2"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except Exception: + d.isDebugBuild = False + if d.isDebugBuild: + (proxy, head, size) = value.split("ppp") + else: + (head, size) = value.split("pp") + d.check(0 <= size and size <= 100 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + childType = value.type[0] + + def helper(node): + (left, parent, right, color, isnil, pad, value) = d.split( + "pppcc@{%s}" % childType.name, node) + if left != head: + for res in helper(left): + yield res + yield value + if right != head: + for res in helper(right): + yield res + + (smallest, root) = d.split("pp", head) + with Children(d, size, maxNumChild=1000): + for (item, i) in zip(helper(root), d.childRange()): + d.putSubItem(i, item) + + +def std1TreeMin(d, node): + #_NodePtr __tree_min(_NodePtr __x): + # while (__x->__left_ != nullptr) + # __x = __x->__left_; + # return __x; + # + left = node['__left_'] + if left.pointer(): + node = left + return node + + +def std1TreeIsLeftChild(d, node): + # bool __tree_is_left_child(_NodePtr __x): + # return __x == __x->__parent_->__left_; + # + other = node['__parent_']['__left_'] + return node.pointer() == other.pointer() + + +def std1TreeNext(d, node): + #_NodePtr __tree_next(_NodePtr __x): + # if (__x->__right_ != nullptr) + # return __tree_min(__x->__right_); + # while (!__tree_is_left_child(__x)) + # __x = __x->__parent_; + # return __x->__parent_; + # + right = node['__right_'] + if right.pointer(): + return std1TreeMin(d, right) + while not std1TreeIsLeftChild(d, node): + node = node['__parent_'] + return node['__parent_'] + + +def qdump__std__stack(d, value): + d.putItem(value["c"]) + d.putBetterType(value.type) + + +def qdump__std____debug__stack(d, value): + qdump__std__stack(d, value) + + +def qform__std__string(): + return [DisplayFormat.Latin1String, DisplayFormat.SeparateLatin1String, + DisplayFormat.Utf8String, DisplayFormat.SeparateUtf8String] + + +def qdump__std__string(d, value): + qdumpHelper_std__string(d, value, d.createType("char"), d.currentItemFormat()) + + +def qdumpHelper_std__string(d, value, charType, format): + if d.isQnxTarget(): + qdumpHelper__std__string__QNX(d, value, charType, format) + return + if d.isMsvcTarget(): + qdumpHelper__std__string__MSVC(d, value, charType, format) + return + + # GCC 9, QTCREATORBUG-22753 + try: + data = value["_M_dataplus"]["_M_p"].pointer() + except: + data = value.extractPointer() + try: + size = int(value["_M_string_length"]) + d.putCharArrayHelper(data, size, charType, format) + return + except: + pass + + # We can't lookup the std::string::_Rep type without crashing LLDB, + # so hard-code assumption on member position + # struct { size_type _M_length, size_type _M_capacity, int _M_refcount; } + (size, alloc, refcount) = d.split("ppi", data - 3 * d.ptrSize()) + d.check(refcount >= -1) # Can be -1 according to docs. + d.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000) + d.putCharArrayHelper(data, size, charType, format) + + +def qdumpHelper__std__string__QNX(d, value, charType, format): + size = value['_Mysize'] + alloc = value['_Myres'] + _BUF_SIZE = int(16 / charType.size()) + if _BUF_SIZE <= alloc: # (_BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf); + data = value['_Bx']['_Ptr'] + else: + data = value['_Bx']['_Buf'] + sizePtr = data.cast(d.charType().pointer()) + refcount = int(sizePtr[-1]) + d.check(refcount >= -1) # Can be -1 accoring to docs. + d.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000) + d.putCharArrayHelper(sizePtr, size, charType, format) + + +def qdumpHelper__std__string__MSVC(d, value, charType, format): + if d.isDebugBuild is None: + try: + _ = value["_Mypair"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except Exception: + d.isDebugBuild = False + if d.isDebugBuild: + (_, buffer, size, alloc) = value.split("p16spp") + else: + (buffer, size, alloc) = value.split("16spp") + d.check(0 <= size and size <= alloc and alloc <= 100 * 1000 * 1000) + _BUF_SIZE = int(16 / charType.size()) + if _BUF_SIZE <= alloc: + if d.isDebugBuild: + (proxy, data) = value.split("pp") + else: + data = value.extractPointer() + else: + if d.isDebugBuild: + data = value.address() + d.ptrSize() + else: + data = value.address() + d.putCharArrayHelper(data, size, charType, format) + + +def qdump__std____weak_ptr(d, value): + return qdump__std__shared_ptr(d, value) + + +def qdump__std__weak_ptr(d, value): + return qdump__std__shared_ptr(d, value) + + +def qdump__std__shared_ptr(d, value): + if d.isMsvcTarget(): + i = value["_Ptr"] + else: + i = value["_M_ptr"] + + if i.pointer() == 0: + d.putValue("(null)") + else: + d.putItem(i.dereference()) + d.putBetterType(value.type) + + +def qdump__std__unique_ptr(d, value): + if value.type.size() == d.ptrSize(): + p = d.extractPointer(value) + else: + _, p = value.split("pp"); # For custom deleters. + if p == 0: + d.putValue("(null)") + else: + if d.isMsvcTarget(): + try: + d.putItem(value["_Mypair"]["_Myval2"]) + d.putValue(d.currentValue.value, d.currentValue.encoding) + except: + d.putItem(d.createValue(p, value.type[0])) + else: + d.putItem(d.createValue(p, value.type[0])) + d.putBetterType(value.type) + + +def qdump__std__pair(d, value): + typeCode = '{%s}@{%s}' % (value.type[0].name, value.type[1].name) + first, pad, second = value.split(typeCode) + with Children(d): + key = d.putSubItem('first', first) + value = d.putSubItem('second', second) + key = key.value if key.encoding is None else "..." + value = value.value if value.encoding is None else "..." + d.putValue('(%s, %s)' % (key, value)) + + +def qform__std__unordered_map(): + return [DisplayFormat.CompactMap] + + +def qform__std____debug__unordered_map(): + return [DisplayFormat.CompactMap] + + +def qdump__std__unordered_map(d, value): + if d.isQnxTarget(): + qdump__std__list__QNX(d, value["_List"]) + return + + if d.isMsvcTarget(): + _list = value["_List"] + if d.isDebugBuild is None: + try: + _ = _list["_Mypair"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except Exception: + d.isDebugBuild = False + if d.isDebugBuild: + (_, start, size) = _list.split("ppp") + else: + (start, size) = _list.split("pp") + else: + try: + # gcc ~= 4.7 + size = value["_M_element_count"].integer() + start = value["_M_before_begin"]["_M_nxt"] + except: + try: + # libc++ (Mac) + size = value["_M_h"]["_M_element_count"].integer() + start = value["_M_h"]["_M_bbegin"]["_M_node"]["_M_nxt"] + except: + try: + # gcc 4.9.1 + size = value["_M_h"]["_M_element_count"].integer() + start = value["_M_h"]["_M_before_begin"]["_M_nxt"] + except: + # gcc 4.6.2 + size = value["_M_element_count"].integer() + start = value["_M_buckets"].dereference() + # FIXME: Pointer-aligned? + d.putItemCount(size) + # We don't know where the data is + d.putNumChild(0) + return + + d.putItemCount(size) + if d.isExpanded(): + keyType = value.type[0] + valueType = value.type[1] + if d.isMsvcTarget(): + typeCode = 'pp@{%s}@{%s}' % (keyType.name, valueType.name) + p = d.extractPointer(start) + else: + typeCode = 'p@{%s}@{%s}' % (keyType.name, valueType.name) + p = start.pointer() + with Children(d, size): + for i in d.childRange(): + if d.isMsvcTarget(): + p, _, _, key, _, val = d.split(typeCode, p) + else: + p, _, key, _, val = d.split(typeCode, p) + d.putPairItem(i, (key, val)) + + +def qdump__std____debug__unordered_map(d, value): + qdump__std__unordered_map(d, value) + + +def qform__std__unordered_multimap(): + return qform__std__unordered_map() + + +def qform__std____debug__unordered_multimap(): + return qform__std____debug__unordered_map() + + +def qdump__std__unordered_multimap(d, value): + qdump__std__unordered_map(d, value) + + +def qdump__std____debug__unordered_multimap(d, value): + qdump__std__unordered_multimap(d, value) + + +def qdump__std__unordered_set(d, value): + if d.isQnxTarget() or d.isMsvcTarget(): + qdump__std__list__QNX(d, value["_List"]) + return + + try: + # gcc ~= 4.7 + size = value["_M_element_count"].integer() + start = value["_M_before_begin"]["_M_nxt"] + offset = 0 + except: + try: + # libc++ (Mac) + size = value["_M_h"]["_M_element_count"].integer() + start = value["_M_h"]["_M_bbegin"]["_M_node"]["_M_nxt"] + offset = 0 + except: + try: + # gcc 4.6.2 + size = value["_M_element_count"].integer() + start = value["_M_buckets"].dereference() + offset = d.ptrSize() + except: + # gcc 4.9.1 + size = value["_M_h"]["_M_element_count"].integer() + start = value["_M_h"]["_M_before_begin"]["_M_nxt"] + offset = 0 + + d.putItemCount(size) + if d.isExpanded(): + p = start.pointer() + valueType = value.type[0] + with Children(d, size, childType=valueType): + ptrSize = d.ptrSize() + for i in d.childRange(): + d.putSubItem(i, d.createValue(p + ptrSize - offset, valueType)) + p = d.extractPointer(p + offset) + + +def qdump__std____debug__unordered_set(d, value): + qdump__std__unordered_set(d, value) + + +def qdump__std__unordered_multiset(d, value): + qdump__std__unordered_set(d, value) + + +def qdump__std____debug__unordered_multiset(d, value): + qdump__std__unordered_multiset(d, value) + + +def qform__std__valarray(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std__valarray(d, value): + if d.isMsvcTarget(): + (data, size) = value.split('pp') + else: + (size, data) = value.split('pp') + d.putItemCount(size) + d.putPlotData(data, size, value.type[0]) + + +def qdump__std__variant(d, value): + if d.isMsvcTarget(): + which = int(value["_Which"]) + else: + which = int(value["_M_index"]) + type = d.templateArgument(value.type, which) + + if d.isMsvcTarget(): + storage = value + while which > 0: + storage = storage["_Tail"] + which = which - 1 + storage = storage["_Head"] + else: + storage = value["_M_u"]["_M_first"]["_M_storage"] + storage = storage.cast(type) + d.putItem(storage) + d.putBetterType(type) + + +def qform__std__vector(): + return [DisplayFormat.ArrayPlot] + + +def qedit__std__vector(d, value, data): + import gdb + values = data.split(',') + n = len(values) + innerType = value.type[0].name + cmd = "set $d = (%s*)calloc(sizeof(%s)*%s,1)" % (innerType, innerType, n) + gdb.execute(cmd) + cmd = "set {void*[3]}%s = {$d, $d+%s, $d+%s}" % (value.address(), n, n) + gdb.execute(cmd) + cmd = "set (%s[%d])*$d={%s}" % (innerType, n, data) + gdb.execute(cmd) + + +def qdump__std__vector(d, value): + if d.isQnxTarget() or d.isMsvcTarget(): + qdumpHelper__std__vector__msvc(d, value) + elif value.hasMember("_M_impl"): + qdumpHelper__std__vector__libstdcxx(d, value) + elif value.hasMember("__begin_"): + qdumpHelper__std__vector__libcxx(d, value) + else: + qdumpHelper__std__vector__libstdcxx(d, value) + + +def qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type): + size = int((finish - start) / inner_type.size()) + d.check(finish <= alloc) + if size > 0: + d.checkPointer(start) + d.checkPointer(finish) + d.checkPointer(alloc) + d.check(0 <= size and size <= 1000 * 1000 * 1000) + d.putItemCount(size) + d.putPlotData(start, size, inner_type) + + +def qdumpHelper__std__vector__bool(d, start, size, inner_type): + d.check(0 <= size and size <= 1000 * 1000 * 1000) + d.putItemCount(size) + if d.isExpanded(): + with Children(d, size, maxNumChild=10000, childType=inner_type): + for i in d.childRange(): + q = start + int(i / 8) + with SubItem(d, i): + d.putValue((int(d.extractPointer(q)) >> (i % 8)) & 1) + d.putType("bool") + + +def qdumpHelper__std__vector__libstdcxx(d, value): + inner_type = value.type[0] + if inner_type.name == "bool": + start = value["_M_start"]["_M_p"].pointer() + soffset = value["_M_start"]["_M_offset"].integer() + finish = value["_M_finish"]["_M_p"].pointer() + foffset = value["_M_finish"]["_M_offset"].integer() + alloc = value["_M_end_of_storage"].pointer() + size = (finish - start) * 8 + foffset - soffset # 8 is CHAR_BIT. + qdumpHelper__std__vector__bool(d, start, size, inner_type) + else: + start = value["_M_start"].pointer() + finish = value["_M_finish"].pointer() + alloc = value["_M_end_of_storage"].pointer() + qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type) + + +def qdumpHelper__std__vector__libcxx(d, value): + inner_type = value.type[0] + if inner_type.name == "bool": + start = value["__begin_"].pointer() + size = value["__size_"].integer() + qdumpHelper__std__vector__bool(d, start, size, inner_type) + else: + start = value["__begin_"].pointer() + finish = value["__end_"].pointer() + alloc = value["__end_cap_"].pointer() + qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type) + + +def qdumpHelper__std__vector__msvc(d, value): + inner_type = value.type[0] + if inner_type.name == "bool": + if d.isDebugBuild is None: + try: + _ = value["_Myproxy"] + d.isDebugBuild = True + except RuntimeError: + d.isDebugBuild = False + if d.isDebugBuild: + proxy1, proxy2, start, finish, alloc, size = value.split("pppppi") + else: + start, finish, alloc, size = value.split("pppi") + d.check(0 <= size and size <= 1000 * 1000 * 1000) + qdumpHelper__std__vector__bool(d, start, size, inner_type) + else: + if d.isDebugBuild is None: + try: + _ = value["_Mypair"]["_Myval2"]["_Myproxy"] + d.isDebugBuild = True + except RuntimeError: + d.isDebugBuild = False + if d.isDebugBuild: + proxy, start, finish, alloc = value.split("pppp") + else: + start, finish, alloc = value.split("ppp") + size = (finish - start) // inner_type.size() + d.check(0 <= size and size <= 1000 * 1000 * 1000) + qdumpHelper__std__vector__nonbool(d, start, finish, alloc, inner_type) + + +def qform__std____debug__vector(): + return [DisplayFormat.ArrayPlot] + + +def qdump__std____debug__vector(d, value): + qdump__std__vector(d, value) + + +def qdump__std__initializer_list(d, value): + innerType = value.type[0] + if d.isMsvcTarget(): + start = value["_First"].pointer() + end = value["_Last"].pointer() + size = int((end - start) / innerType.size()) + else: + try: + start = value["_M_array"].pointer() + size = value["_M_len"].integer() + except: + start = value["__begin_"].pointer() + size = value["__size_"].integer() + + d.putItemCount(size) + if d.isExpanded(): + d.putPlotData(start, size, innerType) + + +def qedit__std__string(d, value, data): + d.call('void', value, 'assign', '"%s"' % data.replace('"', '\\"')) + + +def qedit__string(d, expr, value): + qedit__std__string(d, expr, value) + + +def qedit__std____cxx11__string(d, expr, value): + qedit__std__string(d, expr, value) + + +def qedit__std__wstring(d, value, data): + d.call('void', value, 'assign', 'L"%s"' % data.replace('"', '\\"')) + + +def qedit__wstring(d, expr, value): + qedit__std__wstring(d, expr, value) + + +def qedit__std____cxx11__wstring(d, expr, value): + qedit__std__wstring(d, expr, value) + + +def qdump__string(d, value): + qdump__std__string(d, value) + + +def qform__std__wstring(): + return [DisplayFormat.Simple, DisplayFormat.Separate] + + +def qdump__std__wstring(d, value): + qdumpHelper_std__string(d, value, d.createType('wchar_t'), d.currentItemFormat()) + + +def qdump__std__basic_string(d, value): + innerType = value.type[0] + qdumpHelper_std__string(d, value, innerType, d.currentItemFormat()) + + +def qdump__std____cxx11__basic_string(d, value): + innerType = value.type[0] + try: + data = value["_M_dataplus"]["_M_p"].pointer() + size = int(value["_M_string_length"]) + except: + d.putEmptyValue() + d.putPlainChildren(value) + return + d.check(0 <= size) # and size <= alloc and alloc <= 100*1000*1000) + d.putCharArrayHelper(data, size, innerType, d.currentItemFormat()) + + +def qform__std____cxx11__string(d, value): + qform__std__string(d, value) + + +def qdump__std____cxx11__string(d, value): + (data, size) = value.split("pI") + d.check(0 <= size) # and size <= alloc and alloc <= 100*1000*1000) + d.putCharArrayHelper(data, size, d.charType(), d.currentItemFormat()) + +# Needed only to trigger the form report above. + + +def qform__std____cxx11__string(): + return qform__std__string() + + +def qform__std____cxx11__wstring(): + return qform__std__wstring() + + +def qdump__wstring(d, value): + qdump__std__wstring(d, value) + + +def qdump__std__once_flag(d, value): + d.putValue(value.split("i")[0]) + d.putBetterType(value.type) + d.putPlainChildren(value) + + +def qdump____gnu_cxx__hash_set(d, value): + ht = value["_M_ht"] + size = ht["_M_num_elements"].integer() + d.check(0 <= size and size <= 1000 * 1000 * 1000) + d.putItemCount(size) + innerType = value.type[0] + d.putType("__gnu__cxx::hash_set<%s>" % innerType.name) + if d.isExpanded(): + with Children(d, size, maxNumChild=1000, childType=innerType): + buckets = ht["_M_buckets"]["_M_impl"] + bucketStart = buckets["_M_start"] + bucketFinish = buckets["_M_finish"] + p = bucketStart + itemCount = 0 + for i in range((bucketFinish.pointer() - bucketStart.pointer()) // d.ptrSize()): + if p.dereference().pointer(): + cur = p.dereference() + while cur.pointer(): + d.putSubItem(itemCount, cur["_M_val"]) + cur = cur["_M_next"] + itemCount += 1 + p = p + 1 + + +def qdump__uint8_t(d, value): + d.putValue(value.integer()) + + +def qdump__int8_t(d, value): + d.putValue(value.integer()) + + +def qdump__std__byte(d, value): + d.putValue(value.integer()) + + +def qdump__std__optional(d, value): + innerType = value.type[0] + (payload, pad, initialized) = d.split('{%s}@b' % innerType.name, value) + if initialized: + d.putItem(payload) + d.putBetterType(innerType) + else: + d.putSpecialValue("empty") + + +def qdump__std__experimental__optional(d, value): + qdump__std__optional(d, value) diff --git a/share/qtcreator/debugger/python2/utils.py b/share/qtcreator/debugger/python2/utils.py new file mode 100644 index 00000000000..8019d1e530a --- /dev/null +++ b/share/qtcreator/debugger/python2/utils.py @@ -0,0 +1,129 @@ +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h + + +# MT: Why does this not match (anymore?) to debuggerconstants.h : DebuggerStartMode ? +class DebuggerStartMode(): + ( + NoStartMode, + StartInternal, + StartExternal, + AttachExternal, + AttachCrashedExternal, + AttachCore, + AttachToRemoteServer, + AttachToRemoteProcess, + StartRemoteProcess, + ) = range(0, 9) + + +# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h +class DisplayFormat(): + ( + Automatic, + Raw, + Simple, + Enhanced, + Separate, + Latin1String, + SeparateLatin1String, + Utf8String, + SeparateUtf8String, + Local8BitString, + Utf16String, + Ucs4String, + Array10, + Array100, + Array1000, + Array10000, + ArrayPlot, + CompactMap, + DirectQListStorage, + IndirectQListStorage, + ) = range(0, 20) + + +# Breakpoints. Keep synchronized with BreakpointType in breakpoint.h +class BreakpointType(): + ( + UnknownType, + BreakpointByFileAndLine, + BreakpointByFunction, + BreakpointByAddress, + BreakpointAtThrow, + BreakpointAtCatch, + BreakpointAtMain, + BreakpointAtFork, + BreakpointAtExec, + BreakpointAtSysCall, + WatchpointAtAddress, + WatchpointAtExpression, + BreakpointOnQmlSignalEmit, + BreakpointAtJavaScriptThrow, + ) = range(0, 14) + + +# Internal codes for types. Keep in sync with cdbextensions pytype.cpp +class TypeCode(): + ( + Typedef, + Struct, + Void, + Integral, + Float, + Enum, + Pointer, + Array, + Complex, + Reference, + Function, + MemberPointer, + FortranString, + Unresolvable, + Bitfield, + RValueReference, + ) = range(0, 16) + + +# Internal codes for logging channels. Keep in sync woth debuggerconstants.h +class LogChannel(): + ( + LogInput, # Used for user input + LogMiscInput, # Used for misc stuff in the input pane + LogOutput, + LogWarning, + LogError, + LogStatus, # Used for status changed messages + LogTime, # Used for time stamp messages + LogDebug, + LogMisc, + AppOutput, # stdout + AppError, # stderr + AppStuff, # (possibly) windows debug channel + StatusBar, # LogStatus and also put to the status bar + ConsoleOutput # Used to output to console + ) = range(0, 14) + + +def isIntegralTypeName(name): + return name in ( + "int", + "unsigned int", + "signed int", + "short", + "unsigned short", + "long", + "unsigned long", + "long long", + "unsigned long long", + "char", + "signed char", + "unsigned char", + "bool", + ) + + +def isFloatingPointTypeName(name): + return name in ("float", "double", "long double")