From dcaa55200b2f06a7367b15e1d4e77a2984a81605 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 13 Jun 2024 16:19:45 +0200 Subject: [PATCH] Debugger: Add "LLDB Dap" preset lldb has a dap interface since a while now. The "GDB Dap" preset is not compatible, since gdb uses the same executable and lldb has a "lldb-dap" (previously "lldb-vscode") executable. Change-Id: I4105223659de093f0ee44129527c4830d21a3090 Reviewed-by: Artem Sokolovskii --- src/plugins/debugger/CMakeLists.txt | 1 + src/plugins/debugger/dap/dapengine.cpp | 3 + src/plugins/debugger/dap/lldbdapengine.cpp | 176 ++++++++++++++++++ src/plugins/debugger/dap/lldbdapengine.h | 26 +++ src/plugins/debugger/debugger.qbs | 1 + src/plugins/debugger/debuggerplugin.cpp | 1 + src/plugins/debugger/debuggerruncontrol.cpp | 1 + .../projectexplorerconstants.h | 1 + 8 files changed, 210 insertions(+) create mode 100644 src/plugins/debugger/dap/lldbdapengine.cpp create mode 100644 src/plugins/debugger/dap/lldbdapengine.h diff --git a/src/plugins/debugger/CMakeLists.txt b/src/plugins/debugger/CMakeLists.txt index 8f338dd9819..fc405a7fe3a 100644 --- a/src/plugins/debugger/CMakeLists.txt +++ b/src/plugins/debugger/CMakeLists.txt @@ -33,6 +33,7 @@ add_qtc_plugin(Debugger dap/dapclient.cpp dap/dapclient.h dap/dapengine.cpp dap/dapengine.h dap/gdbdapengine.cpp dap/gdbdapengine.h + dap/lldbdapengine.cpp dap/lldbdapengine.h dap/pydapengine.cpp dap/pydapengine.h debugger.qrc debugger_global.h diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index d2f94b9feeb..e311bb36822 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -6,6 +6,7 @@ #include "cmakedapengine.h" #include "dapclient.h" #include "gdbdapengine.h" +#include "lldbdapengine.h" #include "pydapengine.h" #include @@ -1058,6 +1059,8 @@ DebuggerEngine *createDapEngine(Utils::Id runMode) return new CMakeDapEngine; if (runMode == ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE) return new GdbDapEngine; + if (runMode == ProjectExplorer::Constants::DAP_LLDB_DEBUG_RUN_MODE) + return new LldbDapEngine; if (runMode == ProjectExplorer::Constants::DAP_PY_DEBUG_RUN_MODE) return new PyDapEngine; diff --git a/src/plugins/debugger/dap/lldbdapengine.cpp b/src/plugins/debugger/dap/lldbdapengine.cpp new file mode 100644 index 00000000000..32a85c89d41 --- /dev/null +++ b/src/plugins/debugger/dap/lldbdapengine.cpp @@ -0,0 +1,176 @@ +// Copyright (C) 2024 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 "lldbdapengine.h" + +#include "dapclient.h" + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +// TODO: Same class as in gdbdapengine.cpp. Refactor into one place. +class ProcessDataProvider : public IDataProvider +{ +public: + ProcessDataProvider(const DebuggerRunParameters &rp, + const CommandLine &cmd, + QObject *parent = nullptr) + : IDataProvider(parent) + , 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); + if (m_runParameters.debugger.workingDirectory.isDir()) + m_proc.setWorkingDirectory(m_runParameters.debugger.workingDirectory); + 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 + { + if (m_proc.state() == QProcess::Running) + 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 LldbDapClient : public DapClient +{ +public: + LldbDapClient(IDataProvider *provider, QObject *parent = nullptr) + : DapClient(provider, parent) + {} + +private: + const QLoggingCategory &logCategory() override + { + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.lldb", + QtWarningMsg); + return logCategory; + } +}; + +LldbDapEngine::LldbDapEngine() + : DapEngine() +{ + setObjectName("LldbDapEngine"); + setDebuggerName("LLDB"); + setDebuggerType("DAP"); +} + +void LldbDapEngine::handleDapInitialize() +{ + if (!isLocalAttachEngine()) { + DapEngine::handleDapInitialize(); + return; + } + + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); + m_dapClient->postRequest("attach", QJsonObject{{"__restart", ""}}); + qCDebug(logCategory()) << "handleDapAttach"; +} + +bool LldbDapEngine::isLocalAttachEngine() const +{ + return runParameters().startMode == AttachToLocalProcess; +} + +void LldbDapEngine::handleDapConfigurationDone() +{ + if (!isLocalAttachEngine()) { + DapEngine::handleDapConfigurationDone(); + return; + } + + notifyEngineRunAndInferiorStopOk(); +} + +void LldbDapEngine::setupEngine() +{ + QTC_ASSERT(state() == EngineSetupRequested, qCDebug(logCategory()) << state()); + + const DebuggerRunParameters &rp = runParameters(); + CommandLine cmd{rp.debugger.command.executable()}; + + if (isLocalAttachEngine()) + cmd.addArgs({"--debugger-pid", QString::number(rp.attachPID.pid())}); + + IDataProvider *dataProvider = new ProcessDataProvider(rp, cmd, this); + m_dapClient = new LldbDapClient(dataProvider, this); + + connectDataGeneratorSignals(); + m_dapClient->dataProvider()->start(); +} + +bool LldbDapEngine::acceptsBreakpoint(const BreakpointParameters &bp) const +{ + const auto mimeType = Utils::mimeTypeForFile(bp.fileName); + return mimeType.matchesName(Utils::Constants::C_HEADER_MIMETYPE) + || mimeType.matchesName(Utils::Constants::C_SOURCE_MIMETYPE) + || mimeType.matchesName(Utils::Constants::CPP_HEADER_MIMETYPE) + || mimeType.matchesName(Utils::Constants::CPP_SOURCE_MIMETYPE) + || bp.type == BreakpointByFunction; +} + +const QLoggingCategory &LldbDapEngine::logCategory() +{ + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.lldb", + QtWarningMsg); + return logCategory; +} + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/lldbdapengine.h b/src/plugins/debugger/dap/lldbdapengine.h new file mode 100644 index 00000000000..276714dccd5 --- /dev/null +++ b/src/plugins/debugger/dap/lldbdapengine.h @@ -0,0 +1,26 @@ +// Copyright (C) 2024 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 "dapengine.h" + +namespace Debugger::Internal { + +class LldbDapEngine : public DapEngine +{ +public: + LldbDapEngine(); + +private: + void setupEngine() override; + + void handleDapInitialize() override; + void handleDapConfigurationDone() override; + + bool isLocalAttachEngine() const; + bool acceptsBreakpoint(const BreakpointParameters &bp) const override; + const QLoggingCategory &logCategory() override; +}; + +} // Debugger::Internal diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 3c5825eb627..ed54252ef72 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -123,6 +123,7 @@ QtcPlugin { "dapclient.cpp", "dapclient.h", "dapengine.cpp", "dapengine.h", "gdbdapengine.cpp", "gdbdapengine.h", + "lldbdapengine.cpp", "lldbdapengine.h", "pydapengine.cpp", "pydapengine.h", ] } diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index cc3c847b636..527c6649209 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1224,6 +1224,7 @@ void DebuggerPluginPrivate::createDapDebuggerPerspective(QWidget *globalLogWindo ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE, /*forceSkipDeploy=*/true}, DapPerspective{Tr::tr("GDB Preset"), ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE}, + DapPerspective{Tr::tr("LLDB Preset"), ProjectExplorer::Constants::DAP_LLDB_DEBUG_RUN_MODE}, DapPerspective{Tr::tr("Python Preset"), ProjectExplorer::Constants::DAP_PY_DEBUG_RUN_MODE}, }; diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 2f980f7fdc4..b14e9ee30b0 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -1096,6 +1096,7 @@ DebuggerRunWorkerFactory::DebuggerRunWorkerFactory() addSupportedRunMode(ProjectExplorer::Constants::DEBUG_RUN_MODE); addSupportedRunMode(ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE); addSupportedRunMode(ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE); + addSupportedRunMode(ProjectExplorer::Constants::DAP_LLDB_DEBUG_RUN_MODE); addSupportedDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); addSupportedDeviceType("DockerDeviceType"); diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 7638d323c96..8a053c243d8 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -171,6 +171,7 @@ const char NORMAL_RUN_MODE[]="RunConfiguration.NormalRunMode"; const char DEBUG_RUN_MODE[]="RunConfiguration.DebugRunMode"; const char DAP_CMAKE_DEBUG_RUN_MODE[]="RunConfiguration.CmakeDebugRunMode"; const char DAP_GDB_DEBUG_RUN_MODE[]="RunConfiguration.DapGdbDebugRunMode"; +const char DAP_LLDB_DEBUG_RUN_MODE[]="RunConfiguration.DapLldbDebugRunMode"; const char DAP_PY_DEBUG_RUN_MODE[]="RunConfiguration.DapPyDebugRunMode"; const char QML_PROFILER_RUN_MODE[]="RunConfiguration.QmlProfilerRunMode"; const char QML_PROFILER_RUNNER[]="RunConfiguration.QmlProfilerRunner";