DAP Protocol implementations.

More work for seperating debugger interface and the gdb mi client
This commit is contained in:
Roy Qu 2024-03-09 23:08:23 +08:00
parent 9a66022b8a
commit d302fcc10f
8 changed files with 252 additions and 54 deletions

View File

@ -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 \

View File

@ -0,0 +1,88 @@
#include "dapdebugger.h"
#include "../utils.h"
#include "dapprotocol.h"
#include "../systemconsts.h"
#include <QFileInfo>
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<QProcess>();
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());
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#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<QProcess> mProcess;
bool mStop;
};
#endif

View File

@ -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<DAPProtocolMessage> parseDAPMessage(const QByteArray& contentPart);
#endif

View File

@ -270,12 +270,14 @@ void Debugger::updateRegisterValues(const QHash<int, QString> &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;

View File

@ -45,13 +45,6 @@ enum class DebuggerType {
DAP
};
struct DebugCommand{
QString command;
QString params;
DebugCommandSource source;
};
using PDebugCommand = std::shared_ptr<DebugCommand>;
struct WatchVar;
using PWatchVar = std::shared_ptr<WatchVar>;
struct WatchVar {
@ -482,8 +475,6 @@ public:
const QStringList &consoleOutput() const;
const PDebugCommand &currentCmd() 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<PDebugCommand> mCmdQueue;
bool mCmdRunning;
QList<PDebugCommand> mInferiorStoppedHookCommands;
bool mInferiorRunning;
bool mProcessExited;
PDebugCommand mCurrentCmd;
QStringList mConsoleOutput;
QStringList mFullOutput;
QSemaphore mStartSemaphore;

View File

@ -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();
}

View File

@ -27,6 +27,14 @@
#include <memory>
#include <QRegularExpression>
struct DebugCommand{
QString command;
QString params;
DebugCommandSource source;
};
using PDebugCommand = std::shared_ptr<DebugCommand>;
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 &currentCmd() 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<PDebugCommand> mCmdQueue;
PDebugCommand mCurrentCmd;
QList<PDebugCommand> mInferiorStoppedHookCommands;
};
#endif // GDBMI_DEBUGGER_H