forked from qt-creator/qt-creator
Debugger: Move QML stack access to DumperBase
... and make it work with LLDB. Change-Id: Idaec029942dbcc726931781caff830173f7b126a Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
@@ -1784,6 +1784,53 @@ class DumperBase:
|
||||
def isReportableQmlFrame(self, functionName):
|
||||
return functionName and functionName.find("QV4::Moth::VME::exec") >= 0
|
||||
|
||||
def extractQmlData(self, value):
|
||||
if value.type.code == PointerCode:
|
||||
value = value.dereference()
|
||||
data = value["data"]
|
||||
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
|
||||
|
||||
def extractQmlRuntimeString(self, compilationUnitPtr, index):
|
||||
# This mimics compilationUnit->runtimeStrings[index]
|
||||
# typeof runtimeStrings = QV4.StringValue **
|
||||
runtimeStrings = compilationUnitPtr.dereference()["runtimeStrings"]
|
||||
entry = runtimeStrings[index]
|
||||
text = self.extractPointer(entry.dereference(), self.ptrSize())
|
||||
(elided, fn) = self.encodeStringHelper(text, 100)
|
||||
return self.encodedUtf16ToUtf8(fn)
|
||||
|
||||
def extractQmlLocation(self, engine):
|
||||
if self.currentCallContext is None:
|
||||
context = engine["current"] # QV4.ExecutionContext * or derived
|
||||
self.currentCallContext = context
|
||||
else:
|
||||
context = self.currentCallContext["parent"]
|
||||
ctxCode = int(context["type"])
|
||||
compilationUnit = context["compilationUnit"]
|
||||
functionName = "Unknown JS";
|
||||
ns = self.qtNamespace()
|
||||
|
||||
# QV4.ExecutionContext.Type_SimpleCallContext - 4
|
||||
# QV4.ExecutionContext.Type_CallContext - 5
|
||||
if ctxCode == 4 or ctxCode == 5:
|
||||
callContextDataType = self.lookupQtType("QV4::Heap::CallContext")
|
||||
callContext = context.cast(callContextDataType.pointer())
|
||||
functionObject = callContext["function"]
|
||||
function = functionObject["function"]
|
||||
# QV4.CompiledData.Function
|
||||
compiledFunction = function["compiledFunction"].dereference()
|
||||
index = int(compiledFunction["nameIndex"])
|
||||
functionName = "JS: " + self.extractQmlRuntimeString(compilationUnit, index)
|
||||
|
||||
string = self.parseAndEvaluate("((%s)0x%x)->fileName()"
|
||||
% (compilationUnit.type, compilationUnit))
|
||||
fileName = self.encodeStringUtf8(string)
|
||||
|
||||
return {'functionName': functionName,
|
||||
'lineNumber': int(context["lineNumber"]),
|
||||
'fileName': fileName,
|
||||
'context': context }
|
||||
|
||||
|
||||
# Some "Enums"
|
||||
|
||||
|
||||
@@ -1706,52 +1706,6 @@ class Dumper(DumperBase):
|
||||
self.typesToReport[typestring] = typeobj
|
||||
return typeobj
|
||||
|
||||
def extractQmlData(self, value):
|
||||
if value.type.code == PointerCode:
|
||||
value = value.dereference()
|
||||
data = value["data"]
|
||||
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
|
||||
|
||||
def extractQmlRuntimeString(self, compilationUnitPtr, index):
|
||||
# This mimics compilationUnit->runtimeStrings[index]
|
||||
runtimeStrings = compilationUnitPtr.dereference()["runtimeStrings"] # QV4.StringValue *
|
||||
entry = (runtimeStrings + index) # QV4.StringValue *
|
||||
text = entry["text"]
|
||||
(elided, fn) = self.encodeStringHelper(toInteger(text), 100)
|
||||
return self.encodedUtf16ToUtf8(fn) # string
|
||||
|
||||
def putQmlLocation(self, level, frame, sal):
|
||||
engine = frame.read_var("engine") # QV4.ExecutionEngine *
|
||||
if self.currentCallContext is None:
|
||||
context = engine["current"] # QV4.ExecutionContext * or derived
|
||||
self.currentCallContext = context
|
||||
else:
|
||||
context = self.currentCallContext["parent"]
|
||||
ctxCode = int(context["type"])
|
||||
compilationUnit = context["compilationUnit"] # QV4.CompiledData.CompilationUnit *
|
||||
functionName = "### JS ###";
|
||||
ns = self.qtNamespace()
|
||||
|
||||
# QV4.ExecutionContext.Type_SimpleCallContext - 4
|
||||
# QV4.ExecutionContext.Type_CallContext - 5
|
||||
if ctxCode == 4 or ctxCode == 5:
|
||||
callContextDataType = self.lookupQtType("QV4::Heap::CallContext")
|
||||
callContext = context.cast(callContextDataType.pointer())
|
||||
functionObject = callContext["function"]
|
||||
function = functionObject["function"]
|
||||
compiledFunction = function["compiledFunction"].dereference() # QV4.CompiledData.Function
|
||||
index = int(compiledFunction["nameIndex"])
|
||||
functionName = "### JS ### " + self.extractQmlRuntimeString(compilationUnit, index)
|
||||
|
||||
string = gdb.parse_and_eval("((%s)0x%x)->fileName()"
|
||||
% (compilationUnit.type, compilationUnit))
|
||||
fileName = self.encodeStringUtf8(string)
|
||||
|
||||
lineNumber = int(context["lineNumber"])
|
||||
self.put(('frame={level="%s",func="%s",file="%s",'
|
||||
'fullname="%s",line="%s",language="js",addr="0x%x"}')
|
||||
% (level, functionName, fileName, fileName, lineNumber, context))
|
||||
|
||||
def stackListFrames(self, n, options):
|
||||
self.prepare("options:" + options + ",pe")
|
||||
self.output = []
|
||||
@@ -1779,7 +1733,13 @@ class Dumper(DumperBase):
|
||||
|
||||
if self.nativeMixed:
|
||||
if self.isReportableQmlFrame(functionName):
|
||||
self.putQmlLocation(i, frame, sal)
|
||||
engine = frame.read_var("engine")
|
||||
h = self.extractQmlLocation(engine)
|
||||
self.put(('frame={level="%s",func="%s",file="%s",'
|
||||
'fullname="%s",line="%s",language="js",addr="0x%x"}')
|
||||
% (i, h['functionName'], h['fileName'], h['fileName'],
|
||||
h['lineNumber'], h['context']))
|
||||
|
||||
i += 1
|
||||
frame = frame.older()
|
||||
continue
|
||||
|
||||
@@ -90,7 +90,7 @@ Value = lldb.SBValue
|
||||
|
||||
def impl_SBValue__add__(self, offset):
|
||||
if self.GetType().IsPointerType():
|
||||
if isinstance(offset, int) or isinstance(offset, long):
|
||||
if isinstance(offset, int):
|
||||
pass
|
||||
else:
|
||||
offset = offset.GetValueAsSigned()
|
||||
@@ -138,7 +138,7 @@ def impl_SBValue__long__(self):
|
||||
return int(self.GetValue(), 0)
|
||||
|
||||
def impl_SBValue__getitem__(value, index):
|
||||
if isinstance(index, int):
|
||||
if isinstance(index, int) or isinstance(index, long):
|
||||
type = value.GetType()
|
||||
if type.IsPointerType():
|
||||
innertype = value.Dereference().GetType()
|
||||
@@ -846,6 +846,7 @@ class Dumper(DumperBase):
|
||||
|
||||
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
|
||||
|
||||
self.currentCallContext = None
|
||||
result = 'stack={current-thread="%s"' % thread.GetThreadID()
|
||||
result += ',frames=['
|
||||
for i in xrange(n):
|
||||
@@ -867,9 +868,15 @@ class Dumper(DumperBase):
|
||||
|
||||
if self.nativeMixed:
|
||||
if self.isReportableQmlFrame(functionName):
|
||||
#self.putQmlLocation(i, frame, sal)
|
||||
functionName = "### JS ###";
|
||||
language = "js"
|
||||
engine = frame.FindVariable("engine")
|
||||
self.context = engine
|
||||
h = self.extractQmlLocation(engine)
|
||||
pc = 0
|
||||
functionName = h['functionName']
|
||||
fullname = h['fileName']
|
||||
lineNumber = h['lineNumber']
|
||||
addr = h['context']
|
||||
language = 'js'
|
||||
|
||||
elif not functionName is None:
|
||||
if functionName.startswith("qt_v4"):
|
||||
@@ -1669,6 +1676,9 @@ class Dumper(DumperBase):
|
||||
instructions = function.GetInstructions(self.target)
|
||||
else:
|
||||
base = args.get('address', 0)
|
||||
if int(base) == 0xffffffffffffffff:
|
||||
warn("INVALID DISASSEMBLER BASE")
|
||||
return
|
||||
addr = lldb.SBAddress(base, self.target)
|
||||
instructions = self.target.ReadInstructions(addr, 100)
|
||||
|
||||
|
||||
@@ -1000,6 +1000,12 @@ void LldbEngine::refreshStack(const GdbMi &stack)
|
||||
frame.usable = usable.data().toInt();
|
||||
else
|
||||
frame.usable = QFileInfo(frame.file).isReadable();
|
||||
if (item["language"].data() == "js"
|
||||
|| frame.file.endsWith(QLatin1String(".js"))
|
||||
|| frame.file.endsWith(QLatin1String(".qml"))) {
|
||||
frame.language = QmlLanguage;
|
||||
frame.fixQmlFrame(startParameters());
|
||||
}
|
||||
frames.append(frame);
|
||||
}
|
||||
bool canExpand = stack["hasmore"].toInt();
|
||||
|
||||
Reference in New Issue
Block a user