Files
qt-creator/share/qtcreator/debugger/cdbbridge.py
hjk 67072d3f5b Debugger: Re-work bridges
The type cache has been split into smaller caches for individual
aspects. Type ids are now integral, not strings.

In addition, there is new supporting code for logging, timing and
profiling

Change-Id: I6db72a149650d42aecf8b899869c542b1303d43b
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
2024-05-07 10:15:34 +00:00

924 lines
34 KiB
Python

# Copyright (C) 2016 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import inspect
import os
import sys
import cdbext
import re
import threading
from utils import TypeCode
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
from dumper import DumperBase, SubItem, Children, DisplayFormat, UnnamedSubItem
class FakeVoidType(cdbext.Type):
def __init__(self, name, dumper):
cdbext.Type.__init__(self)
self.typeName = name.strip()
self.dumper = dumper
def name(self):
return self.typeName
def bitsize(self):
return self.dumper.ptrSize() * 8
def code(self):
if self.typeName.endswith('*'):
return TypeCode.Pointer
if self.typeName.endswith(']'):
return TypeCode.Array
return TypeCode.Void
def unqualified(self):
return self
def target(self):
code = self.code()
if code == TypeCode.Pointer:
return FakeVoidType(self.typeName[:-1], self.dumper)
if code == TypeCode.Void:
return self
try:
return FakeVoidType(self.typeName[:self.typeName.rindex('[')], self.dumper)
except:
return FakeVoidType('void', self.dumper)
def targetName(self):
return self.target().name()
def arrayElements(self):
try:
return int(self.typeName[self.typeName.rindex('[') + 1:self.typeName.rindex(']')])
except:
return 0
def stripTypedef(self):
return self
def fields(self):
return []
def templateArgument(self, pos, numeric):
return None
def templateArguments(self):
return []
class Dumper(DumperBase):
def __init__(self):
DumperBase.__init__(self)
self.outputLock = threading.Lock()
self.isCdb = True
#FIXME
typeid = self.typeid_for_string('@QVariantMap')
del self.type_code_cache[typeid]
del self.type_target_cache[typeid]
del self.type_size_cache[typeid]
del self.type_alignment_cache[typeid]
def enumValue(self, nativeValue):
val = nativeValue.nativeDebuggerValue()
# remove '0n' decimal prefix of the native cdb value output
return val.replace('(0n', '(')
def fromNativeValue(self, nativeValue):
self.check(isinstance(nativeValue, cdbext.Value))
val = self.Value(self)
val.name = nativeValue.name()
# There is no cdb api for the size of bitfields.
# Workaround this issue by parsing the native debugger text for integral types.
if nativeValue.type().code() == TypeCode.Integral:
try:
integerString = nativeValue.nativeDebuggerValue()
except UnicodeDecodeError:
integerString = '' # cannot decode - read raw
if integerString == 'true':
val.ldata = int(1).to_bytes(1, byteorder='little')
elif integerString == 'false':
val.ldata = int(0).to_bytes(1, byteorder='little')
else:
integerString = integerString.replace('`', '')
integerString = integerString.split(' ')[0]
if integerString.startswith('0n'):
integerString = integerString[2:]
base = 10
elif integerString.startswith('0x'):
base = 16
else:
base = 10
signed = not nativeValue.type().name().startswith('unsigned')
try:
val.ldata = int(integerString, base).to_bytes((nativeValue.type().bitsize() +7) // 8,
byteorder='little', signed=signed)
except:
# read raw memory in case the integerString can not be interpreted
pass
if nativeValue.type().code() == TypeCode.Enum:
val.ldisplay = self.enumValue(nativeValue)
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.typeid = self.from_native_type(nativeValue.type())
val.nativeValue = nativeValue
val.laddress = nativeValue.address()
val.lbitsize = nativeValue.bitsize()
return val
def nativeTypeId(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type))
name = nativeType.name()
if name is None or len(name) == 0:
c = '0'
elif name == 'struct {...}':
c = 's'
elif name == 'union {...}':
c = 'u'
else:
return name
typeId = c + ''.join(['{%s:%s}' % (f.name(), self.nativeTypeId(f.type()))
for f in nativeType.fields()])
return typeId
def from_native_type(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type))
typeid = self.typeid_for_string(self.nativeTypeId(nativeType))
self.type_nativetype_cache[typeid] = nativeType
if nativeType.name().startswith('void'):
nativeType = FakeVoidType(nativeType.name(), self)
code = nativeType.code()
if code == TypeCode.Pointer:
if nativeType.name().startswith('<function>'):
code = TypeCode.Function
elif nativeType.targetName() != nativeType.name():
return self.create_pointer_typeid(self.typeid_for_string(nativeType.targetName()))
if code == TypeCode.Array:
# cdb reports virtual function tables as arrays those ar handled separetly by
# the DumperBase. Declare those types as structs prevents a lookup to a
# none existing type
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
targetName = nativeType.targetName().strip()
self.type_name_cache[typeid] = nativeType.name()
self.type_code_cache[typeid] = code
self.type_target_cache[typeid] = self.typeid_for_string(targetName)
self.type_size_cache[typeid] = nativeType.bitsize() // 8
return typeid
code = TypeCode.Struct
self.type_name_cache[typeid] = nativeType.name()
self.type_size_cache[typeid] = nativeType.bitsize() // 8
self.type_code_cache[typeid] = code
self.type_modulename_cache[typeid] = nativeType.module()
self.type_enum_display_cache[typeid] = lambda intval, addr, form: \
self.nativeTypeEnumDisplay(nativeType, intval, form)
return typeid
def listNativeValueChildren(self, nativeValue, include_bases):
fields = []
index = 0
nativeMember = nativeValue.childFromIndex(index)
while nativeMember:
# Why this restriction to things with address? Can't nativeValue
# be e.g. located in registers, without address?
if nativeMember.address() != 0:
if include_bases or nativeMember.name() != nativeMember.type().name():
field = self.fromNativeValue(nativeMember)
fields.append(field)
index += 1
nativeMember = nativeValue.childFromIndex(index)
return fields
def listValueChildren(self, value, include_bases=True):
nativeValue = value.nativeValue
if nativeValue is None:
nativeValue = cdbext.createValue(value.address(), self.lookupNativeType(value.type.name, 0))
return self.listNativeValueChildren(nativeValue, include_bases)
def nativeListMembers(self, value, native_type, include_bases):
nativeValue = value.nativeValue
if nativeValue is None:
nativeValue = cdbext.createValue(value.address(), native_type)
return self.listNativeValueChildren(nativeValue, include_bases)
def nativeStructAlignment(self, nativeType):
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
def handleItem(nativeFieldType, align):
a = self.type_alignment(self.from_native_type(nativeFieldType))
return a if a > align else align
align = 1
for f in nativeType.fields():
align = handleItem(f.type(), align)
return align
def nativeTypeEnumDisplay(self, nativeType, intval, form):
value = self.nativeParseAndEvaluate('(%s)%d' % (nativeType.name(), intval))
if value is None:
return ''
return self.enumValue(value)
def enumExpression(self, enumType, enumValue):
ns = self.qtNamespace()
return ns + "Qt::" + enumType + "(" \
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
def pokeValue(self, typeName, *args):
return None
def parseAndEvaluate(self, exp):
return self.fromNativeValue(self.nativeParseAndEvaluate(exp))
def nativeParseAndEvaluate(self, exp):
return cdbext.parseAndEvaluate(exp)
def isWindowsTarget(self):
return True
def isQnxTarget(self):
return False
def isArmArchitecture(self):
return False
def isMsvcTarget(self):
return True
def qtCoreModuleName(self):
modules = cdbext.listOfModules()
# first check for an exact module name match
for coreName in ['Qt6Core', 'Qt6Cored', 'Qt5Cored', 'Qt5Core', 'QtCored4', 'QtCore4']:
if coreName in modules:
self.qtCoreModuleName = lambda: coreName
return coreName
# maybe we have a libinfix build.
for pattern in ['Qt6Core.*', 'Qt5Core.*', 'QtCore.*']:
matches = [module for module in modules if re.match(pattern, module)]
if matches:
coreName = matches[0]
self.qtCoreModuleName = lambda: coreName
return coreName
return None
def qtDeclarativeModuleName(self):
modules = cdbext.listOfModules()
for declarativeModuleName in ['Qt6Qmld', 'Qt6Qml', 'Qt5Qmld', 'Qt5Qml']:
if declarativeModuleName in modules:
self.qtDeclarativeModuleName = lambda: declarativeModuleName
return declarativeModuleName
matches = [module for module in modules if re.match('Qt[56]Qml.*', module)]
if matches:
declarativeModuleName = matches[0]
self.qtDeclarativeModuleName = lambda: declarativeModuleName
return declarativeModuleName
return None
def qtHookDataSymbolName(self):
hookSymbolName = 'qtHookData'
coreModuleName = self.qtCoreModuleName()
if coreModuleName is not None:
hookSymbolName = '%s!%s%s' % (coreModuleName, self.qtNamespace(), hookSymbolName)
else:
resolved = cdbext.resolveSymbol('*' + hookSymbolName)
if resolved:
hookSymbolName = resolved[0]
else:
hookSymbolName = '*%s' % hookSymbolName
self.qtHookDataSymbolName = lambda: hookSymbolName
return hookSymbolName
def qtDeclarativeHookDataSymbolName(self):
hookSymbolName = 'qtDeclarativeHookData'
declarativeModuleName = self.qtDeclarativeModuleName()
if declarativeModuleName is not None:
hookSymbolName = '%s!%s%s' % (declarativeModuleName, self.qtNamespace(), hookSymbolName)
else:
resolved = cdbext.resolveSymbol('*' + hookSymbolName)
if resolved:
hookSymbolName = resolved[0]
else:
hookSymbolName = '*%s' % hookSymbolName
self.qtDeclarativeHookDataSymbolName = lambda: hookSymbolName
return hookSymbolName
def qtNamespace(self):
namespace = ''
qstrdupSymbolName = '*qstrdup'
coreModuleName = self.qtCoreModuleName()
if coreModuleName is not None:
qstrdupSymbolName = '%s!%s' % (coreModuleName, qstrdupSymbolName)
resolved = cdbext.resolveSymbol(qstrdupSymbolName)
if resolved:
name = resolved[0].split('!')[1]
namespaceIndex = name.find('::')
if namespaceIndex > 0:
namespace = name[:namespaceIndex + 2]
self.qtNamespace = lambda: namespace
self.qtCustomEventFunc = self.parseAndEvaluate(
'%s!%sQObject::customEvent' %
(self.qtCoreModuleName(), namespace)).address()
self.qtNamespace = lambda: namespace
return namespace
def qtVersion(self):
qtVersion = None
try:
qtVersion = self.parseAndEvaluate(
'((void**)&%s)[2]' % self.qtHookDataSymbolName()).integer()
except:
if self.qtCoreModuleName() is not None:
try:
versionValue = cdbext.call(self.qtCoreModuleName() + '!qVersion()')
version = self.extractCString(self.fromNativeValue(versionValue).address())
(major, minor, patch) = version.decode('latin1').split('.')
qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
except:
pass
if qtVersion is None:
qtVersion = self.fallbackQtVersion
self.qtVersion = lambda: qtVersion
return qtVersion
def putVtableItem(self, address):
funcName = cdbext.getNameByAddress(address)
if funcName is None:
self.putItem(self.createPointerValue(address, 'void'))
else:
self.putValue(funcName)
self.putType('void*')
self.putAddress(address)
def putVTableChildren(self, item, itemCount):
p = item.address()
for i in range(itemCount):
deref = self.extractPointer(p)
if deref == 0:
n = i
break
with SubItem(self, i):
self.putVtableItem(deref)
p += self.ptrSize()
return itemCount
def ptrSize(self):
size = cdbext.pointerSize()
self.ptrSize = lambda: size
return size
def stripQintTypedefs(self, typeName):
if typeName.startswith('qint'):
prefix = ''
size = typeName[4:]
elif typeName.startswith('quint'):
prefix = 'unsigned '
size = typeName[5:]
else:
return typeName
if size == '8':
return '%schar' % prefix
elif size == '16':
return '%sshort' % prefix
elif size == '32':
return '%sint' % prefix
elif size == '64':
return '%sint64' % prefix
else:
return typeName
def lookupNativeType(self, name, module=0):
if name.startswith('void'):
return FakeVoidType(name, self)
return cdbext.lookupType(name, module)
def reportResult(self, result, args):
cdbext.reportResult('result={%s}' % result)
def readRawMemory(self, address, size):
mem = cdbext.readRawMemory(address, size)
if len(mem) != size:
raise Exception("Invalid memory request: %d bytes from 0x%x" % (size, address))
return mem
def findStaticMetaObject(self, type):
ptr = 0
if type.moduleName is not None:
# Try to find the static meta object in the same module as the type definition. This is
# an optimization that improves the performance of looking up the meta object for not
# exported types.
ptr = cdbext.getAddressByName(type.moduleName + '!' + type.name + '::staticMetaObject')
if ptr == 0:
# If we do not find the meta object in the same module or we do not have the module name
# we fall back to the slow lookup over all modules.
ptr = cdbext.getAddressByName(type.name + '::staticMetaObject')
return ptr
def fetchVariables(self, args):
self.resetStats()
(ok, res) = self.tryFetchInterpreterVariables(args)
if ok:
self.reportResult(res, args)
return
self.setVariableFetchingOptions(args)
self.output = []
self.currentIName = 'local'
self.put('data=[')
self.anonNumber = 0
variables = []
try:
for val in cdbext.listOfLocals(self.partialVariable):
dumperVal = self.fromNativeValue(val)
dumperVal.lIsInScope = dumperVal.name not in self.uninitialized
variables.append(dumperVal)
self.handleLocals(variables)
self.handleWatches(args)
except Exception:
t,v,tb = sys.exc_info()
self.showException("FETCH VARIABLES", t, v, tb)
self.put('],partial="%d"' % (len(self.partialVariable) > 0))
self.put(',timings=%s' % self.timings)
if self.forceQtNamespace:
self.qtNamespaceToReport = self.qtNamespace()
if self.qtNamespaceToReport:
self.put(',qtnamespace="%s"' % self.qtNamespaceToReport)
self.qtNamespaceToReport = None
self.reportResult(''.join(self.output), args)
self.output = []
def report(self, stuff):
sys.stdout.write(stuff + "\n")
def findValueByExpression(self, exp):
return cdbext.parseAndEvaluate(exp)
def nativeValueDereferenceReference(self, value):
return self.nativeValueDereferencePointer(value)
def nativeValueDereferencePointer(self, value):
def nativeVtCastValue(nativeValue):
# If we have a pointer to a derived instance of the pointer type cdb adds a
# synthetic '__vtcast_<derived type name>' member as the first child
if nativeValue.hasChildren():
vtcastCandidate = nativeValue.childFromIndex(0)
vtcastCandidateName = vtcastCandidate.name()
if vtcastCandidateName.startswith('__vtcast_'):
# found a __vtcast member
# make sure that it is not an actual field
for field in nativeValue.type().fields():
if field.name() == vtcastCandidateName:
return None
return vtcastCandidate
return None
nativeValue = value.nativeValue
if nativeValue is None:
if not self.isExpanded():
raise Exception("Casting not expanded values is to expensive")
nativeValue = self.nativeParseAndEvaluate('(%s)0x%x' % (value.type.name, value.pointer()))
castVal = nativeVtCastValue(nativeValue)
if castVal is not None:
val = self.fromNativeValue(castVal)
else:
val = self.Value(self)
val.laddress = value.pointer()
val.typeid = self.typeid_for_string(value.type.targetName)
val.nativeValue = value.nativeValue
return val
def callHelper(self, rettype, value, function, args):
raise Exception("cdb does not support calling functions")
def nameForCoreId(self, id):
for dll in ['Utilsd', 'Utils']:
idName = cdbext.call('%s!Utils::nameForId(%d)' % (dll, id))
if idName is not None:
break
return self.fromNativeValue(idName)
def putCallItem(self, name, rettype, value, func, *args):
return
def symbolAddress(self, symbolName):
res = self.nativeParseAndEvaluate(symbolName)
return None if res is None else res.address()
def putItem(self, value: DumperBase.Value):
typeobj = value.type # unqualified()
typeName = typeobj.name
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())
self.putField('size', self.type_size(value.typeid))
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.strip() 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.target()
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.strip() 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 = self.type_encoding_cache.get(innerType.typeid, None)
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)
(length, data) = self.encodeCArray(ptr, 2, limit)
self.putValue(data, 'utf16', 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 createValue(self, datish, typish):
if isinstance(datish, int): # Used as address.
return self.createValueFromAddressAndType(datish, typish)
if isinstance(datish, bytes):
val = self.Value(self)
val.typeid = self.create_typeid(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)
val.typeid = self.create_typeid(typish)
val.laddress = address
if self.useDynamicType:
val.typeid = self.dynamic_typeid_at_address(val.typeid, address)
return val