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

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