diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 1e3d4bf184e..266eb50d4a0 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -110,7 +110,8 @@ TypeCodeFunction, \ TypeCodeMemberPointer, \ TypeCodeFortranString, \ TypeCodeUnresolvable, \ - = range(0, 14) +TypeCodeBitfield, \ + = range(0, 15) def isIntegralTypeName(name): return name in ('int', 'unsigned int', 'signed int', @@ -288,8 +289,11 @@ class DumperBase: self.typesToReport = {} self.qtNamespaceToReport = None self.passExceptions = False + self.isTesting = False self.typeData = {} + self.isBigEndian = False + self.packCode = '<' self.resetCaches() self.resetStats() @@ -333,6 +337,7 @@ class DumperBase: 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', '0')) self.nativeMixed = int(args.get('nativemixed', '0')) self.autoDerefPointers = int(args.get('autoderef', '0')) @@ -389,11 +394,11 @@ class DumperBase: self.counts[key] = 1 def preping(self, key): - import time + return self.pretimings[key] = time.time() def ping(self, key): - import time + return elapsed = int(1000000 * (time.time() - self.pretimings[key])) self.timings.append([key, elapsed]) @@ -518,6 +523,9 @@ class DumperBase: nativeType = self.lookupNativeType(typeName) return None if nativeType is None else self.fromNativeType(nativeType) + def nativeDynamicType(self, address, baseType): + return baseType # Override in backends. + def listTemplateParameters(self, typename): targs = [] if not typename.endswith('>'): @@ -880,9 +888,9 @@ class DumperBase: 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. - baseIndex += 1 with UnnamedSubItem(self, "@%d" % baseIndex): self.putField('iname', self.currentIName) self.putField('name', '[%s]' % item.name) @@ -918,7 +926,7 @@ class DumperBase: self.checkIntType(tsize) self.checkIntType(maximum) - code = (None, 'b', 'H', None, 'I')[tsize] + code = self.packCode + (None, 'b', 'H', None, 'I')[tsize] #blob = self.readRawMemory(base, 1) blob = bytes() while maximum > 1: @@ -1284,6 +1292,11 @@ class DumperBase: return False def putFormattedPointer(self, value): + self.preping('formattedPointer') + self.putFormattedPointerX(value) + self.ping('formattedPointer') + + def putFormattedPointerX(self, value): #warn("PUT FORMATTED: %s" % value) pointer = value.pointer() #warn('POINTER: 0x%x' % pointer) @@ -2573,6 +2586,11 @@ class DumperBase: return False def putItem(self, value): + self.preping('putItem') + self.putItemX(value) + self.ping('putItem') + + def putItemX(self, value): #warn('PUT ITEM: %s' % value.stringify()) typeobj = value.type #unqualified() @@ -2597,10 +2615,7 @@ class DumperBase: if typeobj.code == TypeCodeTypedef: #warn('TYPEDEF VALUE: %s' % value.stringify()) - #strippedType = typeobj.ltarget self.putItem(value.detypedef()) - if value.lbitsize is not None and value.lbitsize != value.type.size() * 8: - typeName += ' : %s' % value.lbitsize self.putBetterType(typeName) return @@ -2627,16 +2642,17 @@ class DumperBase: self.putCStyleArray(value) return + if typeobj.code == TypeCodeBitfield: + #warn('BITFIELD VALUE: %s %s' % (value.name, value)) + self.putNumChild(0) + self.putValue(value.lvalue) + self.putType(typeName) + return + if typeobj.code == TypeCodeIntegral: #warn('INTEGER: %s %s' % (value.name, value)) val = value.value() self.putNumChild(0) - if value.lbitsize is not None and value.lbitsize != value.type.size() * 8: - typeName += ' : %s' % value.lbitsize - val = val >> value.lbitpos - val = val & ((1 << value.lbitsize) - 1) - #warn('VAL: %s BITPOS: %s BITSIZE: %s' - # % (val, value.lbitpos, value.lbitsize)) self.putValue(val) self.putType(typeName) return @@ -2806,6 +2822,8 @@ class DumperBase: return self.detypedef().value() if self.type.code == TypeCodeIntegral: return self.integer() + if self.type.code == TypeCodeBitfield: + return self.integer() if self.type.code == TypeCodeFloat: return self.floatingPoint() if self.type.code == TypeCodeTypedef: @@ -2887,15 +2905,40 @@ class DumperBase: error('BAD INDEX TYPE %s' % type(field)) #warn('FIELD: %s ' % field) - fieldBitsize = field.bitsize() - fieldSize = None if fieldBitsize is None else fieldBitsize >> 3 - fieldBitpos = field.bitpos() - fieldOffset = fieldBitpos >> 3 - fieldBitpos -= fieldOffset * 8 - fieldType = field.fieldType() - val = self.dumper.Value(self.dumper) val.name = field.name + val.isBaseClass = field.isBaseClass + 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.lbitsize + fieldSize = (fieldBitsize + 7) // 8 + fieldBitpos = field.lbitpos + fieldOffset = fieldBitpos // 8 + fieldType = field.fieldType() + + if fieldType.code == TypeCodeBitfield: + fieldBitpos -= fieldOffset * 8 + ldata = self.data() + data = 0 + for i in range(fieldSize): + data = data << 8 + if self.dumper.isBigEndian: + byte = ldata[i] + else: + byte = ldata[fieldOffset + fieldSize - 1 - i] + data += ord(byte) + 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 @@ -2904,21 +2947,11 @@ class DumperBase: else: self.dumper.check(False) - if fieldBitsize is not None and fieldBitsize != fieldType.size() * 8: - data = val.extractInteger(fieldBitsize, True) - data = data >> fieldBitpos - data = data & ((1 << fieldBitsize) - 1) - val.laddress = None - val.ldata = bytes(struct.pack('Q', data)) - val.type = None - if val.laddress is not None and fieldType is not None: - if fieldType.code == TypeCodePointer: - objectAddress = self.dumper.extractPointer(val.laddress) - val = self.dumper.createPointerValue(objectAddress, fieldType.ltarget) - elif fieldType.code == TypeCodeReference: - objectAddress = self.dumper.extractPointer(val.laddress) - val = self.dumper.createReferenceValue(objectAddress, fieldType.ltarget) + if fieldType.code == TypeCodeReference: + if val.laddress is not None: + val = self.dumper.createReferenceValue(val.laddress, fieldType.ltarget) + val.name = field.name if val.type is None: val.type = fieldType @@ -2926,7 +2959,6 @@ class DumperBase: val.check() val.name = field.name val.lbitsize = fieldBitsize - val.isBaseClass = field.isBaseClass return val # This is the generic version for synthetic values. @@ -2935,9 +2967,22 @@ class DumperBase: def members(self, includeBases): if self.type.code == TypeCodeTypedef: return self.detypedef().members(includeBases) + + tdata = self.type.typeData() + #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)) + + #warn("FIELDS: %s" % fields) res = [] anonNumber = 0 - for field in self.type.fields(self): + for field in fields: if field.isBaseClass and not includeBases: continue if field.name is None or len(field.name) == 0: @@ -2946,7 +2991,7 @@ class DumperBase: # multiple anonymous unions in the struct. anonNumber += 1 field.name = '#%s' % anonNumber - res.append(field) + res.append(self.extractField(field)) return res def __add__(self, other): @@ -2957,7 +3002,7 @@ class DumperBase: address = self.pointer() + stripped.dereference().size() val = self.dumper.Value(self.dumper) val.laddress = None - val.ldata = bytes(struct.pack('Q', address)) + val.ldata = bytes(struct.pack(self.dumper.packCode + 'Q', address)) val.type = self.type return val error('BAD DATA TO ADD TO: %s %s' % (self.type, other)) @@ -2971,10 +3016,10 @@ class DumperBase: val.laddress = self.pointer() if val.laddress is None and self.laddress is not None: val.laddress = self.laddress - val.type = self.type.dereference().dynamicType(val.laddress) + val.type = self.dumper.nativeDynamicType(val.laddress, self.type.dereference()) elif self.type.code == TypeCodePointer: val.laddress = self.pointer() - val.type = self.type.dereference().dynamicType(val.laddress) + val.type = self.dumper.nativeDynamicType(val.laddress, self.type.dereference()) else: error("WRONG: %s" % self.type.code) #warn("DEREFERENCING FROM: %s" % self) @@ -3007,10 +3052,12 @@ class DumperBase: error('NOT IMPLEMENTED') def zeroExtend(self, data, size): + ext = '\0' * (size - len(data)) if sys.version_info[0] == 3: - return data + bytes('\0' * (size - len(data)), encoding='latin1') + pad = bytes(ext, encoding='latin1') else: - return data + bytes('\0' * (size - len(data))) + pad = bytes(ext) + return pad + data if self.dumper.isBigEndian else data + pad def cast(self, typish): self.check() @@ -3051,6 +3098,7 @@ class DumperBase: error('CANNOT CONVERT TO BYTES: %s' % self) def extractInteger(self, bitsize, unsigned): + self.dumper.preping('extractInt') self.check() if bitsize > 32: size = 8 @@ -3065,27 +3113,30 @@ class DumperBase: size = 1 code = 'B' if unsigned else 'b' rawBytes = self.data(size) - try: - return struct.unpack_from(code, rawBytes, 0)[0] - except: - pass - error('Cannot extract: Code: %s Bytes: %s Bitsize: %s Size: %s' - % (code, self.dumper.hexencode(rawBytes), bitsize, size)) + res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0] + #warn('Extract: Code: %s Bytes: %s Bitsize: %s Size: %s' + # % (self.dumper.packCode + code, self.dumper.hexencode(rawBytes), bitsize, size)) + self.dumper.ping('extractInt') + return res def extractSomething(self, code, bitsize): + self.dumper.preping('extractSomething') self.check() size = (bitsize + 7) >> 3 rawBytes = self.data(size) - return struct.unpack_from(code, rawBytes, 0)[0] + res = struct.unpack_from(self.dumper.packCode + code, rawBytes, 0)[0] + self.dumper.ping('extractSomething') + return res def to(self, pattern): return self.split(pattern)[0] def split(self, pattern): + self.dumper.preping('split') #warn('EXTRACT STRUCT FROM: %s' % self.type) (pp, size, fields) = self.dumper.describeStruct(pattern) #warn('SIZE: %s ' % size) - result = struct.unpack_from(pp, self.data(size)) + result = struct.unpack_from(self.dumper.packCode + pp, self.data(size)) def structFixer(field, thing): #warn('STRUCT MEMBER: %s' % type(thing)) if field.isStruct: @@ -3101,6 +3152,7 @@ class DumperBase: return thing if len(fields) != len(result): error('STRUCT ERROR: %s %s' (fields, result)) + self.dumper.ping('split') return tuple(map(structFixer, fields, result)) def checkPointer(self, p, align = 1): @@ -3127,7 +3179,6 @@ class DumperBase: self.lfields = None # None or Value -> list of member Values self.lalignment = None # Function returning alignment of this struct self.lbitsize = None - self.lbitpos = None self.ltarget = None # Inner type for arrays self.templateArguments = [] self.code = None @@ -3141,7 +3192,6 @@ class DumperBase: tdata.lfields = self.lfields tdata.lalignment = self.lalignment tdata.lbitsize = self.lbitsize - tdata.lbitpos = self.lbitpos tdata.ltarget = self.ltarget tdata.templateArguments = self.templateArguments tdata.code = self.code @@ -3199,8 +3249,8 @@ class DumperBase: tdata = self.typeData() if tdata is None: return 'Type(id="%s")' % self.typeId - return 'Type(name="%s",bsize=%s,bpos=%s,code=%s)' \ - % (tdata.name, tdata.lbitsize, tdata.lbitpos, tdata.code) + return 'Type(name="%s",bsize=%s,code=%s)' \ + % (tdata.name, tdata.lbitsize, tdata.code) def __getitem__(self, index): if self.dumper.isInt(index): @@ -3223,7 +3273,12 @@ class DumperBase: 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 + self.dumper.preping('dynamicType %s 0x%s' % (self.name, address)) dynTypeName = self.dynamicTypeName(address) + self.dumper.ping('dynamicType %s 0x%s' % (self.name, address)) if dynTypeName is not None: return self.dumper.createType(dynTypeName) return self @@ -3327,13 +3382,6 @@ class DumperBase: def target(self): return self.typeData().ltarget - def fields(self, value): - tdata = self.typeData() - if tdata.code == TypeCodeTypedef: - return tdata.ltarget.fields(value) - if tdata.lfields is not None: - return tdata.lfields(value) - return [] def field(self, value, name, bitoffset = 0): #warn('GETTING FIELD %s FOR: %s' % (name, self.name)) @@ -3398,9 +3446,8 @@ class DumperBase: def __init__(self, dumper): self.dumper = dumper self.name = None - self.nativeIndex = None # Backend-defined index value self.isBaseClass = False - self.isVirtualBase = False + self.isArtificial = False self.ltype = None self.lbitsize = None self.lbitpos = None @@ -3408,34 +3455,23 @@ class DumperBase: def __str__(self): typename = None if self.ltype is None else self.ltype.stringify() - return ('Field(name="%s",ltype=%s,bpos=%s,bsize=%s,nidx=%s)') \ - % (self.name, typename, self.lbitpos, self.lbitsize, - self.nativeIndex) + return ('Field(name="%s",ltype=%s,bpos=%s,bsize=%s)') \ + % (self.name, typename, self.lbitpos, self.lbitsize) def check(self): pass def size(self): - return self.bitsize() >> 3 + return self.lbitsize // 8 def offset(self): - return self.bitpos() >> 3 + return self.lbitpos // 8 def bitsize(self): - self.check() - if self.lbitsize is not None: - return self.lbitsize - fieldType = self.fieldType() - # FIXME: enforce return value != None. - if fieldType is not None: - return fieldType.bitsize() - return None + return self.lbitsize def bitpos(self): - if self.lbitpos is not None: - #warn('BITPOS KNOWN: %s %s' % (self.name, self.lbitpos)) - return self.lbitpos - error('DONT KNOW BITPOS FOR FIELD: %s ' % self) + return self.lbitpos def fieldType(self): if self.ltype is not None: @@ -3443,12 +3479,13 @@ class DumperBase: error('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): error('wrong') - size = self.ptrSize() - code = 'I' if size == 4 else 'Q' - return bytes(struct.pack(code, address)) + return bytes(struct.pack(self.packCode + self.ptrCode(), address)) def createPointerValue(self, targetAddress, targetTypish): if not isinstance(targetTypish, self.Type) and not isinstance(targetTypish, str): @@ -3476,6 +3513,20 @@ class DumperBase: val.type = self.createReferenceType(targetType) return val + def createBitfieldValue(self, targetType, bitsize): + if not isinstance(targetType, self.Type): + error('Expected type in createBitfieldValue(), got %s' + % type(targetType)) + targetTypeId = targetType.typeId + typeId = '%s:%d' % (targetTypeId, bitsize) + tdata = self.TypeData(self) + tdata.name = '%s : %d' % (targetType.name, bitsize) + tdata.typeId = typeId + tdata.code = TypeCodeArray + tdata.ltarget = targetType + self.registerType(typeId, tdata) + return self.Type(self, typeId) + def createPointerType(self, targetType): if not isinstance(targetType, self.Type): error('Expected type in createPointerType(), got %s' @@ -3519,6 +3570,19 @@ class DumperBase: self.registerType(typeId, tdata) return self.Type(self, typeId) + def createBitfieldType(self, targetTypeId, bitsize): + if not isinstance(targetTypeId, str): + error('Expected type in createBitfieldType(), got %s' + % type(targetType)) + typeId = '%s:%d' % (targetTypeId, bitsize) + tdata = self.TypeData(self) + tdata.name = '%s : %d' % (targetTypeId, bitsize) + tdata.typeId = typeId + tdata.code = TypeCodeBitfield + tdata.lbitsize = bitsize + self.registerType(typeId, tdata) + return self.Type(self, typeId) + def createTypedefedType(self, targetType, typeId): if not isinstance(targetType, self.Type): error('Expected type in createTypedefType(), got %s' @@ -3661,7 +3725,7 @@ class DumperBase: if self.autoPadNext: self.currentBitsize = 8 * ((self.currentBitsize + 7) >> 3) # Fill up byte. padding = (fieldAlign - (self.currentBitsize >> 3)) % fieldAlign - #warn('AUTO PADDING AT %s BITS BY %s BYTES' % (self.currentBitsize, padding)) + warn('AUTO PADDING AT %s BITS BY %s BYTES' % (self.currentBitsize, padding)) field = self.dumper.Field(self.dumper) field.code = None #field.lbitpos = self.currentBitsize @@ -3707,7 +3771,7 @@ class DumperBase: else: typeName += c elif c == 'p': # Pointer as int - builder.addField(ptrSize, 'Q' if ptrSize == 8 else 'I', fieldAlign = ptrSize) + builder.addField(ptrSize, self.ptrCode(), fieldAlign = ptrSize) elif c == 'P': # Pointer as Value builder.addField(ptrSize, '%ss' % ptrSize, fieldAlign = ptrSize) elif c in ('d'): diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index a6f10cbd6bd..93e061b7ede 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -206,26 +206,30 @@ class Dumper(DumperBase): self.setVariableFetchingOptions(args) def fromFrameValue(self, nativeValue): + #warn("FROM FRAME VALUE: %s" % nativeValue.address) val = nativeValue - if self.useDynamicType: - try: - val = nativeValue.cast(nativeValue.dynamic_type) - except: - pass + try: + val = nativeValue.cast(nativeValue.dynamic_type) + except: + pass return self.fromNativeValue(val) def fromNativeValue(self, nativeValue): - #warn("FROM NATIVE VALUE: %s" % nativeValue) + #warn("FROM NATIVE VALUE: %s" % nativeValue.address) self.check(isinstance(nativeValue, gdb.Value)) nativeType = nativeValue.type code = nativeType.code if code == gdb.TYPE_CODE_REF: - targetType = self.fromNativeType(nativeType.target().unqualified()) + targetType = self.fromNativeType(nativeType.target().unqualified(), nativeValue) val = self.createReferenceValue(toInteger(nativeValue.address), targetType) #warn("CREATED REF: %s" % val) return val if code == gdb.TYPE_CODE_PTR: - targetType = self.fromNativeType(nativeType.target().unqualified()) + try: + nativeTargetValue = nativeValue.dereference() + except: + nativeTargetValue = None + targetType = self.fromNativeType(nativeType.target().unqualified(), nativeTargetValue) val = self.createPointerValue(toInteger(nativeValue), targetType) #warn("CREATED PTR 1: %s" % val) if not nativeValue.address is None: @@ -241,7 +245,7 @@ class Dumper(DumperBase): else: # Cast may fail (e.g for arrays, see test for Bug5799) val = self.fromNativeValue(nativeValue.cast(targetType)) - val.type = self.fromNativeType(nativeType) + val.type = self.fromNativeType(nativeType, nativeValue) #warn("CREATED TYPEDEF: %s" % val) return val @@ -257,7 +261,7 @@ class Dumper(DumperBase): buf[i] = int(y[i]) val.ldata = bytes(buf) - val.type = self.fromNativeType(nativeType) + val.type = self.fromNativeType(nativeType, nativeValue) val.lIsInScope = not nativeValue.is_optimized_out code = nativeType.code if code == gdb.TYPE_CODE_ENUM: @@ -276,7 +280,7 @@ class Dumper(DumperBase): self.ptrSize = lambda: result return result - def fromNativeType(self, nativeType): + def fromNativeType(self, nativeType, nativeValue = None): self.check(isinstance(nativeType, gdb.Type)) code = nativeType.code #warn('FROM NATIVE TYPE: %s' % nativeType) @@ -284,7 +288,9 @@ class Dumper(DumperBase): if code == gdb.TYPE_CODE_PTR: #warn('PTR') - targetType = self.fromNativeType(nativeType.target().unqualified()) + if nativeValue is not None: + nativeValue = nativeValue.dereference() + targetType = self.fromNativeType(nativeType.target().unqualified(), nativeValue) return self.createPointerType(targetType) if code == gdb.TYPE_CODE_REF: @@ -344,8 +350,7 @@ class Dumper(DumperBase): if tdata.code == TypeCodeStruct: tdata.lalignment = lambda : \ self.nativeStructAlignment(nativeType) - tdata.lfields = lambda value : \ - self.listMembers(nativeType, value) + tdata.lfields = self.listMembers(nativeType, nativeValue) #tdata.lfieldByName = lambda name : \ # self.nativeTypeFieldTypeByName(nativeType, name) tdata.templateArguments = self.listTemplateParameters(nativeType) @@ -394,6 +399,7 @@ class Dumper(DumperBase): return typeId def nativeStructAlignment(self, nativeType): + self.preping('align ' + str(nativeType)) #warn("NATIVE ALIGN FOR %s" % nativeType.name) def handleItem(nativeFieldType, align): a = self.fromNativeType(nativeFieldType).alignment() @@ -401,62 +407,174 @@ class Dumper(DumperBase): align = 1 for f in nativeType.fields(): align = handleItem(f.type, align) + self.ping('align ' + str(nativeType)) return align - def listMembers(self, nativeType, value): - if not nativeType.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): - return - if value.laddress == 0: - warn("CANNOT LIST MEMBERS OF NULL VALUE OF %s" % nativeType) - return - if value.laddress is None: - # FIXME: Happens e.g. for QVariant(QBitArray) - addr = self.pokeValue(value) # FIXME: Far too expensive. + #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 fromNativeField(self, nativeField): + field = self.Field(self) + field.name = nativeField.name + field.isBaseClass = nativeField.is_base_class + # 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 field.name is not None and len(field.name) == 0: + field.name = None + nativeFieldType = nativeField.type.unqualified() + + if hasattr(nativeField, 'bitpos'): + field.lbitpos = nativeField.bitpos + + if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0: + field.lbitsize = nativeField.bitsize else: - addr = value.laddress - nativeTypePointer = nativeType.unqualified().pointer() - nativeValue = gdb.Value(addr).cast(nativeTypePointer).dereference() + field.lbitsize = 8 * nativeFieldType.sizeof - #warn('FIELDS FOR %s' % nativeType) + if field.lbitsize != nativeFieldType.sizeof * 8: + field.ltype = self.createBitfieldType(str(nativeFieldType), field.lbitsize) + else: + field.ltype = self.fromNativeType(nativeFieldType) + return field + + def memberFromField(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, nativeType, nativeValue): + #warn("LISTING MEMBERS OF %s" % nativeType) + try: + nativeValueAddress = toInteger(nativeValue.address) + except: + nativeValueAddress = None + + if nativeValue is None or nativeValueAddress is None: + return lambda value : self.listDelayedMembers(nativeType, value) + + def needsDeep(someNativeType): + if someNativeType.sizeof >= ptrSize: + for nativeField in someNativeType.fields(): + if nativeField.is_base_class: + if nativeField.bitpos < 0 or needsDeep(nativeField.type): + return True + return False + + ptrSize = self.ptrSize() + if needsDeep(nativeType): + return self.listDeepMembers(nativeType, nativeValue, nativeValueAddress) + + return self.listSimpleMembers(nativeType, nativeValue, nativeValueAddress) + + def listSimpleMembers(self, nativeType, nativeValue, nativeValueAddress): + #warn('SIMPLE FIELDS FOR %s' % nativeType) + fields = [] for nativeField in nativeType.fields(): - #warn('FIELD: %s' % nativeField) + nativeFieldType = nativeField.type.unqualified() + #warn('SIMPLE FIELD: %s' % nativeField.name) #warn(' BITSIZE: %s' % nativeField.bitsize) + #warn(' NAME: %s' % nativeField.name) + #warn(' TYPE: %s' % nativeFieldType) + #warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType)) + field = self.fromNativeField(nativeField) + if field.ltype.code != TypeCodeBitfield: + nativeMember = self.memberFromField(nativeValue, nativeField) + if nativeMember is None: + warn('CANNOT EXTRACT FIELD %s FROM %s' % (nativeField.name, nativeValue)) + #continue + if nativeMember.address is None: # Could be a static member. + warn('NO ADDRESS OF %s' % nativeMember) + continue + nativeMemberAddress = toInteger(nativeMember.address) + offset = nativeMemberAddress - nativeValueAddress + field.lbitpos = 8 * offset + fields.append(field) + return fields + + def listDelayedMembers(self, nativeType, value): + #warn('DELAYED FIELDS FOR %s' % nativeType) + if value.laddress is None: + error('No address for object of type %s' % nativeType) + nativeTypePointer = nativeType.unqualified().pointer() + nativeValue = gdb.Value(value.laddress).cast(nativeTypePointer).dereference() + return self.listSimpleMembers(nativeType, nativeValue, value.laddress) + + # The is expensive as it recurses through all base classes. Ideally this + # should only be used if virtual inheritance is known to be involved. + def listDeepMembers(self, nativeType, nativeValue, nativeBaseAddress, bpbase = 0): + #warn('DEEP FIELDS FOR %s AT 0x%x' % (nativeType, nativeBaseAddress)) + fields = [] + for nativeField in nativeType.fields(): + nativeFieldType = nativeField.type.unqualified() + #warn('DEEP FIELD: %s' % nativeField.name) + #warn(' SIZE: %s' % nativeField.bitsize) + #warn(' POS: %s' % nativeField.bitpos) + #warn(' BPBASE: %s' % bpbase) #warn(' ARTIFICIAL: %s' % nativeField.artificial) #warn(' NAME: %s' % nativeField.name) - #warn(' TYPE: %s' % nativeField.type) - #warn(' TYPEID: %s' % self.nativeTypeId(nativeField.type)) - val = nativeValue[nativeField] - #warn('VAL: %s' % val) - try: - # Remove 'const', fails for 'const bool' members in some containers. - val = val.cast(nativeField.type.unqualified()) - except: - pass - try: - member = self.fromNativeValue(val) - except: - #warn('CANNOT CREATE FIELD: %s' % nativeField.name) - continue - member.name = nativeField.name + #warn(' TYPE: %s' % nativeFieldType) + #warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType)) + nativeMember = self.memberFromField(nativeValue, nativeField) if nativeField.is_base_class: - member.isBaseClass = True + #warn(' THIS IS VIRTUAL BASE OF TYPE %s' % nativeField.type) + # This is a virtual base, we need to recurse *now*, as later + # there is no way to generate a nativeValue with this type. + #warn(' NATIVE MEMBER: %s' % nativeMember) + pureFieldType = self.fromNativeType(nativeFieldType) + fieldTypeId = pureFieldType.typeId + ' in ' + str(nativeType) + fieldTypeData = pureFieldType.typeData().copy() + bp = - nativeField.bitpos + fieldTypeData.lfields = self.listDeepMembers( + nativeField.type, nativeMember, nativeBaseAddress, bpbase + bp - 64) + self.registerType(fieldTypeId, fieldTypeData) + + field = self.Field(self) + field.name = nativeField.name + #field.name = fieldTypeId + field.ltype = self.Type(self, fieldTypeId) + field.isBaseClass = True + field.isArtificial = True + field.lbitsize = None + field.lbitpos = None + + fields.append(field) else: - if hasattr(nativeField, 'bitpos'): - member.lbitpos = nativeField.bitpos - # Correction for some bitfields. Size 0 can occur for - # types without debug information. - bitsize = 8 * nativeField.type.sizeof - if bitsize > 0: - member.lbitpos = nativeField.bitpos % bitsize - if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0: - member.lbitsize = nativeField.bitsize - else: - member.lbitsize = 8 * nativeField.type.sizeof - #warn('MEMBER: %s' % member) - yield member - if value.laddress is None: - self.releaseValue(addr) + # This is a normal field or non-virtual base. + field = self.fromNativeField(nativeField) + if field.ltype.code != TypeCodeBitfield: + #if field.lbitpos is None: + try: + nativeMemberAddress = toInteger(nativeMember.address) + #warn(' NATIVE MEMBER ADDRESS: 0x%x' % nativeMemberAddress) + offset = nativeMemberAddress - nativeBaseAddress + #offset = bpbase + nativeField.bitpos + #warn("DEEP OFFSET: %s POS: %s" % (offset // 8, field.lbitpos // 8)) + field.lbitpos = offset * 8 + except: + field.lbitpos = None + field.lvalue = "XXX" + #else: + # warn("REUSING BITPOS %s FOR %s" % (field.lbitpos, field.name)) + fields.append(field) + return fields + def listLocals(self, partialVar): frame = gdb.selected_frame() @@ -492,19 +610,21 @@ class Dumper(DumperBase): # "NotImplementedError: Symbol type not yet supported in # Python scripts." #warn("SYMBOL %s (%s, %s)): " % (symbol, name, symbol.name)) - if False and self.passExceptions: - value = self.fromFrameValue(frame.read_var(name, block)) + if self.passExceptions and not self.isTesting: + nativeValue = frame.read_var(name, block) + value = self.fromFrameValue(nativeValue) value.name = name - #warn("READ 1: %s" % value.stringify()) + #warn("READ 0: %s" % value.stringify()) items.append(value) continue try: # Same as above, but for production. - value = self.fromFrameValue(frame.read_var(name, block)) + nativeValue = frame.read_var(name, block) + value = self.fromFrameValue(nativeValue) value.name = name - #warn("READ 1: %s" % value.stringify()) items.append(value) + #warn("READ 1: %s" % value.stringify()) continue except: pass @@ -554,6 +674,11 @@ class Dumper(DumperBase): self.resetStats() self.prepare(args) + self.preping('endian') + self.isBigEndian = gdb.execute('show endian', to_string = True).find('big endian') > 0 + self.ping('endian') + self.packCode = '>' if self.isBigEndian else '<' + (ok, res) = self.tryFetchInterpreterVariables(args) if ok: safePrint(res) @@ -566,6 +691,7 @@ class Dumper(DumperBase): partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None variables = self.listLocals(partialName) + #warn("VARIABLES: %s" % variables) # Take care of the return value of the last function call. if len(self.resultVarName) > 0: @@ -705,7 +831,13 @@ class Dumper(DumperBase): return self.cachedInferior def readRawMemory(self, address, size): - return self.selectedInferior().read_memory(address, size) + #warn('READ: %s FROM 0x%x' % (size, address)) + if address == 0 or size == 0: + return bytes() + self.preping('readMem') + res = self.selectedInferior().read_memory(address, size) + self.ping('readMem') + return res def findStaticMetaObject(self, typename): symbolName = typename + "::staticMetaObject" @@ -774,21 +906,6 @@ class Dumper(DumperBase): # Use fallback until we have a better answer. return self.fallbackQtVersion - def isQt3Support(self): - if self.qtVersion() >= 0x050000: - return False - else: - try: - # This will fail on Qt 4 without Qt 3 support - gdb.execute("ptype QChar::null", to_string=True) - self.cachedIsQt3Suport = True - except: - self.cachedIsQt3Suport = False - - # Memoize good results. - self.isQt3Support = lambda: self.cachedIsQt3Suport - return self.cachedIsQt3Suport - def createSpecialBreakpoints(self, args): self.specialBreakpoints = [] def newSpecial(spec): @@ -891,6 +1008,12 @@ class Dumper(DumperBase): self.importPlainDumper(printer) def qtNamespace(self): + self.preping('qtNamespace') + res = self.qtNamespaceX() + self.ping('qtNamespace') + return res + + def qtNamespaceX(self): if not self.currentQtNamespaceGuess is None: return self.currentQtNamespaceGuess @@ -919,28 +1042,8 @@ class Dumper(DumperBase): except: pass - try: - # Last fall backs. - s = gdb.execute("ptype QByteArray", to_string=True) - if s.find("QMemArray") >= 0: - # Qt 3. - self.qtNamespaceToReport = "" - self.qtNamespace = lambda: "" - self.qtVersion = lambda: 0x30308 - self.fallbackQtVersion = 0x30308 - return "" - # Seemingly needed with Debian's GDB 7.4.1 - pos1 = s.find("class") - pos2 = s.find("QByteArray") - if pos1 > -1 and pos2 > -1: - ns = s[s.find("class") + 6:s.find("QByteArray")] - self.qtNamespaceToReport = ns - self.qtNamespace = lambda: ns - return ns - except: - pass - self.currentQtNamespaceGuess = "" - return "" + self.currentQtNamespaceGuess = '' + return '' def assignValue(self, args): typeName = self.hexdecode(args['type']) @@ -963,18 +1066,31 @@ class Dumper(DumperBase): gdb.execute(cmd) def nativeDynamicTypeName(self, address, baseType): - 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] + # Needed for Gdb13393 test. + nativeType = self.lookupNativeType(baseType.name) + 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) + 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 diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index c306d3b8850..51b9ce1babd 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -225,17 +225,11 @@ class Dumper(DumperBase): return align def listMembers(self, nativeType, value): - if value.laddress is not None: - sbaddr = lldb.SBAddress(value.laddress, self.target) - nativeValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType) - else: - try: - nativeValue = self.target.CreateValueFromData('x', value.data(), nativeType) - except: - return - - nativeValue.SetPreferSyntheticValue(False) - nativeType = nativeValue.GetType() + #warn("ADDR: 0x%x" % self.fakeAddress) + fakeAddress = self.fakeAddress if value.laddress is None else value.laddress + sbaddr = lldb.SBAddress(fakeAddress, self.target) + fakeValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType) + fakeValue.SetPreferSyntheticValue(False) baseNames = {} for i in range(nativeType.GetNumberOfDirectBaseClasses()): @@ -253,25 +247,44 @@ class Dumper(DumperBase): if bitsize > 0: #bitpos = bitpos % bitsize bitpos = bitpos % 8 # Reported type is always wrapping type! - #warn("BIT SIZE: %s POS: %s NAME: %s" % (bitsize, bitpos, f.name)) fieldBits[f.name] = (bitsize, bitpos) # Normal members and non-empty base classes. - for i in range(nativeValue.GetNumChildren()): - fieldObj = nativeValue.GetChildAtIndex(i) - fieldObj.SetPreferSyntheticValue(False) - fieldName = fieldObj.GetName() - member = self.fromNativeValue(fieldObj) - member.name = fieldName - if fieldName in baseNames: - member.isBaseClass = True - if fieldName in fieldBits: - (member.lbitsize, member.lbitpos) = fieldBits[fieldName] + for i in range(fakeValue.GetNumChildren()): + nativeField = fakeValue.GetChildAtIndex(i) + nativeField.SetPreferSyntheticValue(False) + + field = self.Field(self) + field.name = nativeField.GetName() + nativeFieldType = nativeField.GetType() + + if field.name in fieldBits: + (field.lbitsize, field.lbitpos) = fieldBits[field.name] else: - member.lbitsize = fieldObj.GetType().GetByteSize() * 8 - #member.lbitpos = (caddr - addr) * 8 - #warn("MEMBER: %s" % member) - yield member + field.lbitsize = nativeFieldType.GetByteSize() * 8 + + if field.lbitsize != nativeFieldType.GetByteSize() * 8: + field.ltype = self.createBitfieldType(self.typeName(nativeFieldType), field.lbitsize) + else: + fakeMember = fakeValue.GetChildAtIndex(i) + #try: + fakeMemberAddress = fakeMember.GetLoadAddress() + #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. + # continue + + offset = fakeMemberAddress - fakeAddress + + field.lbitpos = 8 * offset + field.ltype = self.fromNativeType(nativeFieldType) + + if field.name in baseNames: + field.isBaseClass = True + field.baseIndex = baseNames[field.name] + + yield field # Empty bases are not covered above. for i in range(nativeType.GetNumberOfDirectBaseClasses()): @@ -357,10 +370,7 @@ class Dumper(DumperBase): return self.createTypedefedType(targetType, nativeType.GetName()) nativeType = nativeType.GetUnqualifiedType() - if hasattr(nativeType, 'GetDisplayTypeName'): - typeName = nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) - else: - typeName = nativeType.GetName() # Xcode 5 (lldb-310) + typeName = self.typeName(nativeType) if code in (lldb.eTypeClassArray, lldb.eTypeClassVector): #warn('ARRAY: %s' % nativeType.GetName()) @@ -480,11 +490,16 @@ class Dumper(DumperBase): #warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs])) return targs - def nativeTypeId(self, nativeType): + def typeName(self, nativeType): if hasattr(nativeType, 'GetDisplayTypeName'): - name = nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) - else: - name = nativeType.GetName() # Xcode 5 (lldb-310) + return nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) + return nativeType.GetName() # Xcode 5 (lldb-310) + + def nativeTypeId(self, nativeType): + name = self.typeName(nativeType) + + def nativeTypeId(self, nativeType): + name = self.typeName(nativeType) if name is None or len(name) == 0: c = '0' elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassStruct: @@ -1048,6 +1063,10 @@ class Dumper(DumperBase): self.setVariableFetchingOptions(args) + anyModule = self.target.GetModuleAtIndex(0) + anySymbol = anyModule.GetSymbolAtIndex(0) + self.fakeAddress = int(anySymbol.GetStartAddress()) + frame = self.currentFrame() if frame is None: self.reportResult('error="No frame"', args) diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index e0064dc2253..99f3f129fa2 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -1632,6 +1632,7 @@ def qdump__QVariant(d, value): else: #warn('DIRECT ITEM 1: %s' % innerType) val = d.createValue(data, innerType) + val.laddress = value.laddress d.putEmptyValue(-99) d.putItem(val) diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 00595375feb..786201dfcba 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -1374,7 +1374,7 @@ void tst_Dumpers::dumper() "python theDumper.fetchVariables({" "'token':2,'fancy':1,'forcens':1," "'autoderef':1,'dyntype':1,'passexceptions':1," - "'qobjectnames':1," + "'testing':1,'qobjectnames':1," "'expanded':[" + expandedq + "]})\n"; cmds += "quit\n"; @@ -1398,7 +1398,7 @@ void tst_Dumpers::dumper() "!qtcreatorcdbext.script -t 42 theDumper.fetchVariables({" "'token':2,'fancy':1,'forcens':1," "'autoderef':1,'dyntype':1,'passexceptions':0," - "'qobjectnames':1," + "'testing':1,'qobjectnames':1," "'expanded':[" + expandedq + "]})\n" "q\n"; } else if (m_debuggerEngine == LldbEngine) { @@ -1413,7 +1413,7 @@ void tst_Dumpers::dumper() // "sc print(dir())\n" "sc Tester('" + t->buildPath.toLatin1() + "/doit', {'fancy':1,'forcens':1," "'autoderef':1,'dyntype':1,'passexceptions':1," - "'qobjectnames':1," + "'testing':1,'qobjectnames':1," "'expanded':[" + expandedq + "]})\n" "quit\n"; @@ -5943,7 +5943,12 @@ void tst_Dumpers::dumper_data() " { T1() : i1(1) {} int i1; };\n" "struct T2 : virtual VEmpty, virtual VData\n" " { T2() : i2(1) {} int i2; };\n" - "struct TT : T1, T2 { TT() : c(1) {} int c; };\n"; + "struct TT : T1, T2 { TT() : c(1) {} int c; };\n" + + "struct A { int a = 1; char aa = 'a'; };\n" + "struct B : virtual A { int b = 2; float bb = 2; };\n" + "struct C : virtual A { int c = 3; double cc = 3; };\n" + "struct D : virtual B, virtual C { int d = 4; };\n"; QTest::newRow("Inheritance") << Data(inheritanceData, @@ -5956,7 +5961,11 @@ void tst_Dumpers::dumper_data() "TT tt;\n" "tt.T1::v = 44;\n" "tt.T2::v = 45;\n" - "unused(&tt.T2::v);\n") + "unused(&tt.T2::v);\n" + "D dd; unused(&dd);\n" + "D *dp = new D; unused(&dp);\n" + "D &dr = dd; unused(&dr);\n") + + Cxx11Profile() + Check("c.c", "1", "int") + Check("c.@1.@2.a", "42", "int") % NoLldbEngine + Check("c.@1.@4.v", "45", "int") % NoLldbEngine @@ -5970,7 +5979,26 @@ void tst_Dumpers::dumper_data() + Check("tt.@1.@2.v", "45", "int") % NoLldbEngine + Check("tt.@2.@2.v", "45", "int") % NoLldbEngine + Check("tt.@1.@1.v", "45", "int") % LldbEngine - + Check("tt.@2.@1.v", "45", "int") % LldbEngine; + + Check("tt.@2.@1.v", "45", "int") % LldbEngine + + + Check("dd.@1.@1.a", "1", "int") // B::a + + Check("dd.@2.@1.a", "1", "int") // C::a + + Check("dd.@1.b", "2", "int") + + Check("dd.@2.c", "3", "int") + + Check("dd.d", "4", "int") + + + Check("dp.@1.@1.a", "1", "int") // B::a + + Check("dp.@2.@1.a", "1", "int") // C::a + + Check("dp.@1.b", "2", "int") + + Check("dp.@2.c", "3", "int") + + Check("dp.d", "4", "int") + + + Check("dr.@1.@1.a", "1", "int") // B::a + + Check("dr.@2.@1.a", "1", "int") // C::a + + Check("dr.@1.b", "2", "int") + + Check("dr.@2.c", "3", "int") + + Check("dr.d", "4", "int"); + QTest::newRow("Gdb13393") << Data(