https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/130104
>From 2c51a8bdb27764a358e76e554d693a4af57074fc Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Thu, 6 Mar 2025 14:13:58 +0100 Subject: [PATCH] [lldb-dap] Adding support for well typed events. This adds a mechanism for registering well typed events with the DAP. For a proof of concept, this updates the 'exited' and the 'process' event to use the new protocol serialization handlers and updates the call sites to use the new helper. --- lldb/tools/lldb-dap/CMakeLists.txt | 6 +- lldb/tools/lldb-dap/DAP.cpp | 5 +- lldb/tools/lldb-dap/DAP.h | 14 +++ lldb/tools/lldb-dap/EventHelper.cpp | 90 ------------------- lldb/tools/lldb-dap/EventHelper.h | 6 -- lldb/tools/lldb-dap/Events/EventHandler.h | 57 ++++++++++++ .../lldb-dap/Events/ExitedEventHandler.cpp | 20 +++++ .../lldb-dap/Events/ProcessEventHandler.cpp | 34 +++++++ .../lldb-dap/Handler/AttachRequestHandler.cpp | 2 +- .../Handler/InitializeRequestHandler.cpp | 2 +- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 5 +- lldb/tools/lldb-dap/Protocol/ProtocolBase.h | 4 +- .../lldb-dap/Protocol/ProtocolEvents.cpp | 46 ++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolEvents.h | 76 ++++++++++++++++ lldb/tools/lldb-dap/lldb-dap.cpp | 1 + 15 files changed, 262 insertions(+), 106 deletions(-) create mode 100644 lldb/tools/lldb-dap/Events/EventHandler.h create mode 100644 lldb/tools/lldb-dap/Events/ExitedEventHandler.cpp create mode 100644 lldb/tools/lldb-dap/Events/ProcessEventHandler.cpp create mode 100644 lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp create mode 100644 lldb/tools/lldb-dap/Protocol/ProtocolEvents.h diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index adad75a79fa7a..54e6f3ead2695 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -37,6 +37,9 @@ add_lldb_tool(lldb-dap Transport.cpp Watchpoint.cpp + Events/ExitedEventHandler.cpp + Events/ProcessEventHandler.cpp + Handler/ResponseHandler.cpp Handler/AttachRequestHandler.cpp Handler/BreakpointLocationsHandler.cpp @@ -75,8 +78,9 @@ add_lldb_tool(lldb-dap Handler/VariablesRequestHandler.cpp Protocol/ProtocolBase.cpp - Protocol/ProtocolTypes.cpp + Protocol/ProtocolEvents.cpp Protocol/ProtocolRequests.cpp + Protocol/ProtocolTypes.cpp LINK_LIBS liblldb diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index a1e2187288768..a2ae96eb5d967 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -8,6 +8,7 @@ #include "DAP.h" #include "DAPLog.h" +#include "Events/EventHandler.h" #include "Handler/RequestHandler.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" @@ -82,7 +83,9 @@ DAP::DAP(llvm::StringRef path, std::ofstream *log, configuration_done_sent(false), waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), - reverse_request_seq(0), repl_mode(default_repl_mode) {} + reverse_request_seq(0), repl_mode(default_repl_mode), + onExited(ExitedEventHandler(*this)), + onProcess(ProcessEventHandler(*this)) {} DAP::~DAP() = default; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 4c57f9fef3d89..fe902e670cf04 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -58,6 +58,9 @@ typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint> InstructionBreakpointMap; +/// A debug adapter initiated event. +template <typename... Args> using OutgoingEvent = std::function<void(Args...)>; + enum class OutputType { Console, Stdout, Stderr, Telemetry }; /// Buffer size for handling output events. @@ -230,6 +233,17 @@ struct DAP { void operator=(const DAP &rhs) = delete; /// @} + /// Typed Events Handlers + /// @{ + + /// onExited sends an event that the debuggee has exited. + OutgoingEvent<> onExited; + /// onProcess sends an event that indicates that the debugger has begun + /// debugging a new process. + OutgoingEvent<> onProcess; + + /// @} + ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 705eb0a457d9c..7908674eb4642 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -32,87 +32,6 @@ static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) { dap.SendJSON(llvm::json::Value(std::move(event))); } -// "ProcessEvent": { -// "allOf": [ -// { "$ref": "#/definitions/Event" }, -// { -// "type": "object", -// "description": "Event message for 'process' event type. The event -// indicates that the debugger has begun debugging a -// new process. Either one that it has launched, or one -// that it has attached to.", -// "properties": { -// "event": { -// "type": "string", -// "enum": [ "process" ] -// }, -// "body": { -// "type": "object", -// "properties": { -// "name": { -// "type": "string", -// "description": "The logical name of the process. This is -// usually the full path to process's executable -// file. Example: /home/myproj/program.js." -// }, -// "systemProcessId": { -// "type": "integer", -// "description": "The system process id of the debugged process. -// This property will be missing for non-system -// processes." -// }, -// "isLocalProcess": { -// "type": "boolean", -// "description": "If true, the process is running on the same -// computer as the debug adapter." -// }, -// "startMethod": { -// "type": "string", -// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ], -// "description": "Describes how the debug engine started -// debugging this process.", -// "enumDescriptions": [ -// "Process was launched under the debugger.", -// "Debugger attached to an existing process.", -// "A project launcher component has launched a new process in -// a suspended state and then asked the debugger to attach." -// ] -// } -// }, -// "required": [ "name" ] -// } -// }, -// "required": [ "event", "body" ] -// } -// ] -// } -void SendProcessEvent(DAP &dap, LaunchMethod launch_method) { - lldb::SBFileSpec exe_fspec = dap.target.GetExecutable(); - char exe_path[PATH_MAX]; - exe_fspec.GetPath(exe_path, sizeof(exe_path)); - llvm::json::Object event(CreateEventObject("process")); - llvm::json::Object body; - EmplaceSafeString(body, "name", std::string(exe_path)); - const auto pid = dap.target.GetProcess().GetProcessID(); - body.try_emplace("systemProcessId", (int64_t)pid); - body.try_emplace("isLocalProcess", true); - const char *startMethod = nullptr; - switch (launch_method) { - case Launch: - startMethod = "launch"; - break; - case Attach: - startMethod = "attach"; - break; - case AttachForSuspendedLaunch: - startMethod = "attachForSuspendedLaunch"; - break; - } - body.try_emplace("startMethod", startMethod); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - // Send a thread stopped event for all threads as long as the process // is stopped. void SendThreadStoppedEvent(DAP &dap) { @@ -235,13 +154,4 @@ void SendContinuedEvent(DAP &dap) { dap.SendJSON(llvm::json::Value(std::move(event))); } -// Send a "exited" event to indicate the process has exited. -void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) { - llvm::json::Object event(CreateEventObject("exited")); - llvm::json::Object body; - body.try_emplace("exitCode", (int64_t)process.GetExitStatus()); - event.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(event))); -} - } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h index 90b009c73089e..e6a54e63d00df 100644 --- a/lldb/tools/lldb-dap/EventHelper.h +++ b/lldb/tools/lldb-dap/EventHelper.h @@ -14,10 +14,6 @@ namespace lldb_dap { struct DAP; -enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch }; - -void SendProcessEvent(DAP &dap, LaunchMethod launch_method); - void SendThreadStoppedEvent(DAP &dap); void SendTerminatedEvent(DAP &dap); @@ -26,8 +22,6 @@ void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process); void SendContinuedEvent(DAP &dap); -void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process); - } // namespace lldb_dap #endif diff --git a/lldb/tools/lldb-dap/Events/EventHandler.h b/lldb/tools/lldb-dap/Events/EventHandler.h new file mode 100644 index 0000000000000..f0fac0d635ce2 --- /dev/null +++ b/lldb/tools/lldb-dap/Events/EventHandler.h @@ -0,0 +1,57 @@ +//===-- EventHandler.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_DAP_EVENTS_EVENT_HANDLER +#define LLDB_TOOLS_LLDB_DAP_EVENTS_EVENT_HANDLER + +#include "DAP.h" +#include "Protocol/ProtocolBase.h" +#include "Protocol/ProtocolEvents.h" +#include "lldb/API/SBProcess.h" + +namespace lldb_dap { + +template <typename Body, typename... Args> class BaseEventHandler { +public: + BaseEventHandler(DAP &dap) : dap(dap) {} + + virtual ~BaseEventHandler() = default; + + virtual llvm::StringLiteral getEvent() const = 0; + virtual Body Handler(Args...) const = 0; + + void operator()(Args... args) const { + Body body = Handler(args...); + protocol::Event event{/*event=*/getEvent().str(), /*body=*/std::move(body)}; + dap.Send(event); + } + +protected: + DAP &dap; +}; + +/// Handler for the event indicates that the debuggee has exited and returns its +/// exit code. +class ExitedEventHandler : public BaseEventHandler<protocol::ExitedEventBody> { +public: + using BaseEventHandler::BaseEventHandler; + llvm::StringLiteral getEvent() const override { return "exited"; } + protocol::ExitedEventBody Handler() const override; +}; + +class ProcessEventHandler + : public BaseEventHandler<protocol::ProcessEventBody> { +public: + using BaseEventHandler::BaseEventHandler; + llvm::StringLiteral getEvent() const override { return "process"; } + protocol::ProcessEventBody Handler() const override; +}; + +} // end namespace lldb_dap + +#endif diff --git a/lldb/tools/lldb-dap/Events/ExitedEventHandler.cpp b/lldb/tools/lldb-dap/Events/ExitedEventHandler.cpp new file mode 100644 index 0000000000000..010b2df3de456 --- /dev/null +++ b/lldb/tools/lldb-dap/Events/ExitedEventHandler.cpp @@ -0,0 +1,20 @@ +//===-- ExitedEventHandler.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Events/EventHandler.h" +#include "lldb/API/SBProcess.h" + +namespace lldb_dap { + +protocol::ExitedEventBody ExitedEventHandler::Handler() const { + protocol::ExitedEventBody body; + body.exitCode = dap.target.GetProcess().GetExitStatus(); + return body; +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Events/ProcessEventHandler.cpp b/lldb/tools/lldb-dap/Events/ProcessEventHandler.cpp new file mode 100644 index 0000000000000..52b3ae1982126 --- /dev/null +++ b/lldb/tools/lldb-dap/Events/ProcessEventHandler.cpp @@ -0,0 +1,34 @@ +//===-- ProcessEventHandler.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Events/EventHandler.h" +#include "Protocol/ProtocolEvents.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" + +using namespace llvm; +using namespace lldb_dap::protocol; + +namespace lldb_dap { + +ProcessEventBody ProcessEventHandler::Handler() const { + ProcessEventBody body; + + char path[PATH_MAX] = {0}; + dap.target.GetExecutable().GetPath(path, sizeof(path)); + body.name = path; + body.systemProcessId = dap.target.GetProcess().GetProcessID(); + body.isLocalProcess = dap.target.GetPlatform().GetName() == + lldb::SBPlatform::GetHostPlatform().GetName(); + body.startMethod = dap.is_attach ? ProcessEventBody::StartMethod::attach + : ProcessEventBody::StartMethod::launch; + body.pointerSize = dap.target.GetAddressByteSize(); + return body; +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 20f7c80a1ed90..95516610eb5b5 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -201,7 +201,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendJSON(llvm::json::Value(std::move(response))); if (error.Success()) { - SendProcessEvent(dap, Attach); + dap.onProcess(); dap.SendJSON(CreateEventObject("initialized")); } } diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index 3262b70042a0e..d9b905b6914cc 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -178,7 +178,7 @@ static void EventThreadFunction(DAP &dap) { // Run any exit LLDB commands the user specified in the // launch.json dap.RunExitCommands(); - SendProcessExitedEvent(dap, process); + dap.onExited(); dap.SendTerminatedEvent(); done = true; } diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index f64c186376a36..2c8dddef9bde5 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -124,10 +124,7 @@ void LaunchRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendJSON(llvm::json::Value(std::move(response))); if (!status.Fail()) { - if (dap.is_attach) - SendProcessEvent(dap, Attach); // this happens when doing runInTerminal - else - SendProcessEvent(dap, Launch); + dap.onProcess(); } dap.SendJSON(CreateEventObject("initialized")); } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h index e10a903b80aaa..2ddfeb8ae0852 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h @@ -17,8 +17,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_H -#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_H +#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H +#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_BASE_H #include "llvm/Support/JSON.h" #include <cstdint> diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp new file mode 100644 index 0000000000000..5c0eb911408e7 --- /dev/null +++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp @@ -0,0 +1,46 @@ +//===-- ProtocolEvents.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Protocol/ProtocolEvents.h" +#include "llvm/Support/JSON.h" + +using namespace llvm; + +namespace lldb_dap::protocol { + +json::Value toJSON(const ExitedEventBody &EEB) { + return json::Object{{"exitCode", EEB.exitCode}}; +} + +json::Value toJSON(const ProcessEventBody::StartMethod &m) { + switch (m) { + case ProcessEventBody::StartMethod::launch: + return "launch"; + case ProcessEventBody::StartMethod::attach: + return "attach"; + case ProcessEventBody::StartMethod::attachForSuspendedLaunch: + return "attachForSuspendedLaunch"; + } +} + +json::Value toJSON(const ProcessEventBody &PEB) { + json::Object result{{"name", PEB.name}}; + + if (PEB.systemProcessId) + result.insert({"systemProcessId", PEB.systemProcessId}); + if (PEB.isLocalProcess) + result.insert({"isLocalProcess", PEB.isLocalProcess}); + if (PEB.startMethod) + result.insert({"startMethod", PEB.startMethod}); + if (PEB.pointerSize) + result.insert({"pointerSize", PEB.pointerSize}); + + return std::move(result); +} + +} // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h new file mode 100644 index 0000000000000..58601008341b2 --- /dev/null +++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h @@ -0,0 +1,76 @@ +//===-- ProtocolBase.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains POD structs based on the DAP specification at +// https://microsoft.github.io/debug-adapter-protocol/specification +// +// This is not meant to be a complete implementation, new interfaces are added +// when they're needed. +// +// Each struct has a toJSON and fromJSON function, that converts between +// the struct and a JSON representation. (See JSON.h) +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H +#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_EVENTS_H + +#include "lldb/lldb-types.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" + +namespace lldb_dap::protocol { + +// MARK: Events + +/// The event indicates that the debuggee has exited and returns its exit code. +struct ExitedEventBody { + static llvm::StringLiteral getEvent() { return "exited"; } + + /// The exit code returned from the debuggee. + int exitCode; +}; +llvm::json::Value toJSON(const ExitedEventBody &); + +/// The event indicates that the debugger has begun debugging a new process. +/// Either one that it has launched, or one that it has attached to. +struct ProcessEventBody { + /// The logical name of the process. This is usually the full path to + /// process's executable file. Example: /home/example/myproj/program.js. + std::string name; + + /// The process ID of the debugged process, as assigned by the operating + /// system. This property should be omitted for logical processes that do not + /// map to operating system processes on the machine. + std::optional<lldb::pid_t> systemProcessId; + + /// If true, the process is running on the same computer as the debug adapter. + std::optional<bool> isLocalProcess; + + enum class StartMethod { + /// Process was launched under the debugger. + launch, + /// Debugger attached to an existing process. + attach, + /// A project launcher component has launched a new process in a suspended + /// state and then asked the debugger to attach. + attachForSuspendedLaunch + }; + + /// Describes how the debug engine started debugging this process. + std::optional<StartMethod> startMethod; + + /// The size of a pointer or address for this process, in bits. This value may + /// be used by clients when formatting addresses for display. + std::optional<int> pointerSize; +}; +llvm::json::Value toJSON(const ProcessEventBody &); + +} // namespace lldb_dap::protocol + +#endif diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 59e31cf8e2cc8..f2ff2c48d5afc 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -9,6 +9,7 @@ #include "DAP.h" #include "DAPLog.h" #include "EventHelper.h" +#include "Events/EventHandler.h" #include "Handler/RequestHandler.h" #include "RunInTerminal.h" #include "Transport.h" _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits