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.outputLock = threading.Lock()
self.isCdb = True 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): def enumValue(self, nativeValue):
val = nativeValue.nativeDebuggerValue() val = nativeValue.nativeDebuggerValue()
# remove '0n' decimal prefix of the native cdb value output # 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(): elif not nativeValue.type().resolved and nativeValue.type().code() == TypeCode.Struct and not nativeValue.hasChildren():
val.ldisplay = self.enumValue(nativeValue) val.ldisplay = self.enumValue(nativeValue)
val.isBaseClass = val.name == nativeValue.type().name() val.isBaseClass = val.name == nativeValue.type().name()
val.typeid = self.from_native_type(nativeValue.type())
val.nativeValue = nativeValue val.nativeValue = nativeValue
val.laddress = nativeValue.address() val.laddress = nativeValue.address()
val.lbitsize = nativeValue.bitsize() val.lbitsize = nativeValue.bitsize()
@@ -137,14 +145,10 @@ class Dumper(DumperBase):
for f in nativeType.fields()]) for f in nativeType.fields()])
return typeId return typeId
def nativeValueType(self, nativeValue): def from_native_type(self, nativeType):
return self.fromNativeType(nativeValue.type())
def fromNativeType(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type)) self.check(isinstance(nativeType, cdbext.Type))
typeId = self.nativeTypeId(nativeType) typeid = self.typeid_for_string(self.nativeTypeId(nativeType))
if self.typeData.get(typeId, None) is not None: self.type_nativetype_cache[typeid] = nativeType
return self.Type(self, typeId)
if nativeType.name().startswith('void'): if nativeType.name().startswith('void'):
nativeType = FakeVoidType(nativeType.name(), self) nativeType = FakeVoidType(nativeType.name(), self)
@@ -154,70 +158,61 @@ class Dumper(DumperBase):
if nativeType.name().startswith('<function>'): if nativeType.name().startswith('<function>'):
code = TypeCode.Function code = TypeCode.Function
elif nativeType.targetName() != nativeType.name(): elif nativeType.targetName() != nativeType.name():
return self.createPointerType(nativeType.targetName()) return self.create_pointer_typeid(self.typeid_for_string(nativeType.targetName()))
if code == TypeCode.Array: if code == TypeCode.Array:
# cdb reports virtual function tables as arrays those ar handled separetly by # cdb reports virtual function tables as arrays those ar handled separetly by
# the DumperBase. Declare those types as structs prevents a lookup to a # the DumperBase. Declare those types as structs prevents a lookup to a
# none existing type # none existing type
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '): if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
targetName = nativeType.targetName() targetName = nativeType.targetName().strip()
count = nativeType.arrayElements() self.type_name_cache[typeid] = nativeType.name()
if targetName.endswith(']'): self.type_code_cache[typeid] = code
(prefix, suffix, inner_count) = self.splitArrayType(targetName) self.type_target_cache[typeid] = self.typeid_for_string(targetName)
type_name = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix) self.type_size_cache[typeid] = nativeType.bitsize() // 8
else: return typeid
type_name = '%s[%d]' % (targetName, count)
tdata = self.TypeData(self, typeId)
tdata.name = type_name
tdata.code = TypeCode.Array
tdata.ltarget = targetName
tdata.lbitsize = lambda: nativeType.bitsize()
return self.Type(self, typeId)
code = TypeCode.Struct code = TypeCode.Struct
tdata = self.TypeData(self, typeId) self.type_name_cache[typeid] = nativeType.name()
tdata.name = nativeType.name() self.type_size_cache[typeid] = nativeType.bitsize() // 8
tdata.lbitsize = lambda: nativeType.bitsize() self.type_code_cache[typeid] = code
tdata.code = code self.type_modulename_cache[typeid] = nativeType.module()
tdata.moduleName = lambda: nativeType.module() self.type_enum_display_cache[typeid] = lambda intval, addr, form: \
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.nativeTypeEnumDisplay(nativeType, intval, form) self.nativeTypeEnumDisplay(nativeType, intval, form)
tdata.templateArguments = lambda: \ return typeid
self.listTemplateParameters(nativeType.name())
return self.Type(self, typeId)
def listNativeValueChildren(self, nativeValue): def listNativeValueChildren(self, nativeValue, include_bases):
fields = []
index = 0 index = 0
nativeMember = nativeValue.childFromIndex(index) nativeMember = nativeValue.childFromIndex(index)
while nativeMember: 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 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 index += 1
nativeMember = nativeValue.childFromIndex(index) nativeMember = nativeValue.childFromIndex(index)
return fields
def listValueChildren(self, value): def listValueChildren(self, value, include_bases=True):
nativeValue = value.nativeValue nativeValue = value.nativeValue
if nativeValue is None: if nativeValue is None:
nativeValue = cdbext.createValue(value.address(), self.lookupNativeType(value.type.name, 0)) 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 nativeValue = value.nativeValue
if nativeValue is None: if nativeValue is None:
nativeValue = cdbext.createValue(value.address(), nativeType) nativeValue = cdbext.createValue(value.address(), native_type)
return self.listNativeValueChildren(nativeValue) return self.listNativeValueChildren(nativeValue, include_bases)
def nativeStructAlignment(self, nativeType): def nativeStructAlignment(self, nativeType):
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name) #DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
def handleItem(nativeFieldType, align): def handleItem(nativeFieldType, align):
a = self.fromNativeType(nativeFieldType).alignment() a = self.type_alignment(self.from_native_type(nativeFieldType))
return a if a > align else align return a if a > align else align
align = 1 align = 1
for f in nativeType.fields(): for f in nativeType.fields():
@@ -398,20 +393,6 @@ class Dumper(DumperBase):
else: else:
return typeName 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): def lookupNativeType(self, name, module=0):
if name.startswith('void'): if name.startswith('void'):
return FakeVoidType(name, self) return FakeVoidType(name, self)
@@ -439,9 +420,6 @@ class Dumper(DumperBase):
ptr = cdbext.getAddressByName(type.name + '::staticMetaObject') ptr = cdbext.getAddressByName(type.name + '::staticMetaObject')
return ptr return ptr
def warn(self, msg):
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
def fetchVariables(self, args): def fetchVariables(self, args):
self.resetStats() self.resetStats()
(ok, res) = self.tryFetchInterpreterVariables(args) (ok, res) = self.tryFetchInterpreterVariables(args)
@@ -458,13 +436,17 @@ class Dumper(DumperBase):
self.anonNumber = 0 self.anonNumber = 0
variables = [] variables = []
for val in cdbext.listOfLocals(self.partialVariable): try:
dumperVal = self.fromNativeValue(val) for val in cdbext.listOfLocals(self.partialVariable):
dumperVal.lIsInScope = dumperVal.name not in self.uninitialized dumperVal = self.fromNativeValue(val)
variables.append(dumperVal) dumperVal.lIsInScope = dumperVal.name not in self.uninitialized
variables.append(dumperVal)
self.handleLocals(variables) self.handleLocals(variables)
self.handleWatches(args) 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('],partial="%d"' % (len(self.partialVariable) > 0))
self.put(',timings=%s' % self.timings) self.put(',timings=%s' % self.timings)
@@ -485,9 +467,6 @@ class Dumper(DumperBase):
def findValueByExpression(self, exp): def findValueByExpression(self, exp):
return cdbext.parseAndEvaluate(exp) return cdbext.parseAndEvaluate(exp)
def nativeDynamicTypeName(self, address, baseType):
return None # Does not work with cdb
def nativeValueDereferenceReference(self, value): def nativeValueDereferenceReference(self, value):
return self.nativeValueDereferencePointer(value) return self.nativeValueDereferencePointer(value)
@@ -518,7 +497,7 @@ class Dumper(DumperBase):
else: else:
val = self.Value(self) val = self.Value(self)
val.laddress = value.pointer() 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 val.nativeValue = value.nativeValue
return val return val
@@ -540,14 +519,11 @@ class Dumper(DumperBase):
res = self.nativeParseAndEvaluate(symbolName) res = self.nativeParseAndEvaluate(symbolName)
return None if res is None else res.address() return None if res is None else res.address()
def putItemX(self, value): def putItem(self, value: DumperBase.Value):
#DumperBase.warn('PUT ITEM: %s' % value.stringify())
typeobj = value.type # unqualified() typeobj = value.type # unqualified()
typeName = typeobj.name typeName = typeobj.name
self.addToCache(typeobj) # Fill type cache
if not value.lIsInScope: if not value.lIsInScope:
self.putSpecialValue('optimizedout') self.putSpecialValue('optimizedout')
#self.putType(typeobj) #self.putType(typeobj)
@@ -571,8 +547,7 @@ class Dumper(DumperBase):
return return
self.putAddress(value.address()) self.putAddress(value.address())
if value.lbitsize is not None: self.putField('size', self.type_size(value.typeid))
self.putField('size', value.lbitsize // 8)
if typeobj.code == TypeCode.Function: if typeobj.code == TypeCode.Function:
#DumperBase.warn('FUNCTION VALUE: %s' % value) #DumperBase.warn('FUNCTION VALUE: %s' % value)
@@ -738,7 +713,7 @@ class Dumper(DumperBase):
#DumperBase.warn('INAME: %s' % self.currentIName) #DumperBase.warn('INAME: %s' % self.currentIName)
if self.autoDerefPointers: if self.autoDerefPointers:
# Generic pointer type with AutomaticFormat, but never dereference char types: # Generic pointer type with AutomaticFormat, but never dereference char types:
if value.type.targetName not in ( if value.type.targetName.strip() not in (
'char', 'char',
'signed char', 'signed char',
'int8_t', 'int8_t',
@@ -769,7 +744,7 @@ class Dumper(DumperBase):
def putCStyleArray(self, value): def putCStyleArray(self, value):
arrayType = value.type arrayType = value.type
innerType = arrayType.ltarget innerType = arrayType.target()
address = value.address() address = value.address()
if address: if address:
self.putValue('@0x%x' % address, priority=-1) self.putValue('@0x%x' % address, priority=-1)
@@ -783,7 +758,7 @@ class Dumper(DumperBase):
p = value.address() p = value.address()
if displayFormat != DisplayFormat.Raw and p: if displayFormat != DisplayFormat.Raw and p:
if innerType.name in ( if innerType.name.strip() in (
'char', 'char',
'int8_t', 'int8_t',
'qint8', 'qint8',
@@ -828,7 +803,7 @@ class Dumper(DumperBase):
innerSize = innerType.size() innerSize = innerType.size()
self.putNumChild(n) self.putNumChild(n)
#DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType)) #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() maxNumChild = self.maxArrayCount()
if enc: if enc:
self.put('childtype="%s",' % innerType.name) self.put('childtype="%s",' % innerType.name)
@@ -863,12 +838,8 @@ class Dumper(DumperBase):
if innerType in ('wchar_t', 'WCHAR'): if innerType in ('wchar_t', 'WCHAR'):
self.putType(typeName) self.putType(typeName)
charSize = self.lookupType('wchar_t').size() (length, data) = self.encodeCArray(ptr, 2, limit)
(length, data) = self.encodeCArray(ptr, charSize, limit) self.putValue(data, 'utf16', length=length)
if charSize == 2:
self.putValue(data, 'utf16', length=length)
else:
self.putValue(data, 'ucs4', length=length)
return True return True
if displayFormat == DisplayFormat.Latin1String: if displayFormat == DisplayFormat.Latin1String:
@@ -931,19 +902,12 @@ class Dumper(DumperBase):
self.putItem(derefValue) self.putItem(derefValue)
self.currentChildType = savedCurrentChildType 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): def createValue(self, datish, typish):
if self.isInt(datish): # Used as address. if isinstance(datish, int): # Used as address.
return self.createValueFromAddressAndType(datish, typish) return self.createValueFromAddressAndType(datish, typish)
if isinstance(datish, bytes): if isinstance(datish, bytes):
val = self.Value(self) val = self.Value(self)
if isinstance(typish, self.Type): val.typeid = self.create_typeid(typish)
val._type = typish
else:
val._type = self.Type(self, typish)
#DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish))) #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish)))
val.ldata = datish val.ldata = datish
val.check() val.check()
@@ -952,11 +916,8 @@ class Dumper(DumperBase):
def createValueFromAddressAndType(self, address, typish): def createValueFromAddressAndType(self, address, typish):
val = self.Value(self) val = self.Value(self)
if isinstance(typish, self.Type): val.typeid = self.create_typeid(typish)
val._type = typish
else:
val._type = self.Type(self, typish)
val.laddress = address val.laddress = address
if self.useDynamicType: if self.useDynamicType:
val._type = val.type.dynamicType(address) val.typeid = self.dynamic_typeid_at_address(val.typeid, address)
return val return val

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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