aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@digia.com>2014-02-07 18:10:02 +0100
committerhjk <hjk121@nokiamail.com>2014-02-25 16:48:06 +0100
commite64fefdbc466b1247a2cee68617ad904d2388712 (patch)
tree680e4d22a99cf6244904edf3f440d8e0d549f319 /src/plugins
parent27ae878040153904826a3b99459967e209612cde (diff)
Add a stack window menu entry to display QML stack frame.
Add language field to stack frame. Add virtual for loading QML stack invoked by stack window context menu, implement for CDB, GDB. Task-number: QTCREATORBUG-11144 Change-Id: Ic39be3978b40d96ed18cb69a8355296ec572ece7 Reviewed-by: hjk <hjk121@nokiamail.com>
Diffstat (limited to 'src/plugins')
-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);
}