DAP : Refactor DapEngine

Extracted client part to additional class DapClient.

Change-Id: Iba472d2fb2c2390f38ad9a3a75e9e1d9a76f912c
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Artem Sokolovskii
2023-08-15 16:48:15 +02:00
parent 98026b29c6
commit 0192c3c26d
8 changed files with 477 additions and 362 deletions

View File

@@ -28,6 +28,7 @@ add_qtc_plugin(Debugger
console/consoleproxymodel.cpp console/consoleproxymodel.h
console/consoleview.cpp console/consoleview.h
dap/cmakedapengine.cpp dap/cmakedapengine.h
dap/dapclient.cpp dap/dapclient.h
dap/dapengine.cpp dap/dapengine.h
dap/gdbdapengine.cpp dap/gdbdapengine.h
debugger.qrc

View File

@@ -3,6 +3,8 @@
#include "cmakedapengine.h"
#include "dapclient.h"
#include <coreplugin/messagemanager.h>
#include <debugger/debuggermainwindow.h>
@@ -69,6 +71,23 @@ private:
const QString m_socketName;
};
class CMakeDapClient : public DapClient
{
public:
CMakeDapClient(std::unique_ptr<IDataProvider> provider)
: DapClient(std::move(provider))
{}
void sendInitialize()
{
postRequest("initialize",
QJsonObject{{"clientID", "QtCreator"},
{"clientName", "QtCreator"},
{"adapterID", "cmake"},
{"pathFormat", "path"}});
}
};
CMakeDapEngine::CMakeDapEngine()
: DapEngine()
{
@@ -76,24 +95,6 @@ CMakeDapEngine::CMakeDapEngine()
setDebuggerName("CmakeDAP");
}
void CMakeDapEngine::handleDapStarted()
{
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "initialize"},
{"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"}
}}
});
qCDebug(dapEngineLog) << "handleDapStarted";
}
void CMakeDapEngine::setupEngine()
{
QTC_ASSERT(state() == EngineSetupRequested, qCDebug(dapEngineLog) << state());
@@ -101,24 +102,26 @@ void CMakeDapEngine::setupEngine()
qCDebug(dapEngineLog) << "build system name"
<< ProjectExplorer::ProjectTree::currentBuildSystem()->name();
std::unique_ptr<IDataProvider> dataProvider;
if (TemporaryDirectory::masterDirectoryFilePath().osType() == Utils::OsType::OsTypeWindows) {
m_dataGenerator = std::make_unique<LocalSocketDataProvider>("\\\\.\\pipe\\cmake-dap");
dataProvider = std::make_unique<LocalSocketDataProvider>("\\\\.\\pipe\\cmake-dap");
} else {
m_dataGenerator = std::make_unique<LocalSocketDataProvider>(
dataProvider = std::make_unique<LocalSocketDataProvider>(
TemporaryDirectory::masterDirectoryPath() + "/cmake-dap.sock");
}
m_dapClient = std::make_unique<CMakeDapClient>(std::move(dataProvider));
connectDataGeneratorSignals();
connect(ProjectExplorer::ProjectTree::currentBuildSystem(),
&ProjectExplorer::BuildSystem::debuggingStarted,
this,
[this] { m_dataGenerator->start(); });
[this] { m_dapClient->dataProvider()->start(); });
ProjectExplorer::ProjectTree::currentBuildSystem()->requestDebugging();
QTimer::singleShot(5000, this, [this] {
if (!m_dataGenerator->isRunning()) {
m_dataGenerator->kill();
if (!m_dapClient->dataProvider()->isRunning()) {
m_dapClient->dataProvider()->kill();
MessageManager::writeDisrupting(
"CMake server is not running. Please check that your CMake is 3.27 or higher.");
return;

View File

@@ -13,7 +13,6 @@ public:
CMakeDapEngine();
private:
void handleDapStarted() override;
void setupEngine() override;
/* Needed for CMake support issue:25176 */

View File

@@ -0,0 +1,237 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "dapclient.h"
#include "qjsonarray.h"
#include <utils/process.h>
#include <QDebug>
#include <QJsonDocument>
#include <QLoggingCategory>
using namespace Core;
using namespace Utils;
static Q_LOGGING_CATEGORY(dapEngineLog, "qtc.dbg.dapengine", QtWarningMsg);
namespace Debugger::Internal {
DapClient::DapClient(std::unique_ptr<IDataProvider> dataProvider)
: m_dataProvider(std::move(dataProvider))
{
connect(m_dataProvider.get(),
&IDataProvider::readyReadStandardOutput,
this,
&DapClient::readOutput);
connect(m_dataProvider.get(),
&IDataProvider::readyReadStandardError,
this,
&DapClient::readyReadStandardError);
connect(m_dataProvider.get(), &IDataProvider::done, this, &DapClient::done);
connect(m_dataProvider.get(), &IDataProvider::started, this, &DapClient::started);
}
DapClient::~DapClient() = default;
void DapClient::postRequest(const QString &command, const QJsonObject &arguments)
{
QJsonObject ob = {
{"command", command},
{"type", "request"},
{"arguments", arguments}
};
static int seq = 1;
QJsonObject obseq = ob;
obseq.insert("seq", seq++);
const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact);
const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data;
qCDebug(dapEngineLog) << msg;
m_dataProvider->writeRaw(msg);
}
void DapClient::sendInitialize()
{
postRequest("initialize", QJsonObject{{"clientID", "QtCreator"}, {"clientName", "QtCreator"}});
}
void DapClient::sendLaunch(const Utils::FilePath &executable)
{
postRequest("launch",
QJsonObject{{"noDebug", false}, {"program", executable.path()}, {"__restart", ""}});
}
void DapClient::sendConfigurationDone()
{
postRequest("configurationDone");
}
void DapClient::sendDisconnect()
{
postRequest("disconnect", QJsonObject{{"restart", false}, {"terminateDebuggee", true}});
}
void DapClient::sendTerminate()
{
postRequest("terminate", QJsonObject{{"restart", false}});
}
void DapClient::sendContinue(int threadId)
{
QTC_ASSERT(threadId != -1, return);
postRequest("continue", QJsonObject{{"threadId", threadId}});
}
void DapClient::sendPause()
{
postRequest("pause");
}
void DapClient::sendStepIn(int threadId)
{
QTC_ASSERT(threadId != -1, return);
postRequest("stepIn", QJsonObject{{"threadId", threadId}});
}
void DapClient::sendStepOut(int threadId)
{
QTC_ASSERT(threadId != -1, return);
postRequest("stepOut", QJsonObject{{"threadId", threadId}});
}
void DapClient::sendStepOver(int threadId)
{
QTC_ASSERT(threadId != -1, return);
postRequest("next", QJsonObject{{"threadId", threadId}});
}
void DapClient::stackTrace(int threadId)
{
QTC_ASSERT(threadId != -1, return);
postRequest("stackTrace",
QJsonObject{{"threadId", threadId}, {"startFrame", 0}, {"levels", 10}});
}
void DapClient::scopes(int frameId)
{
postRequest("scopes", QJsonObject{{"frameId", frameId}});
}
void DapClient::threads()
{
postRequest("threads");
}
void DapClient::variables(int variablesReference)
{
postRequest("variables", QJsonObject{{"variablesReference", variablesReference}});
}
void DapClient::setBreakpoints(const QJsonArray &breakpoints, const FilePath &fileName)
{
postRequest("setBreakpoints",
QJsonObject{{"source", QJsonObject{{"path", fileName.path()}}},
{"breakpoints", breakpoints}});
}
void DapClient::readOutput()
{
m_inbuffer.append(m_dataProvider->readAllStandardOutput());
qCDebug(dapEngineLog) << m_inbuffer;
while (true) {
// Something like
// Content-Length: 128\r\n
// {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n
// FIXME: There coud be more than one header line.
int pos1 = m_inbuffer.indexOf("Content-Length:");
if (pos1 == -1)
break;
pos1 += 15;
int pos2 = m_inbuffer.indexOf('\n', pos1);
if (pos2 == -1)
break;
const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt();
if (len < 4)
break;
pos2 += 3; // Skip \r\n\r
if (pos2 + len > m_inbuffer.size())
break;
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error);
m_inbuffer = m_inbuffer.mid(pos2 + len);
emitSignals(doc);
}
}
void DapClient::emitSignals(const QJsonDocument &doc)
{
QJsonObject ob = doc.object();
const QJsonValue t = ob.value("type");
const QString type = t.toString();
qCDebug(dapEngineLog) << "dap response" << ob;
if (type == "response") {
DapResponseType type = DapResponseType::Unknown;
const QString command = ob.value("command").toString();
if (command == "configurationDone") {
type = DapResponseType::ConfigurationDone;
} else if (command == "continue") {
type = DapResponseType::Continue;
} else if (command == "stackTrace") {
type = DapResponseType::StackTrace;
} else if (command == "scopes") {
type = DapResponseType::Scopes;
} else if (command == "variables") {
type = DapResponseType::Variables;
} else if (command == "stepIn") {
type = DapResponseType::StepIn;
} else if (command == "stepOut") {
type = DapResponseType::StepOut;
} else if (command == "next") {
type = DapResponseType::StepOver;
} else if (command == "threads") {
type = DapResponseType::DapThreads;
}
emit responseReady(type, ob);
return;
}
if (type == "event") {
const QString event = ob.value("event").toString();
DapEventType type = DapEventType::Unknown;
if (event == "initialized") {
type = DapEventType::Initialized;
} else if (event == "stopped") {
type = DapEventType::Stopped;
} else if (event == "thread") {
type = DapEventType::DapThread;
} else if (event == "output") {
type = DapEventType::Output;
} else if (event == "breakpoint") {
type = DapEventType::DapBreakpoint;
} else if (event == "exited") {
type = DapEventType::Exited;
}
emit eventReady(type, ob);
}
}
} // namespace Debugger::Internal

View File

@@ -0,0 +1,113 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <debugger/debuggerengine.h>
#include <utils/process.h>
namespace Debugger::Internal {
class IDataProvider : public QObject
{
Q_OBJECT
public:
virtual void start() = 0;
virtual bool isRunning() const = 0;
virtual void writeRaw(const QByteArray &input) = 0;
virtual void kill() = 0;
virtual QByteArray readAllStandardOutput() = 0;
virtual QString readAllStandardError() = 0;
virtual int exitCode() const = 0;
virtual QString executable() const = 0;
virtual QProcess::ExitStatus exitStatus() const = 0;
virtual QProcess::ProcessError error() const = 0;
virtual Utils::ProcessResult result() const = 0;
virtual QString exitMessage() const = 0;
signals:
void started();
void done();
void readyReadStandardOutput();
void readyReadStandardError();
};
enum class DapResponseType
{
ConfigurationDone,
Continue,
StackTrace,
Scopes,
DapThreads,
Variables,
StepIn,
StepOut,
StepOver,
Unknown
};
enum class DapEventType
{
Initialized,
Stopped,
Exited,
DapThread,
Output,
DapBreakpoint,
Unknown
};
class DapClient : public QObject
{
Q_OBJECT
public:
DapClient(std::unique_ptr<IDataProvider> dataProvider);
~DapClient();
IDataProvider *dataProvider() const { return m_dataProvider.get(); }
void postRequest(const QString &command, const QJsonObject &arguments = {});
virtual void sendInitialize();
void sendLaunch(const Utils::FilePath &executable);
void sendConfigurationDone();
void sendDisconnect();
void sendTerminate();
void sendPause();
void sendContinue(int threadId);
void sendStepIn(int threadId);
void sendStepOut(int threadId);
void sendStepOver(int threadId);
void stackTrace(int threadId);
void scopes(int frameId);
void threads();
void variables(int variablesReference);
void setBreakpoints(const QJsonArray &breakpoints, const Utils::FilePath &fileName);
void emitSignals(const QJsonDocument &doc);
signals:
void started();
void done();
void readyReadStandardError();
void responseReady(DapResponseType type, const QJsonObject &response);
void eventReady(DapEventType type, const QJsonObject &response);
private:
void readOutput();
private:
std::unique_ptr<IDataProvider> m_dataProvider = nullptr;
QByteArray m_inbuffer;
};
} // namespace Debugger::Internal

View File

@@ -2,7 +2,9 @@
// 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 "dapclient.h"
#include "gdbdapengine.h"
#include <debugger/breakhandler.h>
@@ -79,28 +81,13 @@ void DapEngine::executeDebuggerCommand(const QString &/*command*/)
// postDirectCommand(command);
}
void DapEngine::postDirectCommand(const QJsonObject &ob)
{
static int seq = 1;
QJsonObject obseq = ob;
obseq.insert("seq", seq++);
const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact);
const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data;
qCDebug(dapEngineLog) << msg;
m_dataGenerator->writeRaw(msg);
showMessage(QString::fromUtf8(msg), LogInput);
}
void DapEngine::runCommand(const DebuggerCommand &cmd)
{
if (state() == EngineSetupRequested) { // cmd has been triggered too early
showMessage("IGNORED COMMAND: " + cmd.function);
return;
}
QTC_ASSERT(m_dataGenerator->isRunning(), notifyEngineIll());
QTC_ASSERT(m_dapClient->dataProvider()->isRunning(), notifyEngineIll());
// postDirectCommand(cmd.args.toObject());
// const QByteArray data = QJsonDocument(cmd.args.toObject()).toJson(QJsonDocument::Compact);
// m_proc.writeRaw("Content-Length: " + QByteArray::number(data.size()) + "\r\n" + data + "\r\n");
@@ -112,14 +99,7 @@ void DapEngine::shutdownInferior()
{
QTC_ASSERT(state() == InferiorShutdownRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "disconnect"},
{"type", "request"},
{"arguments", QJsonObject{
{"restart", false},
{"terminateDebuggee", true}
}}
});
m_dapClient->sendDisconnect();
qCDebug(dapEngineLog) << "DapEngine::shutdownInferior()";
notifyInferiorShutdownFinished();
@@ -129,30 +109,17 @@ void DapEngine::shutdownEngine()
{
QTC_ASSERT(state() == EngineShutdownRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "terminate"},
{"type", "request"},
{"arguments", QJsonObject{
{"restart", false}
}}
});
m_dapClient->sendTerminate();
qCDebug(dapEngineLog) << "DapEngine::shutdownEngine()";
m_dataGenerator->kill();
m_dapClient->dataProvider()->kill();
}
void DapEngine::handleDapStarted()
{
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "initialize"},
{"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.
}}
});
m_dapClient->sendInitialize();
qCDebug(dapEngineLog) << "handleDapStarted";
}
@@ -161,10 +128,7 @@ void DapEngine::handleDapConfigurationDone()
{
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "configurationDone"},
{"type", "request"}
});
m_dapClient->sendConfigurationDone();
qCDebug(dapEngineLog) << "handleDapConfigurationDone";
}
@@ -174,70 +138,14 @@ void DapEngine::handleDapLaunch()
{
QTC_ASSERT(state() == EngineRunRequested, qCDebug(dapEngineLog) << state());
postDirectCommand({
{"command", "launch"},
{"type", "request"},
{"arguments", QJsonObject{
{"noDebug", false},
{"program", runParameters().inferior.command.executable().path()},
{"__restart", ""}
}}
});
m_dapClient->sendLaunch(runParameters().inferior.command.executable());
qCDebug(dapEngineLog) << "handleDapLaunch";
}
void DapEngine::interruptInferior()
{
postDirectCommand({
{"command", "pause"},
{"type", "request"}
});
}
void DapEngine::dapStackTrace()
{
if (m_currentThreadId == -1)
return;
postDirectCommand({
{"command", "stackTrace"},
{"type", "request"},
{"arguments", QJsonObject{
{"threadId", m_currentThreadId},
{"startFrame", 0},
{"levels", 10}
}}
});
}
void DapEngine::dapScopes(int frameId)
{
postDirectCommand({
{"command", "scopes"},
{"type", "request"},
{"arguments", QJsonObject{
{"frameId", frameId}
}}
});
}
void DapEngine::threads()
{
postDirectCommand({
{"command", "threads"},
{"type", "request"}
});
}
void DapEngine::dapVariables(int variablesReference)
{
postDirectCommand({
{"command", "variables"},
{"type", "request"},
{"arguments", QJsonObject{
{"variablesReference", variablesReference}
}}
});
m_dapClient->sendPause();
}
void DapEngine::executeStepIn(bool)
@@ -246,15 +154,7 @@ void DapEngine::executeStepIn(bool)
return;
notifyInferiorRunRequested();
postDirectCommand({
{"command", "stepIn"},
{"type", "request"},
{"arguments", QJsonObject{
{"threadId", m_currentThreadId},
}}
});
m_dapClient->sendStepIn(m_currentThreadId);
}
void DapEngine::executeStepOut()
@@ -263,15 +163,7 @@ void DapEngine::executeStepOut()
return;
notifyInferiorRunRequested();
postDirectCommand({
{"command", "stepOut"},
{"type", "request"},
{"arguments", QJsonObject{
{"threadId", m_currentThreadId},
}}
});
m_dapClient->sendStepOut(m_currentThreadId);
}
void DapEngine::executeStepOver(bool)
@@ -281,26 +173,13 @@ void DapEngine::executeStepOver(bool)
notifyInferiorRunRequested();
postDirectCommand({
{"command", "next"},
{"type", "request"},
{"arguments", QJsonObject{
{"threadId", m_currentThreadId},
}}
});
m_dapClient->sendStepOver(m_currentThreadId);
}
void DapEngine::continueInferior()
{
notifyInferiorRunRequested();
postDirectCommand({
{"command", "continue"},
{"type", "request"},
{"arguments", QJsonObject{
{"threadId", m_currentThreadId},
}}
});
m_dapClient->sendContinue(m_currentThreadId);
}
void DapEngine::executeRunToLine(const ContextData &data)
@@ -395,16 +274,7 @@ void DapEngine::dapInsertBreakpoint(const Breakpoint &bp)
}
}
postDirectCommand( {
{"command", "setBreakpoints"},
{"type", "request"},
{"arguments", QJsonObject{
{"source", QJsonObject{
{"path", params.fileName.path()}
}},
{"breakpoints", breakpoints}
}}
});
m_dapClient->setBreakpoints(breakpoints, params.fileName);
qCDebug(dapEngineLog) << "insertBreakpoint" << bp->modelId() << bp->responseId();
}
@@ -437,23 +307,15 @@ void DapEngine::dapRemoveBreakpoint(const Breakpoint &bp)
const BreakpointParameters &params = bp->requestedParameters();
QJsonArray breakpoints;
for (const auto &breakpoint : breakHandler()->breakpoints())
for (const auto &breakpoint : breakHandler()->breakpoints()) {
if (breakpoint->responseId() != bp->responseId()
&& params.fileName == breakpoint->requestedParameters().fileName) {
QJsonObject jsonBp = createBreakpoint(breakpoint);
breakpoints.append(jsonBp);
}
}
postDirectCommand({
{"command", "setBreakpoints"},
{"type", "request"},
{"arguments", QJsonObject{
{"source", QJsonObject{
{"path", params.fileName.path()}
}},
{"breakpoints", breakpoints}
}}
});
m_dapClient->setBreakpoints(breakpoints, params.fileName);
qCDebug(dapEngineLog) << "removeBreakpoint" << bp->modelId() << bp->responseId();
}
@@ -566,7 +428,7 @@ QString DapEngine::errorMessage(QProcess::ProcessError error) const
return Tr::tr("The DAP process failed to start. Either the "
"invoked program \"%1\" is missing, or you may have insufficient "
"permissions to invoke the program.")
.arg(m_dataGenerator->executable());
.arg(m_dapClient->dataProvider()->executable());
case QProcess::Crashed:
return Tr::tr("The DAP process crashed some time after starting "
"successfully.");
@@ -588,14 +450,15 @@ QString DapEngine::errorMessage(QProcess::ProcessError error) const
void DapEngine::handleDapDone()
{
if (m_dataGenerator->result() == ProcessResult::StartFailed) {
if (m_dapClient->dataProvider()->result() == ProcessResult::StartFailed) {
notifyEngineSetupFailed();
showMessage("ADAPTER START FAILED");
ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), m_dataGenerator->exitMessage());
ICore::showWarningWithOptions(Tr::tr("Adapter start failed"),
m_dapClient->dataProvider()->exitMessage());
return;
}
const QProcess::ProcessError error = m_dataGenerator->error();
const QProcess::ProcessError error = m_dapClient->dataProvider()->error();
if (error != QProcess::UnknownError) {
showMessage("HANDLE DAP ERROR");
if (error != QProcess::Crashed)
@@ -604,129 +467,62 @@ void DapEngine::handleDapDone()
return;
}
showMessage(QString("DAP PROCESS FINISHED, status %1, code %2")
.arg(m_dataGenerator->exitStatus()).arg(m_dataGenerator->exitCode()));
.arg(m_dapClient->dataProvider()->exitStatus()).arg(m_dapClient->dataProvider()->exitCode()));
notifyEngineSpontaneousShutdown();
}
void DapEngine::readDapStandardError()
{
QString err = m_dataGenerator->readAllStandardError();
QString err = m_dapClient->dataProvider()->readAllStandardError();
qCDebug(dapEngineLog) << "DAP STDERR:" << err;
//qWarning() << "Unexpected DAP stderr:" << err;
showMessage("Unexpected DAP stderr: " + err);
//handleOutput(err);
}
void DapEngine::readDapStandardOutput()
{
m_inbuffer.append(m_dataGenerator->readAllStandardOutput());
qCDebug(dapEngineLog) << m_inbuffer;
while (true) {
// Something like
// Content-Length: 128\r\n
// {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n
// FIXME: There coud be more than one header line.
int pos1 = m_inbuffer.indexOf("Content-Length:");
if (pos1 == -1)
break;
pos1 += 15;
int pos2 = m_inbuffer.indexOf('\n', pos1);
if (pos2 == -1)
break;
const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt();
if (len < 4)
break;
pos2 += 3; // Skip \r\n\r
if (pos2 + len > m_inbuffer.size())
break;
QJsonParseError error;
const auto doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error);
m_inbuffer = m_inbuffer.mid(pos2 + len);
handleOutput(doc);
}
}
void DapEngine::handleOutput(const QJsonDocument &data)
{
QJsonObject ob = data.object();
const QJsonValue t = ob.value("type");
const QString type = t.toString();
qCDebug(dapEngineLog) << "dap response" << ob;
if (type == "response") {
handleResponse(ob);
return;
}
if (type == "event") {
handleEvent(ob);
return;
}
showMessage("UNKNOWN TYPE:" + type);
}
void DapEngine::handleResponse(const QJsonObject &response)
void DapEngine::handleResponse(DapResponseType type, const QJsonObject &response)
{
const QString command = response.value("command").toString();
if (command == "configurationDone") {
switch (type) {
case DapResponseType::ConfigurationDone:
showMessage("configurationDone", LogDebug);
qCDebug(dapEngineLog) << "configurationDone success";
notifyEngineRunAndInferiorRunOk();
return;
}
if (command == "continue") {
break;
case DapResponseType::Continue:
showMessage("continue", LogDebug);
qCDebug(dapEngineLog) << "continue success";
notifyInferiorRunOk();
return;
}
if (command == "stackTrace") {
break;
case DapResponseType::StackTrace:
handleStackTraceResponse(response);
return;
}
if (command == "scopes") {
break;
case DapResponseType::Scopes:
handleScopesResponse(response);
return;
}
if (command == "variables") {
break;
case DapResponseType::Variables: {
auto variables = response.value("body").toObject().value("variables").toArray();
refreshLocals(variables);
return;
break;
}
if (command == "stepIn" || command == "stepOut" || command == "next") {
case DapResponseType::StepIn:
case DapResponseType::StepOut:
case DapResponseType::StepOver:
if (response.value("success").toBool()) {
showMessage(command, LogDebug);
notifyInferiorRunOk();
} else {
notifyInferiorRunFailed();
}
return;
}
if (command == "threads") {
break;
case DapResponseType::DapThreads:
handleThreadsResponse(response);
return;
}
break;
default:
showMessage("UNKNOWN RESPONSE:" + command);
};
}
void DapEngine::handleStackTraceResponse(const QJsonObject &response)
@@ -743,7 +539,7 @@ void DapEngine::handleStackTraceResponse(const QJsonObject &response)
gotoLocation(Location(file, line));
refreshStack(stackFrames);
dapScopes(stackFrame.value("id").toInt());
m_dapClient->scopes(stackFrame.value("id").toInt());
}
void DapEngine::handleScopesResponse(const QJsonObject &response)
@@ -761,7 +557,7 @@ void DapEngine::handleScopesResponse(const QJsonObject &response)
m_currentWatchItem = m_rootWatchItem;
watchHandler()->removeAllData();
watchHandler()->notifyUpdateStarted();
dapVariables(variablesReference);
m_dapClient->variables(variablesReference);
}
}
}
@@ -785,18 +581,33 @@ void DapEngine::handleThreadsResponse(const QJsonObject &response)
handler->setCurrentThread(threadsHandler()->threadForId(QString::number(m_currentThreadId)));
}
void DapEngine::handleEvent(const QJsonObject &event)
void DapEngine::handleEvent(DapEventType type, const QJsonObject &event)
{
const QString eventType = event.value("event").toString();
const QJsonObject body = event.value("body").toObject();
showMessage(eventType, LogDebug);
if (eventType == "exited") {
switch (type) {
case DapEventType::Initialized:
qCDebug(dapEngineLog) << "initialize success";
handleDapLaunch();
handleDapConfigurationDone();
break;
case DapEventType::Stopped:
handleStoppedEvent(event);
break;
case DapEventType::Exited:
notifyInferiorExited();
return;
}
if (eventType == "output") {
break;
case DapEventType::DapThread:
m_dapClient->threads();
if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1)
claimInitialBreakpoints();
break;
case DapEventType::DapBreakpoint:
handleBreakpointEvent(event);
break;
case DapEventType::Output: {
const QString category = body.value("category").toString();
const QString output = body.value("output").toString();
if (category == "stdout")
@@ -805,34 +616,11 @@ void DapEngine::handleEvent(const QJsonObject &event)
showMessage(output, AppError);
else
showMessage(output, LogDebug);
return;
break;
}
if (eventType == "initialized") {
qCDebug(dapEngineLog) << "initialize success";
handleDapLaunch();
handleDapConfigurationDone();
return;
}
if (eventType == "stopped") {
handleStoppedEvent(event);
return;
}
if (eventType == "thread") {
threads();
if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1)
claimInitialBreakpoints();
return;
}
if (eventType == "breakpoint") {
handleBreakpointEvent(event);
return;
}
default:
showMessage("UNKNOWN EVENT:" + eventType);
};
}
void DapEngine::handleStoppedEvent(const QJsonObject &event)
@@ -857,8 +645,8 @@ void DapEngine::handleStoppedEvent(const QJsonObject &event)
else
notifyInferiorSpontaneousStop();
dapStackTrace();
threads();
m_dapClient->stackTrace(m_currentThreadId);
m_dapClient->threads();
}
void DapEngine::handleBreakpointEvent(const QJsonObject &event)
@@ -936,7 +724,7 @@ void DapEngine::refreshLocals(const QJsonArray &variables)
const auto front = m_variablesReferenceQueue.front();
m_variablesReferenceQueue.pop();
dapVariables(front.first);
m_dapClient->variables(front.first);
m_currentWatchItem = front.second;
}
@@ -977,7 +765,7 @@ void DapEngine::updateAll()
void DapEngine::updateLocals()
{
dapStackTrace();
m_dapClient->stackTrace(m_currentThreadId);
}
bool DapEngine::hasCapability(unsigned cap) const
@@ -996,19 +784,18 @@ void DapEngine::claimInitialBreakpoints()
void DapEngine::connectDataGeneratorSignals()
{
if (!m_dataGenerator)
if (!m_dapClient)
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,
connect(m_dapClient.get(), &DapClient::started, this, &DapEngine::handleDapStarted);
connect(m_dapClient.get(), &DapClient::done, this, &DapEngine::handleDapDone);
connect(m_dapClient.get(),
&DapClient::readyReadStandardError,
this,
&DapEngine::readDapStandardError);
connect(m_dapClient.get(), &DapClient::responseReady, this, &DapEngine::handleResponse);
connect(m_dapClient.get(), &DapClient::eventReady, this, &DapEngine::handleEvent);
}
DebuggerEngine *createDapEngine(Utils::Id runMode)

View File

@@ -13,38 +13,16 @@
namespace Debugger::Internal {
class DapClient;
class DebuggerCommand;
class IDataProvider;
class GdbMi;
enum class DapResponseType;
enum class DapEventType;
/*
* A debugger engine for the debugger adapter protocol.
*/
class IDataProvider : public QObject
{
Q_OBJECT
public:
virtual void start() = 0;
virtual bool isRunning() const = 0;
virtual void writeRaw(const QByteArray &input) = 0;
virtual void kill() = 0;
virtual QByteArray readAllStandardOutput() = 0;
virtual QString readAllStandardError() = 0;
virtual int exitCode() const = 0;
virtual QString executable() const = 0;
virtual QProcess::ExitStatus exitStatus() const = 0;
virtual QProcess::ProcessError error() const = 0;
virtual Utils::ProcessResult result() const = 0;
virtual QString exitMessage() const = 0;
signals:
void started();
void done();
void readyReadStandardOutput();
void readyReadStandardError();
};
class DapEngine : public DebuggerEngine
{
public:
@@ -91,7 +69,6 @@ protected:
void updateItem(const QString &iname) override;
void runCommand(const DebuggerCommand &cmd) override;
void postDirectCommand(const QJsonObject &ob);
void refreshLocation(const GdbMi &reportedLocation);
void refreshStack(const QJsonArray &stackFrames);
@@ -105,14 +82,10 @@ protected:
void claimInitialBreakpoints();
virtual void handleDapStarted();
void handleDapStarted();
void handleDapLaunch();
void handleDapConfigurationDone();
void dapStackTrace();
void dapScopes(int frameId);
void threads();
void dapVariables(int variablesReference);
void dapRemoveBreakpoint(const Breakpoint &bp);
void dapInsertBreakpoint(const Breakpoint &bp);
@@ -120,14 +93,12 @@ protected:
void readDapStandardOutput();
void readDapStandardError();
void handleOutput(const QJsonDocument &data);
void handleResponse(const QJsonObject &response);
void handleResponse(DapResponseType type, const QJsonObject &response);
void handleStackTraceResponse(const QJsonObject &response);
void handleScopesResponse(const QJsonObject &response);
void handleThreadsResponse(const QJsonObject &response);
void handleEvent(const QJsonObject &event);
void handleEvent(DapEventType type, const QJsonObject &event);
void handleBreakpointEvent(const QJsonObject &event);
void handleStoppedEvent(const QJsonObject &event);
@@ -136,7 +107,7 @@ protected:
void connectDataGeneratorSignals();
QByteArray m_inbuffer;
std::unique_ptr<IDataProvider> m_dataGenerator = nullptr;
std::unique_ptr<DapClient> m_dapClient = nullptr;
int m_nextBreakpointId = 1;
int m_currentThreadId = -1;

View File

@@ -3,6 +3,8 @@
#include "gdbdapengine.h"
#include "dapclient.h"
#include <debugger/debuggermainwindow.h>
#include <utils/temporarydirectory.h>
@@ -87,9 +89,11 @@ void GdbDapEngine::setupEngine()
const DebuggerRunParameters &rp = runParameters();
const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}};
m_dataGenerator = std::make_unique<ProcessDataProvider>(rp, cmd);
std::unique_ptr<IDataProvider> dataProvider = std::make_unique<ProcessDataProvider>(rp, cmd);
m_dapClient = std::make_unique<DapClient>(std::move(dataProvider));
connectDataGeneratorSignals();
m_dataGenerator->start();
m_dapClient->dataProvider()->start();
notifyEngineSetupOk();
}