From 0192c3c26dc27f8d5c8c650db32a8af775a784fb Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Tue, 15 Aug 2023 16:48:15 +0200 Subject: [PATCH] DAP : Refactor DapEngine Extracted client part to additional class DapClient. Change-Id: Iba472d2fb2c2390f38ad9a3a75e9e1d9a76f912c Reviewed-by: Reviewed-by: hjk --- src/plugins/debugger/CMakeLists.txt | 1 + src/plugins/debugger/dap/cmakedapengine.cpp | 49 +-- src/plugins/debugger/dap/cmakedapengine.h | 1 - src/plugins/debugger/dap/dapclient.cpp | 237 ++++++++++++ src/plugins/debugger/dap/dapclient.h | 113 ++++++ src/plugins/debugger/dap/dapengine.cpp | 385 +++++--------------- src/plugins/debugger/dap/dapengine.h | 45 +-- src/plugins/debugger/dap/gdbdapengine.cpp | 8 +- 8 files changed, 477 insertions(+), 362 deletions(-) create mode 100644 src/plugins/debugger/dap/dapclient.cpp create mode 100644 src/plugins/debugger/dap/dapclient.h diff --git a/src/plugins/debugger/CMakeLists.txt b/src/plugins/debugger/CMakeLists.txt index aea2effcf2e..cc632969e03 100644 --- a/src/plugins/debugger/CMakeLists.txt +++ b/src/plugins/debugger/CMakeLists.txt @@ -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 diff --git a/src/plugins/debugger/dap/cmakedapengine.cpp b/src/plugins/debugger/dap/cmakedapengine.cpp index 1b3614a5f12..6d7996a1e1b 100644 --- a/src/plugins/debugger/dap/cmakedapengine.cpp +++ b/src/plugins/debugger/dap/cmakedapengine.cpp @@ -3,6 +3,8 @@ #include "cmakedapengine.h" +#include "dapclient.h" + #include #include @@ -69,6 +71,23 @@ private: const QString m_socketName; }; +class CMakeDapClient : public DapClient +{ +public: + CMakeDapClient(std::unique_ptr 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 dataProvider; if (TemporaryDirectory::masterDirectoryFilePath().osType() == Utils::OsType::OsTypeWindows) { - m_dataGenerator = std::make_unique("\\\\.\\pipe\\cmake-dap"); + dataProvider = std::make_unique("\\\\.\\pipe\\cmake-dap"); } else { - m_dataGenerator = std::make_unique( + dataProvider = std::make_unique( TemporaryDirectory::masterDirectoryPath() + "/cmake-dap.sock"); } + m_dapClient = std::make_unique(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; diff --git a/src/plugins/debugger/dap/cmakedapengine.h b/src/plugins/debugger/dap/cmakedapengine.h index 4d03249e348..c385590779f 100644 --- a/src/plugins/debugger/dap/cmakedapengine.h +++ b/src/plugins/debugger/dap/cmakedapengine.h @@ -13,7 +13,6 @@ public: CMakeDapEngine(); private: - void handleDapStarted() override; void setupEngine() override; /* Needed for CMake support issue:25176 */ diff --git a/src/plugins/debugger/dap/dapclient.cpp b/src/plugins/debugger/dap/dapclient.cpp new file mode 100644 index 00000000000..41097123ba3 --- /dev/null +++ b/src/plugins/debugger/dap/dapclient.cpp @@ -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 + +#include +#include +#include + +using namespace Core; +using namespace Utils; + +static Q_LOGGING_CATEGORY(dapEngineLog, "qtc.dbg.dapengine", QtWarningMsg); + +namespace Debugger::Internal { + +DapClient::DapClient(std::unique_ptr 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 + diff --git a/src/plugins/debugger/dap/dapclient.h b/src/plugins/debugger/dap/dapclient.h new file mode 100644 index 00000000000..3cb2d49b4d5 --- /dev/null +++ b/src/plugins/debugger/dap/dapclient.h @@ -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 + +#include + +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 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 m_dataProvider = nullptr; + + QByteArray m_inbuffer; +}; + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index 75497ed103d..c6fb33fc0c2 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -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 @@ -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 ¶ms = 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; - showMessage("UNKNOWN RESPONSE:" + command); + 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; - } - - showMessage("UNKNOWN EVENT:" + eventType); + 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) diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h index 6871e3e468e..c4f332668e5 100644 --- a/src/plugins/debugger/dap/dapengine.h +++ b/src/plugins/debugger/dap/dapengine.h @@ -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 m_dataGenerator = nullptr; + std::unique_ptr m_dapClient = nullptr; int m_nextBreakpointId = 1; int m_currentThreadId = -1; diff --git a/src/plugins/debugger/dap/gdbdapengine.cpp b/src/plugins/debugger/dap/gdbdapengine.cpp index 316a0c7d547..7d0cefaa9ca 100644 --- a/src/plugins/debugger/dap/gdbdapengine.cpp +++ b/src/plugins/debugger/dap/gdbdapengine.cpp @@ -3,6 +3,8 @@ #include "gdbdapengine.h" +#include "dapclient.h" + #include #include @@ -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(rp, cmd); + std::unique_ptr dataProvider = std::make_unique(rp, cmd); + m_dapClient = std::make_unique(std::move(dataProvider)); + connectDataGeneratorSignals(); - m_dataGenerator->start(); + m_dapClient->dataProvider()->start(); notifyEngineSetupOk(); }