From d302fcc10f9805d6fa2868a2eac379ab091facf2 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Sat, 9 Mar 2024 23:08:23 +0800 Subject: [PATCH] DAP Protocol implementations. More work for seperating debugger interface and the gdb mi client --- RedPandaIDE/RedPandaIDE.pro | 2 + RedPandaIDE/debugger/dapdebugger.cpp | 88 ++++++++++++++++++++++++++ RedPandaIDE/debugger/dapdebugger.h | 47 ++++++++++++++ RedPandaIDE/debugger/dapprotocol.h | 3 + RedPandaIDE/debugger/debugger.cpp | 57 +++++------------ RedPandaIDE/debugger/debugger.h | 25 ++++---- RedPandaIDE/debugger/gdbmidebugger.cpp | 61 ++++++++++++++++++ RedPandaIDE/debugger/gdbmidebugger.h | 23 +++++++ 8 files changed, 252 insertions(+), 54 deletions(-) create mode 100644 RedPandaIDE/debugger/dapdebugger.cpp create mode 100644 RedPandaIDE/debugger/dapdebugger.h diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index aca74fd6..5b8e677a 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -130,6 +130,7 @@ SOURCES += \ debugger/gdbmidebugger.cpp \ debugger/gdbmiresultparser.cpp \ debugger/dapprotocol.cpp \ + debugger/dapdebugger.cpp \ cpprefacter.cpp \ parser/cppparser.cpp \ parser/cpppreprocessor.cpp \ @@ -261,6 +262,7 @@ HEADERS += \ debugger/gdbmidebugger.h \ debugger/gdbmiresultparser.h \ debugger/dapprotocol.h \ + debugger/dapdebugger.h \ cpprefacter.h \ customfileiconprovider.h \ parser/cppparser.h \ diff --git a/RedPandaIDE/debugger/dapdebugger.cpp b/RedPandaIDE/debugger/dapdebugger.cpp new file mode 100644 index 00000000..95711491 --- /dev/null +++ b/RedPandaIDE/debugger/dapdebugger.cpp @@ -0,0 +1,88 @@ +#include "dapdebugger.h" +#include "../utils.h" +#include "dapprotocol.h" +#include "../systemconsts.h" + +#include + +DAPDebuggerClient::DAPDebuggerClient(Debugger *debugger, QObject *parent): + DebuggerClient{debugger, parent} +{ + +} + +void DAPDebuggerClient::run() +{ + mStop = false; + bool errorOccured = false; + mInferiorRunning = false; + mProcessExited = false; + QString cmd = debuggerPath(); +// QString arguments = "--annotate=2"; + QStringList arguments{"--interpret=mi", "--silent"}; + QString workingDir = QFileInfo(debuggerPath()).path(); + + mProcess = std::make_shared(); + auto action = finally([&]{ + mProcess.reset(); + }); + mProcess->setProgram(cmd); + mProcess->setArguments(arguments); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString path = env.value("PATH"); + QStringList pathAdded = binDirs(); + if (!path.isEmpty()) { + path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path; + } else { + path = pathAdded.join(PATH_SEPARATOR); + } + QString cmdDir = extractFileDir(cmd); + if (!cmdDir.isEmpty()) { + path = cmdDir + PATH_SEPARATOR + path; + } + env.insert("PATH",path); + mProcess->setProcessEnvironment(env); + + mProcess->setWorkingDirectory(workingDir); + + connect(mProcess.get(), &QProcess::errorOccurred, + [&](){ + errorOccured= true; + }); + QByteArray buffer; + QByteArray readed; + + mProcess->start(); + mProcess->waitForStarted(5000); + mStartSemaphore.release(1); + while (true) { + mProcess->waitForFinished(1); + if (mProcess->state()!=QProcess::Running) { + break; + } + if (mStop) { + mProcess->terminate(); + mProcess->kill(); + break; + } + if (errorOccured) + break; + readed = mProcess->readAll(); + buffer += readed; + + if (readed.endsWith("\n")&& outputTerminated(buffer)) { + processDebugOutput(buffer); + buffer.clear(); + mCmdRunning = false; + runNextCmd(); + } else if (!mCmdRunning && readed.isEmpty()){ + runNextCmd(); + } else if (readed.isEmpty()){ + msleep(1); + } + } + if (errorOccured) { + emit processFailed(mProcess->error()); + } +} diff --git a/RedPandaIDE/debugger/dapdebugger.h b/RedPandaIDE/debugger/dapdebugger.h new file mode 100644 index 00000000..8e28d8fd --- /dev/null +++ b/RedPandaIDE/debugger/dapdebugger.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef DAP_DEBUGGER_H +#define DAP_DEBUGGER_H + +#include "debugger.h" + +class DAPDebuggerClient : public DebuggerClient { + Q_OBJECT +public: + explicit DAPDebuggerClient(Debugger* debugger, QObject *parent = nullptr); + + + // QThread interface +protected: + void run() override; + + // DebuggerClient interface +public: + void postCommand(const QString &Command, const QString &Params, DebugCommandSource Source) override; + void registerInferiorStoppedCommand(const QString &Command, const QString &Params) override; + void stopDebug() override; + +protected: + void runNextCmd() override; +private: + void initializeRequest(); +private: + std::shared_ptr mProcess; + bool mStop; +}; + +#endif diff --git a/RedPandaIDE/debugger/dapprotocol.h b/RedPandaIDE/debugger/dapprotocol.h index ead48aca..f21c9545 100644 --- a/RedPandaIDE/debugger/dapprotocol.h +++ b/RedPandaIDE/debugger/dapprotocol.h @@ -57,6 +57,9 @@ QString createDAPResponseMessage( qint64 seq, qint64 request_seq, bool success, const QString& command, const QString& message, const QJsonObject& body); +QString createDAPEventMessage( + qint64 seq, const QString& event, const QJsonObject& body); + std::shared_ptr parseDAPMessage(const QByteArray& contentPart); #endif diff --git a/RedPandaIDE/debugger/debugger.cpp b/RedPandaIDE/debugger/debugger.cpp index 044b6add..c43b5ddb 100644 --- a/RedPandaIDE/debugger/debugger.cpp +++ b/RedPandaIDE/debugger/debugger.cpp @@ -270,12 +270,14 @@ void Debugger::updateRegisterValues(const QHash &values) void Debugger::refreshAll() { refreshWatchVars(); - sendCommand("-stack-list-variables", "--all-values"); - if (memoryModel()->startAddress()>0) - sendCommand("-data-read-memory",QString("%1 x 1 %2 %3 ") - .arg(memoryModel()->startAddress()) - .arg(pSettings->debugger().memoryViewRows()) - .arg(pSettings->debugger().memoryViewColumns()) + if (mExecuting && mClient) + mClient->refreshStackVariables(); + if (memoryModel()->startAddress()>0 + && mExecuting && mClient) + mClient->readMemory( + memoryModel()->startAddress(), + pSettings->debugger().memoryViewRows(), + pSettings->debugger().memoryViewColumns() ); } @@ -314,7 +316,9 @@ bool Debugger::inferiorRunning() void Debugger::interrupt() { - sendCommand("-exec-interrupt", ""); + if (mExecuting && mClient) { + mClient->interrupt(); + } } bool Debugger::isForProject() const @@ -437,13 +441,8 @@ PBreakpoint Debugger::breakpointAt(int line, const Editor *editor, int *index) void Debugger::setBreakPointCondition(int index, const QString &condition, bool forProject) { PBreakpoint breakpoint=mBreakpointModel->setBreakPointCondition(index,condition, forProject); - if (condition.isEmpty()) { - sendCommand("-break-condition", - QString("%1").arg(breakpoint->number)); - } else { - sendCommand("-break-condition", - QString("%1 %2").arg(breakpoint->number).arg(condition)); - } + if (mExecuting && mClient) + mClient->setBreakpointCondition(breakpoint); } void Debugger::sendAllBreakpointsToDebugger() @@ -495,8 +494,8 @@ void Debugger::loadForProject(const QString &filename, const QString &projectFol void Debugger::addWatchpoint(const QString &expression) { QString s=expression.trimmed(); - if (!s.isEmpty()) { - sendCommand("-break-watch",s,DebugCommandSource::Other); + if (mExecuting && mClient) { + mClient->addWatchpoint(expression); } } @@ -545,12 +544,12 @@ void Debugger::modifyWatchVarExpression(const QString &oldExpr, const QString &n void Debugger::refreshWatchVars() { - if (mExecuting) { + if (mExecuting && mClient) { sendAllWatchVarsToDebugger(); if (mDebuggerType==DebuggerType::LLDB_MI) { for (PWatchVar var:mWatchModel->watchVars()) { if (!var->name.isEmpty()) - sendCommand("-var-update",QString(" --all-values %1").arg(var->name)); + mClient->refreshWatchVar(var); } } else { sendCommand("-var-update"," --all-values *"); @@ -1003,23 +1002,6 @@ DebuggerClient::DebuggerClient(Debugger* debugger, QObject *parent) : QThread(pa mCmdRunning = false; } -void DebuggerClient::clearCmdQueue() -{ - QMutexLocker locker(&mCmdQueueMutex); - mCmdQueue.clear(); -} - -void DebuggerClient::runInferiorStoppedHook() -{ - QMutexLocker locker(&mCmdQueueMutex); - foreach (const PDebugCommand& cmd, mInferiorStoppedHookCommands) { - mCmdQueue.push_front(cmd); - } -} - - - - const QStringList &DebuggerClient::binDirs() const { return mBinDirs; @@ -1065,11 +1047,6 @@ bool DebuggerClient::updateCPUInfo() const return mUpdateCPUInfo; } -const PDebugCommand &DebuggerClient::currentCmd() const -{ - return mCurrentCmd; -} - const QStringList &DebuggerClient::consoleOutput() const { return mConsoleOutput; diff --git a/RedPandaIDE/debugger/debugger.h b/RedPandaIDE/debugger/debugger.h index 5562212e..202f9162 100644 --- a/RedPandaIDE/debugger/debugger.h +++ b/RedPandaIDE/debugger/debugger.h @@ -45,13 +45,6 @@ enum class DebuggerType { DAP }; -struct DebugCommand{ - QString command; - QString params; - DebugCommandSource source; -}; - -using PDebugCommand = std::shared_ptr; struct WatchVar; using PWatchVar = std::shared_ptr; struct WatchVar { @@ -482,8 +475,6 @@ public: const QStringList &consoleOutput() const; - const PDebugCommand ¤tCmd() const; - bool updateCPUInfo() const; bool receivedSFWarning() const; @@ -501,6 +492,15 @@ public: void addBinDir(const QString &binDir); Debugger* debugger() { return mDebugger; } + + //requests + virtual void interrupt() = 0; + virtual void refreshStackVariables() = 0; + virtual void readMemory(qulonglong startAddress, int rows, int cols) = 0; + virtual void setBreakpointCondition(PBreakpoint breakpoint) = 0; + virtual void addWatchpoint(const QString& watchExp) = 0; + virtual void refreshWatchVar(PWatchVar var) = 0; + signals: void parseStarted(); void invalidateAllVars(); @@ -540,21 +540,18 @@ signals: void varsValueUpdated(); protected: virtual void runNextCmd() = 0; - void clearCmdQueue(); - void runInferiorStoppedHook(); protected: #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) QRecursiveMutex mCmdQueueMutex; #else QMutex mCmdQueueMutex; #endif - QQueue mCmdQueue; + bool mCmdRunning; - QList mInferiorStoppedHookCommands; bool mInferiorRunning; bool mProcessExited; - PDebugCommand mCurrentCmd; + QStringList mConsoleOutput; QStringList mFullOutput; QSemaphore mStartSemaphore; diff --git a/RedPandaIDE/debugger/gdbmidebugger.cpp b/RedPandaIDE/debugger/gdbmidebugger.cpp index fb5610e0..f195d12c 100644 --- a/RedPandaIDE/debugger/gdbmidebugger.cpp +++ b/RedPandaIDE/debugger/gdbmidebugger.cpp @@ -860,4 +860,65 @@ void GDBMIDebuggerClient::asyncUpdate() mAsyncUpdated = false; } +const PDebugCommand &GDBMIDebuggerClient::currentCmd() const +{ + return mCurrentCmd; +} +void GDBMIDebuggerClient::interrupt() +{ + postCommand("-exec-interrupt", "", DebugCommandSource::Other); +} + +void GDBMIDebuggerClient::refreshStackVariables() +{ + postCommand("-stack-list-variables", "--all-values", DebugCommandSource::Other); +} + +void GDBMIDebuggerClient::readMemory(qulonglong startAddress, int rows, int cols) +{ + postCommand("-data-read-memory",QString("%1 x 1 %2 %3 ") + .arg(startAddress) + .arg(rows) + .arg(cols), + DebugCommandSource::Other + ); +} + +void GDBMIDebuggerClient::setBreakpointCondition(PBreakpoint breakpoint) +{ + Q_ASSERT(breakpoint!=nullptr); + QString condition = breakpoint->condition; + if (condition.isEmpty()) { + postCommand("-break-condition", + QString("%1").arg(breakpoint->number), DebugCommandSource::Other); + } else { + postCommand("-break-condition", + QString("%1 %2").arg(breakpoint->number).arg(condition), DebugCommandSource::Other); + } +} + +void GDBMIDebuggerClient::addWatchpoint(const QString &watchExp) +{ + if (!watchExp.isEmpty()) + postCommand("-break-watch", watchExp, DebugCommandSource::Other); +} + +void GDBMIDebuggerClient::refreshWatchVar(PWatchVar var) +{ + +} + +void GDBMIDebuggerClient::runInferiorStoppedHook() +{ + QMutexLocker locker(&mCmdQueueMutex); + foreach (const PDebugCommand& cmd, mInferiorStoppedHookCommands) { + mCmdQueue.push_front(cmd); + } +} + +void GDBMIDebuggerClient::clearCmdQueue() +{ + QMutexLocker locker(&mCmdQueueMutex); + mCmdQueue.clear(); +} diff --git a/RedPandaIDE/debugger/gdbmidebugger.h b/RedPandaIDE/debugger/gdbmidebugger.h index 20111eb8..6d8c1127 100644 --- a/RedPandaIDE/debugger/gdbmidebugger.h +++ b/RedPandaIDE/debugger/gdbmidebugger.h @@ -27,6 +27,14 @@ #include #include +struct DebugCommand{ + QString command; + QString params; + DebugCommandSource source; +}; + +using PDebugCommand = std::shared_ptr; + class GDBMIDebuggerClient: public DebuggerClient { Q_OBJECT public: @@ -37,6 +45,14 @@ public: void postCommand(const QString &Command, const QString &Params, DebugCommandSource Source) override; void registerInferiorStoppedCommand(const QString &Command, const QString &Params) override; void stopDebug() override; + const PDebugCommand ¤tCmd() const; + + void interrupt() override; + void refreshStackVariables() override; + void readMemory(qulonglong startAddress, int rows, int cols) override; + void setBreakpointCondition(PBreakpoint breakpoint) override; + void addWatchpoint(const QString& watchExp) override; + void refreshWatchVar(PWatchVar var) override; // QThread interface protected: void run() override; @@ -63,6 +79,8 @@ private: void processResultRecord(const QByteArray& line); void processDebugOutput(const QByteArray& debugOutput); QByteArray removeToken(const QByteArray& line) const; + void runInferiorStoppedHook(); + void clearCmdQueue(); private slots: void asyncUpdate(); private: @@ -77,6 +95,11 @@ private: bool mAsyncUpdated; static const QRegularExpression REGdbSourceLine; + + QQueue mCmdQueue; + PDebugCommand mCurrentCmd; + QList mInferiorStoppedHookCommands; + }; #endif // GDBMI_DEBUGGER_H