Debugger: Use less GDB inferior calls in normal field extraction

They are not usable in core files.

Change-Id: I2134b61f27c27862c12a679d0acf7bebc9fcc7a2
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
hjk
2016-11-07 08:57:26 +01:00
parent ffe305527b
commit 9040c4081e
5 changed files with 468 additions and 240 deletions

View File

@@ -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'):

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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(