DAP: Separate CMake and Gdb logic in DAP engine

This commit refactors the Debug Adapter Protocol (DAP) engine
to separate the logic for CMake-based projects and Gdb-based
debugging.

- Moved CMake-specific code to a new CMakeDAPEngine class
- Moved Gdb-specific code to a new GdbDAPEngine class

Change-Id: Ia616e7b7ea2ff2071bcadd26b28b620f9aca6ac4
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Artem Sokolovskii
2023-08-08 15:37:39 +02:00
parent 8fcf3a695f
commit 1836b50f5d
8 changed files with 287 additions and 160 deletions

View File

@@ -2,6 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "dapengine.h"
#include "cmakedapengine.h"
#include "gdbdapengine.h"
#include <debugger/breakhandler.h>
#include <debugger/debuggeractions.h>
@@ -40,6 +42,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <QDateTime>
#include <QDebug>
@@ -59,106 +62,8 @@ static Q_LOGGING_CATEGORY(dapEngineLog, "qtc.dbg.dapengine", QtWarningMsg)
namespace Debugger::Internal {
class ProcessDataProvider : public IDataProvider
{
public:
ProcessDataProvider(const DebuggerRunParameters &rp, const CommandLine &cmd)
: m_runParameters(rp)
, m_cmd(cmd)
{
connect(&m_proc, &Process::started, this, &IDataProvider::started);
connect(&m_proc, &Process::done, this, &IDataProvider::done);
connect(&m_proc,
&Process::readyReadStandardOutput,
this,
&IDataProvider::readyReadStandardOutput);
connect(&m_proc,
&Process::readyReadStandardError,
this,
&IDataProvider::readyReadStandardError);
}
~ProcessDataProvider()
{
m_proc.kill();
m_proc.waitForFinished();
}
void start() override
{
m_proc.setProcessMode(ProcessMode::Writer);
m_proc.setEnvironment(m_runParameters.debugger.environment);
m_proc.setCommand(m_cmd);
m_proc.start();
}
bool isRunning() const override { return m_proc.isRunning(); }
void writeRaw(const QByteArray &data) override { m_proc.writeRaw(data); }
void kill() override { m_proc.kill(); }
QByteArray readAllStandardOutput() override { return m_proc.readAllStandardOutput().toUtf8(); }
QString readAllStandardError() override { return m_proc.readAllStandardError(); }
int exitCode() const override { return m_proc.exitCode(); }
QString executable() const override { return m_proc.commandLine().executable().toUserOutput(); }
QProcess::ExitStatus exitStatus() const override { return m_proc.exitStatus(); }
QProcess::ProcessError error() const override { return m_proc.error(); }
Utils::ProcessResult result() const override { return m_proc.result(); }
QString exitMessage() const override { return m_proc.exitMessage(); };
private:
Utils::Process m_proc;
const DebuggerRunParameters m_runParameters;
const CommandLine m_cmd;
};
class LocalSocketDataProvider : public IDataProvider
{
public:
LocalSocketDataProvider(const QString &socketName)
: m_socketName(socketName)
{
connect(&m_socket, &QLocalSocket::connected, this, &IDataProvider::started);
connect(&m_socket, &QLocalSocket::disconnected, this, &IDataProvider::done);
connect(&m_socket, &QLocalSocket::readyRead, this, &IDataProvider::readyReadStandardOutput);
connect(&m_socket,
&QLocalSocket::errorOccurred,
this,
&IDataProvider::readyReadStandardError);
}
~LocalSocketDataProvider() { m_socket.disconnectFromServer(); }
void start() override { m_socket.connectToServer(m_socketName, QIODevice::ReadWrite); }
bool isRunning() const override { return m_socket.isOpen(); }
void writeRaw(const QByteArray &data) override { m_socket.write(data); }
void kill() override {
if (m_socket.isOpen())
m_socket.disconnectFromServer();
else {
m_socket.abort();
emit done();
}
}
QByteArray readAllStandardOutput() override { return m_socket.readAll(); }
QString readAllStandardError() override { return QString(); }
int exitCode() const override { return 0; }
QString executable() const override { return m_socket.serverName(); }
QProcess::ExitStatus exitStatus() const override { return QProcess::NormalExit; }
QProcess::ProcessError error() const override { return QProcess::UnknownError; }
Utils::ProcessResult result() const override { return ProcessResult::FinishedWithSuccess; }
QString exitMessage() const override { return QString(); };
private:
QLocalSocket m_socket;
const QString m_socketName;
};
DapEngine::DapEngine()
{
setObjectName("DapEngine");
setDebuggerName("DAP");
m_rootWatchItem = new WatchItem();
m_currentWatchItem = m_rootWatchItem;
}
@@ -236,58 +141,6 @@ void DapEngine::shutdownEngine()
m_dataGenerator->kill();
}
void DapEngine::setupEngine()
{
QTC_ASSERT(state() == EngineSetupRequested, qCDebug(dapEngineLog) << state());
const auto connectDataGeneratorSignals = [this] {
if (!m_dataGenerator)
return;
connect(m_dataGenerator.get(), &IDataProvider::started, this, &DapEngine::handleDapStarted);
connect(m_dataGenerator.get(), &IDataProvider::done, this, &DapEngine::handleDapDone);
connect(m_dataGenerator.get(),
&IDataProvider::readyReadStandardOutput,
this,
&DapEngine::readDapStandardOutput);
connect(m_dataGenerator.get(),
&IDataProvider::readyReadStandardError,
this,
&DapEngine::readDapStandardError);
};
Perspective *currentPerspective = DebuggerMainWindow::instance()->currentPerspective();
if (currentPerspective->parentPerspectiveId() == Constants::CMAKE_PERSPECTIVE_ID) {
qCDebug(dapEngineLog) << "build system name" << ProjectExplorer::ProjectTree::currentBuildSystem()->name();
if (TemporaryDirectory::masterDirectoryFilePath().osType() == Utils::OsType::OsTypeWindows) {
m_dataGenerator = std::make_unique<LocalSocketDataProvider>("\\\\.\\pipe\\cmake-dap");
} else {
m_dataGenerator = std::make_unique<LocalSocketDataProvider>(
TemporaryDirectory::masterDirectoryPath() + "/cmake-dap.sock");
}
connectDataGeneratorSignals();
connect(ProjectExplorer::ProjectTree::currentBuildSystem(),
&ProjectExplorer::BuildSystem::debuggingStarted,
this,
[this] {
m_dataGenerator->start();
});
ProjectExplorer::ProjectTree::currentBuildSystem()->requestDebugging();
} else {
const DebuggerRunParameters &rp = runParameters();
const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}};
m_dataGenerator = std::make_unique<ProcessDataProvider>(rp, cmd);
connectDataGeneratorSignals();
m_dataGenerator->start();
}
notifyEngineSetupOk();
}
// From the docs:
// The sequence of events/requests is as follows:
// * adapters sends initialized event (after the initialize request has returned)
@@ -310,9 +163,7 @@ void DapEngine::handleDapStarted()
{"type", "request"},
{"arguments", QJsonObject {
{"clientID", "QtCreator"}, // The ID of the client using this adapter.
{"clientName", "QtCreator"}, // The human-readable name of the client using this adapter.
{"adapterID", "cmake"},
{"pathFormat", "path"}
{"clientName", "QtCreator"} // The human-readable name of the client using this adapter.
}}
});
@@ -1083,9 +934,29 @@ void DapEngine::claimInitialBreakpoints()
qCDebug(dapEngineLog) << "claimInitialBreakpoints";
}
DebuggerEngine *createDapEngine()
void DapEngine::connectDataGeneratorSignals()
{
return new DapEngine;
if (!m_dataGenerator)
return;
connect(m_dataGenerator.get(), &IDataProvider::started, this, &DapEngine::handleDapStarted);
connect(m_dataGenerator.get(), &IDataProvider::done, this, &DapEngine::handleDapDone);
connect(m_dataGenerator.get(),
&IDataProvider::readyReadStandardOutput,
this,
&DapEngine::readDapStandardOutput);
connect(m_dataGenerator.get(),
&IDataProvider::readyReadStandardError,
this,
&DapEngine::readDapStandardError);
}
DebuggerEngine *createDapEngine(Utils::Id runMode)
{
if (runMode == ProjectExplorer::Constants::CMAKE_DEBUG_RUN_MODE)
return new CMakeDapEngine;
return new GdbDapEngine;
}
} // Debugger::Internal