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>
This commit is contained in:
hjk
2024-03-20 12:32:54 +01:00
parent 1218175c78
commit 67072d3f5b
6 changed files with 2249 additions and 2112 deletions

View File

@@ -75,6 +75,13 @@ class Dumper(DumperBase):
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
@@ -117,6 +124,7 @@ class Dumper(DumperBase):
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()
@@ -137,14 +145,10 @@ class Dumper(DumperBase):
for f in nativeType.fields()])
return typeId
def nativeValueType(self, nativeValue):
return self.fromNativeType(nativeValue.type())
def fromNativeType(self, nativeType):
def from_native_type(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type))
typeId = self.nativeTypeId(nativeType)
if self.typeData.get(typeId, None) is not None:
return self.Type(self, typeId)
typeid = self.typeid_for_string(self.nativeTypeId(nativeType))
self.type_nativetype_cache[typeid] = nativeType
if nativeType.name().startswith('void'):
nativeType = FakeVoidType(nativeType.name(), self)
@@ -154,70 +158,61 @@ class Dumper(DumperBase):
if nativeType.name().startswith('<function>'):
code = TypeCode.Function
elif nativeType.targetName() != nativeType.name():
return self.createPointerType(nativeType.targetName())
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()
count = 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)
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
tdata = self.TypeData(self, typeId)
tdata.name = nativeType.name()
tdata.lbitsize = lambda: nativeType.bitsize()
tdata.code = code
tdata.moduleName = lambda: nativeType.module()
if code == TypeCode.Struct:
tdata.lfields = lambda value: \
self.listFields(nativeType, value)
tdata.lalignment = lambda: \
self.nativeStructAlignment(nativeType)
tdata.enumDisplay = lambda intval, addr, form: \
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)
tdata.templateArguments = lambda: \
self.listTemplateParameters(nativeType.name())
return self.Type(self, typeId)
return typeid
def listNativeValueChildren(self, nativeValue):
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:
yield self.fromNativeValue(nativeMember)
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):
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)
return self.listNativeValueChildren(nativeValue, include_bases)
def listFields(self, nativeType, value):
def nativeListMembers(self, value, native_type, include_bases):
nativeValue = value.nativeValue
if nativeValue is None:
nativeValue = cdbext.createValue(value.address(), nativeType)
return self.listNativeValueChildren(nativeValue)
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.fromNativeType(nativeFieldType).alignment()
a = self.type_alignment(self.from_native_type(nativeFieldType))
return a if a > align else align
align = 1
for f in nativeType.fields():
@@ -398,20 +393,6 @@ class Dumper(DumperBase):
else:
return typeName
def lookupType(self, typeNameIn, module=0):
if len(typeNameIn) == 0:
return None
typeName = self.stripQintTypedefs(typeNameIn)
if self.typeData.get(typeName, None) is None:
nativeType = self.lookupNativeType(typeName, module)
if nativeType is None:
return None
_type = self.fromNativeType(nativeType)
if _type.typeId != typeName:
self.registerTypeAlias(_type.typeId, typeName)
return _type
return self.Type(self, typeName)
def lookupNativeType(self, name, module=0):
if name.startswith('void'):
return FakeVoidType(name, self)
@@ -439,9 +420,6 @@ class Dumper(DumperBase):
ptr = cdbext.getAddressByName(type.name + '::staticMetaObject')
return ptr
def warn(self, msg):
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
def fetchVariables(self, args):
self.resetStats()
(ok, res) = self.tryFetchInterpreterVariables(args)
@@ -458,6 +436,7 @@ class Dumper(DumperBase):
self.anonNumber = 0
variables = []
try:
for val in cdbext.listOfLocals(self.partialVariable):
dumperVal = self.fromNativeValue(val)
dumperVal.lIsInScope = dumperVal.name not in self.uninitialized
@@ -465,6 +444,9 @@ class Dumper(DumperBase):
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)
@@ -485,9 +467,6 @@ class Dumper(DumperBase):
def findValueByExpression(self, exp):
return cdbext.parseAndEvaluate(exp)
def nativeDynamicTypeName(self, address, baseType):
return None # Does not work with cdb
def nativeValueDereferenceReference(self, value):
return self.nativeValueDereferencePointer(value)
@@ -518,7 +497,7 @@ class Dumper(DumperBase):
else:
val = self.Value(self)
val.laddress = value.pointer()
val._type = DumperBase.Type(self, value.type.targetName)
val.typeid = self.typeid_for_string(value.type.targetName)
val.nativeValue = value.nativeValue
return val
@@ -540,14 +519,11 @@ class Dumper(DumperBase):
res = self.nativeParseAndEvaluate(symbolName)
return None if res is None else res.address()
def putItemX(self, value):
#DumperBase.warn('PUT ITEM: %s' % value.stringify())
def putItem(self, value: DumperBase.Value):
typeobj = value.type # unqualified()
typeName = typeobj.name
self.addToCache(typeobj) # Fill type cache
if not value.lIsInScope:
self.putSpecialValue('optimizedout')
#self.putType(typeobj)
@@ -571,8 +547,7 @@ class Dumper(DumperBase):
return
self.putAddress(value.address())
if value.lbitsize is not None:
self.putField('size', value.lbitsize // 8)
self.putField('size', self.type_size(value.typeid))
if typeobj.code == TypeCode.Function:
#DumperBase.warn('FUNCTION VALUE: %s' % value)
@@ -738,7 +713,7 @@ class Dumper(DumperBase):
#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 (
if value.type.targetName.strip() not in (
'char',
'signed char',
'int8_t',
@@ -769,7 +744,7 @@ class Dumper(DumperBase):
def putCStyleArray(self, value):
arrayType = value.type
innerType = arrayType.ltarget
innerType = arrayType.target()
address = value.address()
if address:
self.putValue('@0x%x' % address, priority=-1)
@@ -783,7 +758,7 @@ class Dumper(DumperBase):
p = value.address()
if displayFormat != DisplayFormat.Raw and p:
if innerType.name in (
if innerType.name.strip() in (
'char',
'int8_t',
'qint8',
@@ -828,7 +803,7 @@ class Dumper(DumperBase):
innerSize = innerType.size()
self.putNumChild(n)
#DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType))
enc = innerType.simpleEncoding()
enc = self.type_encoding_cache.get(innerType.typeid, None)
maxNumChild = self.maxArrayCount()
if enc:
self.put('childtype="%s",' % innerType.name)
@@ -863,12 +838,8 @@ class Dumper(DumperBase):
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:
(length, data) = self.encodeCArray(ptr, 2, limit)
self.putValue(data, 'utf16', length=length)
else:
self.putValue(data, 'ucs4', length=length)
return True
if displayFormat == DisplayFormat.Latin1String:
@@ -931,19 +902,12 @@ class Dumper(DumperBase):
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.
if isinstance(datish, int): # 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)
val.typeid = self.create_typeid(typish)
#DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish)))
val.ldata = datish
val.check()
@@ -952,11 +916,8 @@ class Dumper(DumperBase):
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.typeid = self.create_typeid(typish)
val.laddress = address
if self.useDynamicType:
val._type = val.type.dynamicType(address)
val.typeid = self.dynamic_typeid_at_address(val.typeid, address)
return val

File diff suppressed because it is too large Load Diff

View File

@@ -10,11 +10,10 @@ import gdb
import os
import os.path
import re
import sys
import struct
import tempfile
from dumper import DumperBase, Children, toInteger, TopLevelItem
from dumper import DumperBase, Children, TopLevelItem
from utils import TypeCode
from gdbtracepoint import *
@@ -119,7 +118,6 @@ ScanStackCommand()
class PlainDumper():
def __init__(self, printer):
self.printer = printer
self.typeCache = {}
def __call__(self, d, value):
if value.nativeValue is None:
@@ -137,8 +135,6 @@ class PlainDumper():
if isinstance(val, str):
# encode and avoid extra quotes ('"') at beginning and end
d.putValue(d.hexencode(val), 'utf8:1:0')
elif sys.version_info[0] <= 2 and isinstance(val, unicode):
d.putValue(val)
elif val is not None: # Assuming LazyString
d.putCharArrayValue(val.address, val.length,
val.type.target().sizeof)
@@ -166,7 +162,7 @@ def importPlainDumpers(args):
gdb.execute('disable pretty-printer .* .*')
except:
# Might occur in non-ASCII directories
DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS')
theDumper.warn('COULD NOT DISABLE PRETTY PRINTERS')
else:
theDumper.usePlainDumpers = True
theDumper.importPlainDumpers()
@@ -192,15 +188,17 @@ class Dumper(DumperBase):
# These values will be kept between calls to 'fetchVariables'.
self.isGdb = True
self.typeCache = {}
self.interpreterBreakpointResolvers = []
def warn(self, message):
print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode('latin1'))
def prepare(self, args):
self.output = []
self.setVariableFetchingOptions(args)
def fromFrameValue(self, nativeValue):
#DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address)
#self.warn('FROM FRAME VALUE: %s' % nativeValue.address)
val = nativeValue
if self.useDynamicType:
try:
@@ -209,71 +207,43 @@ class Dumper(DumperBase):
pass
return self.fromNativeValue(val)
def nativeValueType(self, nativeValue):
return self.fromNativeType(nativeValue.type)
def fromNativeValue(self, nativeValue):
#DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue)
#self.warn('FROM NATIVE VALUE: %s' % nativeValue)
self.check(isinstance(nativeValue, gdb.Value))
nativeType = nativeValue.type
code = nativeType.code
val = self.Value(self)
val.nativeValue = nativeValue
if code == gdb.TYPE_CODE_REF:
targetType = self.fromNativeType(nativeType.target().unqualified())
val = self.createReferenceValue(toInteger(nativeValue.address), targetType)
val.nativeValue = nativeValue
#DumperBase.warn('CREATED REF: %s' % val)
target_typeid = self.from_native_type(nativeType.target().unqualified())
val.ldata = int(nativeValue.address)
if self.useDynamicType: # needed for Gdb13393
target_typeid = self.dynamic_typeid_at_address(target_typeid, val.ldata)
val.typeid = self.create_reference_typeid(target_typeid)
#self.warn('CREATED REF: %s' % val)
return val
if code == gdb.TYPE_CODE_PTR:
try:
nativeTargetValue = nativeValue.dereference()
except:
nativeTargetValue = None
targetType = self.fromNativeType(nativeType.target().unqualified())
val = self.createPointerValue(toInteger(nativeValue), targetType)
# The nativeValue is needed in case of multiple inheritance, see
# QTCREATORBUG-17823. Using it triggers nativeValueDereferencePointer()
# later which
# is surprisingly expensive.
val.nativeValue = nativeValue
#DumperBase.warn('CREATED PTR 1: %s' % val)
target_typeid = self.from_native_type(nativeType.target().unqualified())
val.ldata = int(nativeValue)
val.typeid = self.create_pointer_typeid(target_typeid)
#self.warn('CREATED PTR 1: %s' % val)
if nativeValue.address is not None:
val.laddress = toInteger(nativeValue.address)
#DumperBase.warn('CREATED PTR 2: %s' % val)
val.laddress = int(nativeValue.address)
#self.warn('CREATED PTR 2: %s' % val)
return val
if code == gdb.TYPE_CODE_TYPEDEF:
targetType = nativeType.strip_typedefs().unqualified()
#DumperBase.warn('TARGET TYPE: %s' % targetType)
if targetType.code == gdb.TYPE_CODE_ARRAY:
val = self.Value(self)
else:
try:
# Cast may fail for arrays, for typedefs to __uint128_t with
# gdb.error: That operation is not available on integers
# of more than 8 bytes.
# See test for Bug5799, QTCREATORBUG-18450.
val = self.fromNativeValue(nativeValue.cast(targetType))
except:
val = self.Value(self)
#DumperBase.warn('CREATED TYPEDEF: %s' % val)
else:
val = self.Value(self)
val.nativeValue = nativeValue
if nativeValue.address is not None:
val.laddress = toInteger(nativeValue.address)
else:
size = nativeType.sizeof
chars = self.lookupNativeType('unsigned char')
y = nativeValue.cast(chars.array(0, int(nativeType.sizeof - 1)))
buf = bytearray(struct.pack('x' * size))
for i in range(size):
val.laddress = int(nativeValue.address)
elif code == gdb.TYPE_CODE_STRUCT:
try:
buf[i] = int(y[i])
val.ldata = nativeValue.bytes # GDB 15 only
except:
pass
val.ldata = bytes(buf)
val.ldata = self.nativeDataFromValueFallback(nativeValue, nativeValue.type.sizeof)
val._type = self.fromNativeType(nativeType)
val.typeid = self.from_native_type(nativeType)
val.lIsInScope = not nativeValue.is_optimized_out
code = nativeType.code
if code == gdb.TYPE_CODE_ENUM:
@@ -283,10 +253,15 @@ class Dumper(DumperBase):
val.ldisplay += ' (%s)' % intval
elif code == gdb.TYPE_CODE_COMPLEX:
val.ldisplay = str(nativeValue)
elif code in [gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_INT]:
elif code == gdb.TYPE_CODE_BOOL:
# FIXME: why?
# Using ldata breaks StdVariant test, not setting lvalue breaks the Bitfield[s2] test.
val.lvalue = int(nativeValue)
val.ldata = None
elif code == gdb.TYPE_CODE_INT:
try:
# extract int presentation from native value and remember it
val.lvalue = int(nativeValue)
val.ldata = int(nativeValue)
except:
# GDB only support converting integers of max. 64 bits to Python int as of now
pass
@@ -294,65 +269,81 @@ class Dumper(DumperBase):
# val.type.ltarget = nativeValue[0].type.unqualified()
return val
def nativeDataFromValueFallback(self, nativeValue, size):
chars = self.lookupNativeType('unsigned char')
try:
y = nativeValue.cast(chars.array(0, int(size - 1)))
buf = bytearray(struct.pack('x' * size))
for i in range(size):
try:
buf[i] = int(y[i])
except:
pass
return bytes(buf)
except:
self.warn('VALUE EXTRACTION FAILED: VALUE: %s SIZE: %s' % (nativeValue, size))
return None
def ptrSize(self):
result = gdb.lookup_type('void').pointer().sizeof
self.ptrSize = lambda: result
return result
def fromNativeType(self, nativeType):
def from_native_type(self, nativeType):
self.check(isinstance(nativeType, gdb.Type))
code = nativeType.code
#DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType)
#self.warn('FROM NATIVE TYPE: %s' % nativeType)
nativeType = nativeType.unqualified()
typeid_str = self.native_type_key(nativeType)
known_typeid = self.typeid_from_typekey.get(typeid_str, None)
if known_typeid is not None:
return known_typeid
code = nativeType.code
if code == gdb.TYPE_CODE_PTR:
#DumperBase.warn('PTR')
targetType = self.fromNativeType(nativeType.target().unqualified())
return self.createPointerType(targetType)
#self.warn('PTR')
target_typeid = self.from_native_type(nativeType.target().unqualified())
typeid = self.create_pointer_typeid(target_typeid)
if code == gdb.TYPE_CODE_REF:
#DumperBase.warn('REF')
targetType = self.fromNativeType(nativeType.target().unqualified())
return self.createReferenceType(targetType)
elif code == gdb.TYPE_CODE_REF:
#self.warn('REF')
target_typeid = self.from_native_type(nativeType.target().unqualified())
typeid = self.create_reference_typeid(target_typeid)
if hasattr(gdb, "TYPE_CODE_RVALUE_REF"):
if code == gdb.TYPE_CODE_RVALUE_REF:
#DumperBase.warn('RVALUEREF')
targetType = self.fromNativeType(nativeType.target())
return self.createRValueReferenceType(targetType)
elif code == gdb.TYPE_CODE_RVALUE_REF and hasattr(gdb, "TYPE_CODE_RVALUE_REF"):
#self.warn('RVALUEREF')
target_typeid = self.from_native_type(nativeType.target())
typeid = self.create_rvalue_reference_typeid(target_typeid)
if code == gdb.TYPE_CODE_ARRAY:
#DumperBase.warn('ARRAY')
elif code == gdb.TYPE_CODE_ARRAY:
#self.warn('ARRAY')
nativeTargetType = nativeType.target().unqualified()
targetType = self.fromNativeType(nativeTargetType)
target_typeid = self.from_native_type(nativeTargetType)
if nativeType.sizeof == 0:
# QTCREATORBUG-23998, note that nativeType.name == None here,
# whereas str(nativeType) returns sth like 'QObject [5]'
count = self.arrayItemCountFromTypeName(str(nativeType), 1)
else:
count = nativeType.sizeof // nativeTargetType.sizeof
return self.createArrayType(targetType, count)
typeid = self.create_array_typeid(target_typeid, count)
if code == gdb.TYPE_CODE_TYPEDEF:
#DumperBase.warn('TYPEDEF')
elif code == gdb.TYPE_CODE_TYPEDEF:
#self.warn('TYPEDEF')
nativeTargetType = nativeType.unqualified()
while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF:
nativeTargetType = nativeTargetType.strip_typedefs().unqualified()
targetType = self.fromNativeType(nativeTargetType)
return self.createTypedefedType(targetType, str(nativeType),
self.nativeTypeId(nativeType))
target_typeid = self.from_native_type(nativeTargetType)
typeid = self.create_typedefed_typeid(target_typeid, str(nativeType), typeid_str)
if code == gdb.TYPE_CODE_ERROR:
elif code == gdb.TYPE_CODE_ERROR:
self.warn('Type error: %s' % nativeType)
return self.Type(self, '')
typeid = 0 # the invalid id
typeId = self.nativeTypeId(nativeType)
res = self.typeData.get(typeId, None)
if res is None:
tdata = self.TypeData(self, typeId)
tdata.name = str(nativeType)
tdata.lbitsize = nativeType.sizeof * 8
tdata.code = {
else:
typeid = self.typeid_for_string(typeid_str)
type_code = {
#gdb.TYPE_CODE_TYPEDEF : TypeCode.Typedef, # Handled above.
gdb.TYPE_CODE_METHOD: TypeCode.Function,
gdb.TYPE_CODE_VOID: TypeCode.Void,
@@ -372,38 +363,60 @@ class Dumper(DumperBase):
gdb.TYPE_CODE_COMPLEX: TypeCode.Complex,
gdb.TYPE_CODE_STRING: TypeCode.FortranString,
}[code]
if tdata.code == TypeCode.Enum:
tdata.enumDisplay = lambda intval, addr, form: \
self.nativeTypeEnumDisplay(nativeType, intval, form)
if tdata.code == TypeCode.Struct:
tdata.lalignment = lambda: \
self.nativeStructAlignment(nativeType)
tdata.lfields = lambda value: \
self.listMembers(value, nativeType)
tdata.templateArguments = lambda: \
self.listTemplateParameters(nativeType)
# warn('CREATE TYPE: %s' % typeId)
#else:
# warn('REUSE TYPE: %s' % typeId)
return self.Type(self, typeId)
self.type_name_cache[typeid] = str(nativeType)
self.type_size_cache[typeid] = nativeType.sizeof
self.type_code_cache[typeid] = type_code
self.type_nativetype_cache[typeid] = nativeType
def listTemplateParameters(self, nativeType):
targs = []
pos = 0
while True:
if type_code == TypeCode.Enum:
self.type_enum_display_cache[typeid] = lambda intval, addr, form: \
self.nativeTypeEnumDisplay(nativeType, intval, form)
self.type_nativetype_cache[typeid] = nativeType
# FIXME: Field offset caching (or later extraction?) broken
# if code == gdb.TYPE_CODE_STRUCT:
# field_type_name = self.type_name_cache.get(typeid, '')
# #self.warn("CACHING FIELDS OF %s '%s'" % (typeid, field_type_name))
# try:
# fields = nativeType.fields()
# #self.warn("FOUND FIELDS %s" % fields)
# except:
# #self.warn("NO FIELDS IN %s '%s'" % (typeid, field_type_name))
# fields = []
# for nativeField in fields:
# field_name = nativeField.name
# if field_name.startswith('std::allocator'):
# continue
# field_bitpos = nativeField.bitpos
# field_typeid = self.typeid_for_string(str(nativeType))
# field_size = nativeField.type.sizeof
# #self.warn("CACHING '%s' OF %s AT BITPOS %s SIZE %s" %
# # (field_name, typeid, field_bitpos, field_size))
# self.type_fields_cache[(typeid, field_name)] = self.Field(
# name=field_name,
# typeid=field_typeid,
# bitpos=field_bitpos,
# bitsize=field_size * 8
# )
# pass
#self.warn("FROM NATIVE TYPE: %s %s %s" % (typeid, id(nativeType), nativeType))
self.typeid_from_typekey[str(nativeType)] = typeid
return typeid
def nativeTemplateParameter(self, typeid, index, nativeType):
try:
targ = nativeType.template_argument(pos)
targ = nativeType.template_argument(index)
except:
break
return None
if isinstance(targ, gdb.Type):
targs.append(self.fromNativeType(targ.unqualified()))
elif isinstance(targ, gdb.Value):
targs.append(self.fromNativeValue(targ).value())
else:
return self.Type(self, self.from_native_type(targ.unqualified()))
if isinstance(targ, gdb.Value):
return self.fromNativeValue(targ).value()
raise RuntimeError('UNKNOWN TEMPLATE PARAMETER')
pos += 1
targs2 = self.listTemplateParametersManually(str(nativeType))
return targs if len(targs) >= len(targs2) else targs2
def nativeTypeEnumDisplay(self, nativeType, intval, form):
try:
@@ -432,7 +445,7 @@ class Dumper(DumperBase):
pass
return form % intval
def nativeTypeId(self, nativeType):
def native_type_key(self, nativeType):
if nativeType and (nativeType.code == gdb.TYPE_CODE_TYPEDEF):
return '%s{%s}' % (nativeType, nativeType.strip_typedefs())
name = str(nativeType)
@@ -444,133 +457,106 @@ class Dumper(DumperBase):
c = 's'
else:
return name
typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type))
id_str = c + ''.join(['{%s:%s}' %
(f.name, self.typeid_for_string(self.native_type_key(f.type)))
for f in nativeType.fields()])
return typeId
#self.warn("NATIVE TYPE KEY: %s" % id_str)
return id_str
def nativeStructAlignment(self, nativeType):
#DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name)
def handleItem(nativeFieldType, align):
a = self.fromNativeType(nativeFieldType).alignment()
return a if a > align else align
align = 1
for f in nativeType.fields():
align = handleItem(f.type, align)
return align
#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 memberFromNativeFieldAndValue(self, nativeField, nativeValue, fieldName, value):
nativeMember = self.nativeMemberFromField(nativeValue, nativeField)
if nativeMember is None:
val = self.Value(self)
val.name = fieldName
val._type = self.fromNativeType(nativeField.type)
val.lIsInScope = False
return val
val = self.fromNativeValue(nativeMember)
nativeFieldType = nativeField.type.unqualified()
if nativeField.bitsize:
val.lvalue = int(nativeMember)
val.laddress = None
fieldType = self.fromNativeType(nativeFieldType)
val._type = self.createBitfieldType(fieldType, nativeField.bitsize)
val.isBaseClass = nativeField.is_base_class
val.name = fieldName
return val
def nativeMemberFromField(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, value, nativeType):
def nativeListMembers(self, value, nativeType, include_base):
nativeValue = value.nativeValue
value_size = self.type_size(value.typeid)
ldata = bytes(self.value_data(value, value_size))
laddress = value.laddress
anonNumber = 0
#DumperBase.warn('LISTING FIELDS FOR %s' % nativeType)
fields = []
#self.warn('LISTING FIELDS FOR %s' % nativeType)
for nativeField in nativeType.fields():
fieldName = nativeField.name
if not include_base and nativeField.is_base_class:
continue
field_name = nativeField.name
# 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 fieldName is None or len(fieldName) == 0:
# Something without a name.
# Anonymous union? We need a dummy name to distinguish
# multiple anonymous unions in the struct.
if field_name is None or len(field_name) == 0:
anonNumber += 1
fieldName = '#%s' % anonNumber
#DumperBase.warn('FIELD: %s' % fieldName)
field_name = '#%s' % anonNumber
#self.warn('FIELD: %s' % field_name)
nativeFieldType = nativeField.type.unqualified()
field_typeid = self.from_native_type(nativeFieldType)
#self.warn(' TYPE: %s' % nativeFieldType)
#self.warn(' TYPE KEY: %s' % self.native_type_key(nativeFieldType))
if nativeValue is not None:
try:
native_member = nativeValue[nativeField]
except:
self.warn(' COULD NOT ACCESS FIELD: %s' % nativeFieldType)
continue
val = self.fromNativeValue(native_member)
if nativeField.bitsize:
val.lvalue = None
val.ldata = int(native_member)
val.laddress = None
val.typeid = self.create_bitfield_typeid(field_typeid, nativeField.bitsize)
val.isBaseClass = nativeField.is_base_class
val.name = field_name
fields.append(val)
continue
# hasattr(nativeField, 'bitpos') == False indicates a static field,
# but if we have access to a nativeValue .fromNativeField will
# but if we have access to a nativeValue, so fromNativeField will
# also succeed. We essentially skip only static members from
# artificial values, like array members constructed from address.
if hasattr(nativeField, 'bitpos') or nativeValue is not None:
yield self.fromNativeField(nativeField, nativeValue, fieldName)
if not hasattr(nativeField, 'bitpos'):
continue
def fromNativeField(self, nativeField, nativeValue, fieldName):
nativeFieldType = nativeField.type.unqualified()
#DumperBase.warn(' TYPE: %s' % nativeFieldType)
#DumperBase.warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType))
if hasattr(nativeField, 'bitpos'):
bitpos = nativeField.bitpos
else:
bitpos = 0
if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0:
bitsize = nativeField.bitsize
else:
bitsize = 8 * nativeFieldType.sizeof
fieldType = self.fromNativeType(nativeFieldType)
if bitsize != nativeFieldType.sizeof * 8:
fieldType = self.createBitfieldType(fieldType, bitsize)
else:
fieldType = fieldType
field_typeid = self.from_native_type(nativeFieldType)
is_bitfield = bitsize != nativeFieldType.sizeof * 8
if nativeValue is None:
extractor = None
else:
extractor = lambda value, \
capturedNativeField = nativeField, \
capturedNativeValue = nativeValue, \
capturedFieldName = fieldName: \
self.memberFromNativeFieldAndValue(capturedNativeField,
capturedNativeValue,
capturedFieldName,
value)
val = self.Value(self)
val.name = field_name
val.isBaseClass = nativeField.is_base_class
if is_bitfield:
val.typeid = self.create_bitfield_typeid(field_typeid, bitsize)
val.ldata = self.value_extract_bits(value, bitpos, bitsize)
else:
val.typeid = field_typeid
field_offset = bitpos // 8
if laddress is not None:
val.laddress = laddress + field_offset
field_size = (bitsize + 7) // 8
val.ldata = ldata[field_offset:field_offset + field_size]
#self.warn('GOT VAL %s FOR FIELD %s' % (val, nativeField))
fields.append(val)
return fields
#DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos))
return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class,
bitsize=bitsize, bitpos=bitpos, type=fieldType,
extractor=extractor)
def listLocals(self, partialVar):
frame = gdb.selected_frame()
try:
block = frame.block()
#DumperBase.warn('BLOCK: %s ' % block)
#self.warn('BLOCK: %s ' % block)
except RuntimeError as error:
#DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
#self.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
return []
except:
self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS')
@@ -594,14 +580,12 @@ class Dumper(DumperBase):
if partialVar is not None and partialVar != name:
continue
# 'NotImplementedError: Symbol type not yet supported in
# Python scripts.'
#DumperBase.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name))
#self.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name))
if self.passExceptions and not self.isTesting:
nativeValue = frame.read_var(name, block)
value = self.fromFrameValue(nativeValue)
value.name = name
#DumperBase.warn('READ 0: %s' % value.stringify())
#self.warn('READ 0: %s' % value.stringify())
items.append(value)
continue
@@ -610,14 +594,14 @@ class Dumper(DumperBase):
nativeValue = frame.read_var(name, block)
value = self.fromFrameValue(nativeValue)
value.name = name
#DumperBase.warn('READ 1: %s' % value.stringify())
#self.warn('READ 1: %s' % value.stringify())
items.append(value)
continue
except:
pass
try:
#DumperBase.warn('READ 2: %s' % item.value)
#self.warn('READ 2: %s' % item.value)
value = self.fromFrameValue(frame.read_var(name))
value.name = name
items.append(value)
@@ -631,8 +615,8 @@ class Dumper(DumperBase):
pass
try:
#DumperBase.warn('READ 3: %s %s' % (name, item.value))
#DumperBase.warn('ITEM 3: %s' % item.value)
#self.warn('READ 3: %s %s' % (name, item.value))
#self.warn('ITEM 3: %s' % item.value)
value = self.fromFrameValue(gdb.parse_and_eval(name))
value.name = name
items.append(value)
@@ -661,16 +645,17 @@ class Dumper(DumperBase):
return self.ptrSize() == 8
def fetchVariables(self, args):
start_time = time.perf_counter()
self.resetStats()
self.prepare(args)
self.isBigEndian = gdb.execute('show endian', to_string=True).find('big endian') > 0
self.packCode = '>' if self.isBigEndian else '<'
(ok, res) = self.tryFetchInterpreterVariables(args)
if ok:
safePrint(res)
return
#(ok, res) = self.tryFetchInterpreterVariables(args)
#if ok:
# safePrint(res)
# return
self.put('data=[')
@@ -679,7 +664,7 @@ class Dumper(DumperBase):
partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None
variables = self.listLocals(partialName)
#DumperBase.warn('VARIABLES: %s' % variables)
#self.warn('VARIABLES: %s' % variables)
# Take care of the return value of the last function call.
if len(self.resultVarName) > 0:
@@ -711,9 +696,12 @@ class Dumper(DumperBase):
self.put(',qtnamespace="%s"' % self.qtNamespaceToReport)
self.qtNamespaceToReport = None
run_time = time.perf_counter() - start_time
#self.warn("PTIME: %s" % run_time)
self.put(',partial="%d"' % isPartial)
self.put(',runtime="%s"' % run_time)
self.put(',counts=%s' % self.counts)
self.put(',timings=%s' % self.timings)
#self.put(',timings=%s' % self.timings)
self.reportResult(''.join(self.output), args)
def parseAndEvaluate(self, exp):
@@ -721,7 +709,7 @@ class Dumper(DumperBase):
return None if val is None else self.fromNativeValue(val)
def nativeParseAndEvaluate(self, exp):
#DumperBase.warn('EVALUATE "%s"' % exp)
#self.warn('EVALUATE "%s"' % exp)
try:
val = gdb.parse_and_eval(exp)
return val
@@ -744,20 +732,20 @@ class Dumper(DumperBase):
else:
arg += a
#DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg))
typeName = value.type.name
if typeName.find(':') >= 0:
typeName = "'" + typeName + "'"
#self.warn('CALL: %s -> %s(%s)' % (value, function, arg))
type_name = value.type.name
if type_name.find(':') >= 0:
type_name = "'" + type_name + "'"
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
#exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
#exp = '((class %s*)%s)->%s(%s)' % (type_name, value.laddress, function, arg)
addr = value.address()
if addr is None:
addr = self.pokeValue(value)
#DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr))
exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg)
#DumperBase.warn('CALL: %s' % exp)
#self.warn('PTR: %s -> %s(%s)' % (value, function, addr))
exp = '((%s*)0x%x)->%s(%s)' % (type_name, addr, function, arg)
#self.warn('CALL: %s' % exp)
result = gdb.parse_and_eval(exp)
#DumperBase.warn(' -> %s' % result)
#self.warn(' -> %s' % result)
res = self.fromNativeValue(result)
if value.address() is None:
self.releaseValue(addr)
@@ -765,9 +753,9 @@ class Dumper(DumperBase):
def makeExpression(self, value):
typename = '::' + value.type.name
#DumperBase.warn(' TYPE: %s' % typename)
#self.warn(' TYPE: %s' % typename)
exp = '(*(%s*)(0x%x))' % (typename, value.address())
#DumperBase.warn(' EXP: %s' % exp)
#self.warn(' EXP: %s' % exp)
return exp
def makeStdString(init):
@@ -785,14 +773,14 @@ class Dumper(DumperBase):
size = value.type.size()
data = value.data()
h = self.hexencode(data)
#DumperBase.warn('DATA: %s' % h)
#self.warn('DATA: %s' % h)
string = ''.join('\\x' + h[2 * i:2 * i + 2] for i in range(size))
exp = '(%s*)memcpy(calloc(1, %d), "%s", %d)' \
% (value.type.name, size, string, size)
#DumperBase.warn('EXP: %s' % exp)
#self.warn('EXP: %s' % exp)
res = gdb.parse_and_eval(exp)
#DumperBase.warn('RES: %s' % res)
return toInteger(res)
#self.warn('RES: %s' % res)
return int(res)
def releaseValue(self, address):
gdb.parse_and_eval('free(0x%x)' % address)
@@ -819,7 +807,7 @@ class Dumper(DumperBase):
return self.cachedInferior
def readRawMemory(self, address, size):
#DumperBase.warn('READ: %s FROM 0x%x' % (size, address))
#self.warn('READ: %s FROM 0x%x' % (size, address))
if address == 0 or size == 0:
return bytes()
res = self.selectedInferior().read_memory(address, size)
@@ -832,7 +820,7 @@ class Dumper(DumperBase):
return 0
try:
# Older GDB ~7.4 don't have gdb.Symbol.value()
return toInteger(symbol.value().address)
return int(symbol.value().address)
except:
pass
@@ -1020,7 +1008,7 @@ class Dumper(DumperBase):
def findSymbol(self, symbolName):
try:
return toInteger(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
return int(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
except:
return 0
@@ -1052,30 +1040,24 @@ class Dumper(DumperBase):
def handleQtCoreLoaded(self, objfile):
fd, tmppath = tempfile.mkstemp()
os.close(fd)
try:
cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
symbols = gdb.execute(cmd, to_string=True)
except:
try:
# command syntax depends on gdb version - below is gdb < 8
cmd = 'maint print msymbols %s "%s"' % (tmppath, objfile.filename)
symbols = gdb.execute(cmd, to_string=True)
except:
pass
ns = ''
with open(tmppath) as f:
ns1re = re.compile(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ')
ns2re = re.compile(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ')
for line in f:
if line.find('msgHandlerGrabbed ') >= 0:
if 'msgHandlerGrabbed ' in line:
# [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
# section .tbss Myns::msgHandlerGrabbed qlogging.cpp
ns = re.split(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ', line)[2]
ns = ns1re.split(line)[2]
if len(ns):
ns += '::'
break
if line.find('currentThreadData ') >= 0:
if 'currentThreadData ' in line:
# [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
# section .tbss UU::currentThreadData qthread_unix.cpp\\n
ns = re.split(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ', line)[2]
ns = ns2re.split(line)[2]
if len(ns):
ns += '::'
break
@@ -1104,21 +1086,21 @@ class Dumper(DumperBase):
self.qtPropertyFunc = self.findSymbol(sym)
def assignValue(self, args):
typeName = self.hexdecode(args['type'])
type_name = self.hexdecode(args['type'])
expr = self.hexdecode(args['expr'])
value = self.hexdecode(args['value'])
simpleType = int(args['simpleType'])
ns = self.qtNamespace()
if typeName.startswith(ns):
typeName = typeName[len(ns):]
typeName = typeName.replace('::', '__')
pos = typeName.find('<')
if type_name.startswith(ns):
type_name = type_name[len(ns):]
type_name = type_name.replace('::', '__')
pos = type_name.find('<')
if pos != -1:
typeName = typeName[0:pos]
if typeName in self.qqEditable and not simpleType:
#self.qqEditable[typeName](self, expr, value)
type_name = type_name[0:pos]
if type_name in self.qqEditable and not simpleType:
#self.qqEditable[type_name](self, expr, value)
expr = self.parseAndEvaluate(expr)
self.qqEditable[typeName](self, expr, value)
self.qqEditable[type_name](self, expr, value)
else:
cmd = 'set variable (%s)=%s' % (expr, value)
gdb.execute(cmd)
@@ -1152,63 +1134,28 @@ class Dumper(DumperBase):
nativeValue = value.nativeValue
return self.fromNativeValue(nativeValue.cast(nativeValue.type.target()))
def nativeDynamicTypeName(self, address, baseType):
def nativeDynamicType(self, address, base_typeid):
# Needed for Gdb13393 test.
nativeType = self.lookupNativeType(baseType.name)
nativeType = self.type_nativetype_cache.get(base_typeid, None)
if nativeType is None:
return None
return base_typeid
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)
if nativeType is None:
return baseType
nativeTypePointer = nativeType.pointer()
nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference()
return self.fromNativeType(nativeValue.dynamic_type)
return self.from_native_type(nativeValue.dynamic_type)
def enumExpression(self, enumType, enumValue):
return self.qtNamespace() + 'Qt::' + enumValue
def lookupNativeType(self, typeName):
nativeType = self.lookupNativeTypeHelper(typeName)
if nativeType is not None:
self.check(isinstance(nativeType, gdb.Type))
return nativeType
def lookupNativeTypeHelper(self, typeName):
typeobj = self.typeCache.get(typeName)
#DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj))
if typeobj is not None:
return typeobj
if typeName == 'void':
typeobj = gdb.lookup_type(typeName)
self.typeCache[typeName] = typeobj
self.typesToReport[typeName] = typeobj
def lookupNativeType(self, type_name):
if type_name == 'void':
typeobj = gdb.lookup_type(type_name)
self.typesToReport[type_name] = typeobj
return typeobj
#try:
# typeobj = gdb.parse_and_eval('{%s}&main' % typeName).typeobj
# typeobj = gdb.parse_and_eval('{%s}&main' % type_name).typeobj
# if not typeobj is None:
# self.typeCache[typeName] = typeobj
# self.typesToReport[typeName] = typeobj
# self.typesToReport[type_name] = typeobj
# return typeobj
#except:
# pass
@@ -1217,22 +1164,21 @@ class Dumper(DumperBase):
# gcc produces '{anonymous}', gdb '(anonymous namespace)'
# '<unnamed>' has been seen too. The only thing gdb
# understands when reading things back is '(anonymous namespace)'
if typeName.find('{anonymous}') != -1:
ts = typeName
if type_name.find('{anonymous}') != -1:
ts = type_name
ts = ts.replace('{anonymous}', '(anonymous namespace)')
typeobj = self.lookupNativeType(ts)
if typeobj is not None:
self.typeCache[typeName] = typeobj
self.typesToReport[typeName] = typeobj
self.typesToReport[type_name] = typeobj
return typeobj
#DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj))
#self.warn(" RESULT FOR 7.2: '%s': %s" % (type_name, typeobj))
# This part should only trigger for
# gdb 7.1 for types with namespace separators.
# And anonymous namespaces.
ts = typeName
ts = type_name
while True:
if ts.startswith('class '):
ts = ts[6:]
@@ -1259,36 +1205,32 @@ class Dumper(DumperBase):
typeobj = self.lookupNativeType(ts[0:-1])
if typeobj is not None:
typeobj = typeobj.pointer()
self.typeCache[typeName] = typeobj
self.typesToReport[typeName] = typeobj
self.typesToReport[type_name] = typeobj
return typeobj
try:
#DumperBase.warn("LOOKING UP 1 '%s'" % ts)
#self.warn("LOOKING UP 1 '%s'" % ts)
typeobj = gdb.lookup_type(ts)
except RuntimeError as error:
#DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
#self.warn("LOOKING UP '%s' FAILED" % ts)
pass
#self.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
# See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
exp = "(class '%s'*)0" % ts
try:
typeobj = self.parse_and_eval(exp).type.target()
#DumperBase.warn("LOOKING UP 3 '%s'" % typeobj)
#self.warn("LOOKING UP 3 '%s'" % typeobj)
except:
# Can throw 'RuntimeError: No type named class Foo.'
pass
except:
#DumperBase.warn("LOOKING UP '%s' FAILED" % ts)
pass
if typeobj is not None:
#DumperBase.warn('CACHING: %s' % typeobj)
self.typeCache[typeName] = typeobj
self.typesToReport[typeName] = typeobj
#self.warn('CACHING: %s' % typeobj)
self.typesToReport[type_name] = typeobj
# This could still be None as gdb.lookup_type('char[3]') generates
# 'RuntimeError: No type named char[3]'
#self.typeCache[typeName] = typeobj
#self.typesToReport[typeName] = typeobj
#self.typesToReport[type_name] = typeobj
return typeobj
def doContinue(self):
@@ -1332,7 +1274,7 @@ class Dumper(DumperBase):
if typeobj.code == gdb.TYPE_CODE_PTR:
dereftype = typeobj.target().unqualified()
if dereftype.name == needle:
addr = toInteger(value)
addr = int(value)
res = None
for pat in pats:
try:
@@ -1442,14 +1384,6 @@ class Dumper(DumperBase):
def reportResult(self, result, args):
print('result={token="%s",%s}' % (args.get("token", 0), result))
def profile1(self, args):
'''Internal profiling'''
import cProfile
tempDir = tempfile.gettempdir() + '/bbprof'
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
import pstats
pstats.Stats(tempDir).sort_stats('time').print_stats()
def profile2(self, args):
import timeit
print(timeit.repeat('theDumper.fetchVariables(%s)' % args,

View File

@@ -116,33 +116,29 @@ class Dumper(DumperBase):
self.isInterrupting_ = False
self.interpreterBreakpointResolvers = []
DumperBase.warn = Dumper.warn_impl
self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
@staticmethod
def warn_impl(message):
if message[-1:] == '\n':
message += '\n'
def warn(self, msg):
#self.put('{name="%s",value="",type="",numchild="0"},' % toCString(msg))
if msg[-1:] == '\n':
msg += '\n'
print('@\nbridgemessage={msg="%s",channel="%s"}\n@'
% (message.replace('"', '$'), LogChannel.AppError))
def fromNativeFrameValue(self, nativeValue):
return self.fromNativeValue(nativeValue)
% (msg.replace('"', '$'), LogChannel.AppError))
def fromNativeValue(self, nativeValue):
self.check(isinstance(nativeValue, lldb.SBValue))
nativeType = nativeValue.GetType()
typeName = nativeType.GetName()
type_name = nativeType.GetName()
code = nativeType.GetTypeClass()
# Display the result of GetSummary() for Core Foundation string
# and string-like types.
summary = None
if self.useFancy:
if (typeName.startswith('CF')
or typeName.startswith('__CF')
or typeName.startswith('NS')
or typeName.startswith('__NSCF')):
if (type_name.startswith('CF')
or type_name.startswith('__CF')
or type_name.startswith('NS')
or type_name.startswith('__NSCF')):
if code == lldb.eTypeClassPointer:
summary = nativeValue.Dereference().GetSummary()
elif code == lldb.eTypeClassReference:
@@ -156,26 +152,26 @@ class Dumper(DumperBase):
nativeTargetType = nativeType.GetDereferencedType()
if not nativeTargetType.IsPointerType():
nativeTargetType = nativeTargetType.GetUnqualifiedType()
targetType = self.fromNativeType(nativeTargetType)
val = self.createReferenceValue(nativeValue.GetValueAsUnsigned(), targetType)
target_typeid = self.from_native_type(nativeTargetType)
target_address = nativeValue.GetValueAsUnsigned()
val = self.Value(self)
val.ldata = target_address.to_bytes(self.ptrSize(), 'little')
if self.useDynamicType:
target_typeid = self.dynamic_typeid_at_address(target_typeid, target_address)
val.typeid = self.create_reference_typeid(target_typeid)
val.laddress = nativeValue.AddressOf().GetValueAsUnsigned()
#DumperBase.warn('CREATED REF: %s' % val)
#self.warn('CREATED REF: %s' % val)
elif code == lldb.eTypeClassPointer:
nativeTargetType = nativeType.GetPointeeType()
if not nativeTargetType.IsPointerType():
nativeTargetType = nativeTargetType.GetUnqualifiedType()
targetType = self.fromNativeType(nativeTargetType)
val = self.createPointerValue(nativeValue.GetValueAsUnsigned(), targetType)
#DumperBase.warn('CREATED PTR 1: %s' % val)
target_typeid = self.from_native_type(nativeTargetType)
val = self.Value(self)
val.ldata = nativeValue.GetValueAsUnsigned()
val.typeid = self.create_pointer_typeid(target_typeid)
val.laddress = nativeValue.AddressOf().GetValueAsUnsigned()
#DumperBase.warn('CREATED PTR 2: %s' % val)
elif code == lldb.eTypeClassTypedef:
nativeTargetType = nativeType.GetUnqualifiedType()
if hasattr(nativeTargetType, 'GetCanonicalType'):
nativeTargetType = nativeTargetType.GetCanonicalType()
val = self.fromNativeValue(nativeValue.Cast(nativeTargetType))
val._type = self.fromNativeType(nativeType)
#DumperBase.warn('CREATED TYPEDEF: %s' % val)
else:
val = self.Value(self)
address = nativeValue.GetLoadAddress()
@@ -193,7 +189,7 @@ class Dumper(DumperBase):
except:
pass
val._type = self.fromNativeType(nativeType)
val.typeid = self.from_native_type(nativeType)
if code == lldb.eTypeClassEnumeration:
intval = nativeValue.GetValueAsSigned()
@@ -209,43 +205,32 @@ class Dumper(DumperBase):
val.ldisplay = str(nativeValue.GetValue())
#elif code == lldb.eTypeClassArray:
# if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x
# val.type.ltarget = self.fromNativeType(nativeType.GetArrayElementType())
# val.type.ltarget = self.from_native_type(nativeType.GetArrayElementType())
# else:
# fields = nativeType.get_fields_array()
# if len(fields):
# val.type.ltarget = self.fromNativeType(fields[0])
# val.type.ltarget = self.from_native_type(fields[0])
#elif code == lldb.eTypeClassVector:
# val.type.ltarget = self.fromNativeType(nativeType.GetVectorElementType())
# val.type.ltarget = self.from_native_type(nativeType.GetVectorElementType())
val.summary = summary
val.lIsInScope = nativeValue.IsInScope()
val.name = nativeValue.GetName()
return val
def nativeStructAlignment(self, nativeType):
def handleItem(nativeFieldType, align):
a = self.fromNativeType(nativeFieldType).alignment()
return a if a > align else align
align = 1
for i in range(nativeType.GetNumberOfDirectBaseClasses()):
base = nativeType.GetDirectBaseClassAtIndex(i)
align = handleItem(base.GetType(), align)
for i in range(nativeType.GetNumberOfFields()):
child = nativeType.GetFieldAtIndex(i)
align = handleItem(child.GetType(), align)
return align
def listMembers(self, value, nativeType):
#DumperBase.warn("ADDR: 0x%x" % self.fakeAddress_)
def nativeListMembers(self, value, nativeType, include_base):
#self.warn("ADDR: 0x%x" % self.fakeAddress_)
nativeValue = value.nativeValue
if nativeValue is None:
if value.laddress:
fakeAddress = lldb.SBAddress(value.laddress, self.target)
fakeLAddress = value.laddress
else:
fakeAddress = self.fakeAddress_
fakeLAddress = self.fakeLAddress_
nativeValue = self.target.CreateValueFromAddress('x', fakeAddress, nativeType)
fakeValue = self.target.CreateValueFromAddress('x', fakeAddress, nativeType)
fakeValue.SetPreferSyntheticValue(False)
nativeValue.SetPreferSyntheticValue(False)
baseNames = {}
for i in range(nativeType.GetNumberOfDirectBaseClasses()):
@@ -262,45 +247,62 @@ class Dumper(DumperBase):
# Normal members and non-empty base classes.
anonNumber = 0
for i in range(fakeValue.GetNumChildren()):
nativeField = fakeValue.GetChildAtIndex(i)
fields = []
for i in range(nativeValue.GetNumChildren()):
nativeField = nativeValue.GetChildAtIndex(i)
nativeField.SetPreferSyntheticValue(False)
fieldName = nativeField.GetName()
nativeFieldType = nativeField.GetType()
if fieldName in fieldBits:
(fieldBitsize, fieldBitpos, isBitfield) = fieldBits[fieldName]
(bitsize, bitpos, isBitfield) = fieldBits[fieldName]
else:
fieldBitsize = nativeFieldType.GetByteSize() * 8
fieldBitpos = None
bitsize = nativeFieldType.GetByteSize() * 8
bitpos = None
isBitfield = False
if isBitfield: # Bit fields
fieldType = self.createBitfieldType(
self.createType(self.typeName(nativeFieldType)), fieldBitsize)
yield self.Field(self, name=fieldName, type=fieldType,
bitsize=fieldBitsize, bitpos=fieldBitpos)
field_typeid = self.create_bitfield_typeid(
self.create_typeid(nativeFieldType.GetName()), bitsize)
val = self.Value(self)
val.name = fieldName
val.isBaseClass = False
val.typeid = field_typeid
val.ldata = self.value_extract_bits(value, bitpos, bitsize)
val.laddress = None
fields.append(val)
elif fieldName is None: # Anon members
anonNumber += 1
fieldName = '#%s' % anonNumber
fakeMember = fakeValue.GetChildAtIndex(i)
fakeMember = nativeValue.GetChildAtIndex(i)
fakeMemberAddress = fakeMember.GetLoadAddress()
offset = fakeMemberAddress - fakeLAddress
yield self.Field(self, name=fieldName, type=self.fromNativeType(nativeFieldType),
bitsize=fieldBitsize, bitpos=8 * offset)
val = self.Value(self)
val.name = fieldName
val.isBaseClass = False
val.typeid = typeid=self.from_native_type(nativeFieldType)
field_offset = fakeMemberAddress - fakeLAddress
if value.laddress is not None:
val.laddress = value.laddress + field_offset
if value.ldata is not None:
field_size = (bitsize + 7) // 8
val.ldata = value.ldata[field_offset:field_offset + field_size]
fields.append(val)
elif fieldName in baseNames: # Simple bases
member = self.fromNativeValue(fakeValue.GetChildAtIndex(i))
member = self.fromNativeValue(nativeValue.GetChildAtIndex(i))
member.isBaseClass = True
yield member
fields.append(member)
else: # Normal named members
member = self.fromNativeValue(fakeValue.GetChildAtIndex(i))
member = self.fromNativeValue(nativeValue.GetChildAtIndex(i))
member.name = nativeField.GetName()
yield member
fields.append(member)
if include_base:
# Empty bases are not covered above.
for i in range(nativeType.GetNumberOfDirectBaseClasses()):
fieldObj = nativeType.GetDirectBaseClassAtIndex(i)
@@ -309,39 +311,35 @@ class Dumper(DumperBase):
if fieldType.GetNumberOfDirectBaseClasses() == 0:
member = self.Value(self)
fieldName = fieldObj.GetName()
member._type = self.fromNativeType(fieldType)
member.typeid = self.from_native_type(fieldType)
member.name = fieldName
member.fields = []
if False:
# This would be correct if we came here only for
# truly empty base classes. Alas, we don't, see below.
member.ldata = bytes()
member.lbitsize = fieldType.GetByteSize() * 8
else:
# This is a hack. LLDB 3.8 reports declared but not defined
# types as having no fields and(!) size == 1. At least
# for the common case of a single base class we can
# fake the contents by using the whole derived object's
# data as base class data.
data = fakeValue.GetData()
data = nativeValue.GetData()
size = nativeType.GetByteSize()
member.lbitsize = size * 8
error = lldb.SBError()
member.laddress = value.laddress
member.ldata = data.ReadRawData(error, 0, size)
member.isBaseClass = True
member.ltype = self.fromNativeType(fieldType)
member.name = fieldName
yield member
fields.append(member)
return fields
def ptrSize(self):
result = self.target.GetAddressByteSize()
self.ptrSize = lambda: result
return result
def fromNativeType(self, nativeType):
def from_native_type(self, nativeType):
self.check(isinstance(nativeType, lldb.SBType))
code = nativeType.GetTypeClass()
# eTypeClassInvalid = (0u),
# eTypeClassArray = (1u << 0),
@@ -367,43 +365,41 @@ class Dumper(DumperBase):
# // Define a mask that can be used for any type when finding types
# eTypeClassAny = (0xffffffffu)
#DumperBase.warn('CURRENT: %s' % self.typeData.keys())
#DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType.GetName())
#self.warn('CURRENT: %s' % self.typeData.keys())
#self.warn('FROM NATIVE TYPE: %s' % nativeType.GetName())
typeid_str = self.native_type_key(nativeType)
known_typeid = self.typeid_from_typekey.get(typeid_str, None)
if known_typeid is not None:
return known_typeid
code = nativeType.GetTypeClass()
if code == lldb.eTypeClassInvalid:
return None
typeid = 0
if code == lldb.eTypeClassBuiltin:
nativeType = nativeType.GetUnqualifiedType()
elif code == lldb.eTypeClassPointer:
#self.warn('PTR: %s' % nativeTargetType.name)
target_typeid = self.from_native_type(nativeType.GetPointeeType())
typeid = self.create_pointer_typeid(target_typeid)
if code == lldb.eTypeClassPointer:
#DumperBase.warn('PTR')
nativeTargetType = nativeType.GetPointeeType()
if not nativeTargetType.IsPointerType():
nativeTargetType = nativeTargetType.GetUnqualifiedType()
#DumperBase.warn('PTR: %s' % nativeTargetType.name)
return self.createPointerType(self.fromNativeType(nativeTargetType))
if code == lldb.eTypeClassReference:
elif code == lldb.eTypeClassReference:
#DumperBase.warn('REF')
nativeTargetType = nativeType.GetDereferencedType()
if not nativeTargetType.IsPointerType():
nativeTargetType = nativeTargetType.GetUnqualifiedType()
#DumperBase.warn('REF: %s' % nativeTargetType.name)
return self.createReferenceType(self.fromNativeType(nativeTargetType))
target_typeid = self.from_native_type(nativeType.GetDereferencedType())
typeid = self.create_reference_typeid(target_typeid)
if code == lldb.eTypeClassTypedef:
elif code == lldb.eTypeClassTypedef:
#DumperBase.warn('TYPEDEF')
nativeTargetType = nativeType.GetUnqualifiedType()
if hasattr(nativeTargetType, 'GetCanonicalType'):
nativeTargetType = nativeTargetType.GetCanonicalType()
targetType = self.fromNativeType(nativeTargetType)
return self.createTypedefedType(targetType, nativeType.GetName(),
self.nativeTypeId(nativeType))
target_typeid = self.from_native_type(nativeTargetType)
typeid = self.create_typedefed_typeid(target_typeid, nativeType.GetName(),
typeid_str)
elif code in (lldb.eTypeClassArray, lldb.eTypeClassVector):
nativeType = nativeType.GetUnqualifiedType()
typeName = self.typeName(nativeType)
if code in (lldb.eTypeClassArray, lldb.eTypeClassVector):
type_name = nativeType.GetName()
#DumperBase.warn('ARRAY: %s' % nativeType.GetName())
if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x
nativeTargetType = nativeType.GetArrayElementType()
@@ -412,82 +408,82 @@ class Dumper(DumperBase):
#DumperBase.warn('BAD: %s ' % nativeTargetType.get_fields_array())
nativeTargetType = nativeType.GetVectorElementType()
count = nativeType.GetByteSize() // nativeTargetType.GetByteSize()
targetTypeName = nativeTargetType.GetName()
if targetTypeName.startswith('(anon'):
typeName = nativeType.GetName()
pos1 = typeName.rfind('[')
targetTypeName = typeName[0:pos1].strip()
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
targetType = self.fromNativeType(nativeTargetType)
targetType.setTdata(targetType.tdata.copy())
targetType.tdata.name = targetTypeName
return self.createArrayType(targetType, count)
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
target_typename = nativeTargetType.GetName()
if target_typename.startswith('(anon'):
type_name = nativeType.GetName()
pos1 = type_name.rfind('[')
target_typename = type_name[0:pos1].strip()
#DumperBase.warn("TARGET TYPENAME: %s" % target_typename)
target_typeid = self.from_native_type(nativeTargetType)
#target_typeid.setTdata(target_typeid.tdata.copy())
#target_typeid.tdata.name = target_typename
typeid = self.create_array_typeid(target_typeid, count)
elif hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
nativeTargetType = nativeType.GetVectorElementType()
count = nativeType.GetByteSize() // nativeTargetType.GetByteSize()
targetType = self.fromNativeType(nativeTargetType)
return self.createArrayType(targetType, count)
return self.createType(nativeType.GetName())
target_typeid = self.from_native_type(nativeTargetType)
typeid = self.create_array_typeid(target_typeid, count)
else:
typeid = self.create_type(nativeType.GetName())
typeId = self.nativeTypeId(nativeType)
res = self.typeData.get(typeId, None)
if res is None:
else:
nativeType = nativeType.GetUnqualifiedType()
type_name = nativeType.GetName()
typeid = self.typeid_for_string(typeid_str)
#if not typeid in self.typeid_cache:
# # This strips typedefs for pointers. We don't want that.
# typeobj.nativeType = nativeType.GetUnqualifiedType()
tdata = self.TypeData(self, typeId)
tdata.name = typeName
tdata.lbitsize = nativeType.GetByteSize() * 8
self.type_name_cache[typeid] = type_name
self.type_size_cache[typeid] = nativeType.GetByteSize()
type_code = None
if code == lldb.eTypeClassBuiltin:
if utils.isFloatingPointTypeName(typeName):
tdata.code = TypeCode.Float
elif utils.isIntegralTypeName(typeName):
tdata.code = TypeCode.Integral
elif typeName in ('__int128', 'unsigned __int128'):
tdata.code = TypeCode.Integral
elif typeName == 'void':
tdata.code = TypeCode.Void
elif typeName == 'wchar_t':
tdata.code = TypeCode.Integral
elif typeName in ("char16_t", "char32_t", "char8_t"):
tdata.code = TypeCode.Integral
if utils.isFloatingPointTypeName(type_name):
type_code = TypeCode.Float
elif utils.isIntegralTypeName(type_name):
type_code = TypeCode.Integral
elif type_name in ('__int128', 'unsigned __int128'):
type_code = TypeCode.Integral
elif type_name == 'void':
type_code = TypeCode.Void
elif type_name == 'wchar_t':
type_code = TypeCode.Integral
elif type_name in ("char16_t", "char32_t", "char8_t"):
type_code = TypeCode.Integral
else:
self.warn('UNKNOWN TYPE KEY: %s: %s' % (typeName, code))
self.warn('UNKNOWN TYPE KEY: %s: %s' % (type_name, code))
elif code == lldb.eTypeClassEnumeration:
tdata.code = TypeCode.Enum
tdata.enumDisplay = lambda intval, addr, form: \
type_code = TypeCode.Enum
self.type_enum_display_cache[typeid] = lambda intval, addr, form: \
self.nativeTypeEnumDisplay(nativeType, intval, form)
elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat):
tdata.code = TypeCode.Complex
type_code = TypeCode.Complex
elif code in (lldb.eTypeClassClass, lldb.eTypeClassStruct, lldb.eTypeClassUnion):
tdata.code = TypeCode.Struct
tdata.lalignment = lambda: \
self.nativeStructAlignment(nativeType)
tdata.lfields = lambda value: \
self.listMembers(value, nativeType)
tdata.templateArguments = lambda: \
self.listTemplateParametersHelper(nativeType)
type_code = TypeCode.Struct
elif code == lldb.eTypeClassFunction:
tdata.code = TypeCode.Function
type_code = TypeCode.Function
elif code == lldb.eTypeClassMemberPointer:
tdata.code = TypeCode.MemberPointer
# warn('CREATE TYPE: %s' % typeId)
#else:
# warn('REUSE TYPE: %s' % typeId)
return self.Type(self, typeId)
type_code = TypeCode.MemberPointer
def listTemplateParametersHelper(self, nativeType):
stringArgs = self.listTemplateParameters(nativeType.GetName())
n = nativeType.GetNumberOfTemplateArguments()
if n != len(stringArgs):
# Something wrong in the debug info.
# Should work in theory, doesn't work in practice.
# Items like std::allocator<std::pair<unsigned int const, float> report 0
# for nativeType.GetNumberOfTemplateArguments() with LLDB 3.8
return stringArgs
if code is not None:
self.type_code_cache[typeid] = type_code
targs = []
for i in range(nativeType.GetNumberOfTemplateArguments()):
kind = nativeType.GetTemplateArgumentKind(i)
self.type_nativetype_cache[typeid] = nativeType
self.typeid_from_typekey[typeid_str] = typeid
# self.warn('REUSE TYPE: %s' % typeid)
return typeid
def nativeTemplateParameter(self, typeid, index, nativeType):
#n = nativeType.GetNumberOfTemplateArguments()
#if n != len(stringArgs):
# # Something wrong in the debug info.
# # Should work in theory, doesn't work in practice.
# # Items like std::allocator<std::pair<unsigned int const, float> report 0
# # for nativeType.GetNumberOfTemplateArguments() with LLDB 3.8
# return stringArgs
kind = nativeType.GetTemplateArgumentKind(index)
# eTemplateArgumentKindNull = 0,
# eTemplateArgumentKindType,
# eTemplateArgumentKindDeclaration,
@@ -497,9 +493,9 @@ class Dumper(DumperBase):
# eTemplateArgumentKindExpression,
# eTemplateArgumentKindPack
if kind == lldb.eTemplateArgumentKindType:
innerType = nativeType.GetTemplateArgumentType(
i).GetUnqualifiedType().GetCanonicalType()
targs.append(self.fromNativeType(innerType))
innerType = nativeType.GetTemplateArgumentType(index) \
.GetUnqualifiedType().GetCanonicalType()
return self.Type(self, self.from_native_type(innerType))
#elif kind == lldb.eTemplateArgumentKindIntegral:
# innerType = nativeType.GetTemplateArgumentType(i).GetUnqualifiedType().GetCanonicalType()
# #DumperBase.warn('INNER TYP: %s' % innerType)
@@ -517,36 +513,34 @@ class Dumper(DumperBase):
# value -= 0x100000000
# #DumperBase.warn('KIND: %s' % kind)
# targs.append(value)
else:
#DumperBase.warn('UNHANDLED TEMPLATE TYPE : %s' % kind)
targs.append(stringArgs[i]) # Best we can do.
#else:
# #DumperBase.warn('UNHANDLED TEMPLATE TYPE : %s' % kind)
# targs.append(stringArgs[i]) # Best we can do.
#DumperBase.warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs]))
return targs
#return targs
return None
def typeName(self, nativeType):
# Don't use GetDisplayTypeName since LLDB removed the inline namespace __1
# https://reviews.llvm.org/D74478
return nativeType.GetName()
def nativeTypeId(self, nativeType):
if nativeType and (nativeType.GetTypeClass() == lldb.eTypeClassTypedef):
def native_type_key(self, nativeType):
code = nativeType.GetTypeClass()
if nativeType and code == lldb.eTypeClassTypedef:
nativeTargetType = nativeType.GetUnqualifiedType()
if hasattr(nativeTargetType, 'GetCanonicalType'):
nativeTargetType = nativeTargetType.GetCanonicalType()
return '%s{%s}' % (nativeType.name, nativeTargetType.name)
name = self.typeName(nativeType)
# Don't use GetDisplayTypeName since LLDB removed the inline namespace __1
# https://reviews.llvm.org/D74478
name = nativeType.GetName()
if name is None or len(name) == 0:
c = '0'
elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassStruct:
c = 's'
elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassUnion:
c = 'u'
elif name == '(anonymous struct)':
c = 's' if code == lldb.eTypeClassStruct else 'u'
else:
return name
fields = nativeType.get_fields_array()
typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.GetType())) for f in fields])
#DumperBase.warn('NATIVE TYPE ID FOR %s IS %s' % (name, typeId))
return typeId
id_str = c + ''.join(['{%s:%s}' %
(f.name, self.typeid_for_string(self.native_type_key(f.GetType())))
for f in fields])
return id_str
def nativeTypeEnumDisplay(self, nativeType, intval, form):
if hasattr(nativeType, 'get_enum_members_array'):
@@ -575,12 +569,82 @@ class Dumper(DumperBase):
return '(' + ' | '.join(flags) + ') (' + (form % intval) + ')'
return form % intval
def nativeDynamicTypeName(self, address, baseType):
return None # FIXME: Seems sufficient, no idea why.
addr = self.target.ResolveLoadAddress(address)
ctx = self.target.ResolveSymbolContextForAddress(addr, 0)
sym = ctx.GetSymbol()
return sym.GetName()
def nativeDynamicType(self, address, base_typeid):
return self.nativeDynamicType_2(address, base_typeid)
def nativeDynamicType_1(self, address, base_typeid):
# Solutions 1: Breaks StdUniquePtr and QVariant1 test
return base_typeid
def nativeDynamicType_2(self, address, base_typeid):
# Solution 2: ~10% slower in total than Solution 1
typename = self.type_name(base_typeid)
#self.warn("LOOKING FOR DYN TYPE: 0x%x %s" % (address, typename))
#self.warn(" PRETTY: 0x%x %s" % (address, self.prettySymbolByAddress(address)))
expr = '(void*)%s' % address
value = self.target.EvaluateExpression(expr)
#self.warn("VALUE: %s" % value)
if value.GetType().GetName() == "void *":
#self.warn("NO DYN TYPE: %s" % value)
return base_typeid
dvalue = value.Dereference()
#self.warn("DVALUE: %s" % value)
sbtype = dvalue.GetType()
#self.warn("TYPE: %s" % sbtype)
#self.warn("OUTPUT: %s" % output)
#self.warn("DYNTYPE: %s" % dyn_typename)
return self.from_native_type(sbtype)
def nativeDynamicType_3(self, address, base_typeid):
# Solution 3: Doesn't improve over 1
typename = self.type_name(base_typeid)
self.warn("LOOKING FOR DYN TYPE: 0x%x %s" % (address, typename))
#self.warn(" PRETTY: 0x%x %s" % (address, self.prettySymbolByAddress(address)))
nativeType = self.type_nativetype_cache.get(base_typeid, None)
#self.warn(" NATIVE BASE %s" % nativeType)
if nativeType is None:
return base_typeid
#versionValue = self.target.EvaluateExpression('qtHookData[2]').GetNonSyntheticValue()
addr = lldb.SBAddress(address, self.target)
value = self.target.CreateValueFromAddress('x', addr, nativeType)
self.warn(" VALUE %s" % value)
return base_typeid
def nativeDynamicType_4(self, address, base_typeid):
#self.warn("RESULT: %s" % result)
#self.warn("ADDRESS: 0x%x" % address)
#thread = self.currentThread()
#frame = thread.GetFrameAtIndex(0)
#expr = '(void*)%s' % address
#value = self.target.EvaluateExpression(expr)
#sbtype = self.lookupNativeType(typename)
#addr = self.target.ResolveLoadAddress(address)
#addr = lldb.SBAddress(address, self.target)
#value = self.target.CreateValueFromAddress('x', addr, sbtype)
#x = lldb::DynamicValueType()
#lldb.eNoDynamicValues
#lldb.eDynamicCanRunTarget
#lldb.eDynamicDontRunTarget
#dyn_value = value.GetDynamicValue(lldb.eDynamicDontRunTarget)
#typ = dyn_value.GetType()
self.warn("GOT DYN VALUE: %s" % dyn_value)
#self.warn("GOT TYPE: %s FOR OBJECT AT 0x%x" % (typ, address))
return self.from_native_type(typ)
#result = lldb.SBCommandReturnObject()
#cmd = 'p (void*)%s' % address
#self.debugger.GetCommandInterpreter().HandleCommand(cmd, result)
#if not result.Succeeded():
# return self.Type(self, typeid)
#output = result.GetOutput().strip()
#dyn_typename = output[1:output.find('$') - 4]
#sbtype = self.lookupNativeType(dyn_typename)
def stateName(self, s):
try:
@@ -638,11 +702,11 @@ class Dumper(DumperBase):
#DumperBase.warn(' -> %s' % result)
return self.fromNativeValue(result)
def pokeValue(self, typeName, *args):
def pokeValue(self, type_name, *args):
thread = self.currentThread()
frame = thread.GetFrameAtIndex(0)
inner = ','.join(args)
value = frame.EvaluateExpression(typeName + '{' + inner + '}')
value = frame.EvaluateExpression(type_name + '{' + inner + '}')
#DumperBase.warn(' TYPE: %s' % value.type)
#DumperBase.warn(' ADDR: 0x%x' % value.address)
#DumperBase.warn(' VALUE: %s' % value)
@@ -830,8 +894,6 @@ class Dumper(DumperBase):
#DumperBase.warn('RECURSE PTR')
typeobj = self.lookupNativeType(name[:-1].strip())
if typeobj is not None:
#DumperBase.warn('RECURSE RESULT X: %s' % typeobj)
self.fromNativeType(typeobj.GetPointerType())
#DumperBase.warn('RECURSE RESULT: %s' % typeobj.GetPointerType())
return typeobj.GetPointerType()
@@ -1289,14 +1351,12 @@ class Dumper(DumperBase):
def findSymbol(self, symbolName):
return self.target.FindFirstGlobalVariable(symbolName)
def warn(self, msg):
self.put('{name="%s",value="",type="",numchild="0"},' % toCString(msg))
def fetchVariables(self, args):
(ok, res) = self.tryFetchInterpreterVariables(args)
if ok:
self.reportResult(res, args)
return
start_time = time.perf_counter()
#(ok, res) = self.tryFetchInterpreterVariables(args)
#if ok:
# self.reportResult(res, args)
# return
self.setVariableFetchingOptions(args)
@@ -1360,13 +1420,15 @@ class Dumper(DumperBase):
# This can happen for unnamed function parameters with
# default values: void foo(int = 0)
continue
value = self.fromNativeFrameValue(val)
value = self.fromNativeValue(val)
variables.append(value)
self.handleLocals(variables)
self.handleWatches(args)
self.put('],partial="%d"' % isPartial)
run_time = time.perf_counter() - start_time
self.put('],partial="%d",runtime="%s"' % (isPartial, run_time))
self.reportResult(self.takeOutput(), args)
@@ -1987,14 +2049,14 @@ class Dumper(DumperBase):
value = self.hexdecode(args['value'])
simpleType = int(args['simpleType'])
lhs = self.findValueByExpression(expr)
typeName = lhs.GetType().GetName()
typeName = typeName.replace('::', '__')
pos = typeName.find('<')
type_name = lhs.GetType().GetName()
type_name = type_name.replace('::', '__')
pos = type_name.find('<')
if pos != -1:
typeName = typeName[0:pos]
if typeName in self.qqEditable and not simpleType:
type_name = type_name[0:pos]
if type_name in self.qqEditable and not simpleType:
expr = self.parseAndEvaluate(expr)
self.qqEditable[typeName](self, expr, value)
self.qqEditable[type_name](self, expr, value)
else:
self.parseAndEvaluate(expr + '=' + value)
self.reportResult(self.describeError(error), args)
@@ -2098,10 +2160,10 @@ class Tester(Dumper):
if 'QT_CREATOR_LLDB_PROCESS' in os.environ:
# Initialize Qt Creator dumper
try:
#try:
theDumper = Dumper()
except Exception as error:
print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))
#except Exception as error:
# print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))
# ------------------------------ For use in LLDB ------------------------------

View File

@@ -221,15 +221,15 @@ def qdump__Qt__ItemDataRole(d, value):
def qdump__QStandardItemData(d, value):
d.createType('@Qt::ItemDataRole') # warm up cache
#d.createType('@Qt::ItemDataRole') # warm up cache
role, pad, val = value.split('{@Qt::ItemDataRole}@{@QVariant}')
d.putPairContents(role.value(), (role, val), 'role', 'value')
def qdump__QStandardItem(d, value):
d.createType('@QStandardItemData') # warm up cache
d.createType('@QStandardItem')
d.createType('@QStandardItem*')
#d.createType('@QStandardItemData') # warm up cache
#d.createType('@QStandardItem')
#d.createType('@QStandardItem*')
vtable, dptr = value.split('pp')
if d.qtVersion() >= 0x060000:
@@ -549,14 +549,11 @@ def qdump__QDir(d, value):
#d.putCallItem('absolutePath', '@QString', value, 'absolutePath')
#d.putCallItem('canonicalPath', '@QString', value, 'canonicalPath')
with SubItem(d, 'absolutePath'):
typ = d.lookupType(ns + 'QString')
d.putItem(d.createValue(privAddress + absoluteDirEntryOffset, typ))
d.putItem(d.createValue(privAddress + absoluteDirEntryOffset, '@QString'))
with SubItem(d, 'entryInfoList'):
typ = d.lookupType(ns + 'QFileInfo')
qdumpHelper_QList(d, privAddress + fileInfosOffset, typ)
qdumpHelper_QList(d, privAddress + fileInfosOffset, '@QFileInfo')
with SubItem(d, 'entryList'):
typ = d.lookupType(ns + 'QStringList')
d.putItem(d.createValue(privAddress + filesOffset, typ))
d.putItem(d.createValue(privAddress + filesOffset, '@QStringList'))
d.putFields(value)
@@ -1095,14 +1092,15 @@ def qform__QList():
def qdump__QList(d, value):
return qdumpHelper_QList(d, value, d.createType(value.type[0]))
return qdumpHelper_QList(d, value, value.type[0])
def qdump__QVariantList(d, value):
qdumpHelper_QList(d, value, d.createType('@QVariant'))
qdumpHelper_QList(d, value, '@QVariant')
def qdumpHelper_QList(d, value, innerType):
def qdumpHelper_QList(d, value, inner_typish):
innerType = d.createType(inner_typish)
data, size = d.listData(value, check=True)
d.putItemCount(size)
@@ -1224,7 +1222,8 @@ def qdump__QLocale(d, value):
# index = int(value['d']['d']['m_data']...)
#d.check(index >= 0)
#d.check(index <= qqLocalesCount)
if d.qtVersion() < 0x50000:
qtVersion = d.qtVersion()
if qtVersion < 0x50000:
d.putStringValue(d.call('const char *', value, 'name'))
d.putPlainChildren(value)
return
@@ -1238,14 +1237,20 @@ def qdump__QLocale(d, value):
= d.split('2s{short}2s'
+ '{@QChar}{@QChar}{short}{@QChar}{@QChar}'
+ '{@QChar}{@QChar}{@QChar}', data)
prefix = ns + 'QLocale::'
try:
d.putStringValue(d.call('const char *', value, 'name'))
if qtVersion >= 0x060700:
res = d.call('const char *', value, 'name', prefix + 'TagSeparator::Underscore')
else:
res = d.call('const char *', value, 'name')
d.putStringValue(res)
except:
pass
d.putExpandable()
if d.isExpanded():
with Children(d):
prefix = ns + 'QLocale::'
d.putSubItem('country', d.createValue(countryId, prefix + 'Country'))
d.putSubItem('language', d.createValue(languageId, prefix + 'Language'))
d.putSubItem('numberOptions', d.createValue(numberOptions, prefix + 'NumberOptions'))
@@ -1425,7 +1430,9 @@ if False:
d.putSpecialValue('minimumitemcount', 0)
def qdump__QPair(d, value):
# FIXME: Qt 5
# remvign the _xxxx makes GDB work with Qt 5 but breaks LLDB
def qdump__QPair_xxxx(d, value):
typeCode = '{%s}@{%s}' % (value.type[0].name, value.type[1].name)
first, pad, second = value.split(typeCode)
with Children(d):
@@ -1857,7 +1864,7 @@ def qdump__QStringRef(d, value):
def qdump__QStringList(d, value):
qdumpHelper_QList(d, value, d.createType('@QString'))
qdumpHelper_QList(d, value, '@QString')
d.putBetterType(value.type)
@@ -2304,22 +2311,22 @@ def qdump__QVector(d, value):
if d.qtVersion() >= 0x060000:
data, length = d.listData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType(value.type.ltarget[0]))
d.putPlotData(data, length, value.type.target()[0])
# g++ 9.3 does not add the template parameter list to the debug info.
# Fake it for the common case:
if value.type.name == d.qtNamespace() + "QVector":
d.putBetterType(value.type.name + '<' + value.type.ltarget[0].name + '>')
d.putBetterType(value.type.name + '<' + value.type.target()[0].name + '>')
else:
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType(value.type[0]))
d.putPlotData(data, length, value.type[0])
if False:
def qdump__QObjectConnectionList(d, value):
data, length = d.vectorData(value)
d.putItemCount(length)
d.putPlotData(data, length, d.createType('@QObjectPrivate::ConnectionList'))
d.putPlotData(data, length, '@QObjectPrivate::ConnectionList')
def qdump__QVarLengthArray(d, value):
@@ -2913,15 +2920,13 @@ def qdump_64__QJSValue_6(d, value):
elif typ > 7:
val = d.Value(d)
val.ldata = struct.pack('q', dd ^ 0xfffc000000000000)
val._type = d.createType('double')
d.putItem(val)
d.putItem(val.cast('double'))
d.putType(value.type.name + ' (double)')
elif typ <= 3: # Heap
if dd & 1: # String
val = d.Value(d)
val.ldata = struct.pack('q', dd & ~1)
val._type = d.createType('@QString*')
d.putItem(val)
d.putItem(val.cast('@QString*'))
d.putType(value.type.name + ' (QString)')
else:
# FIXME: Arrays, Objects missing.
@@ -2955,16 +2960,13 @@ def qdump_64__QJSValue_6(d, value):
val = d.Value(d)
val.ldata = struct.pack('q', pointer)
if typ == 1:
val._type = d.createType('double*')
d.putItem(val)
d.putItem(val.cast('double*'))
d.putType(value.type.name + ' (double)')
elif typ == 3:
val._type = d.createType('@QV4::Value*')
d.putItem(val)
d.putItem(val.cast('@QV4::Value*'))
d.putType(value.type.name + ' (QV4::Value)')
elif typ == 5:
val._type = d.createType('@QString*')
d.putItem(val)
d.putItem(val.cast('@QString*'))
d.putType(value.type.name + ' (QString)')
else:
@@ -3538,7 +3540,7 @@ def qdump__QCborValue(d, value):
d.putPlainChildren(value)
def qdump__QCborValue_proxy(d, value):
item_data, container_ptr, item_type, is_cbor = value.data()
item_data, container_ptr, item_type, is_cbor = value.ldata
def typename(key, is_cbor):
if is_cbor:

View File

@@ -1171,6 +1171,7 @@ private:
bool m_isQnxGdb = false;
bool m_useGLibCxxDebug = false;
int m_totalDumpTime = 0;
int m_totalInnerTime = 0;
};
void tst_Dumpers::initTestCase()
@@ -1343,7 +1344,9 @@ void tst_Dumpers::cleanup()
void tst_Dumpers::cleanupTestCase()
{
qCDebug(lcDumpers) << "Dumpers total: " << QTime::fromMSecsSinceStartOfDay(m_totalDumpTime);
qCDebug(lcDumpers) << QTime::fromMSecsSinceStartOfDay(m_totalDumpTime);
qCDebug(lcDumpers, "TotalOuter: %5d", m_totalDumpTime);
qCDebug(lcDumpers, "TotalInner: %5d", m_totalInnerTime);
}
void tst_Dumpers::dumper()
@@ -1859,7 +1862,10 @@ void tst_Dumpers::dumper()
dumperTimer.start();
debugger.write(cmds.toLocal8Bit());
QVERIFY(debugger.waitForFinished());
m_totalDumpTime += dumperTimer.elapsed();
const int elapsed = dumperTimer.elapsed();
//< QTime::fromMSecsSinceStartOfDay(elapsed);
qCDebug(lcDumpers, "CaseOuter: %5d", elapsed);
m_totalDumpTime += elapsed;
output = debugger.readAllStandardOutput();
QByteArray fullOutput = output;
//qCDebug(lcDumpers) << "stdout: " << output;
@@ -1890,6 +1896,9 @@ void tst_Dumpers::dumper()
actual.fromStringMultiple(QString::fromLocal8Bit(contents));
context.nameSpace = actual["qtnamespace"].data();
int runtime = actual["runtime"].data().toFloat() * 1000;
qCDebug(lcDumpers, "CaseInner: %5d", runtime);
m_totalInnerTime += runtime;
actual = actual["data"];
//qCDebug(lcDumpers) << "FOUND NS: " << context.nameSpace;
@@ -1913,7 +1922,13 @@ void tst_Dumpers::dumper()
if (context.nameSpace == "::")
context.nameSpace.clear();
contents.replace("\\\"", "\"");
actual.fromString(QString::fromLocal8Bit(contents));
actual.fromStringMultiple(QString::fromLocal8Bit(contents));
int runtime = actual["runtime"].data().toFloat() * 1000;
qCDebug(lcDumpers, "CaseInner: %5d", runtime);
m_totalInnerTime += runtime;
actual = actual["data"];
//qCDebug(lcDumpers).noquote() << "\nACTUAL: " << actual.toString() << "\nXYYY";
} else {
QByteArray localsAnswerStart("<qtcreatorcdbext>|R|42|");
QByteArray locals("|script|");
@@ -1967,8 +1982,8 @@ void tst_Dumpers::dumper()
auto test = [&](const Check &check, bool *removeIt, bool single) {
if (!check.matches(m_debuggerEngine, m_debuggerVersion, context)) {
if (single)
qCDebug(lcDumpers) << "SKIPPING NON-MATCHING TEST " << check;
//if (single)
// qCDebug(lcDumpers) << "SKIPPING NON-MATCHING TEST " << check;
return true; // we have not failed
}
@@ -2071,12 +2086,8 @@ void tst_Dumpers::dumper()
}
}
if (ok) {
m_keepTemp = false;
} else {
local.forAllChildren([](WatchItem *item) { qCDebug(lcDumpers) << item->internalName(); });
int pos1 = 0, pos2 = -1;
int pos1 = 0;
int pos2 = -1;
while (true) {
pos1 = fullOutput.indexOf("bridgemessage={msg=", pos2 + 1);
if (pos1 == -1)
@@ -2085,8 +2096,14 @@ void tst_Dumpers::dumper()
pos2 = fullOutput.indexOf("\"}", pos1 + 1);
if (pos2 == -1)
break;
qCDebug(lcDumpers) << "MSG: " << fullOutput.mid(pos1, pos2 - pos1 - 1);
qCDebug(lcDumpers) << "MSG: " << fullOutput.mid(pos1, pos2 - pos1);
}
if (ok) {
m_keepTemp = false;
} else {
local.forAllChildren([](WatchItem *item) { qCDebug(lcDumpers) << item->internalName(); });
qCDebug(lcDumpers).noquote() << "CONTENTS : " << contents;
qCDebug(lcDumpers).noquote() << "FULL OUTPUT : " << fullOutput.data();
qCDebug(lcDumpers) << "Qt VERSION : " << QString::number(context.qtVersion, 16);
@@ -4374,7 +4391,7 @@ void tst_Dumpers::dumper_data()
+ NetworkProfile()
+ Check("ha", ValuePattern(".*127.0.0.1.*"), "@QHostAddress")
+ Check("ha.a", "2130706433", TypeDef("unsigned int", "@quint32"))
+ Check("ha.a", "2130706433", "@quint32")
+ Check("ha.ipString", ValuePattern(".*127.0.0.1.*"), "@QString")
% QtVersion(0, 0x50800)
//+ Check("ha.protocol", "@QAbstractSocket::IPv4Protocol (0)",
@@ -4383,7 +4400,7 @@ void tst_Dumpers::dumper_data()
// "@QAbstractSocket::NetworkLayerProtocol") % LldbEngine
+ Check("ha.scopeId", "\"\"", "@QString")
+ Check("ha1", ValuePattern(".*127.0.0.1.*"), "@QHostAddress")
+ Check("ha1.a", "2130706433", TypeDef("unsigned int", "@quint32"))
+ Check("ha1.a", "2130706433", "@quint32")
+ Check("ha1.ipString", "\"127.0.0.1\"", "@QString")
% QtVersion(0, 0x50800)
//+ Check("ha1.protocol", "@QAbstractSocket::IPv4Protocol (0)",
@@ -6226,7 +6243,7 @@ void tst_Dumpers::dumper_data()
QTest::newRow("Bitfields")
<< Data("",
"enum E { V1, V2 };"
"enum E { V1, V2 };\n"
"struct S\n"
"{\n"
" S() : front(13), x(2), y(3), z(39), e(V2), c(1), b(0), f(5),"
@@ -6257,7 +6274,7 @@ void tst_Dumpers::dumper_data()
+ Check("s.x", "2", "unsigned int : 3") % NoCdbEngine
+ Check("s.y", "3", "unsigned int : 4") % NoCdbEngine
+ Check("s.z", "39", "unsigned int : 18") % NoCdbEngine
+ Check("s.e", "V2 (1)", "E : 3") % GdbEngine
// + Check("s.e", "V2 (1)", "E : 3") % GdbEngine FIXME
+ Check("s.g", "46", "char : 7") % GdbEngine
+ Check("s.h", "47", "char") % GdbEngine
+ Check("s.x", "2", "unsigned int") % CdbEngine
@@ -7348,11 +7365,21 @@ void tst_Dumpers::dumper_data()
"Base *b = &d;\n",
"&d, &b")
+ Check("b.@1.a", "a", "21", "int")
+ Check("b.b", "b", "42", "int");
// https://bugreports.qt.io/browse/QTCREATORBUG-18450
QTest::newRow("Bug18450")
<< Data("using quint128 = __uint128_t;\n",
"quint128 x = 42;\n",
"&x")
+ NoCdbEngine
+ Check("x", "42", "quint128");
// https://bugreports.qt.io/browse/QTCREATORBUG-17823
QTest::newRow("Bug17823")
@@ -7615,8 +7642,7 @@ void tst_Dumpers::dumper_data()
"&d, &s, &ptrConst, &ref, &refConst, &ptrToPtr, &sharedPtr")
+ GdbEngine
+ GdbVersion(70500)
+ NoCdbEngine
+ BoostProfile()
+ Check("d", "", "Derived")
@@ -8323,11 +8349,13 @@ void tst_Dumpers::dumper_data()
QTest::newRow("Sql")
<< Data("#include <QSqlField>\n"
<< Data("#include <QCoreApplication>\n"
"#include <QSqlField>\n"
"#include <QSqlDatabase>\n"
"#include <QSqlQuery>\n"
"#include <QSqlRecord>\n",
"QCoreApplication app(argc, argv);\n"
"QSqlDatabase db = QSqlDatabase::addDatabase(\"QSQLITE\");\n"
"db.setDatabaseName(\":memory:\");\n"
"Q_ASSERT(db.open());\n"