Debugger: Enable QObject property display with LLDB

Finally.

Change-Id: I3257ffbb23ca2ea4eec9a97335a95580c9c4482b
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
hjk
2014-01-24 17:02:23 +01:00
parent 864e61d63f
commit cc3facd944
4 changed files with 26 additions and 32 deletions

View File

@@ -347,7 +347,7 @@ class DumperBase:
return base64.b16encode(s).decode("utf8") return base64.b16encode(s).decode("utf8")
#def toBlob(self, value): #def toBlob(self, value):
# return self.extractBlob(value.address, value.type.sizeof) # """Abstract"""
def isArmArchitecture(self): def isArmArchitecture(self):
return False return False
@@ -897,7 +897,8 @@ class DumperBase:
return True return True
except: except:
self.knownNonQObjectTypes.insert(str(value.type)) #warn("NO QOBJECT: %s" % value.type)
#self.knownNonQObjectTypes.add(str(value.type))
pass pass
@@ -921,10 +922,7 @@ class DumperBase:
# This is called is when a QObject derived class is expanded # This is called is when a QObject derived class is expanded
def putQObjectGuts(self, qobject): def putQObjectGuts(self, qobject, smo):
smo = self.childWithName(qobject, "staticMetaObject")
if smo is None:
return
with SubItem(self, "[properties]"): with SubItem(self, "[properties]"):
propertyNames = self.staticQObjectPropertyNames(smo) propertyNames = self.staticQObjectPropertyNames(smo)
propertyCount = len(propertyNames) propertyCount = len(propertyNames)

View File

@@ -1418,7 +1418,8 @@ class Dumper(DumperBase):
with Children(self, 1, childType=innerType): with Children(self, 1, childType=innerType):
self.putFields(value) self.putFields(value)
if isQObject: if isQObject:
self.putQObjectGuts(value) smo = value["staticMetaObject"]
self.putQObjectGuts(value, smo)
def putPlainChildren(self, value): def putPlainChildren(self, value):

View File

@@ -79,6 +79,7 @@ import lldb
qqWatchpointOffset = 10000 qqWatchpointOffset = 10000
lldb.theDumper = None
def warn(message): def warn(message):
print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'")) print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
@@ -182,16 +183,7 @@ def impl_SBValue__getitem__(value, index):
address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned
return value.CreateValueFromAddress(None, address, innertype) return value.CreateValueFromAddress(None, address, innertype)
return value.GetChildAtIndex(index) return value.GetChildAtIndex(index)
result = value.GetChildMemberWithName(index) return value.GetChildMemberWithName(index)
if int(result.GetLoadAddress()) == 0xffffffffffffffff:
options = lldb.SBExpressionOptions()
typeClass = result.GetType().GetTypeClass()
if typeClass != lldb.eTypeClassBuiltin:
i = value.GetIndexOfChildWithName(index)
field = value.GetType().GetFieldAtIndex(i)
addr = value.GetLoadAddress() + field.GetOffsetInBytes()
result = value.CreateValueFromAddress(result.GetName(), addr, result.GetType())
return result
def impl_SBValue__deref(value): def impl_SBValue__deref(value):
result = value.Dereference() result = value.Dereference()
@@ -263,6 +255,8 @@ class Dumper(DumperBase):
def __init__(self): def __init__(self):
DumperBase.__init__(self) DumperBase.__init__(self)
lldb.theDumper = self
self.debugger = lldb.SBDebugger.Create() self.debugger = lldb.SBDebugger.Create()
#self.debugger.SetLoggingCallback(loggingCallback) #self.debugger.SetLoggingCallback(loggingCallback)
#Same as: self.debugger.HandleCommand("log enable lldb dyld step") #Same as: self.debugger.HandleCommand("log enable lldb dyld step")
@@ -408,7 +402,7 @@ class Dumper(DumperBase):
type = value.type.name type = value.type.name
exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg) exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg)
#warn("CALL: %s" % exp) #warn("CALL: %s" % exp)
result = value.CreateValueFromExpression('$tmp', exp) result = value.CreateValueFromExpression('', exp)
#warn(" -> %s" % result) #warn(" -> %s" % result)
return result return result
@@ -857,6 +851,9 @@ class Dumper(DumperBase):
error = lldb.SBError() error = lldb.SBError()
return Blob(self.process.ReadMemory(base, size, error)) return Blob(self.process.ReadMemory(base, size, error))
def readCArray(self, base, size):
return self.extractBlob(base, size).toBytes()
def toBlob(self, value): def toBlob(self, value):
data = value.GetData() data = value.GetData()
size = int(data.GetByteSize()) size = int(data.GetByteSize())
@@ -868,15 +865,9 @@ class Dumper(DumperBase):
return Blob(bytes(buf)) return Blob(bytes(buf))
def isQObject(self, value): def isQObject(self, value):
try: needle = value.GetType().GetName() + "::staticMetaObject"
vtable = value.Cast(self.voidPtrType().GetPointerType()) value = self.target.FindFirstGlobalVariable(needle)
metaObjectEntry = vtable.Dereference() return value.IsValid()
addr = lldb.SBAddress(long(metaObjectEntry), self.target)
symbol = addr.GetSymbol()
name = symbol.GetMangledName()
return name.find("10metaObjectEv") > 0
except:
return False
def stripNamespaceFromType(self, typeName): def stripNamespaceFromType(self, typeName):
#type = stripClassTag(typeName) #type = stripClassTag(typeName)
@@ -898,7 +889,7 @@ class Dumper(DumperBase):
def putSubItem(self, component, value, tryDynamic=True): def putSubItem(self, component, value, tryDynamic=True):
if not value.IsValid(): if not value.IsValid():
warn("INVALID SUBITEM") warn("INVALID SUBITEM: %s" % value.GetName())
return return
with SubItem(self, component): with SubItem(self, component):
self.putItem(value, tryDynamic) self.putItem(value, tryDynamic)
@@ -1004,8 +995,10 @@ class Dumper(DumperBase):
#numchild = 1 if value.MightHaveChildren() else 0 #numchild = 1 if value.MightHaveChildren() else 0
numchild = value.GetNumChildren() numchild = value.GetNumChildren()
self.putType(typeName) self.putType(typeName)
isQObject = False
if typeClass == lldb.eTypeClassStruct or typeClass == lldb.eTypeClassClass: if typeClass == lldb.eTypeClassStruct or typeClass == lldb.eTypeClassClass:
if self.isQObject(value): if self.isQObject(value):
isQObject = True
self.context = value self.context = value
if not self.putQObjectNameValue(value): # Is this too expensive? if not self.putQObjectNameValue(value): # Is this too expensive?
self.putEmptyValue() self.putEmptyValue()
@@ -1023,6 +1016,10 @@ class Dumper(DumperBase):
if self.currentIName in self.expandedINames: if self.currentIName in self.expandedINames:
with Children(self): with Children(self):
self.putFields(value) self.putFields(value)
if isQObject:
needle = value.GetType().GetName() + "::staticMetaObject"
smo = self.target.FindFirstGlobalVariable(needle)
self.putQObjectGuts(value, smo)
def warn(self, msg): def warn(self, msg):
self.put('{name="%s",value="",type=""},' % msg) self.put('{name="%s",value="",type=""},' % msg)

View File

@@ -2228,7 +2228,6 @@ void tst_Dumpers::dumper_data()
QTest::newRow("QObject2") QTest::newRow("QObject2")
<< Data("#include <QWidget>\n" << Data("#include <QWidget>\n"
"#include <QApplication>\n" "#include <QApplication>\n"
"namespace Names {\n"
"namespace Bar {\n" "namespace Bar {\n"
" struct Ui { Ui() { w = 0; } QWidget *w; };\n" " struct Ui { Ui() { w = 0; } QWidget *w; };\n"
" class TestObject : public QObject\n" " class TestObject : public QObject\n"
@@ -2257,11 +2256,10 @@ void tst_Dumpers::dumper_data()
" QByteArray m_myProp2;\n" " QByteArray m_myProp2;\n"
" };\n" " };\n"
"} // namespace Bar\n" "} // namespace Bar\n"
"} // namespace Names\n"
"#include <main.moc>\n", "#include <main.moc>\n",
"" ""
"QApplication app(argc, argv);\n" "QApplication app(argc, argv);\n"
"Names::Bar::TestObject test;\n" "Bar::TestObject test;\n"
"test.setMyProp1(\"Hello\");\n" "test.setMyProp1(\"Hello\");\n"
"test.setMyProp2(\"World\");\n" "test.setMyProp2(\"World\");\n"
"QString s = test.myProp1();\n" "QString s = test.myProp1();\n"
@@ -2269,7 +2267,7 @@ void tst_Dumpers::dumper_data()
"unused(&app, &test, &s);\n") "unused(&app, &test, &s);\n")
% GuiProfile() % GuiProfile()
% Check("s", "\"HelloWorld\"", "@QString") % Check("s", "\"HelloWorld\"", "@QString")
% Check("test", "", "Names::Bar::TestObject") % Check("test", "", "Bar::TestObject")
% Check("test.[properties]", "<4 items>", "") % Check("test.[properties]", "<4 items>", "")
% Check("test.[properties].myProp1", "\"Hello\"", "@QVariant (QString)") % Check("test.[properties].myProp1", "\"Hello\"", "@QVariant (QString)")
% Check("test.[properties].myProp2", "\"World\"", "@QVariant (QByteArray)") % Check("test.[properties].myProp2", "\"World\"", "@QVariant (QByteArray)")