Commit 376f58df authored by hjk's avatar hjk
Browse files

Debugger: Fix QObject/property dumper



Change-Id: Ied68fd07e077a185223a68cc504fff5a5b9391ac
Reviewed-by: default avatarFriedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent 9528230b
......@@ -274,6 +274,9 @@ class DumperBase:
# This keeps canonical forms of the typenames, without array indices etc.
self.cachedFormats = {}
self.knownQObjectTypes = set()
self.knownNonQObjectTypes = set()
def stripForFormat(self, typeName):
if typeName in self.cachedFormats:
......@@ -769,6 +772,8 @@ class DumperBase:
self.putItem(value.dereference())
def putQObjectNameValue(self, value):
if str(value.type) in self.knownNonQObjectTypes:
return
try:
intSize = self.intSize()
ptrSize = self.ptrSize()
......@@ -817,14 +822,50 @@ class DumperBase:
if size == 0:
return False
str = self.readMemory(data, 2 * size)
self.putValue(str, Hex4EncodedLittleEndian, 1)
raw = self.readMemory(data, 2 * size)
self.putValue(raw, Hex4EncodedLittleEndian, 1)
return True
except:
self.knownNonQObjectTypes.insert(str(value.type))
pass
def staticQObjectPropertyNames(self, metaobject):
properties = []
dd = metaobject["d"]
data = self.dereferenceValue(dd["data"])
byteArrayDataType = self.lookupType(self.qtNamespace() + "QByteArrayData")
byteArrayDataSize = byteArrayDataType.sizeof
sd = self.dereferenceValue(dd["stringdata"])
propertyCount = self.extractInt(data + 24)
propertyData = self.extractInt(data + 28)
for i in range(propertyCount):
x = data + (propertyData + 3 * i) * 4
literal = sd + self.extractInt(x) * byteArrayDataSize
ldata, lsize, lalloc = self.byteArrayDataHelper(literal)
properties.append(self.readCArray(ldata, lsize))
return properties
# This is called is when a QObject derived class is expanded
def putQObjectGuts(self, qobject):
smo = self.childWithName(qobject, "staticMetaObject")
if smo is None:
return
with SubItem(self, "[properties]"):
propertyNames = self.staticQObjectPropertyNames(smo)
propertyCount = len(propertyNames)
self.putItemCount(propertyCount)
self.putNumChild(propertyCount)
with Children(self):
for i in range(propertyCount):
name = propertyNames[i]
self.putCallItem(name, qobject, "property", '"' + name + '"')
def isKnownMovableType(self, type):
if type in (
"QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
......
......@@ -717,12 +717,11 @@ class Dumper(DumperBase):
def call(self, value, func, *args):
return self.call2(value, func, args)
def hasChildWithName(self, value, name):
def childWithName(self, value, name):
try:
value[name]
return True
return value[name]
except:
return False
return None
def makeValue(self, type, init):
type = "::" + stripClassTag(str(type));
......@@ -961,6 +960,11 @@ class Dumper(DumperBase):
return toInteger(value.cast(self.voidPtrType()))
def isQObject(self, value):
typeName = str(value.type)
if typeName in self.knownQObjectTypes:
return True
if typeName in self.knownNonQObjectTypes:
return False
try:
vtable = self.dereference(toInteger(value.address)) # + ptrSize
if vtable & 0x3: # This is not a pointer.
......@@ -972,9 +976,15 @@ class Dumper(DumperBase):
s = gdb.execute("info symbol 0x%x" % metaObjectEntry, to_string=True)
#warn("S: %s " % s)
#return s.find("::metaObject() const") > 0
return s.find("::metaObject() const") > 0 or s.find("10metaObjectEv") > 0
#return str(metaObjectEntry).find("::metaObject() const") > 0
if s.find("::metaObject() const") > 0 or s.find("10metaObjectEv") > 0:
self.knownQObjectTypes.add(typeName)
return True
else:
self.knownNonQObjectTypes.add(typeName)
return False
except:
self.knownNonQObjectTypes.add(typeName)
return False
def isQObject_B(self, value):
......@@ -1398,7 +1408,8 @@ class Dumper(DumperBase):
#warn("INAME: %s " % self.currentIName)
#warn("INAMES: %s " % self.expandedINames)
#warn("EXPANDED: %s " % (self.currentIName in self.expandedINames))
if self.isQObject(value):
isQObject = self.isQObject(value)
if isQObject:
self.putQObjectNameValue(value) # Is this too expensive?
self.putType(typeName)
self.putEmptyValue()
......@@ -1408,6 +1419,9 @@ class Dumper(DumperBase):
innerType = None
with Children(self, 1, childType=innerType):
self.putFields(value)
if isQObject:
self.putQObjectGuts(value)
def putPlainChildren(self, value):
self.putEmptyValue(-99)
......@@ -1423,6 +1437,41 @@ class Dumper(DumperBase):
return bytesToString(binascii.hexlify(mem.tobytes()))
return binascii.hexlify(mem)
def readCArray(self, base, size):
inferior = self.selectedInferior()
if sys.version_info[0] >= 3:
mem = bytes()
for i in range(size):
char = inferior.read_memory(base + i, 1)[0]
if not char:
break
mem += char
return mem.decode("utf8")
else:
mem = ""
for i in range(size):
char = inferior.read_memory(base + i, 1)[0]
if not char:
break
mem += char
return mem
def readCString(self, base):
inferior = self.selectedInferior()
mem = ""
while True:
char = inferior.read_memory(base, 1)[0]
if not char:
break
mem += char
base += 1
#if sys.version_info[0] >= 3:
# return mem.tobytes()
return mem
#if sys.version_info[0] >= 3:
# return bytesToString(binascii.hexlify(mem.tobytes()))
#return binascii.hexlify(mem)
def putFields(self, value, dumpBase = True):
fields = value.type.fields()
......@@ -1638,15 +1687,15 @@ class Dumper(DumperBase):
# FIXME: This only works when call from inside a Qt function frame.
namespace = ""
try:
str = gdb.execute("ptype QString::Null", to_string=True)
out = gdb.execute("ptype QString::Null", to_string=True)
# The result looks like:
# "type = const struct myns::QString::Null {"
# " <no data fields>"
# "}"
pos1 = str.find("struct") + 7
pos2 = str.find("QString::Null")
pos1 = out.find("struct") + 7
pos2 = out.find("QString::Null")
if pos1 > -1 and pos2 > -1:
namespace = str[pos1:pos2]
namespace = out[pos1:pos2]
self.cachedQtNamespace = namespace
self.ns = lambda: self.cachedQtNamespace
except:
......
......@@ -378,8 +378,9 @@ class Dumper(DumperBase):
typeClass = typeobj.GetTypeClass()
return typeClass == lldb.eTypeClassBuiltin
def hasChildWithName(self, value, name):
return value.GetChildMemberWithName(name).IsValid()
def childWithName(self, value, name):
child = value.GetChildMemberWithName(name)
return child if child.IsValid() else None
def childAt(self, value, index):
return value.GetChildAtIndex(index)
......
......@@ -1020,6 +1020,114 @@ def extractCString(table, offset):
offset += 1
return result
def qdump__QMetaObjectPrivate(d, value):
d.putEmptyValue()
d.putNumChild(1)
if d.isExpanded():
with Children(d):
# int revision;
# int className;
# int classInfoCount, classInfoData;
# int methodCount, methodData;
# int propertyCount, propertyData;
# int enumeratorCount, enumeratorData;
# int constructorCount, constructorData; //since revision 2
# int flags; //since revision 3
# int signalCount; //since revision 4
d.putIntItem("revision", value["revision"])
d.putIntItem("methodCount", value["methodCount"])
d.putIntItem("propertyCount", value["propertyCount"])
d.putIntItem("enumeratorCount", value["enumeratorCount"])
d.putIntItem("constructorCount", value["constructorCount"])
d.putIntItem("flags", value["flags"])
d.putIntItem("signalCount", value["signalCount"])
def qdump__QMetaObject(d, value):
d.putEmptyValue()
d.putNumChild(1)
if d.isExpanded():
with Children(d):
dd = value["d"]
d.putSubItem("d", dd)
data = d.dereferenceValue(dd["data"])
propertyNames = d.staticQObjectPropertyNames(value)
propertyIndex = 0
for propertyName in propertyNames:
with SubItem(d, "property_%s" % propertyIndex):
d.putValue(propertyName)
propertyIndex += 1
#byteArrayDataType = d.lookupType(d.qtNamespace() + "QByteArrayData")
#byteArrayDataSize = byteArrayDataType.sizeof
#sd = d.dereferenceValue(dd["stringdata"])
#stringdata, size, alloc = d.byteArrayDataHelper(sd)
#propertyCount = d.extractInt(data + 24)
#propertyData = d.extractInt(data + 28)
## This is the 'data' member in the qt_meta_stringdata_qobject__*_t struct
#d.putIntItem("_byteArrayDataSize", byteArrayDataSize)
#d.putAddressItem("_data", data)
#d.putAddressItem("_sd_", stringdata)
#with SubItem(d, "_sd"):
# d.putValue(d.readMemory(stringdata, size), Hex2EncodedLatin1)
#with SubItem(d, "_cn"):
# d.putValue(d.readMemory(stringdata + d.extractInt(data + 4), size), Hex2EncodedLatin1)
#for i in range(propertyCount):
# with SubItem(d, "property_%s" % i):
# x = data + (propertyData + 3 * i) * 4
# literal = sd + d.extractInt(x) * byteArrayDataSize
# ldata, lsize, lalloc = d.byteArrayDataHelper(literal)
# d.putValue(d.readMemory(ldata, lsize), Hex2EncodedLatin1)
# d.putNumChild(1)
# if d.isExpanded():
# with Children(d):
# if d.isExpanded():
# d.putAddressItem("_literal", literal)
# d.putIntItem("__data", ldata)
# d.putIntItem("__size", lsize)
# d.putIntItem("__alloc", lalloc)
# d.putIntItem("name", d.extractInt(x))
# d.putIntItem("type", d.extractInt(x + 4))
# d.putIntItem("flags", d.extractInt(x + 8))
methodCount = d.extractInt(data + 16)
methodData = d.extractInt(data + 20)
for i in range(methodCount):
with SubItem(d, "method_%s" % i):
x = data + (methodData + 5 * i) * 4
#d.putEmptyValue()
d.putValue(d.readCString(stringdata + d.extractInt(x)))
d.putNumChild(1)
if d.isExpanded():
with Children(d):
if d.isExpanded():
d.putIntItem("name", d.extractInt(x))
d.putIntItem("argc", d.extractInt(x + 4))
d.putIntItem("argv", d.extractInt(x + 8))
d.putIntItem("type", d.extractInt(x + 12))
d.putIntItem("flags", d.extractInt(x + 16))
d.putSubItem("stringData", dd["stringdata"])
d.putIntItem("revision", d.extractInt(data))
d.putIntItem("className", d.extractInt(data + 4))
d.putIntItem("classInfoCount", d.extractInt(data + 8))
d.putIntItem("className", d.extractInt(data + 12))
d.putIntItem("methodCount", d.extractInt(data + 16))
d.putIntItem("methodData", d.extractInt(data + 20))
d.putIntItem("propertyCount", d.extractInt(data + 24))
d.putIntItem("propertyData", d.extractInt(data + 28))
d.putIntItem("enumeratorCount", d.extractInt(data + 32))
d.putIntItem("enumeratorData", d.extractInt(data + 36))
d.putIntItem("constructorCount", d.extractInt(data + 40))
d.putIntItem("constructorData", d.extractInt(data + 44))
d.putIntItem("flags", d.extractInt(data + 48))
d.putIntItem("signalCount", d.extractInt(data + 52))
def qdump__QObject(d, value):
d.putQObjectNameValue(value)
......@@ -1074,8 +1182,8 @@ def qdump__QObject(d, value):
#d.checkRef(d_ptr["ref"])
d.putNumChild(4)
if d.isExpanded():
print("DIR: %s\n" % dir())
with Children(d):
d.putQObjectGuts(value)
# Local data.
if privateTypeName != ns + "QObjectPrivate":
......@@ -1095,70 +1203,21 @@ def qdump__QObject(d, value):
d.putSubItem("parent", d_ptr["parent"])
d.putSubItem("children", d_ptr["children"])
# Properties.
with SubItem(d, "properties"):
# Prolog
extraData = d_ptr["extraData"] # Capitalization!
if d.isNull(extraData):
dynamicPropertyCount = 0
else:
extraDataType = d.lookupType(
ns + "QObjectPrivate::ExtraData").pointer()
extraData = extraData.cast(extraDataType)
ed = extraData.dereference()
names = ed["propertyNames"]
values = ed["propertyValues"]
#userData = ed["userData"]
namesBegin = names["d"]["begin"]
namesEnd = names["d"]["end"]
namesArray = names["d"]["array"]
dynamicPropertyCount = namesEnd - namesBegin
# Metaobject.
d.putSubItem("metaobject", mo)
#staticPropertyCount = d.call(mo, "propertyCount")
staticPropertyCount = metaData[6]
#warn("PROPERTY COUNT: %s" % staticPropertyCount)
propertyCount = staticPropertyCount + dynamicPropertyCount
# Static Properties.
with SubItem(d, "statics"):
staticPropertyCount = d.call(mo, "propertyCount")
#staticPropertyCount = metaData[6]
#warn("STATIC PROPERTY COUNT: %s" % staticPropertyCount)
d.putNoType()
d.putItemCount(propertyCount)
d.putNumChild(propertyCount)
d.putItemCount(staticPropertyCount)
d.putNumChild(staticPropertyCount)
if d.isExpanded():
# FIXME: Make this global. Don't leak.
variant = "'%sQVariant'" % ns
# Avoid malloc symbol clash with QVector
gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)"
% (variant, variant))
gdb.execute("set $d.d.is_shared = 0")
with Children(d):
# Dynamic properties.
if dynamicPropertyCount != 0:
dummyType = d.voidPtrType().pointer()
namesType = d.lookupType(ns + "QByteArray")
valuesBegin = values["d"]["begin"]
valuesEnd = values["d"]["end"]
valuesArray = values["d"]["array"]
valuesType = d.lookupType(ns + "QVariant")
p = namesArray.cast(dummyType) + namesBegin
q = valuesArray.cast(dummyType) + valuesBegin
for i in xrange(dynamicPropertyCount):
with SubItem(d, i):
pp = p.cast(namesType.pointer()).dereference();
d.putField("key", d.encodeByteArray(pp))
d.putField("keyencoded", Hex2EncodedLatin1)
qq = q.cast(valuesType.pointer().pointer())
qq = qq.dereference();
d.putField("addr", cleanAddress(qq))
d.putField("exp", "*(%s*)%s"
% (variant, cleanAddress(qq)))
t = qdump__QVariant(d, qq)
# Override the "QVariant (foo)" output.
d.putBetterType(t)
p += 1
q += 1
# Static properties.
propertyData = metaData[7]
for i in xrange(staticPropertyCount):
with NoAddress(d):
......@@ -1168,17 +1227,18 @@ def qdump__QObject(d, value):
propertyType = extractCString(metaStringData,
metaData[offset + 1])
with SubItem(d, propertyName):
#flags = metaData[offset + 2]
#warn("FLAGS: %s " % flags)
#warn("PROPERTY: %s %s " % (propertyType, propertyName))
flags = metaData[offset + 2]
warn("FLAGS: %s " % flags)
warn("PROPERTY: %s %s " % (propertyType, propertyName))
# #exp = '((\'%sQObject\'*)%s)->property("%s")' \
# % (ns, value.address, propertyName)
#exp = '"((\'%sQObject\'*)%s)"' %
#(ns, value.address,)
#warn("EXPRESSION: %s" % exp)
prop = d.call(value, "property",
str(cleanAddress(metaStringData + metaData[offset])))
value1 = prop["d"]
exp = '"((%sQObject*)%s)"' % (ns, value.address)
warn("EXPRESSION: %s" % exp)
warn("METAOBJECT: %s" % mo)
addr = cleanAddress(metaStringData + metaData[offset])
warn("ADDRESS: %s" % addr)
prop = d.call(value, "property", str(addr))
warn("PROP: %s" % prop)
#warn(" CODE: %s" % value1["type"])
# Type 1 and 2 are bool and int.
# Try to save a few cycles in this case:
......@@ -1217,6 +1277,64 @@ def qdump__QObject(d, value):
d.putValue("...")
d.putNumChild(0)
# Dynamic Properties.
with SubItem(d, "dynamics"):
# Prolog
extraData = d_ptr["extraData"] # Capitalization!
if d.isNull(extraData):
dynamicPropertyCount = 0
else:
extraDataType = d.lookupType(
ns + "QObjectPrivate::ExtraData").pointer()
extraData = extraData.cast(extraDataType)
ed = extraData.dereference()
names = ed["propertyNames"]
values = ed["propertyValues"]
#userData = ed["userData"]
namesBegin = names["d"]["begin"]
namesEnd = names["d"]["end"]
namesArray = names["d"]["array"]
dynamicPropertyCount = namesEnd - namesBegin
d.putNoType()
d.putItemCount(dynamicPropertyCount)
d.putNumChild(dynamicPropertyCount)
if d.isExpanded() and d.isGdb:
import gdb
# FIXME: Make this global. Don't leak.
variant = "'%sQVariant'" % ns
# Avoid malloc symbol clash with QVector
gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)"
% (variant, variant))
gdb.execute("set $d.d.is_shared = 0")
with Children(d):
dummyType = d.voidPtrType().pointer()
namesType = d.lookupType(ns + "QByteArray")
valuesBegin = values["d"]["begin"]
valuesEnd = values["d"]["end"]
valuesArray = values["d"]["array"]
valuesType = d.lookupType(ns + "QVariant")
p = namesArray.cast(dummyType) + namesBegin
q = valuesArray.cast(dummyType) + valuesBegin
for i in xrange(dynamicPropertyCount):
with SubItem(d, i):
pp = p.cast(namesType.pointer()).dereference();
d.putField("key", d.encodeByteArray(pp))
d.putField("keyencoded", Hex2EncodedLatin1)
qq = q.cast(valuesType.pointer().pointer())
qq = qq.dereference();
d.putField("addr", cleanAddress(qq))
d.putField("exp", "*(%s*)%s"
% (variant, cleanAddress(qq)))
t = qdump__QVariant(d, qq)
# Override the "QVariant (foo)" output.
d.putBetterType(t)
p += 1
q += 1
# Connections.
with SubItem(d, "connections"):
d.putNoType()
......@@ -1910,7 +2028,58 @@ qdumpHelper_QVariants_B = [
"QVariantHash",# 28
]
qdumpHelper_QVariants_C = [
def qdumpHelper_QVariant_31(d, data):
# QVariant::VoidStart
d.putBetterType("%sQVariant (void *)" % d.qtNamespace())
d.putValue("0x%x" % data["ptr"])
def qdumpHelper_QVariant_32(d, data):
# QVariant::Long
d.putBetterType("%sQVariant (long)" % d.qtNamespace())
d.putValue(toInteger(data["l"]))
def qdumpHelper_QVariant_33(d, data):
# QVariant::Short
d.putBetterType("%sQVariant (short)" % d.qtNamespace())
d.putValue(toInteger(data["s"]))
def qdumpHelper_QVariant_34(d, data):
# QVariant::Char
d.putBetterType("%sQVariant (char)" % d.qtNamespace())
d.putValue(toInteger(data["c"]))
def qdumpHelper_QVariant_35(d, data):
# QVariant::ULong
d.putBetterType("%sQVariant (unsigned long)" % d.qtNamespace())
d.putValue(toInteger(data["ul"]))
def qdumpHelper_QVariant_36(d, data):
# QVariant::UShort
d.putBetterType("%sQVariant (unsigned short)" % d.qtNamespace())
d.putValue(toInteger(data["us"]))
def qdumpHelper_QVariant_37(d, data):
# QVariant::UChar
d.putBetterType("%sQVariant (unsigned char)" % d.qtNamespace())
d.putValue(toInteger(data["uc"]))
def qdumpHelper_QVariant_38(d, data):
# QVariant::UChar
d.putBetterType("%sQVariant (float)" % d.qtNamespace())
d.putValue(toInteger(data["f"]))
qdumpHelper_QVariants_D = [
qdumpHelper_QVariant_31,
qdumpHelper_QVariant_32,
qdumpHelper_QVariant_33,
qdumpHelper_QVariant_34,
qdumpHelper_QVariant_35,
qdumpHelper_QVariant_36,
qdumpHelper_QVariant_37,
qdumpHelper_QVariant_38
]
qdumpHelper_QVariants_E = [
"QFont", # 64
"QPixmap", # 65
"QBrush", # 66
......@@ -1947,6 +2116,11 @@ def qdumpHelper__QVariant(d, value):
d.putNumChild(0)
return (None, None, True)
if variantType >= 31 and variantType <= 38:
qdumpHelper_QVariants_D[variantType - 31](d, data)
d.putNumChild(0)
return (None, None, True)
# Unknown user type.
if variantType > 86:
return (None, "", False)
......@@ -1955,7 +2129,7 @@ def qdumpHelper__QVariant(d, value):
if variantType <= 28:
innert = qdumpHelper_QVariants_B[variantType - 7]
else:
innert = qdumpHelper_QVariants_C[variantType - 64]
innert = qdumpHelper_QVariants_E[variantType - 64]
inner = d.qtNamespace() + innert
......@@ -1966,6 +2140,9 @@ def qdumpHelper__QVariant(d, value):
if innerType.sizeof > sizePD or isSpecial:
val = data["ptr"].cast(innerType.pointer().pointer()).dereference().dereference()
else:
# This can break for returned values
#warn("DATA: %s" % data)
#warn("DATA ADDRESS: %s" % d.addressOf(data))
val = data.cast(innerType)