diff options
Diffstat (limited to 'src/plugins/debugger')
-rw-r--r-- | src/plugins/debugger/cdb/cdbengine.cpp | 44 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbengine.h | 2 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerconstants.h | 3 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerengine.cpp | 4 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerengine.h | 1 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 93 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.h | 3 | ||||
-rw-r--r-- | src/plugins/debugger/stackframe.cpp | 38 | ||||
-rw-r--r-- | src/plugins/debugger/stackframe.h | 7 | ||||
-rw-r--r-- | src/plugins/debugger/stackhandler.cpp | 14 | ||||
-rw-r--r-- | src/plugins/debugger/stackhandler.h | 1 | ||||
-rw-r--r-- | src/plugins/debugger/stackwindow.cpp | 6 |
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); } |