forked from qt-creator/qt-creator
Debugger: Fix QML extra stack loading for GDB
Change-Id: I702bb8f183e93fe25857115225fa55607ffb1674 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -998,9 +998,10 @@ class Dumper(DumperBase):
|
|||||||
gdb.execute('continue')
|
gdb.execute('continue')
|
||||||
|
|
||||||
def fetchStack(self, args):
|
def fetchStack(self, args):
|
||||||
def fromNativePath(str):
|
def fromNativePath(string):
|
||||||
return str.replace('\\', '/')
|
return string.replace('\\', '/')
|
||||||
|
|
||||||
|
extraQml = int(args.get('extraqml', '0'))
|
||||||
limit = int(args['limit'])
|
limit = int(args['limit'])
|
||||||
if limit <= 0:
|
if limit <= 0:
|
||||||
limit = 10000
|
limit = 10000
|
||||||
@@ -1008,8 +1009,42 @@ class Dumper(DumperBase):
|
|||||||
self.prepare(args)
|
self.prepare(args)
|
||||||
self.output = []
|
self.output = []
|
||||||
|
|
||||||
frame = gdb.newest_frame()
|
|
||||||
i = 0
|
i = 0
|
||||||
|
if extraQml:
|
||||||
|
frame = gdb.newest_frame()
|
||||||
|
ns = self.qtNamespace()
|
||||||
|
needle = self.qtNamespace() + 'QV4::ExecutionEngine'
|
||||||
|
pat = "%sqt_v4StackTrace(((%sQV4::ExecutionEngine *)0x%x)->currentContext)"
|
||||||
|
done = False
|
||||||
|
while i < limit and frame and not done:
|
||||||
|
block = None
|
||||||
|
try:
|
||||||
|
block = frame.block()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if block is not None:
|
||||||
|
for symbol in block:
|
||||||
|
if symbol.is_variable or symbol.is_argument:
|
||||||
|
value = symbol.value(frame)
|
||||||
|
typeobj = value.type
|
||||||
|
if typeobj.code == gdb.TYPE_CODE_PTR:
|
||||||
|
dereftype = typeobj.target().unqualified()
|
||||||
|
if dereftype.name == needle:
|
||||||
|
addr = toInteger(value)
|
||||||
|
expr = pat % (ns, ns, addr)
|
||||||
|
res = str(gdb.parse_and_eval(expr))
|
||||||
|
pos = res.find('"stack=[')
|
||||||
|
if pos != -1:
|
||||||
|
res = res[pos + 8:-2]
|
||||||
|
res = res.replace('\\\"', '\"')
|
||||||
|
res = res.replace('func=', 'function=')
|
||||||
|
self.put(res)
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
frame = frame.older()
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
frame = gdb.newest_frame()
|
||||||
self.currentCallContext = None
|
self.currentCallContext = None
|
||||||
while i < limit and frame:
|
while i < limit and frame:
|
||||||
with OutputSafer(self):
|
with OutputSafer(self):
|
||||||
|
@@ -249,9 +249,12 @@ void GdbMi::parseList(const QChar *&from, const QChar *to)
|
|||||||
}
|
}
|
||||||
GdbMi child;
|
GdbMi child;
|
||||||
child.parseResultOrValue(from, to);
|
child.parseResultOrValue(from, to);
|
||||||
if (child.isValid())
|
if (child.isValid()) {
|
||||||
m_children.push_back(child);
|
m_children.push_back(child);
|
||||||
skipCommas(from, to);
|
skipCommas(from, to);
|
||||||
|
} else {
|
||||||
|
++from;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3232,56 +3232,11 @@ static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, con
|
|||||||
|
|
||||||
void GdbEngine::loadAdditionalQmlStack()
|
void GdbEngine::loadAdditionalQmlStack()
|
||||||
{
|
{
|
||||||
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
|
DebuggerCommand cmd = stackCommand(-1);
|
||||||
DebuggerCommand cmd("-stack-list-arguments --simple-values", NeedsStop);
|
cmd.arg("extraqml", "1");
|
||||||
cmd.callback = [this](const DebuggerResponse &response) {
|
cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, true); };
|
||||||
if (!response.data.isValid()) {
|
cmd.flags = Discardable | PythonCommand;
|
||||||
showMessage(msgCannotLoadQmlStack("No stack obtained."), LogError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const quint64 contextAddress = findJsExecutionContextAddress(response.data, qtNamespace());
|
|
||||||
if (!contextAddress) {
|
|
||||||
showMessage(msgCannotLoadQmlStack("The address of the JS execution context could not be found."), LogError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Call the debug function of QML with the context address to obtain the QML stack trace.
|
|
||||||
DebuggerCommand cmd;
|
|
||||||
cmd.function = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x"
|
|
||||||
+ QString::number(contextAddress, 16) + ")\"";
|
|
||||||
cmd.callback = CB(handleQmlStackTrace);
|
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
};
|
|
||||||
runCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
|
|
||||||
void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
|
|
||||||
{
|
|
||||||
if (!response.data.isValid()) {
|
|
||||||
showMessage(msgCannotLoadQmlStack("No result obtained."), LogError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Prepend QML stack frames to existing C++ stack frames.
|
|
||||||
QString stackData = response.data["value"].data();
|
|
||||||
const int index = stackData.indexOf("stack=");
|
|
||||||
if (index == -1) {
|
|
||||||
showMessage(msgCannotLoadQmlStack("Malformed result."), LogError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stackData.remove(0, index);
|
|
||||||
stackData.replace("\\\"", "\"");
|
|
||||||
GdbMi stackMi;
|
|
||||||
stackMi.fromString(stackData);
|
|
||||||
const int qmlFrameCount = stackMi.childCount();
|
|
||||||
if (!qmlFrameCount) {
|
|
||||||
showMessage(msgCannotLoadQmlStack("No stack frames obtained."), LogError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QList<StackFrame> qmlFrames;
|
|
||||||
qmlFrames.reserve(qmlFrameCount);
|
|
||||||
for (int i = 0; i < qmlFrameCount; ++i)
|
|
||||||
qmlFrames.append(StackFrame::parseFrame(stackMi.childAt(i), runParameters()));
|
|
||||||
stackHandler()->prependFrames(qmlFrames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DebuggerCommand GdbEngine::stackCommand(int depth)
|
DebuggerCommand GdbEngine::stackCommand(int depth)
|
||||||
|
@@ -360,9 +360,8 @@ protected:
|
|||||||
void handleThreadNames(const DebuggerResponse &response);
|
void handleThreadNames(const DebuggerResponse &response);
|
||||||
DebuggerCommand stackCommand(int depth);
|
DebuggerCommand stackCommand(int depth);
|
||||||
void reloadStack();
|
void reloadStack();
|
||||||
virtual void reloadFullStack() override;
|
void reloadFullStack() override;
|
||||||
virtual void loadAdditionalQmlStack() override;
|
void loadAdditionalQmlStack() override;
|
||||||
void handleQmlStackTrace(const DebuggerResponse &response);
|
|
||||||
int currentFrame() const;
|
int currentFrame() const;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@@ -779,5 +779,11 @@ void QmlCppEngine::setActiveEngine(DebuggerEngine *engine)
|
|||||||
updateViews();
|
updateViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlCppEngine::loadAdditionalQmlStack()
|
||||||
|
{
|
||||||
|
if (m_cppEngine)
|
||||||
|
m_cppEngine->loadAdditionalQmlStack();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
@@ -123,6 +123,7 @@ protected:
|
|||||||
|
|
||||||
void notifyInferiorSetupOk() override;
|
void notifyInferiorSetupOk() override;
|
||||||
void notifyEngineRemoteServerRunning(const QString &, int pid) override;
|
void notifyEngineRemoteServerRunning(const QString &, int pid) override;
|
||||||
|
void loadAdditionalQmlStack() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void engineStateChanged(DebuggerState newState);
|
void engineStateChanged(DebuggerState newState);
|
||||||
|
Reference in New Issue
Block a user