aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/debugger')
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp44
-rw-r--r--src/plugins/debugger/cdb/cdbengine.h2
-rw-r--r--src/plugins/debugger/debuggerconstants.h3
-rw-r--r--src/plugins/debugger/debuggerengine.cpp4
-rw-r--r--src/plugins/debugger/debuggerengine.h1
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp93
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h3
-rw-r--r--src/plugins/debugger/stackframe.cpp38
-rw-r--r--src/plugins/debugger/stackframe.h7
-rw-r--r--src/plugins/debugger/stackhandler.cpp14
-rw-r--r--src/plugins/debugger/stackhandler.h1
-rw-r--r--src/plugins/debugger/stackwindow.cpp6
12 files changed, 211 insertions, 5 deletions
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index fa52f971bb2..893a168f6c3 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -1109,7 +1109,8 @@ bool CdbEngine::hasCapability(unsigned cap) const
|CreateFullBacktraceCapability
|OperateByInstructionCapability
|RunToLineCapability
- |MemoryAddressCapability);
+ |MemoryAddressCapability
+ |AdditionalQmlStackCapability);
}
void CdbEngine::executeStep()
@@ -1471,6 +1472,11 @@ void CdbEngine::activateFrame(int index)
}
const StackFrame frame = frames.at(index);
+ if (frame.language != CppLanguage) {
+ gotoLocation(frame);
+ return;
+ }
+
if (debug || debugLocals)
qDebug("activateFrame idx=%d '%s' %d", index,
qPrintable(frame.file), frame.line);
@@ -2933,6 +2939,9 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
frame.file = QFile::decodeName(fullName.data());
frame.line = frameMi["line"].data().toInt();
frame.usable = false; // To be decided after source path mapping.
+ const GdbMi languageMi = frameMi["language"];
+ if (languageMi.isValid() && languageMi.data() == "js")
+ frame.language = QmlLanguage;
}
frame.function = QLatin1String(frameMi["func"].data());
frame.from = QLatin1String(frameMi["from"].data());
@@ -2987,6 +2996,39 @@ unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
return 0;
}
+void CdbEngine::loadAdditionalQmlStack()
+{
+ postExtensionCommand("qmlstack", QByteArray(), 0, &CdbEngine::handleAdditionalQmlStack);
+}
+
+void CdbEngine::handleAdditionalQmlStack(const CdbExtensionCommandPtr &reply)
+{
+ QString errorMessage;
+ do {
+ if (!reply->success) {
+ errorMessage = QLatin1String(reply->errorMessage);
+ break;
+ }
+ GdbMi stackGdbMi;
+ stackGdbMi.fromString(reply->reply);
+ if (!stackGdbMi.isValid()) {
+ errorMessage = QLatin1String("GDBMI parser error");
+ break;
+ }
+ StackFrames qmlFrames = parseFrames(stackGdbMi);
+ const int qmlFrameCount = qmlFrames.size();
+ if (!qmlFrameCount) {
+ errorMessage = QLatin1String("Empty stack");
+ break;
+ }
+ for (int i = 0; i < qmlFrameCount; ++i)
+ qmlFrames[i].fixQmlFrame(startParameters());
+ stackHandler()->prependFrames(qmlFrames);
+ } while (false);
+ if (!errorMessage.isEmpty())
+ showMessage(QLatin1String("Unable to obtain QML stack trace: ") + errorMessage, LogError);
+}
+
void CdbEngine::mergeStartParametersSourcePathMap()
{
const DebuggerStartParameters &sp = startParameters();
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index 6bca2573904..63f87dd236d 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -124,6 +124,7 @@ public:
virtual void reloadRegisters();
virtual void reloadSourceFiles();
virtual void reloadFullStack();
+ void loadAdditionalQmlStack();
static QString extensionLibraryName(bool is64Bit);
@@ -241,6 +242,7 @@ private:
void handleWidgetAt(const CdbExtensionCommandPtr &);
void handleBreakPoints(const CdbExtensionCommandPtr &);
void handleBreakPoints(const GdbMi &value);
+ void handleAdditionalQmlStack(const CdbExtensionCommandPtr &);
NormalizedSourceFileName sourceMapNormalizeFileNameFromDebugger(const QString &f);
void updateLocalVariable(const QByteArray &iname);
void updateLocals(bool forNewStackFrame = false);
diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h
index 8a8e9d3d25b..cd3eb25159e 100644
--- a/src/plugins/debugger/debuggerconstants.h
+++ b/src/plugins/debugger/debuggerconstants.h
@@ -161,7 +161,8 @@ enum DebuggerCapabilities
RunToLineCapability = 0x800000,
MemoryAddressCapability = 0x1000000,
ShowModuleSectionsCapability = 0x200000,
- WatchComplexExpressionsCapability = 0x400000 // Used to filter out challenges for cdb.
+ WatchComplexExpressionsCapability = 0x400000, // Used to filter out challenges for cdb.
+ AdditionalQmlStackCapability = 0x800000 // C++ debugger engine is able to retrieve QML stack as well.
};
enum LogChannel
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index 7d060f4c73a..7c0283032ac 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -1402,6 +1402,10 @@ void DebuggerEngine::reloadFullStack()
{
}
+void DebuggerEngine::loadAdditionalQmlStack()
+{
+}
+
void DebuggerEngine::reloadDebuggingHelpers()
{
}
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index 539d85a8d2a..d9a3aa51d04 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -184,6 +184,7 @@ public:
virtual void reloadRegisters();
virtual void reloadSourceFiles();
virtual void reloadFullStack();
+ virtual void loadAdditionalQmlStack();
virtual void reloadDebuggingHelpers();
virtual void setRegisterValue(int regnr, const QString &value);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index b828ceaac0e..a1da12ab81c 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -2020,7 +2020,8 @@ bool GdbEngine::hasCapability(unsigned cap) const
| OperateByInstructionCapability
| RunToLineCapability
| WatchComplexExpressionsCapability
- | MemoryAddressCapability))
+ | MemoryAddressCapability
+ | AdditionalQmlStackCapability))
return true;
if (startParameters().startMode == AttachCore)
@@ -3207,6 +3208,90 @@ void GdbEngine::reloadFullStack()
QVariant::fromValue<StackCookie>(StackCookie(true, true)));
}
+void GdbEngine::loadAdditionalQmlStack()
+{
+ // Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
+ postCommand("-stack-list-arguments --simple-values", NeedsStop, CB(handleQmlStackFrameArguments));
+}
+
+// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
+static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, const QByteArray &qtNamespace)
+{
+ const GdbMi frameList = stackArgsResponse.childAt(0);
+ if (!frameList.childCount())
+ return 0;
+ QByteArray jsExecutionContextType = qtNamespace;
+ if (!jsExecutionContextType.isEmpty())
+ jsExecutionContextType.append("::");
+ jsExecutionContextType.append("QV4::ExecutionContext *");
+ foreach (const GdbMi &frameNode, frameList.children()) {
+ foreach (const GdbMi &argNode, frameNode["args"].children()) {
+ if (argNode["type"].data() == jsExecutionContextType) {
+ bool ok;
+ const quint64 address = argNode["value"].data().toULongLong(&ok, 16);
+ if (ok && address)
+ return address;
+ }
+ }
+ }
+ return 0;
+}
+
+static QString msgCannotLoadQmlStack(const QString &why)
+{
+ return _("Unable to load QML stack: ") + why;
+}
+
+void GdbEngine::handleQmlStackFrameArguments(const GdbResponse &response)
+{
+ if (!response.data.isValid()) {
+ 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.
+ QByteArray command = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x";
+ command += QByteArray::number(contextAddress, 16);
+ command += ")\"";
+ postCommand(command, CB(handleQmlStackTrace));
+}
+
+void GdbEngine::handleQmlStackTrace(const GdbResponse &response)
+{
+ if (!response.data.isValid()) {
+ showMessage(msgCannotLoadQmlStack(_("No result obtained.")), LogError);
+ return;
+ }
+ // Prepend QML stack frames to existing C++ stack frames.
+ QByteArray 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) {
+ StackFrame frame = parseStackFrame(stackMi.childAt(i), i);
+ frame.fixQmlFrame(startParameters());
+ qmlFrames.append(frame);
+ }
+ stackHandler()->prependFrames(qmlFrames);
+}
+
void GdbEngine::reloadStack(bool forceGotoLocation)
{
PENDING_DEBUG("RELOAD STACK");
@@ -3233,6 +3318,8 @@ StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
frame.line = frameMi["line"].toInt();
frame.address = frameMi["addr"].toAddress();
frame.usable = QFileInfo(frame.file).isReadable();
+ if (frameMi["language"].data() == "js")
+ frame.language = QmlLanguage;
return frame;
}
@@ -3308,6 +3395,10 @@ void GdbEngine::activateFrame(int frameIndex)
QTC_ASSERT(frameIndex < handler->stackSize(), return);
+ if (handler->frameAt(frameIndex).language == QmlLanguage) {
+ gotoLocation(handler->frameAt(frameIndex));
+ return;
+ }
// Assuming the command always succeeds this saves a roundtrip.
// Otherwise the lines below would need to get triggered
// after a response to this -stack-select-frame here.
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index bc8c46a6dfb..6941e6a9243 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -409,6 +409,9 @@ protected:
void handleThreadNames(const GdbResponse &response);
Q_SLOT void reloadStack(bool forceGotoLocation);
Q_SLOT virtual void reloadFullStack();
+ virtual void loadAdditionalQmlStack();
+ void handleQmlStackFrameArguments(const GdbResponse &response);
+ void handleQmlStackTrace(const GdbResponse &response);
int currentFrame() const;
QList<GdbMi> m_currentFunctionArgs;
diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp
index 936861ec0b4..deda27431b8 100644
--- a/src/plugins/debugger/stackframe.cpp
+++ b/src/plugins/debugger/stackframe.cpp
@@ -28,10 +28,13 @@
****************************************************************************/
#include "stackframe.h"
+#include "debuggerstartparameters.h"
+
#include "watchutils.h"
#include <QDebug>
#include <QDir>
+#include <QFileInfo>
#include <utils/hostosinfo.h>
@@ -45,7 +48,7 @@ namespace Internal {
////////////////////////////////////////////////////////////////////////
StackFrame::StackFrame()
- : level(-1), line(-1), address(0), usable(false)
+ : language(CppLanguage), level(-1), line(-1), address(0), usable(false)
{}
void StackFrame::clear()
@@ -90,7 +93,9 @@ QString StackFrame::toToolTip() const
str << "<tr><td>" << tr("Address:") << "</td><td>"
<< formatToolTipAddress(address) << "</td></tr>";
if (!function.isEmpty())
- str << "<tr><td>" << tr("Function:") << "</td><td>" << function << "</td></tr>";
+ str << "<tr><td>"
+ << (language == CppLanguage ? tr("Function:") : tr("JS-Function:"))
+ << "</td><td>" << function << "</td></tr>";
if (!file.isEmpty())
str << "<tr><td>" << tr("File:") << "</td><td>" << filePath << "</td></tr>";
if (line != -1)
@@ -127,6 +132,35 @@ QString StackFrame::toToolTip() const
return res;
}
+// Try to resolve files of a QML stack (resource files).
+void StackFrame::fixQmlFrame(const DebuggerStartParameters &sp)
+{
+ if (language != QmlLanguage)
+ return;
+ QFileInfo aFi(file);
+ if (aFi.isAbsolute()) {
+ usable = aFi.isFile();
+ return;
+ }
+ if (!file.startsWith(QLatin1String("qrc:/")))
+ return;
+ const QString relativeFile = file.right(file.size() - 5);
+ if (!sp.projectSourceDirectory.isEmpty()) {
+ const QFileInfo pFi(sp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
+ if (pFi.isFile()) {
+ file = pFi.absoluteFilePath();
+ usable = true;
+ return;
+ }
+ const QFileInfo cFi(QDir::currentPath() + QLatin1Char('/') + relativeFile);
+ if (cFi.isFile()) {
+ file = cFi.absoluteFilePath();
+ usable = true;
+ return;
+ }
+ }
+}
+
QDebug operator<<(QDebug d, const StackFrame &f)
{
QString res;
diff --git a/src/plugins/debugger/stackframe.h b/src/plugins/debugger/stackframe.h
index 8de0057f4cd..d62bb4022e2 100644
--- a/src/plugins/debugger/stackframe.h
+++ b/src/plugins/debugger/stackframe.h
@@ -30,6 +30,8 @@
#ifndef DEBUGGER_STACKFRAME_H
#define DEBUGGER_STACKFRAME_H
+#include "debuggerconstants.h"
+
#include <QCoreApplication>
#include <QMetaType>
@@ -38,6 +40,9 @@ class QDebug;
QT_END_NAMESPACE
namespace Debugger {
+
+class DebuggerStartParameters;
+
namespace Internal {
class StackFrame
@@ -48,8 +53,10 @@ public:
bool isUsable() const;
QString toToolTip() const;
QString toString() const;
+ void fixQmlFrame(const DebuggerStartParameters &sp);
public:
+ DebuggerLanguage language;
qint32 level;
QString function;
QString file; // We try to put an absolute file name in there.
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
index bcc57619c92..6144d2919b9 100644
--- a/src/plugins/debugger/stackhandler.cpp
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -205,6 +205,20 @@ void StackHandler::setFrames(const StackFrames &frames, bool canExpand)
emit stackChanged();
}
+void StackHandler::prependFrames(const StackFrames &frames)
+{
+ if (frames.isEmpty())
+ return;
+ const int count = frames.size();
+ beginInsertRows(QModelIndex(), 0, count - 1);
+ for (int i = count - 1; i >= 0; --i)
+ m_stackFrames.prepend(frames.at(i));
+ endInsertRows();
+ if (m_currentIndex >= 0)
+ setCurrentIndex(m_currentIndex + count);
+ emit stackChanged();
+}
+
const StackFrames &StackHandler::frames() const
{
return m_stackFrames;
diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h
index 31910ebeb57..b896b5c4003 100644
--- a/src/plugins/debugger/stackhandler.h
+++ b/src/plugins/debugger/stackhandler.h
@@ -67,6 +67,7 @@ public:
~StackHandler();
void setFrames(const StackFrames &frames, bool canExpand = false);
+ void prependFrames(const StackFrames &frames);
const StackFrames &frames() const;
void setCurrentIndex(int index);
int currentIndex() const { return m_currentIndex; }
diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp
index b86178b1250..abe5c31ebe7 100644
--- a/src/plugins/debugger/stackwindow.cpp
+++ b/src/plugins/debugger/stackwindow.cpp
@@ -168,6 +168,10 @@ void StackTreeView::contextMenuEvent(QContextMenuEvent *ev)
if (engine->hasCapability(CreateFullBacktraceCapability))
menu.addAction(debuggerCore()->action(CreateFullBacktrace));
+ QAction *additionalQmlStackAction = 0;
+ if (engine->hasCapability(AdditionalQmlStackCapability))
+ additionalQmlStackAction = menu.addAction(tr("Load QML stack"));
+
QAction *actShowMemory = 0;
if (engine->hasCapability(ShowMemoryCapability)) {
actShowMemory = menu.addAction(QString());
@@ -242,6 +246,8 @@ void StackTreeView::contextMenuEvent(QContextMenuEvent *ev)
engine->loadSymbolsForStack();
else if (act == actSaveTaskFile)
saveTaskFile(this, handler);
+ else if (act == additionalQmlStackAction)
+ engine->loadAdditionalQmlStack();
else
handleBaseContextAction(act);
}