From 5efa84830bb41bfc11db9295df3ad34f5b9bd9f5 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 26 Sep 2016 14:29:16 +0200 Subject: [PATCH] Debugger: Remove use of native values in dumper.py Lowers the barrier for other debugger backends. Change-Id: I09e0ad09548b6b4220175245cc0d845ac5aa29d0 Reviewed-by: Christian Stenger Reviewed-by: hjk --- share/qtcreator/debugger/README.txt | 20 +- share/qtcreator/debugger/dumper.py | 357 +++++++++---------------- share/qtcreator/debugger/gdbbridge.py | 121 +++------ share/qtcreator/debugger/lldbbridge.py | 126 +++------ share/qtcreator/debugger/qttypes.py | 12 +- share/qtcreator/debugger/stdtypes.py | 14 +- tests/auto/debugger/tst_dumpers.cpp | 30 ++- 7 files changed, 249 insertions(+), 431 deletions(-) diff --git a/share/qtcreator/debugger/README.txt b/share/qtcreator/debugger/README.txt index 7d9323e0cab..7a0f6247023 100644 --- a/share/qtcreator/debugger/README.txt +++ b/share/qtcreator/debugger/README.txt @@ -89,14 +89,18 @@ class Value: class Type: name() -> string # Full name of this type bitsize() -> int # Size of type in bits - arrayType() -> bool # Is this an array? - pointerType() -> bool # Is this a pointer - referenceType() -> bool # ... - functionType() -> bool - typedefedType() -> bool - enumType() -> bool - integralType() -> bool - floatingPointType() -> bool + code() -> TypeCodeTypedef + | TypeCodeStruct + | TypeCodeVoid + | TypeCodeIntegral + | TypeCodeFloat + | TypeCodeEnum + | TypeCodePointer + | TypeCodeArray + | TypeCodeComplex + | TypeCodeReference + | TypeCodeFunction + | TypeCodeMemberPointer unqualified() -> Type # Type without const/volatile target() -> Type # Type dereferenced if it is a pointer type, element if array etc diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 1dd8960d7e0..6783235336b 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -212,8 +212,7 @@ class Children: return True class PairedChildrenData: - def __init__(self, d, pairType, keyType, valueType, useKeyAndValue): - self.useKeyAndValue = useKeyAndValue + def __init__(self, d, pairType, keyType, valueType): self.pairType = pairType self.keyType = keyType self.valueType = valueType @@ -228,7 +227,9 @@ class PairedChildren(Children): keyType = pairType[0].unqualified() if valueType is None: valueType = pairType[1] - d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue) + d.pairData = PairedChildrenData(d, pairType, keyType, valueType) + d.pairData.kname = "key" if useKeyAndValue else "first" + d.pairData.vname = "value" if useKeyAndValue else "second" Children.__init__(self, d, numChild, d.pairData.childType, @@ -671,7 +672,10 @@ class DumperBase: def putIntItem(self, name, value): with SubItem(self, name): - self.putValue(value) + if isinstance(value, self.Value): + self.putValue(value.display()) + else: + self.putValue(value) self.putType("int") self.putNumChild(0) @@ -682,28 +686,23 @@ class DumperBase: self.putNumChild(0) def putPairItem(self, index, pair): - if isinstance(pair, tuple): - (first, second) = pair - elif self.pairData.useKeyAndValue: - (first, second) = (pair["key"], pair["value"]) - else: - (first, second) = (pair["first"], pair["second"]) - + (first, second) = pair if isinstance(pair, tuple) else pair.members() with SubItem(self, index): - self.putNumChild(2) (keystr, keyenc, valstr, valenc) = (None, None, None, None) with Children(self): - with SubItem(self, "key"): - self.putItem(first, True) + with SubItem(self, self.pairData.kname): + self.putItem(first) keystr = self.currentValue.value keyenc = self.currentValue.encoding - with SubItem(self, "value"): - self.putItem(second, True) + with SubItem(self, self.pairData.vname): + self.putItem(second) valstr = self.currentValue.value valenc = self.currentValue.encoding if index is not None: self.put('keyprefix="[%s] ",' % index) - self.put('keyencoded="%s",key="%s",' % (keyenc, keystr)) + self.put('key="%s",' % keystr) + if keyenc is not None: + self.put('keyencoded="%s",' % keyenc) self.putValue(valstr, valenc) def putCallItem(self, name, rettype, value, func, *args): @@ -713,9 +712,8 @@ class DumperBase: except Exception as error: if self.passExceptions: raise error - else: - children = [('error', error)] - self.putSpecialValue("notcallable", children=children) + children = [('error', error)] + self.putSpecialValue("notcallable", children=children) else: self.putItem(result) @@ -772,7 +770,7 @@ class DumperBase: self.put('name="[%s]",' % field.name) self.put('sortgroup="%s"' % (1000 - field.baseIndex)) self.putAddress(baseValue.address()) - self.putItem(baseValue, False) + self.putItem(baseValue) continue with SubItem(self, field.name): @@ -1004,9 +1002,8 @@ class DumperBase: def putCStyleArray(self, value): arrayType = value.type.unqualified() - if self.isGdb and value.nativeValue is not None: - innerType = self.fromNativeType(value.nativeValue[0].type) - else: + innerType = arrayType.ltarget + if innerType is None: innerType = value.type.target() innerTypeName = innerType.unqualified().name address = value.address() @@ -1332,22 +1329,22 @@ class DumperBase: # warn("NO QOBJECT: %s" % value.type) pass - def couldBeQObject(self, objectPtr): - def canBePointer(p): - if self.ptrSize() == 4: - return p > 100000 and (p & 0x3 == 0) - else: - return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff) + def canBePointer(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 couldBeQObject(self, objectPtr): try: (vtablePtr, dd) = self.split('pp', objectPtr) except: self.bump("nostruct-1") return False - if not canBePointer(vtablePtr): + if not self.canBePointer(vtablePtr): self.bump("vtable") return False - if not canBePointer(dd): + if not self.canBePointer(dd): self.bump("d_d_ptr") return False @@ -1358,7 +1355,7 @@ class DumperBase: self.bump("nostruct-2") return False #warn("STRUCT DD: %s 0x%x" % (self.currentIName, qptr)) - if not canBePointer(dvtablePtr): + if not self.canBePointer(dvtablePtr): self.bump("dvtable") #warn("DVT: 0x%x" % dvtablePtr) return False @@ -1367,11 +1364,11 @@ class DumperBase: #warn("QPTR: 0x%x 0x%x" % (qptr, objectPtr)) self.bump("q_ptr") return False - if parentPtr and not canBePointer(parentPtr): + if parentPtr and not self.canBePointer(parentPtr): #warn("PAREN") self.bump("parent") return False - if not canBePointer(childrenDPtr): + if not self.canBePointer(childrenDPtr): #warn("CHILD") self.bump("children") return False @@ -1380,7 +1377,7 @@ class DumperBase: # self.bump("flags") # return False #warn("OK") - #if dynMetaObjectPtr and not canBePointer(dynMetaObjectPtr): + #if dynMetaObjectPtr and not self.canBePointer(dynMetaObjectPtr): # self.bump("dynmo") # return False @@ -1926,13 +1923,13 @@ class DumperBase: displayFormat = self.typeformats.get(needle, AutomaticFormat) return displayFormat - def putSubItem(self, component, value, tryDynamic=True): + def putSubItem(self, component, value): if not isinstance(value, self.Value): error("WRONG VALUE TYPE IN putSubItem: %s" % type(value)) if not isinstance(value.type, self.Type): error("WRONG TYPE TYPE IN putSubItem: %s" % type(value.type)) with SubItem(self, component): - self.putItem(value, tryDynamic) + self.putItem(value) def putArrayData(self, base, n, innerType, childNumChild = None, maxNumChild = 10000): self.checkIntType(base) @@ -2431,20 +2428,17 @@ class DumperBase: return True return False - def putItem(self, value, tryDynamic=True): + def putItem(self, value): #warn("ITEM: %s" % value.stringify()) typeobj = value.type #unqualified() typeName = typeobj.name - tryDynamic &= self.useDynamicType self.addToCache(typeobj) # Fill type cache - if tryDynamic: - self.putAddress(value.address()) + self.putAddress(value.address()) - if not value.isInScope(): + if not value.lIsInScope: self.putSpecialValue("optimizedout") - #self.putValue("optimizedout: %s" % value.nativeValue) #self.putType(typeobj) #self.putSpecialValue('outofscope') self.putNumChild(0) @@ -2500,50 +2494,13 @@ class DumperBase: return if typeobj.code == TypeCodeReference: - try: - # Try to recognize null references explicitly. - if value.address() is 0: - self.putSpecialValue("nullreference") - self.putNumChild(0) - self.putType(typeobj) - return - except: - pass - if self.isLldb: - targetType = value.type.target() - item = value.cast(targetType.pointer()).dereference() - self.putItem(item) - self.putBetterType(value.type.name) - return - + item = value.cast(typeobj.target().pointer()).dereference() else: - if tryDynamic: - try: - # Dynamic references are not supported by gdb, see - # http://sourceware.org/bugzilla/show_bug.cgi?id=14077. - # Find the dynamic type manually using referenced_type. - val = value.referenced_value() - val = val.cast(val.dynamic_type) - self.putItem(val) - self.putBetterType("%s &" % typeobj) - return - except: - pass - - try: - # FIXME: This throws "RuntimeError: Attempt to dereference a - # generic pointer." with MinGW's gcc 4.5 when it "identifies" - # a "QWidget &" as "void &" and with optimized out code. - self.putItem(value.cast(typeobj.target().unqualified())) - self.putBetterType("%s &" % self.currentType.value) - return - except Exception as error: - self.putSpecialValue("optimizedout") - #self.putValue("optimizedout: %s" % error) - self.putType(typeobj) - self.putNumChild(0) - return + item = value.cast(typeobj.target().unqualified()) + self.putItem(item) + self.putBetterType(typeobj.name) + return if typeobj.code == TypeCodeComplex: self.putType(typeobj) @@ -2625,8 +2582,8 @@ class DumperBase: self.type = None self.ldata = None self.laddress = None - self.nativeValue = None self.lIsInScope = True + self.ldisplay = None def check(self): if self.laddress is not None and not self.dumper.isInt(self.laddress): @@ -2639,11 +2596,9 @@ class DumperBase: return self.stringify() def stringify(self): - self.check() addr = "None" if self.laddress is None else ("0x%x" % self.laddress) - return "Value(name='%s',type=%s,data=%s,address=%s,nativeValue=%s)" \ - % (self.name, self.type.stringify(), self.dumper.hexencode(self.ldata), - addr, self.nativeValue) + return "Value(name='%s',type=%s,data=%s,address=%s)" \ + % (self.name, self.type.stringify(), self.dumper.hexencode(self.ldata), addr) def display(self): if self.type.code == TypeCodeEnum: @@ -2651,21 +2606,14 @@ class DumperBase: simple = self.value() if simple is not None: return str(simple) - if self.type.code == TypeCodeComplex: - if self.nativeValue is not None: - if self.dumper.isLldb: - return str(self.nativeValue.GetValue()) - else: - return str( self.nativeValue) - if self.nativeValue is not None: - return str(self.nativeValue) - #return "Value(nativeValue=%s)" % self.nativeValue - if self.ldata is not None: - if sys.version_info[0] == 2 and isinstance(self.ldata, buffer): - return bytes(self.data).encode("hex") - return self.data.encode("hex") + if self.ldisplay is not None: + return self.ldisplay + #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, self.laddress) + return "value of type %s at address 0x%x" % (self.type.name, self.laddress) return "" def simpleDisplay(self, showAddress=True): @@ -2741,66 +2689,52 @@ class DumperBase: return self.extractField(field) def extractField(self, field): - #warn("PARENT BASE 0x%x" % self.address()) - if self.type.code == TypeCodeTypedef: - error("WRONG") + self.dumper.check(self.type.code != TypeCodeTypedef) if not isinstance(field, self.dumper.Field): error("BAD INDEX TYPE %s" % type(field)) - val = None - if self.nativeValue is not None: - #warn("NATIVE, FIELD TYPE: %s " % field) - val = self.dumper.nativeValueChildFromField(self.nativeValue, field) - #warn("BAD INDEX XX VAL: %s TYPE: %s INDEX TYPE: %s " - # % (self, self.type, type(field))) - #warn("FIELD: %s " % field) - fieldType = field.fieldType() fieldBitsize = field.bitsize() fieldSize = None if fieldBitsize is None else fieldBitsize >> 3 - #warn("BITPOS %s BITSIZE: %s" % (fieldBitpos, fieldBitsize)) + fieldBitpos = field.bitpos() + fieldOffset = fieldBitpos >> 3 + fieldBitpos -= fieldOffset * 8 - if val is None: - val = self.dumper.Value(self.dumper) - val.type = fieldType - val.name = field.name + val = self.dumper.Value(self.dumper) + val.name = field.name - if self.laddress is not None: - #warn("ADDRESS") - fieldBitpos = field.bitpos() - fieldOffset = None if fieldBitpos is None else fieldBitpos >> 3 - if fieldBitpos is not None: - #warn("BITPOS: %s" % fieldBitpos) - val.laddress = self.laddress + fieldOffset - else: - error("NO IDEA 1") - elif len(self.ldata) > 0: - #warn("DATA") - fieldBitpos = field.bitpos() - fieldOffset = None if fieldBitpos is None else fieldBitpos >> 3 - if fieldBitpos is not None: - val.ldata = self.ldata[fieldOffset:fieldOffset + fieldSize] - else: - error("NO IDEA 2") - else: - error("NO IDEA 3") + 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) - #warn("BITPOS %s BITSIZE: %s" % (fieldBitpos, fieldBitsize)) if fieldBitsize is not None and fieldBitsize % 8 != 0: - fieldBitpos = field.bitpos() - #warn("CORRECTING: FITPOS %s BITSIZE: %s" % (fieldBitpos, fieldBitsize)) - typeobj = fieldType - typeobj.lbitsize = fieldBitsize data = val.extractInteger(fieldBitsize, True) - data = data >> (fieldBitpos & 3) + data = data >> fieldBitpos data = data & ((1 << fieldBitsize) - 1) val.laddress = None val.ldata = bytes(struct.pack('Q', data)) - val.type = typeobj + + val.type = None + fieldType = field.fieldType() + if val.laddress is not None and fieldType is not None: + if fieldType.code in (TypeCodePointer, TypeCodeReference): + baseType = fieldType.dereference() + address = self.dumper.extractPointer(val.laddress) + dynTypeName = self.dumper.dynamicTypeName(baseType, address) + if dynTypeName is not None: + if fieldType.code == TypeCodePointer: + val.type = self.dumper.createType(dynTypeName + '*') + else: + val.type = self.dumper.createType(dynTypeName + ' &') + if val.type is None: + val.type = fieldType #warn("GOT VAL %s FOR FIELD %s" % (val, field)) val.check() - val.lbitsize = field.bitsize() + val.lbitsize = fieldBitsize return val def members(self): @@ -2813,37 +2747,25 @@ class DumperBase: def __add__(self, other): self.check() if self.dumper.isInt(other): - #warn("OTHER INT: %s" % other) - if self.nativeValue is not None: - #warn("OTHER NATIVE: %s" % self.nativeValue) - #warn("OTHER RESULT 1: %s" % (self.nativeValue + other)) - res = self.dumper.fromNativeValue(self.nativeValue + other) - #warn("OTHER RESULT 2: %s" % (self.nativeValue + other)) - #warn("OTHER COOKED: 0x%x" % res.pointer()) - #warn("OTHER COOKED X: 0x%x" % res.nativeValue) - return res + stripped = self.type.stripTypedefs() + if stripped.code == TypeCodePointer: + address = self.pointer() + stripped.dereference().size() + val = self.dumper.Value(self.dumper) + val.laddress = None + val.ldata = bytes(struct.pack('Q', address)) + val.type = self.type + return val error("BAD DATA TO ADD TO: %s %s" % (self.type, other)) def dereference(self): self.check() - if self.nativeValue is not None: - res = self.dumper.nativeValueDereference(self.nativeValue) - if res is not None: - return res - if self.laddress is not None: - val = self.dumper.Value(self.dumper) - val.type = self.type.dereference() - #val.ldata = bytes(self.dumper.readRawMemory(self.laddress, val.type.size())) - #val.laddress = self.dumper.extractPointer(self.laddress) - #val.ldata = bytes(self.data(self.dumper.ptrSize())) - #val.laddress = self.__int__() - bitsize = self.dumper.ptrSize() * 8 - val.laddress = self.integer() - #warn("DEREFERENCING %s AT 0x%x -- %s TO %s AT 0x%x --- %s" % - # (self.type, self.laddress, self.dumper.hexencode(self.data), - # val.type, val.laddress, self.dumper.hexencode(val.data))) - return val - error("BAD DATA TO DEREFERENCE: %s %s" % (self.type, type(self))) + val = self.dumper.Value(self.dumper) + val.type = self.type.dereference() + val.laddress = self.pointer() + dynTypeName = self.dumper.dynamicTypeName(val.type, val.laddress) + if dynTypeName is not None: + val.type = self.dumper.createType(dynTypeName) + return val def extend(self, size): if self.type.size() < size: @@ -2860,28 +2782,16 @@ class DumperBase: def cast(self, typish): self.check() - typeobj = self.dumper.createType(typish) - if self.nativeValue is not None and typeobj.nativeType is not None: - res = self.dumper.nativeValueCast(self.nativeValue, typeobj.nativeType) - if res is not None: - return res - #error("BAD NATIVE DATA TO CAST: %s %s" % (self.type, typeobj)) val = self.dumper.Value(self.dumper) val.laddress = self.laddress val.ldata = self.ldata - val.type = typeobj - #warn("CAST %s %s" % (self.type.stringify(), typeobj.stringify())) + val.type = self.dumper.createType(typish) return val def downcast(self): self.check() - if self.nativeValue is not None: - return self.dumper.nativeValueDownCast(self.nativeValue) return self - def isInScope(self): - return self.lIsInScope - def address(self): self.check() return self.laddress @@ -2896,38 +2806,28 @@ class DumperBase: return self.ldata if size < len(self.ldata): return self.ldata[:size] - error("DATA PRESENT, BUT NOT BIG ENOUGH: %s WANT: %s" - % (self.stringify(), 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 - if self.nativeValue is not None: - if size is None: - size = self.type.size() - res = self.dumper.nativeValueAsBytes(self.nativeValue, size) - if len(res) > 0: - return res - return res error("CANNOT CONVERT TO BYTES: %s" % self) def extractInteger(self, bitsize, unsigned): self.check() - size = (bitsize + 7) >> 3 - if size == 8: + if bitsize > 32: + size = 8 code = "Q" if unsigned else "q" - elif size == 4: + elif bitsize > 16: + size = 4 code = "I" if unsigned else "i" - elif size == 2: + elif bitsize > 8: + size = 2 code = "H" if unsigned else "h" - elif size == 1: - code = "B" if unsigned else "b" else: - code = None - if code is None: - return None + size = 1 + code = "B" if unsigned else "b" rawBytes = self.data(size) try: return struct.unpack_from(code, rawBytes, 0)[0] @@ -2971,6 +2871,18 @@ class DumperBase: ptr = p if self.isInt(p) else p.pointer() self.readRawMemory(ptr, 1) + def dynamicTypeName(self, baseType, address): + if baseType.code != TypeCodeStruct: + return None + try: + vtbl = self.extractPointer(address) + except: + return None + #warn("VTBL: 0x%x" % vtbl) + if not self.canBePointer(vtbl): + return None + return self.nativeDynamicTypeName(address, baseType) + class Type: def __init__(self, dumper): self.dumper = dumper @@ -2979,6 +2891,7 @@ class DumperBase: self.lfields = None self.lbitsize = None self.lbitpos = None + self.ltarget = None # Inner type for arrays self.templateArguments = None self.code = None @@ -3206,7 +3119,9 @@ class DumperBase: def __str__(self): return ("Field(name='%s',ltype=%s,parentType=%s,bpos=%s,bsize=%s," + "bidx=%s,nidx=%s)") \ - % (self.name, self.ltype, self.parentType, + % (self.name, + None if self.ltype is None else self.ltype.name, + None if self.parentType is None else self.parentType.name, self.lbitpos, self.lbitsize, self.baseIndex, self.nativeIndex) @@ -3294,32 +3209,22 @@ class DumperBase: #warn("CREATE TYPE: %s" % typeobj) typeobj.check() return typeobj - if self.isInt(typish): - # Assume it is an typecode, create an "anonymous" Type - typeobj = self.Type(self) - typeobj.code = typish - typeobj.lbitsize = 8 * size - typeobj.name = ' ' - return typeobj error("NEED TYPE, NOT %s" % type(typish)) def createValue(self, datish, typish): + val = self.Value(self) + val.type = self.createType(typish) if self.isInt(datish): # Used as address. - #warn("CREATING %s AT 0x%x" % (typish, address)) - val = self.Value(self) val.laddress = datish - val.type = self.createType(typish) - val.check() - return val - if isinstance(datish, bytes): - #warn("CREATING %s WITH DATA %s" % (typish, self.hexencode(datish))) - val = self.Value(self) + #warn("CREATING %s AT 0x%x" % (val.type.name, address)) + elif isinstance(datish, bytes): val.ldata = datish - val.type = self.createType(typish) val.type.lbitsize = 8 * len(datish) - val.check() - return val - error("EXPECTING ADDRESS OR BYTES, GOT %s" % type(datish)) + #warn("CREATING %s WITH DATA %s" % (val.type.name, self.hexencode(datish))) + else: + error("EXPECTING ADDRESS OR BYTES, GOT %s" % type(datish)) + val.check() + return val def createListItem(self, data, innerTypish): innerType = self.createType(innerTypish) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index b7e2ce5f028..01c372dbf90 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -248,13 +248,32 @@ class Dumper(DumperBase): return self.fromNativeValue(nativeValue) def fromNativeValue(self, nativeValue): - #self.check(isinstance(nativeType, gdb.Value)) + #self.check(isinstance(nativeValue, gdb.Value)) + nativeType = nativeValue.type val = self.Value(self) - val.nativeValue = nativeValue if not nativeValue.address is None: val.laddress = toInteger(nativeValue.address) - val.type = self.fromNativeType(nativeValue.type) + 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): + buf[i] = int(y[i]) + 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 == gdb.TYPE_CODE_ARRAY: + val.type.ltarget = nativeValue[0].type.unqualified() return val def fromNativeType(self, nativeType): @@ -285,18 +304,6 @@ class Dumper(DumperBase): }[nativeType.code] return typeobj - def nativeValueDereference(self, nativeValue): - return self.nativeValueDownCast(nativeValue.dereference()) - - def nativeValueCast(self, nativeValue, nativeType): - try: - return self.fromNativeValue(nativeValue.cast(nativeType)) - except: - return None - - def nativeValueAddressOf(self, nativeValue): - return toInteger(nativeValue.address) - def nativeTypeDereference(self, nativeType): return self.fromNativeType(nativeType.strip_typedefs().target()) @@ -402,25 +409,6 @@ class Dumper(DumperBase): typeobj = typeobj.strip_typedefs().unqualified() return self.fromNativeType(typeobj) - def nativeValueChildFromField(self, nativeValue, field): - #warn("EXTRACTING: %s FROM %s" % (field, nativeValue)) - if field.nativeIndex is not None: - nativeField = nativeValue.type.fields()[field.nativeIndex] - #warn("NATIVE FIELD: %s" % nativeField) - if nativeField.is_base_class: - return self.fromNativeValue(nativeValue.cast(nativeField.type)) - try: - # Fails with GDB 7.5. - return self.nativeValueDownCast(nativeValue[nativeField]) - except: - # The generic handling is almost good enough, but does not - # downcast the produced values. - return None - if field.name is not None: - return self.nativeValueDownCast(nativeValue[field.name]) - error("FIELD EXTARCTION FAILED: %s" % field) - return None - def listOfLocals(self, partialVar): frame = gdb.selected_frame() @@ -571,20 +559,6 @@ class Dumper(DumperBase): return None return self.fromNativeValue(val) - def nativeValueAsBytes(self, nativeValue, size): - # Assume it's a (backend specific) Value. - if nativeValue.address: - return self.readRawMemory(nativeValue.address, size) - # No address. Possibly the result of an inferior call. - # Note: Only a cast to unsigned char[sizeof(origtype)] succeeds - # in this situation in gdb. - chars = self.lookupNativeType("unsigned char") - y = nativeValue.cast(chars.array(0, int(nativeValue.type.sizeof - 1))) - buf = bytearray(struct.pack('x' * size)) - for i in range(size): - buf[i] = int(y[i]) - return bytes(buf) - def callHelper(self, rettype, value, function, args): # args is a tuple. arg = "" @@ -963,47 +937,16 @@ class Dumper(DumperBase): cmd = "set variable (%s)=%s" % (expr, value) gdb.execute(cmd) - def hasVTable(self, typeobj): - fields = typeobj.fields() - if len(fields) == 0: - return False - if fields[0].isBaseClass: - return hasVTable(fields[0].type) - return str(fields[0].type) == "int (**)(void)" - - def dynamicTypeName(self, value): - if self.hasVTable(value.type): - #vtbl = str(gdb.parse_and_eval("{int(*)(int)}%s" % int(value.address))) - try: - # Fails on 7.1 due to the missing to_string. - vtbl = gdb.execute("info symbol {int*}%s" % int(value.address), - to_string = True) - pos1 = vtbl.find("vtable ") - if pos1 != -1: - pos1 += 11 - pos2 = vtbl.find(" +", pos1) - if pos2 != -1: - return vtbl[pos1 : pos2] - except: - pass - return str(value.type) - - def nativeValueDownCast(self, nativeValue): - try: - return self.fromNativeValue(nativeValue.cast(nativeValue.dynamic_type)) - except: - return self.fromNativeValue(nativeValue) - - def expensiveDowncast(self, value): - try: - return value.cast(value.dynamic_type) - except: - pass - try: - return value.cast(self.lookupType(self.dynamicTypeName(value))) - except: - pass - return value + def nativeDynamicTypeName(self, address, baseType): + vtbl = gdb.execute("info symbol {%s*}0x%x" % (baseType.name, address), to_string = True) + 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 enumExpression(self, enumType, enumValue): return self.qtNamespace() + "Qt::" + enumValue diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 7c21ddb96fd..ba435de20d4 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -57,20 +57,6 @@ def check(exp): if not exp: raise RuntimeError("Check failed") -def impl_SBValue__add__(self, offset): - if self.GetType().IsPointerType(): - itemsize = self.GetType().GetPointeeType().GetByteSize() - address = self.GetValueAsUnsigned() + offset * itemsize - address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned - return self.CreateValueFromAddress(None, address, - self.GetType().GetPointeeType()).AddressOf() - - raise RuntimeError("SBValue.__add__ not implemented: %s" % self.GetType()) - return NotImplemented - - -lldb.SBValue.__add__ = impl_SBValue__add__ - class Dumper(DumperBase): def __init__(self): DumperBase.__init__(self) @@ -154,15 +140,42 @@ class Dumper(DumperBase): def fromNativeValue(self, nativeValue): nativeValue.SetPreferSyntheticValue(False) + nativeType = nativeValue.GetType() val = self.Value(self) - val.nativeValue = nativeValue - val.type = self.fromNativeType(nativeValue.type) + val.type = self.fromNativeType(nativeType) val.lIsInScope = nativeValue.IsInScope() #val.name = nativeValue.GetName() - try: + data = nativeValue.GetData() + error = lldb.SBError() + size = nativeValue.GetType().GetByteSize() + if size > 0: # Happens regularly e.g. for cross-shared-object types. + val.ldata = data.ReadRawData(error, 0, size) + code = nativeType.GetTypeClass() + if code not in (lldb.eTypeClassPointer, lldb.eTypeClassReference): val.laddress = int(nativeValue.GetLoadAddress()) - except: - pass + if code == lldb.eTypeClassEnumeration: + intval = nativeValue.GetValueAsSigned() + if hasattr(nativeType, 'get_enum_members_array'): + for enumMember in nativeType.get_enum_members_array(): + # Even when asking for signed we get unsigned with LLDB 3.8. + diff = enumMember.GetValueAsSigned() - intval + mask = (1 << nativeType.GetByteSize() * 8) - 1 + if diff & mask == 0: + path = nativeType.GetName().split('::') + path[-1] = enumMember.GetName() + val.ldisplay = "%s (%d)" % ('::'.join(path), intval) + val.ldisplay = "%d" % 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()) return val def fromNativeType(self, nativeType): @@ -217,6 +230,7 @@ class Dumper(DumperBase): addr = 0x7fffffffe0a0 sbaddr = lldb.SBAddress(addr, self.target) dummyValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType) + dummyValue.SetPreferSyntheticValue(False) anonNumber = 0 @@ -246,8 +260,8 @@ class Dumper(DumperBase): anonNumber += 1 fieldName = "#%s" % anonNumber fieldType = dummyChild.GetType() - #warn("CHILD AT: %s: %s %s" % (i, fieldName, fieldType)) - #warn(" AT: %s: %s %s" % (i, fieldName, fieldType)) + #warn("CHILD AT: %s: %s %s" % (i, fieldName, fieldType.GetName())) + #warn(" AT: %s: %s %s" % (i, fieldName, fieldType.GetName())) caddr = dummyChild.AddressOf().GetValueAsUnsigned() child = self.Value(self) child.type = self.fromNativeType(fieldType) @@ -298,15 +312,6 @@ class Dumper(DumperBase): #warn("FIELDS: %s" % fields) return fields - def nativeValueDereference(self, nativeValue): - result = nativeValue.Dereference() - if not result.IsValid(): - return None - return self.fromNativeValue(result) - - def nativeValueCast(self, nativeValue, nativeType): - return self.fromNativeValue(nativeValue.Cast(nativeType)) - def nativeTypeUnqualified(self, nativeType): return self.fromNativeType(nativeType.GetUnqualifiedType()) @@ -318,46 +323,6 @@ class Dumper(DumperBase): return self.fromNativeType(typeobj.GetCanonicalType()) return self.fromNativeType(typeobj) - def nativeValueChildFromNameHelper(self, nativeValue, fieldName): - val = nativeValue.GetChildMemberWithName(fieldName) - if val.IsValid(): - return val - nativeType = nativeValue.GetType() - for i in xrange(nativeType.GetNumberOfDirectBaseClasses()): - baseField = nativeType.GetDirectBaseClassAtIndex(i) - baseType = baseField.GetType() - base = nativeValue.Cast(baseType) - val = self.nativeValueChildFromNameHelper(base, fieldName) - if val is not None: - return val - return None - - def nativeValueChildFromField(self, nativeValue, field): - val = None - if field.isVirtualBase is True: - #warn("FETCHING VIRTUAL BASE: %s: %s" % (field.baseIndex, field.name)) - pass - if field.nativeIndex is not None: - #warn("FETCHING BY NATIVE INDEX: %s: %s" % (field.nativeIndex, field.name)) - val = nativeValue.GetChildAtIndex(field.nativeIndex) - if val.IsValid(): - return self.fromNativeValue(val) - elif field.baseIndex is not None: - baseClass = nativeValue.GetType().GetDirectBaseClassAtIndex(field.baseIndex) - if baseClass.IsValid(): - #warn("BASE IS VALID, TYPE: %s" % baseClass.GetType()) - #warn("BASE BITPOS: %s" % field.lbitpos) - #warn("BASE BITSIZE: %s" % field.lbitsize) - # FIXME: This is wrong for virtual bases. - return None # Let standard behavior kick in. - else: - #warn("FETCHING BY NAME: %s: %s" % (field, field.name)) - val = self.nativeValueChildFromNameHelper(nativeValue, field.name) - if val is not None: - return self.fromNativeValue(val) - raise RuntimeError("No such member '%s' in %s" % (field.name, nativeValue.type)) - return None - def nativeTypeFirstBase(self, nativeType): #warn("FIRST BASE FROM: %s" % nativeType) if nativeType.GetNumberOfDirectBaseClasses() == 0: @@ -378,6 +343,13 @@ class Dumper(DumperBase): return "%s (%d)" % ('::'.join(path), intval) return "%d" % 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 @@ -480,9 +452,6 @@ class Dumper(DumperBase): #warn("INNER: %s" % inner) return self.lookupType(inner) - def nativeValueAddressOf(self, nativeValue): - return int(value.GetLoadAddress()) - def nativeTypeDereference(self, nativeType): return self.fromNativeType(nativeType.GetPointeeType()) @@ -917,15 +886,6 @@ class Dumper(DumperBase): return bytes() return res - def nativeValueAsBytes(self, nativeValue, size): - data = nativeValue.GetData() - buf = bytearray(struct.pack('x' * size)) - error = lldb.SBError() - #data.ReadRawData(error, 0, buf) - for i in range(size): - buf[i] = data.GetUnsignedInt8(error, i) - return bytes(buf) - def findStaticMetaObject(self, typeName): symbolName = self.mangleName(typeName + '::staticMetaObject') symbol = self.target.FindFirstGlobalVariable(symbolName) @@ -983,6 +943,7 @@ class Dumper(DumperBase): if len(statics): for i in xrange(len(statics)): staticVar = statics[i] + staticVar.SetPreferSyntheticValue(False) typename = staticVar.GetType().GetName() name = staticVar.GetName() with SubItem(self, i): @@ -1004,6 +965,7 @@ class Dumper(DumperBase): variables = [] for val in values: + val.SetPreferSyntheticValue(False) if not val.IsValid(): continue self.currentContextValue = val diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 5093e2b3105..6318990be11 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -887,11 +887,11 @@ def qdump__QLocale(d, value): #d.check(index <= qqLocalesCount) dd = value.extractPointer() ns = d.qtNamespace() - (data, ref, numberOptions) = d.split("pi{int}", dd) + (data, ref, numberOptions) = d.split("pi4s", dd) (languageId, scriptId, countryId, decimal, group, listt, percent, zero, minus, plus, exponential) \ - = d.split('{short}{short}{QChar}' + = d.split('2s{short}2s' + '{QChar}{QChar}{short}{QChar}{QChar}' + '{QChar}{QChar}{QChar}', data) d.putStringValue(d.call("const char *", value, "name")) @@ -899,9 +899,9 @@ def qdump__QLocale(d, value): if d.isExpanded(): with Children(d): prefix = ns + "QLocale::" - d.putSubItem("country", countryId.extend(4).cast(prefix + "Country")) - d.putSubItem("language", languageId.extend(4).cast(prefix + "Language")) - d.putSubItem("numberOptions", numberOptions.cast(prefix + "NumberOptions")) + 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) @@ -1189,7 +1189,7 @@ def qdump__QSet(d, value): typeCode = 'Pi@{%s}' % keyType.name (pnext, hashval, padding1, key) = d.split(typeCode, node) with SubItem(d, i): - d.putItem(key, i) + d.putItem(key) node = hashDataNextNode(node) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index b65b157bbe2..6c7376e9b02 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -227,12 +227,11 @@ def qdump__std__map(d, value): pairPointer = pairType.pointer() with PairedChildren(d, size, pairType=pairType, maxNumChild=1000): node = impl["_M_header"]["_M_left"] - typeCode = "p@{%s}@{%s}" % (pairType[0].name, pairType[1].name) + nodeSize = node.dereference().type.size() + typeCode = "@{%s}@{%s}" % (pairType[0].name, pairType[1].name) for i in d.childRange(): - pair = (node + 1).cast(pairPointer).dereference() - d.putPairItem(i, pair) - #(pp, pad1, key, pad2, value) = d.split(typeCode, node.integer()) - #d.putPairItem(i, (key, value)) + (pad1, key, pad2, value) = d.split(typeCode, node.pointer() + nodeSize) + d.putPairItem(i, (key, value)) if node["_M_right"].integer() == 0: parent = node["_M_parent"] while True: @@ -384,9 +383,12 @@ def qdump__std__set(d, value): 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(): - d.putSubItem(i, (node + 1).cast(valueType.pointer()).dereference()) + (pad, val) = d.split(typeCode, node.pointer() + nodeSize) + d.putSubItem(i, val) if node["_M_right"].integer() == 0: parent = node["_M_parent"] while node == parent["_M_right"]: diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 970b045539d..bda2d6eeb0d 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -4106,34 +4106,34 @@ void tst_Dumpers::dumper_data() + CoreProfile() + Check("map1", "<3 items>", "std::map<@QString, Foo>") - + Check("map1.0", "[0]", "", "std::pair<@QString const, Foo>") + + Check("map1.0", "[0] \"22.0\"", "", "std::pair<@QString const, Foo>") + Check("map1.0.first", "\"22.0\"", "@QString") + Check("map1.0.second", "", "Foo") + Check("map1.0.second.a", "22", "int") - + Check("map1.1", "[1]", "", "std::pair<@QString const, Foo>") + + Check("map1.1", "[1] \"33.0\"", "", "std::pair<@QString const, Foo>") + Check("map1.2.first", "\"44.0\"", "@QString") + Check("map1.2.second", "", "Foo") + Check("map1.2.second.a", "44", "int") + Check("map2", "<2 items>", "std::map") - + Check("map2.0", "[0]", "", "std::pair") + + Check("map2.0", "[0] \"22.0\"", "", "std::pair") + CheckType("map2.0.first", "char *") // FIXME //+ Check("map2.0.first.0", "50", "char") + Check("map2.0.second", "", "Foo") + Check("map2.0.second.a", "22", "int") - + Check("map2.1", "[1]", "", "std::pair") + + Check("map2.1", "[1] \"33.0\"", "", "std::pair") + CheckType("map2.1.first", "char *") //+ Check("map2.1.first.*first", "51 '3'", "char") + Check("map2.1.second", "", "Foo") + Check("map2.1.second.a", "33", "int") + Check("map3", "<2 items>", "std::map") - + Check("map3.0", "[0]", "", "std::pair") + + Check("map3.0", "[0] 11", "<1 items>", "std::pair") + Check("map3.0.first", "11", "unsigned int") + Check("map3.0.second", "<1 items>", "@QStringList") + Check("map3.0.second.0", "[0]", "\"11\"", "@QString") - + Check("map3.1", "[1]", "", "std::pair") + + Check("map3.1", "[1] 22", "<1 items>", "std::pair") + Check("map3.1.first", "22", "unsigned int") + Check("map3.1.second", "<1 items>", "@QStringList") + Check("map3.1.second.0", "[0]", "\"22\"", "@QString") @@ -4141,26 +4141,26 @@ void tst_Dumpers::dumper_data() + Check("map4.1.second.0", "[0]", "\"22\"", "@QString") + Check("map5", "<2 items>", "std::map<@QString, float>") - + Check("map5.0", "[0]", "", "std::pair<@QString const, float>") + + Check("map5.0", "[0] \"11.0\"", FloatValue("11"), "std::pair<@QString const, float>") + Check("map5.0.first", "\"11.0\"", "@QString") + Check("map5.0.second", FloatValue("11"), "float") - + Check("map5.1", "[1]", "", "std::pair<@QString const, float>") + + Check("map5.1", "[1] \"22.0\"", FloatValue("22"), "std::pair<@QString const, float>") + Check("map5.1.first", "\"22.0\"", "@QString") + Check("map5.1.second", FloatValue("22"), "float") + Check("map6", "<2 items>", "std::map") - + Check("map6.0", "[0]", "", "std::pair") + + Check("map6.0", "[0] 11", "\"11.0\"", "std::pair") + Check("map6.0.first", "11", "int") + Check("map6.0.second", "\"11.0\"", "@QString") - + Check("map6.1", "[1]", "", "std::pair") + + Check("map6.1", "[1] 22", "\"22.0\"", "std::pair") + Check("map6.1.first", "22", "int") + Check("map6.1.second", "\"22.0\"", "@QString") + Check("map7", "<3 items>", "std::map<@QString, @QPointer<@QObject>>") - + Check("map7.0", "[0]", "", "std::pair<@QString const, @QPointer<@QObject>>") + + Check("map7.0", "[0] \".\"", "", "std::pair<@QString const, @QPointer<@QObject>>") + Check("map7.0.first", "\".\"", "@QString") + Check("map7.0.second", "", "@QPointer<@QObject>") - + Check("map7.2", "[2]", "", "std::pair<@QString const, @QPointer<@QObject>>") + + Check("map7.2", "[2] \"Welt\"", "", "std::pair<@QString const, @QPointer<@QObject>>") + Check("map7.2.first", "\"Welt\"", "@QString"); @@ -4948,9 +4948,10 @@ void tst_Dumpers::dumper_data() QTest::newRow("Bitfields") << Data("struct S\n" "{\n" - " S() : x(2), y(3), c(1), b(0), f(5), d(6), i(7) {}\n" + " S() : x(2), y(3), z(39), c(1), b(0), f(5), d(6), i(7) {}\n" " unsigned int x : 3;\n" " unsigned int y : 4;\n" + " unsigned int z : 18;\n" " bool c : 1;\n" " bool b;\n" " float f;\n" @@ -4966,7 +4967,8 @@ void tst_Dumpers::dumper_data() + Check("s.d", FloatValue("6"), "double") + Check("s.i", "7", "int") + Check("s.x", "2", "unsigned int") - + Check("s.y", "3", "unsigned int"); + + Check("s.y", "3", "unsigned int") + + Check("s.z", "39", "unsigned int"); QTest::newRow("Function")