forked from qt-creator/qt-creator
Debugger: defer type look up
Change-Id: I425c2bfc3c88ebf46af161c5434c0c05a3bb9c97 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -11,7 +11,7 @@ from utils import TypeCode
|
|||||||
|
|
||||||
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
||||||
|
|
||||||
from dumper import DumperBase, SubItem
|
from dumper import DumperBase, SubItem, Children, DisplayFormat, UnnamedSubItem
|
||||||
|
|
||||||
|
|
||||||
class FakeVoidType(cdbext.Type):
|
class FakeVoidType(cdbext.Type):
|
||||||
@@ -84,10 +84,9 @@ class Dumper(DumperBase):
|
|||||||
self.check(isinstance(nativeValue, cdbext.Value))
|
self.check(isinstance(nativeValue, cdbext.Value))
|
||||||
val = self.Value(self)
|
val = self.Value(self)
|
||||||
val.name = nativeValue.name()
|
val.name = nativeValue.name()
|
||||||
val._type = self.fromNativeType(nativeValue.type())
|
|
||||||
# There is no cdb api for the size of bitfields.
|
# There is no cdb api for the size of bitfields.
|
||||||
# Workaround this issue by parsing the native debugger text for integral types.
|
# Workaround this issue by parsing the native debugger text for integral types.
|
||||||
if val._type.code == TypeCode.Integral:
|
if nativeValue.type().code() == TypeCode.Integral:
|
||||||
try:
|
try:
|
||||||
integerString = nativeValue.nativeDebuggerValue()
|
integerString = nativeValue.nativeDebuggerValue()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
@@ -106,16 +105,18 @@ class Dumper(DumperBase):
|
|||||||
base = 16
|
base = 16
|
||||||
else:
|
else:
|
||||||
base = 10
|
base = 10
|
||||||
signed = not val._type.name.startswith('unsigned')
|
signed = not nativeValue.type().name().startswith('unsigned')
|
||||||
try:
|
try:
|
||||||
val.ldata = int(integerString, base).to_bytes(val._type.size(),
|
val.ldata = int(integerString, base).to_bytes((nativeValue.type().bitsize() +7) // 8,
|
||||||
byteorder='little', signed=signed)
|
byteorder='little', signed=signed)
|
||||||
except:
|
except:
|
||||||
# read raw memory in case the integerString can not be interpreted
|
# read raw memory in case the integerString can not be interpreted
|
||||||
pass
|
pass
|
||||||
if val._type.code == TypeCode.Enum:
|
if nativeValue.type().code() == TypeCode.Enum:
|
||||||
val.ldisplay = self.enumValue(nativeValue)
|
val.ldisplay = self.enumValue(nativeValue)
|
||||||
val.isBaseClass = val.name == val._type.name
|
elif not nativeValue.type().resolved and nativeValue.type().code() == TypeCode.Struct and not nativeValue.hasChildren():
|
||||||
|
val.ldisplay = self.enumValue(nativeValue)
|
||||||
|
val.isBaseClass = val.name == nativeValue.type().name()
|
||||||
val.nativeValue = nativeValue
|
val.nativeValue = nativeValue
|
||||||
val.laddress = nativeValue.address()
|
val.laddress = nativeValue.address()
|
||||||
val.lbitsize = nativeValue.bitsize()
|
val.lbitsize = nativeValue.bitsize()
|
||||||
@@ -136,6 +137,9 @@ class Dumper(DumperBase):
|
|||||||
for f in nativeType.fields()])
|
for f in nativeType.fields()])
|
||||||
return typeId
|
return typeId
|
||||||
|
|
||||||
|
def nativeValueType(self, nativeValue):
|
||||||
|
return self.fromNativeType(nativeValue.type())
|
||||||
|
|
||||||
def fromNativeType(self, nativeType):
|
def fromNativeType(self, nativeType):
|
||||||
self.check(isinstance(nativeType, cdbext.Type))
|
self.check(isinstance(nativeType, cdbext.Type))
|
||||||
typeId = self.nativeTypeId(nativeType)
|
typeId = self.nativeTypeId(nativeType)
|
||||||
@@ -150,51 +154,66 @@ class Dumper(DumperBase):
|
|||||||
if nativeType.name().startswith('<function>'):
|
if nativeType.name().startswith('<function>'):
|
||||||
code = TypeCode.Function
|
code = TypeCode.Function
|
||||||
elif nativeType.targetName() != nativeType.name():
|
elif nativeType.targetName() != nativeType.name():
|
||||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
return self.createPointerType(nativeType.targetName())
|
||||||
if targetType is not None and targetType is not nativeType:
|
|
||||||
return self.createPointerType(targetType)
|
|
||||||
|
|
||||||
if code == TypeCode.Array:
|
if code == TypeCode.Array:
|
||||||
# cdb reports virtual function tables as arrays those ar handled separetly by
|
# cdb reports virtual function tables as arrays those ar handled separetly by
|
||||||
# the DumperBase. Declare those types as structs prevents a lookup to a
|
# the DumperBase. Declare those types as structs prevents a lookup to a
|
||||||
# none existing type
|
# none existing type
|
||||||
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
|
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
|
||||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
targetName = nativeType.targetName()
|
||||||
if targetType is not None:
|
count = nativeType.arrayElements()
|
||||||
return self.createArrayType(targetType, nativeType.arrayElements())
|
if targetName.endswith(']'):
|
||||||
|
(prefix, suffix, inner_count) = self.splitArrayType(targetName)
|
||||||
|
type_name = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix)
|
||||||
|
else:
|
||||||
|
type_name = '%s[%d]' % (targetName, count)
|
||||||
|
tdata = self.TypeData(self, typeId)
|
||||||
|
tdata.name = type_name
|
||||||
|
tdata.code = TypeCode.Array
|
||||||
|
tdata.ltarget = targetName
|
||||||
|
tdata.lbitsize = lambda: nativeType.bitsize()
|
||||||
|
return self.Type(self, typeId)
|
||||||
|
|
||||||
code = TypeCode.Struct
|
code = TypeCode.Struct
|
||||||
|
|
||||||
tdata = self.TypeData(self, typeId)
|
tdata = self.TypeData(self, typeId)
|
||||||
tdata.name = nativeType.name()
|
tdata.name = nativeType.name()
|
||||||
tdata.lbitsize = nativeType.bitsize()
|
tdata.lbitsize = lambda: nativeType.bitsize()
|
||||||
tdata.code = code
|
tdata.code = code
|
||||||
tdata.moduleName = nativeType.module()
|
tdata.moduleName = lambda: nativeType.module()
|
||||||
if code == TypeCode.Struct:
|
if code == TypeCode.Struct:
|
||||||
tdata.lfields = lambda value: \
|
tdata.lfields = lambda value: \
|
||||||
self.listFields(nativeType, value)
|
self.listFields(nativeType, value)
|
||||||
tdata.lalignment = lambda: \
|
tdata.lalignment = lambda: \
|
||||||
self.nativeStructAlignment(nativeType)
|
self.nativeStructAlignment(nativeType)
|
||||||
if code == TypeCode.Enum:
|
tdata.enumDisplay = lambda intval, addr, form: \
|
||||||
tdata.enumDisplay = lambda intval, addr, form: \
|
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
|
||||||
tdata.templateArguments = lambda: \
|
tdata.templateArguments = lambda: \
|
||||||
self.listTemplateParameters(nativeType.name())
|
self.listTemplateParameters(nativeType.name())
|
||||||
return self.Type(self, typeId)
|
return self.Type(self, typeId)
|
||||||
|
|
||||||
def listFields(self, nativeType, value):
|
def listNativeValueChildren(self, nativeValue):
|
||||||
if value.address() is None or value.address() == 0:
|
|
||||||
raise Exception("")
|
|
||||||
nativeValue = value.nativeValue
|
|
||||||
if nativeValue is None:
|
|
||||||
nativeValue = cdbext.createValue(value.address(), nativeType)
|
|
||||||
index = 0
|
index = 0
|
||||||
nativeMember = nativeValue.childFromIndex(index)
|
nativeMember = nativeValue.childFromIndex(index)
|
||||||
while nativeMember is not None:
|
while nativeMember:
|
||||||
if nativeMember.address() != 0:
|
if nativeMember.address() != 0:
|
||||||
yield self.fromNativeValue(nativeMember)
|
yield self.fromNativeValue(nativeMember)
|
||||||
index += 1
|
index += 1
|
||||||
nativeMember = nativeValue.childFromIndex(index)
|
nativeMember = nativeValue.childFromIndex(index)
|
||||||
|
|
||||||
|
def listValueChildren(self, value):
|
||||||
|
nativeValue = value.nativeValue
|
||||||
|
if nativeValue is None:
|
||||||
|
nativeValue = cdbext.createValue(value.address(), self.lookupNativeType(value.type.name, 0))
|
||||||
|
return self.listNativeValueChildren(nativeValue)
|
||||||
|
|
||||||
|
def listFields(self, nativeType, value):
|
||||||
|
nativeValue = value.nativeValue
|
||||||
|
if nativeValue is None:
|
||||||
|
nativeValue = cdbext.createValue(value.address(), nativeType)
|
||||||
|
return self.listNativeValueChildren(nativeValue)
|
||||||
|
|
||||||
def nativeStructAlignment(self, nativeType):
|
def nativeStructAlignment(self, nativeType):
|
||||||
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
||||||
def handleItem(nativeFieldType, align):
|
def handleItem(nativeFieldType, align):
|
||||||
@@ -307,6 +326,7 @@ class Dumper(DumperBase):
|
|||||||
namespaceIndex = name.find('::')
|
namespaceIndex = name.find('::')
|
||||||
if namespaceIndex > 0:
|
if namespaceIndex > 0:
|
||||||
namespace = name[:namespaceIndex + 2]
|
namespace = name[:namespaceIndex + 2]
|
||||||
|
self.qtNamespace = lambda: namespace
|
||||||
self.qtCustomEventFunc = self.parseAndEvaluate(
|
self.qtCustomEventFunc = self.parseAndEvaluate(
|
||||||
'%s!%sQObject::customEvent' %
|
'%s!%sQObject::customEvent' %
|
||||||
(self.qtCoreModuleName(), namespace)).address()
|
(self.qtCoreModuleName(), namespace)).address()
|
||||||
@@ -498,7 +518,7 @@ class Dumper(DumperBase):
|
|||||||
else:
|
else:
|
||||||
val = self.Value(self)
|
val = self.Value(self)
|
||||||
val.laddress = value.pointer()
|
val.laddress = value.pointer()
|
||||||
val._type = value.type.dereference()
|
val._type = DumperBase.Type(self, value.type.targetName)
|
||||||
val.nativeValue = value.nativeValue
|
val.nativeValue = value.nativeValue
|
||||||
|
|
||||||
return val
|
return val
|
||||||
@@ -519,3 +539,424 @@ class Dumper(DumperBase):
|
|||||||
def symbolAddress(self, symbolName):
|
def symbolAddress(self, symbolName):
|
||||||
res = self.nativeParseAndEvaluate(symbolName)
|
res = self.nativeParseAndEvaluate(symbolName)
|
||||||
return None if res is None else res.address()
|
return None if res is None else res.address()
|
||||||
|
|
||||||
|
def putItemX(self, value):
|
||||||
|
#DumperBase.warn('PUT ITEM: %s' % value.stringify())
|
||||||
|
|
||||||
|
typeobj = value.type # unqualified()
|
||||||
|
typeName = typeobj.name
|
||||||
|
|
||||||
|
self.addToCache(typeobj) # Fill type cache
|
||||||
|
|
||||||
|
if not value.lIsInScope:
|
||||||
|
self.putSpecialValue('optimizedout')
|
||||||
|
#self.putType(typeobj)
|
||||||
|
#self.putSpecialValue('outofscope')
|
||||||
|
self.putNumChild(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not isinstance(value, self.Value):
|
||||||
|
raise RuntimeError('WRONG TYPE IN putItem: %s' % type(self.Value))
|
||||||
|
|
||||||
|
# Try on possibly typedefed type first.
|
||||||
|
if self.tryPutPrettyItem(typeName, value):
|
||||||
|
if typeobj.code == TypeCode.Pointer:
|
||||||
|
self.putOriginalAddress(value.address())
|
||||||
|
else:
|
||||||
|
self.putAddress(value.address())
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Pointer:
|
||||||
|
self.putFormattedPointer(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.putAddress(value.address())
|
||||||
|
if value.lbitsize is not None:
|
||||||
|
self.putField('size', value.lbitsize // 8)
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Function:
|
||||||
|
#DumperBase.warn('FUNCTION VALUE: %s' % value)
|
||||||
|
self.putType(typeobj)
|
||||||
|
self.putSymbolValue(value.pointer())
|
||||||
|
self.putNumChild(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Enum:
|
||||||
|
#DumperBase.warn('ENUM VALUE: %s' % value.stringify())
|
||||||
|
self.putType(typeobj.name)
|
||||||
|
self.putValue(value.display())
|
||||||
|
self.putNumChild(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Array:
|
||||||
|
#DumperBase.warn('ARRAY VALUE: %s' % value)
|
||||||
|
self.putCStyleArray(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Integral:
|
||||||
|
#DumperBase.warn('INTEGER: %s %s' % (value.name, value))
|
||||||
|
val = value.value()
|
||||||
|
self.putNumChild(0)
|
||||||
|
self.putValue(val)
|
||||||
|
self.putType(typeName)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Float:
|
||||||
|
#DumperBase.warn('FLOAT VALUE: %s' % value)
|
||||||
|
self.putValue(value.value())
|
||||||
|
self.putNumChild(0)
|
||||||
|
self.putType(typeobj.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code in (TypeCode.Reference, TypeCode.RValueReference):
|
||||||
|
#DumperBase.warn('REFERENCE VALUE: %s' % value)
|
||||||
|
val = value.dereference()
|
||||||
|
if val.laddress != 0:
|
||||||
|
self.putItem(val)
|
||||||
|
else:
|
||||||
|
self.putSpecialValue('nullreference')
|
||||||
|
self.putBetterType(typeName)
|
||||||
|
return
|
||||||
|
|
||||||
|
if typeobj.code == TypeCode.Complex:
|
||||||
|
self.putType(typeobj)
|
||||||
|
self.putValue(value.display())
|
||||||
|
self.putNumChild(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.putType(typeName)
|
||||||
|
|
||||||
|
if value.summary is not None and self.useFancy:
|
||||||
|
self.putValue(self.hexencode(value.summary), 'utf8:1:0')
|
||||||
|
self.putNumChild(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.putExpandable()
|
||||||
|
self.putEmptyValue()
|
||||||
|
#DumperBase.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address()))
|
||||||
|
if self.showQObjectNames:
|
||||||
|
#with self.timer(self.currentIName):
|
||||||
|
self.putQObjectNameValue(value)
|
||||||
|
if self.isExpanded():
|
||||||
|
self.putField('sortable', 1)
|
||||||
|
with Children(self):
|
||||||
|
baseIndex = 0
|
||||||
|
for item in self.listValueChildren(value):
|
||||||
|
if item.name.startswith('__vfptr'):
|
||||||
|
with SubItem(self, '[vptr]'):
|
||||||
|
# int (**)(void)
|
||||||
|
self.putType(' ')
|
||||||
|
self.putSortGroup(20)
|
||||||
|
self.putValue(item.name)
|
||||||
|
n = 100
|
||||||
|
if self.isExpanded():
|
||||||
|
with Children(self):
|
||||||
|
n = self.putVTableChildren(item, n)
|
||||||
|
self.putNumChild(n)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item.isBaseClass:
|
||||||
|
baseIndex += 1
|
||||||
|
# We cannot use nativeField.name as part of the iname as
|
||||||
|
# it might contain spaces and other strange characters.
|
||||||
|
with UnnamedSubItem(self, "@%d" % baseIndex):
|
||||||
|
self.putField('iname', self.currentIName)
|
||||||
|
self.putField('name', '[%s]' % item.name)
|
||||||
|
self.putSortGroup(1000 - baseIndex)
|
||||||
|
self.putAddress(item.address())
|
||||||
|
self.putItem(item)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
with SubItem(self, item.name):
|
||||||
|
self.putItem(item)
|
||||||
|
if self.showQObjectNames:
|
||||||
|
self.tryPutQObjectGuts(value)
|
||||||
|
|
||||||
|
|
||||||
|
def putFormattedPointerX(self, value: DumperBase.Value):
|
||||||
|
self.putOriginalAddress(value.address())
|
||||||
|
pointer = value.pointer()
|
||||||
|
self.putAddress(pointer)
|
||||||
|
if pointer == 0:
|
||||||
|
self.putType(value.type)
|
||||||
|
self.putValue('0x0')
|
||||||
|
return
|
||||||
|
|
||||||
|
typeName = value.type.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.readRawMemory(pointer, 1)
|
||||||
|
except:
|
||||||
|
# Failure to dereference a pointer should at least
|
||||||
|
# show the value of a pointer.
|
||||||
|
#DumperBase.warn('BAD POINTER: %s' % value)
|
||||||
|
self.putValue('0x%x' % pointer)
|
||||||
|
self.putType(typeName)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.currentIName.endswith('.this'):
|
||||||
|
self.putDerefedPointer(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
displayFormat = self.currentItemFormat(value.type.name)
|
||||||
|
|
||||||
|
if value.type.targetName == 'void':
|
||||||
|
#DumperBase.warn('VOID POINTER: %s' % displayFormat)
|
||||||
|
self.putType(typeName)
|
||||||
|
self.putSymbolValue(pointer)
|
||||||
|
return
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Raw:
|
||||||
|
# Explicitly requested bald pointer.
|
||||||
|
#DumperBase.warn('RAW')
|
||||||
|
self.putType(typeName)
|
||||||
|
self.putValue('0x%x' % pointer)
|
||||||
|
self.putExpandable()
|
||||||
|
if self.currentIName in self.expandedINames:
|
||||||
|
with Children(self):
|
||||||
|
with SubItem(self, '*'):
|
||||||
|
self.putItem(value.dereference())
|
||||||
|
return
|
||||||
|
|
||||||
|
limit = self.displayStringLimit
|
||||||
|
if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String):
|
||||||
|
limit = 1000000
|
||||||
|
if self.tryPutSimpleFormattedPointer(pointer, typeName,
|
||||||
|
value.type.targetName, displayFormat, limit):
|
||||||
|
self.putExpandable()
|
||||||
|
return
|
||||||
|
|
||||||
|
if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array10000:
|
||||||
|
n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10]
|
||||||
|
self.putType(typeName)
|
||||||
|
self.putItemCount(n)
|
||||||
|
self.putArrayData(value.pointer(), n, value.type.targetName)
|
||||||
|
return
|
||||||
|
|
||||||
|
#DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers)
|
||||||
|
#DumperBase.warn('INAME: %s' % self.currentIName)
|
||||||
|
if self.autoDerefPointers:
|
||||||
|
# Generic pointer type with AutomaticFormat, but never dereference char types:
|
||||||
|
if value.type.targetName not in (
|
||||||
|
'char',
|
||||||
|
'signed char',
|
||||||
|
'int8_t',
|
||||||
|
'qint8',
|
||||||
|
'unsigned char',
|
||||||
|
'uint8_t',
|
||||||
|
'quint8',
|
||||||
|
'wchar_t',
|
||||||
|
'CHAR',
|
||||||
|
'WCHAR',
|
||||||
|
'char8_t',
|
||||||
|
'char16_t',
|
||||||
|
'char32_t'
|
||||||
|
):
|
||||||
|
self.putDerefedPointer(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
#DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type)
|
||||||
|
#DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress)
|
||||||
|
self.putType(typeName)
|
||||||
|
self.putSymbolValue(pointer)
|
||||||
|
self.putExpandable()
|
||||||
|
if self.currentIName in self.expandedINames:
|
||||||
|
with Children(self):
|
||||||
|
with SubItem(self, '*'):
|
||||||
|
self.putItem(value.dereference())
|
||||||
|
|
||||||
|
|
||||||
|
def putCStyleArray(self, value):
|
||||||
|
arrayType = value.type
|
||||||
|
innerType = arrayType.ltarget
|
||||||
|
address = value.address()
|
||||||
|
if address:
|
||||||
|
self.putValue('@0x%x' % address, priority=-1)
|
||||||
|
else:
|
||||||
|
self.putEmptyValue()
|
||||||
|
self.putType(arrayType)
|
||||||
|
|
||||||
|
displayFormat = self.currentItemFormat()
|
||||||
|
arrayByteSize = arrayType.size()
|
||||||
|
n = self.arrayItemCountFromTypeName(value.type.name, 100)
|
||||||
|
|
||||||
|
p = value.address()
|
||||||
|
if displayFormat != DisplayFormat.Raw and p:
|
||||||
|
if innerType.name in (
|
||||||
|
'char',
|
||||||
|
'int8_t',
|
||||||
|
'qint8',
|
||||||
|
'wchar_t',
|
||||||
|
'unsigned char',
|
||||||
|
'uint8_t',
|
||||||
|
'quint8',
|
||||||
|
'signed char',
|
||||||
|
'CHAR',
|
||||||
|
'WCHAR',
|
||||||
|
'char8_t',
|
||||||
|
'char16_t',
|
||||||
|
'char32_t'
|
||||||
|
):
|
||||||
|
self.putCharArrayHelper(p, n, innerType, self.currentItemFormat(),
|
||||||
|
makeExpandable=False)
|
||||||
|
else:
|
||||||
|
self.tryPutSimpleFormattedPointer(p, arrayType, innerType,
|
||||||
|
displayFormat, arrayByteSize)
|
||||||
|
self.putNumChild(n)
|
||||||
|
|
||||||
|
if self.isExpanded():
|
||||||
|
if n > 100:
|
||||||
|
addrStep = innerType.size()
|
||||||
|
with Children(self, n, innerType, addrBase=address, addrStep=addrStep):
|
||||||
|
for i in self.childRange():
|
||||||
|
self.putSubItem(i, self.createValue(address + i * addrStep, innerType))
|
||||||
|
else:
|
||||||
|
with Children(self):
|
||||||
|
n = 0
|
||||||
|
for item in self.listValueChildren(value):
|
||||||
|
with SubItem(self, n):
|
||||||
|
n += 1
|
||||||
|
self.putItem(item)
|
||||||
|
|
||||||
|
|
||||||
|
def putArrayData(self, base, n, innerType, childNumChild=None):
|
||||||
|
self.checkIntType(base)
|
||||||
|
self.checkIntType(n)
|
||||||
|
addrBase = base
|
||||||
|
innerType = self.createType(innerType)
|
||||||
|
innerSize = innerType.size()
|
||||||
|
self.putNumChild(n)
|
||||||
|
#DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
|
||||||
|
enc = innerType.simpleEncoding()
|
||||||
|
maxNumChild = self.maxArrayCount()
|
||||||
|
if enc:
|
||||||
|
self.put('childtype="%s",' % innerType.name)
|
||||||
|
self.put('addrbase="0x%x",' % addrBase)
|
||||||
|
self.put('addrstep="0x%x",' % innerSize)
|
||||||
|
self.put('arrayencoding="%s",' % enc)
|
||||||
|
self.put('endian="%s",' % self.packCode)
|
||||||
|
if n > maxNumChild:
|
||||||
|
self.put('childrenelided="%s",' % n)
|
||||||
|
n = maxNumChild
|
||||||
|
self.put('arraydata="')
|
||||||
|
self.put(self.readMemory(addrBase, n * innerSize))
|
||||||
|
self.put('",')
|
||||||
|
else:
|
||||||
|
with Children(self, n, innerType, childNumChild, maxNumChild,
|
||||||
|
addrBase=addrBase, addrStep=innerSize):
|
||||||
|
for i in self.childRange():
|
||||||
|
self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType))
|
||||||
|
|
||||||
|
def tryPutSimpleFormattedPointer(self, ptr, typeName, innerType, displayFormat, limit):
|
||||||
|
if isinstance(innerType, self.Type):
|
||||||
|
innerType = innerType.name
|
||||||
|
if displayFormat == DisplayFormat.Automatic:
|
||||||
|
if innerType in ('char', 'signed char', 'unsigned char', 'uint8_t', 'CHAR'):
|
||||||
|
# Use UTF-8 as default for char *.
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, shown, data) = self.readToFirstZero(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'utf8', length=length)
|
||||||
|
if self.isExpanded():
|
||||||
|
self.putArrayData(ptr, shown, innerType)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if innerType in ('wchar_t', 'WCHAR'):
|
||||||
|
self.putType(typeName)
|
||||||
|
charSize = self.lookupType('wchar_t').size()
|
||||||
|
(length, data) = self.encodeCArray(ptr, charSize, limit)
|
||||||
|
if charSize == 2:
|
||||||
|
self.putValue(data, 'utf16', length=length)
|
||||||
|
else:
|
||||||
|
self.putValue(data, 'ucs4', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Latin1String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'latin1', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.SeparateLatin1String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'latin1', length=length)
|
||||||
|
self.putDisplay('latin1:separate', data)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Utf8String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'utf8', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.SeparateUtf8String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'utf8', length=length)
|
||||||
|
self.putDisplay('utf8:separate', data)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Local8BitString:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 1, limit)
|
||||||
|
self.putValue(data, 'local8bit', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Utf16String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 2, limit)
|
||||||
|
self.putValue(data, 'utf16', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if displayFormat == DisplayFormat.Ucs4String:
|
||||||
|
self.putType(typeName)
|
||||||
|
(length, data) = self.encodeCArray(ptr, 4, limit)
|
||||||
|
self.putValue(data, 'ucs4', length=length)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def putDerefedPointer(self, value):
|
||||||
|
derefValue = value.dereference()
|
||||||
|
savedCurrentChildType = self.currentChildType
|
||||||
|
self.currentChildType = value.type.targetName
|
||||||
|
self.putType(value.type.targetName)
|
||||||
|
derefValue.name = '*'
|
||||||
|
derefValue.autoDerefCount = value.autoDerefCount + 1
|
||||||
|
|
||||||
|
if derefValue.type.code == TypeCode.Pointer:
|
||||||
|
self.putField('autoderefcount', '{}'.format(derefValue.autoDerefCount))
|
||||||
|
|
||||||
|
self.putItem(derefValue)
|
||||||
|
self.currentChildType = savedCurrentChildType
|
||||||
|
|
||||||
|
def extractPointer(self, value):
|
||||||
|
code = 'I' if self.ptrSize() == 4 else 'Q'
|
||||||
|
return self.extractSomething(value, code, 8 * self.ptrSize())
|
||||||
|
|
||||||
|
def createValue(self, datish, typish):
|
||||||
|
if self.isInt(datish): # Used as address.
|
||||||
|
return self.createValueFromAddressAndType(datish, typish)
|
||||||
|
if isinstance(datish, bytes):
|
||||||
|
val = self.Value(self)
|
||||||
|
if isinstance(typish, self.Type):
|
||||||
|
val._type = typish
|
||||||
|
else:
|
||||||
|
val._type = self.Type(self, typish)
|
||||||
|
#DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish)))
|
||||||
|
val.ldata = datish
|
||||||
|
val.check()
|
||||||
|
return val
|
||||||
|
raise RuntimeError('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish))
|
||||||
|
|
||||||
|
def createValueFromAddressAndType(self, address, typish):
|
||||||
|
val = self.Value(self)
|
||||||
|
if isinstance(typish, self.Type):
|
||||||
|
val._type = typish
|
||||||
|
else:
|
||||||
|
val._type = self.Type(self, typish)
|
||||||
|
val.laddress = address
|
||||||
|
if self.useDynamicType:
|
||||||
|
val._type = val.type.dynamicType(address)
|
||||||
|
return val
|
||||||
|
@@ -3041,7 +3041,7 @@ class DumperBase():
|
|||||||
or self.type.name.startswith('unsigned ') \
|
or self.type.name.startswith('unsigned ') \
|
||||||
or self.type.name.find(' unsigned ') != -1
|
or self.type.name.find(' unsigned ') != -1
|
||||||
if bitsize is None:
|
if bitsize is None:
|
||||||
bitsize = self.type.bitsize()
|
bitsize = self.type.lbitsize
|
||||||
return self.extractInteger(bitsize, unsigned)
|
return self.extractInteger(bitsize, unsigned)
|
||||||
|
|
||||||
def floatingPoint(self):
|
def floatingPoint(self):
|
||||||
@@ -3512,26 +3512,40 @@ class DumperBase():
|
|||||||
tdata.moduleName = self.moduleName
|
tdata.moduleName = self.moduleName
|
||||||
return tdata
|
return tdata
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bitsize(self):
|
||||||
|
if callable(self.lbitsize):
|
||||||
|
self.lbitsize = self.lbitsize()
|
||||||
|
return self.lbitsize
|
||||||
|
|
||||||
class Type():
|
class Type():
|
||||||
def __init__(self, dumper, typeId):
|
def __init__(self, dumper, typeId):
|
||||||
self.typeId = typeId
|
self.typeId = typeId.replace('@', dumper.qtNamespace())
|
||||||
self.dumper = dumper
|
self.dumper = dumper
|
||||||
self.tdata = dumper.typeData.get(typeId, None)
|
self.initialized = False
|
||||||
if self.tdata is None:
|
|
||||||
#DumperBase.warn('USING : %s' % self.typeId)
|
|
||||||
self.dumper.lookupType(self.typeId)
|
|
||||||
self.tdata = self.dumper.typeData.get(self.typeId)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
#return self.typeId
|
#return self.typeId
|
||||||
return self.stringify()
|
return self.stringify()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tdata(self):
|
||||||
|
if not self.initialized:
|
||||||
|
self.initialized = True
|
||||||
|
self.data = self.dumper.typeData.get(self.typeId, None)
|
||||||
|
if self.data is None:
|
||||||
|
#DumperBase.warn('USING : %s' % self.typeId)
|
||||||
|
self.dumper.lookupType(self.typeId)
|
||||||
|
self.data = self.dumper.typeData.get(self.typeId)
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def setTdata(self, tdata):
|
||||||
|
self.initialized = True
|
||||||
|
self.data = tdata
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
tdata = self.dumper.typeData.get(self.typeId)
|
return self.typeId if self.tdata is None else self.tdata.name
|
||||||
if tdata is None:
|
|
||||||
return self.typeId
|
|
||||||
return tdata.name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code(self):
|
def code(self):
|
||||||
@@ -3539,7 +3553,7 @@ class DumperBase():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def lbitsize(self):
|
def lbitsize(self):
|
||||||
return self.tdata.lbitsize
|
return self.tdata.bitsize
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lbitpos(self):
|
def lbitpos(self):
|
||||||
@@ -3547,15 +3561,25 @@ class DumperBase():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def ltarget(self):
|
def ltarget(self):
|
||||||
|
if isinstance(self.tdata.ltarget, str):
|
||||||
|
self.tdata.ltarget = self.dumper.createType(self.tdata.ltarget)
|
||||||
return self.tdata.ltarget
|
return self.tdata.ltarget
|
||||||
|
|
||||||
|
@property
|
||||||
|
def targetName(self):
|
||||||
|
if self.tdata.ltarget is None:
|
||||||
|
return ''
|
||||||
|
return self.tdata.ltarget if isinstance(self.tdata.ltarget, str) else self.tdata.ltarget.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def moduleName(self):
|
def moduleName(self):
|
||||||
|
if callable(self.tdata.moduleName):
|
||||||
|
self.tdata.moduleName = self.tdata.moduleName()
|
||||||
return self.tdata.moduleName
|
return self.tdata.moduleName
|
||||||
|
|
||||||
def stringify(self):
|
def stringify(self):
|
||||||
return 'Type(name="%s",bsize=%s,code=%s)' \
|
return 'Type(name="%s",bsize=%s,code=%s)' \
|
||||||
% (self.tdata.name, self.tdata.lbitsize, self.tdata.code)
|
% (self.tdata.name, self.lbitsize, self.tdata.code)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
if self.dumper.isInt(index):
|
if self.dumper.isInt(index):
|
||||||
@@ -3659,7 +3683,7 @@ class DumperBase():
|
|||||||
|
|
||||||
def alignment(self):
|
def alignment(self):
|
||||||
if self.tdata.code == TypeCode.Typedef:
|
if self.tdata.code == TypeCode.Typedef:
|
||||||
return self.tdata.ltarget.alignment()
|
return self.ltarget.alignment()
|
||||||
if self.tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum):
|
if self.tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum):
|
||||||
if self.tdata.name in ('double', 'long long', 'unsigned long long'):
|
if self.tdata.name in ('double', 'long long', 'unsigned long long'):
|
||||||
# Crude approximation.
|
# Crude approximation.
|
||||||
@@ -3678,7 +3702,7 @@ class DumperBase():
|
|||||||
return self.dumper.createPointerType(self)
|
return self.dumper.createPointerType(self)
|
||||||
|
|
||||||
def target(self):
|
def target(self):
|
||||||
return self.tdata.ltarget
|
return self.ltarget
|
||||||
|
|
||||||
def stripTypedefs(self):
|
def stripTypedefs(self):
|
||||||
if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
|
if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
|
||||||
@@ -3687,7 +3711,7 @@ class DumperBase():
|
|||||||
return self.ltarget
|
return self.ltarget
|
||||||
|
|
||||||
def size(self):
|
def size(self):
|
||||||
bs = self.bitsize()
|
bs = self.lbitsize
|
||||||
if bs % 8 != 0:
|
if bs % 8 != 0:
|
||||||
DumperBase.warn('ODD SIZE: %s' % self)
|
DumperBase.warn('ODD SIZE: %s' % self)
|
||||||
return (7 + bs) >> 3
|
return (7 + bs) >> 3
|
||||||
@@ -3797,12 +3821,12 @@ class DumperBase():
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
def createPointerType(self, targetType):
|
def createPointerType(self, targetType):
|
||||||
if not isinstance(targetType, self.Type):
|
if not isinstance(targetType, (str, self.Type)):
|
||||||
raise RuntimeError('Expected type in createPointerType(), got %s'
|
raise RuntimeError('Expected type or str in createPointerType(), got %s'
|
||||||
% type(targetType))
|
% type(targetType))
|
||||||
typeId = targetType.typeId + ' *'
|
typeId = (targetType if isinstance(targetType, str) else targetType.typeId) + ' *'
|
||||||
tdata = self.TypeData(self, typeId)
|
tdata = self.TypeData(self, typeId)
|
||||||
tdata.name = targetType.name + '*'
|
tdata.name = (targetType if isinstance(targetType, str) else targetType.name) + '*'
|
||||||
tdata.lbitsize = 8 * self.ptrSize()
|
tdata.lbitsize = 8 * self.ptrSize()
|
||||||
tdata.code = TypeCode.Pointer
|
tdata.code = TypeCode.Pointer
|
||||||
tdata.ltarget = targetType
|
tdata.ltarget = targetType
|
||||||
@@ -3927,7 +3951,7 @@ class DumperBase():
|
|||||||
tdata = self.typeData.get(typish, None)
|
tdata = self.typeData.get(typish, None)
|
||||||
if tdata is not None:
|
if tdata is not None:
|
||||||
if tdata.lbitsize is not None:
|
if tdata.lbitsize is not None:
|
||||||
if tdata.lbitsize > 0:
|
if callable(tdata.lbitsize) or tdata.lbitsize > 0:
|
||||||
return self.Type(self, typish)
|
return self.Type(self, typish)
|
||||||
|
|
||||||
knownType = self.lookupType(typish)
|
knownType = self.lookupType(typish)
|
||||||
@@ -3944,7 +3968,7 @@ class DumperBase():
|
|||||||
if typish.endswith('*'):
|
if typish.endswith('*'):
|
||||||
tdata.code = TypeCode.Pointer
|
tdata.code = TypeCode.Pointer
|
||||||
tdata.lbitsize = 8 * self.ptrSize()
|
tdata.lbitsize = 8 * self.ptrSize()
|
||||||
tdata.ltarget = self.createType(typish[:-1].strip())
|
tdata.ltarget = typish[:-1].strip()
|
||||||
|
|
||||||
typeobj = self.Type(self, tdata.typeId)
|
typeobj = self.Type(self, tdata.typeId)
|
||||||
#DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
|
#DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
|
||||||
|
@@ -419,7 +419,7 @@ class Dumper(DumperBase):
|
|||||||
targetTypeName = typeName[0:pos1].strip()
|
targetTypeName = typeName[0:pos1].strip()
|
||||||
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
|
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
|
||||||
targetType = self.fromNativeType(nativeTargetType)
|
targetType = self.fromNativeType(nativeTargetType)
|
||||||
targetType.tdata = targetType.tdata.copy()
|
targetType.setTdata(targetType.tdata.copy())
|
||||||
targetType.tdata.name = targetTypeName
|
targetType.tdata.name = targetTypeName
|
||||||
return self.createArrayType(targetType, count)
|
return self.createArrayType(targetType, count)
|
||||||
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
|
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
|
||||||
|
@@ -121,6 +121,9 @@ static std::string stripPointerType(const std::string &typeNameIn)
|
|||||||
std::string typeName = typeNameIn;
|
std::string typeName = typeNameIn;
|
||||||
if (typeName.back() == '*') {
|
if (typeName.back() == '*') {
|
||||||
typeName.pop_back();
|
typeName.pop_back();
|
||||||
|
trimBack(typeName);
|
||||||
|
if (endsWith(typeName, "const"))
|
||||||
|
typeName = typeName.erase(typeName.size() - 5, 5);
|
||||||
} else {
|
} else {
|
||||||
const auto arrayPosition = typeName.find_first_of('[');
|
const auto arrayPosition = typeName.find_first_of('[');
|
||||||
if (arrayPosition != std::string::npos
|
if (arrayPosition != std::string::npos
|
||||||
@@ -296,35 +299,19 @@ int PyType::code() const
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!resolve())
|
if (m_tag >= 0) {
|
||||||
return parseTypeName(name()).value_or(TypeCodeUnresolvable);
|
switch (m_tag) {
|
||||||
|
case SymTagUDT: return TypeCodeStruct;
|
||||||
if (m_tag < 0) {
|
case SymTagEnum: return TypeCodeEnum;
|
||||||
if (const std::optional<TypeCodes> typeCode = parseTypeName(name()))
|
case SymTagTypedef: return TypeCodeTypedef;
|
||||||
return *typeCode;
|
case SymTagFunctionType: return TypeCodeFunction;
|
||||||
|
case SymTagPointerType: return TypeCodePointer;
|
||||||
IDebugSymbolGroup2 *sg = 0;
|
case SymTagArrayType: return TypeCodeArray;
|
||||||
if (FAILED(ExtensionCommandContext::instance()->symbols()->CreateSymbolGroup2(&sg)))
|
case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat;
|
||||||
return TypeCodeStruct;
|
default: break;
|
||||||
|
}
|
||||||
const std::string helperValueName = SymbolGroupValue::pointedToSymbolName(0, name(true));
|
|
||||||
ULONG index = DEBUG_ANY_ID;
|
|
||||||
if (SUCCEEDED(sg->AddSymbol(helperValueName.c_str(), &index)))
|
|
||||||
m_tag = PyValue(index, sg).tag();
|
|
||||||
sg->Release();
|
|
||||||
}
|
}
|
||||||
switch (m_tag) {
|
return parseTypeName(name()).value_or(TypeCodeStruct);
|
||||||
case SymTagUDT: return TypeCodeStruct;
|
|
||||||
case SymTagEnum: return TypeCodeEnum;
|
|
||||||
case SymTagTypedef: return TypeCodeTypedef;
|
|
||||||
case SymTagFunctionType: return TypeCodeFunction;
|
|
||||||
case SymTagPointerType: return TypeCodePointer;
|
|
||||||
case SymTagArrayType: return TypeCodeArray;
|
|
||||||
case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TypeCodeStruct;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyType PyType::target() const
|
PyType PyType::target() const
|
||||||
@@ -533,6 +520,7 @@ PY_FUNC_RET_OBJECT_LIST(fields, PY_OBJ_NAME)
|
|||||||
PY_FUNC_RET_STD_STRING(module, PY_OBJ_NAME)
|
PY_FUNC_RET_STD_STRING(module, PY_OBJ_NAME)
|
||||||
PY_FUNC(moduleId, PY_OBJ_NAME, "K")
|
PY_FUNC(moduleId, PY_OBJ_NAME, "K")
|
||||||
PY_FUNC(arrayElements, PY_OBJ_NAME, "k")
|
PY_FUNC(arrayElements, PY_OBJ_NAME, "k")
|
||||||
|
PY_FUNC_RET_BOOL(resolved, PY_OBJ_NAME)
|
||||||
PY_FUNC_DECL(templateArguments, PY_OBJ_NAME)
|
PY_FUNC_DECL(templateArguments, PY_OBJ_NAME)
|
||||||
{
|
{
|
||||||
PY_IMPL_GUARD;
|
PY_IMPL_GUARD;
|
||||||
@@ -568,6 +556,8 @@ static PyMethodDef typeMethods[] = {
|
|||||||
"Returns the number of elements in an array or 0 for non array types"},
|
"Returns the number of elements in an array or 0 for non array types"},
|
||||||
{"templateArguments", PyCFunction(templateArguments), METH_NOARGS,
|
{"templateArguments", PyCFunction(templateArguments), METH_NOARGS,
|
||||||
"Returns all template arguments."},
|
"Returns all template arguments."},
|
||||||
|
{"resolved", PyCFunction(resolved), METH_NOARGS,
|
||||||
|
"Returns whether the type is resolved"},
|
||||||
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
@@ -29,6 +29,7 @@ public:
|
|||||||
std::string module() const;
|
std::string module() const;
|
||||||
ULONG64 moduleId() const;
|
ULONG64 moduleId() const;
|
||||||
int arrayElements() const;
|
int arrayElements() const;
|
||||||
|
bool resolved() const { return m_resolved.value_or(false); }
|
||||||
|
|
||||||
struct TemplateArgument
|
struct TemplateArgument
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user