https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137448
>From d0d8c7e24fd7ee37f4a3678537df1e28c8318d04 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 10:25:21 +0200 Subject: [PATCH 1/6] adding breakpoints protocol types --- lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 178 +++++++++++++++++++ llvm/include/llvm/Support/JSON.h | 8 + 2 files changed, 186 insertions(+) diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 54941f24efbd9..8b193287acc19 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -322,6 +322,184 @@ enum SteppingGranularity : unsigned { bool fromJSON(const llvm::json::Value &, SteppingGranularity &, llvm::json::Path); +/// Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests. +struct Breakpoint { + /// A machine-readable explanation of why a breakpoint may not be verified. + enum class Reason : unsigned { + /// Indicates a breakpoint might be verified in the future, but + /// the adapter cannot verify it in the current state. + eBreakpointReasonPending, + /// Indicates a breakpoint was not able to be verified, and the + /// adapter does not believe it can be verified without intervention. + eBreakpointReasonFailed, + }; + + /// The identifier for the breakpoint. It is needed if breakpoint events are + /// used to update or remove breakpoints. + std::optional<int> id; + + /// If true, the breakpoint could be set (but not necessarily at the desired + /// location). + bool verified; + + /// A message about the state of the breakpoint. + /// This is shown to the user and can be used to explain why a breakpoint could + /// not be verified. + std::optional<std::string> message; + + /// The source where the breakpoint is located. + std::optional<Source> source; + + /// The start line of the actual range covered by the breakpoint. + std::optional<uint32_t> line; + + /// Start position of the source range covered by the breakpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional<uint32_t> column; + + /// The end line of the actual range covered by the breakpoint. + std::optional<uint32_t> endLine; + + /// End position of the source range covered by the breakpoint. It is measured + /// in UTF-16 code units and the client capability `columnsStartAt1` determines + /// whether it is 0- or 1-based. + /// If no end line is given, then the end column is assumed to be in the start + /// line. + std::optional<uint32_t> endColumn; + + /// A memory reference to where the breakpoint is set. + std::optional<std::string> instructionReference; + + /// The offset from the instruction reference. + /// This can be negative. + std::optional<int64_t> offset; + + /// A machine-readable explanation of why a breakpoint may not be verified. If + /// a breakpoint is verified or a specific reason is not known, the adapter + /// should omit this property. + std::optional<Reason> reason; +}; +llvm::json::Value toJSON(const Breakpoint &); + +/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` request +struct SourceBreakpoint { + /// The source line of the breakpoint or logpoint. + uint32_t line; + + /// Start position within source line of the breakpoint or logpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional<uint32_t> column; + + /// The expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional<std::string> condition; + + /// The expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + /// If both this property and `condition` are specified, `hitCondition` should + /// be evaluated only if the `condition` is met, and the debug adapter should + /// stop only if both conditions are met. + std::optional<std::string> hitCondition; + + /// If this attribute exists and is non-empty, the debug adapter must not + /// 'break' (stop) + /// but log the message instead. Expressions within `{}` are interpolated. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsLogPoints` is true. + /// If either `hitCondition` or `condition` is specified, then the message + /// should only be logged if those conditions are met. + std::optional<std::string> logMessage; + + /// The mode of this breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional<std::string> mode; +}; +bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, + llvm::json::Path); + +/// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. +struct FunctionBreakpoint { + /// The name of the function. + std::string name; + + /// An expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional<std::string> condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + std::optional<std::string> hitCondition; +}; +bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &, + llvm::json::Path); + +/// This enumeration defines all possible access types for data breakpoints. Values: ‘read’, ‘write’, ‘readWrite’ +enum DataBreakpointAccessType : unsigned { + eDataBreakpointAccessTypeRead, + eDataBreakpointAccessTypeWrite, + eDataBreakpointAccessTypeReadWrite +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &, + llvm::json::Path); + +/// Properties of a data breakpoint passed to the `setDataBreakpoints` request. +struct DataBreakpointInfo { + /// An id representing the data. This id is returned from the + /// `dataBreakpointInfo` request. + std::string dataId; + + /// The access type of the data. + std::optional<DataBreakpointAccessType> accessType; + + /// An expression for conditional breakpoints. + std::optional<std::string> condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + std::optional<std::string> hitCondition; +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointInfo &, + llvm::json::Path); + +/// Properties of a breakpoint passed to the `setInstructionBreakpoints` request +struct InstructionBreakpoint { + /// The instruction reference of the breakpoint. + /// This should be a memory or instruction pointer reference from an + /// `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or + /// `Breakpoint`. + std::string instructionReference; + + /// The offset from the instruction reference in bytes. + /// This can be negative. + std::optional<int64_t> offset; + + /// An expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional<std::string> condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + std::optional<std::string> hitCondition; + + /// The mode of this breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional<std::string> mode; +}; +bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, + llvm::json::Path); + + } // namespace lldb_dap::protocol #endif diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index 7f7f5f6228763..f1f4f4db709dd 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) { P.report("expected boolean"); return false; } +inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected integer"); + return false; +} inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { if (auto S = E.getAsUINT64()) { Out = *S; >From 2c39c99082df8429f45258c1c9911a9c8f858e3a Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 10:45:37 +0200 Subject: [PATCH 2/6] add all breakpoint requests JSON types --- .../lldb-dap/Protocol/ProtocolRequests.h | 85 +++++++++++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 60 +++++++++++++ 2 files changed, 145 insertions(+) diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 33f93cc38799a..ae515bd186c76 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -377,6 +377,91 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path); /// body field is required. using StepOutResponse = VoidResponse; +/// Arguments for `setBreakpoints` request. +struct SetBreakpointsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// The code locations of the breakpoints. + std::optional<std::vector<SourceBreakpoint>> breakpoints; + + /// Deprecated: The code locations of the breakpoints. + std::optional<std::vector<uint32_t>> lines; + + /// A value of true indicates that the underlying source has been modified + /// which results in new breakpoint locations. + std::optional<bool> sourceModified; +}; +bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +/// This includes the actual code location and whether the breakpoint could be verified. +/// The breakpoints returned are in the same order as the elements of the breakpoints +/// (or the deprecated lines) array in the arguments. +struct SetBreakpointsResponseBody { + /// Information about the breakpoints. + /// The array elements are in the same order as the elements of the + /// `breakpoints` (or the deprecated `lines`) array in the arguments. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetBreakpointsResponseBody &); + +/// Arguments for `setFunctionBreakpoints` request. +struct SetFunctionBreakpointsArguments { + /// The function names of the breakpoints. + std::vector<FunctionBreakpoint> breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetFunctionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setFunctionBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +struct SetFunctionBreakpointsResponseBody { + /// Information about the breakpoints. The array elements correspond to the + /// elements of the `breakpoints` array. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &); + +/// Arguments for `setExceptionBreakpoints` request. +struct SetExceptionBreakpointsArguments { + /// Set of exception filters specified by their ID. The set of all possible + /// exception filters is defined by the `exceptionBreakpointFilters` + /// capability. The `filter` and `filterOptions` sets are additive. + std::vector<std::string> filters; + + /// Set of exception filters and their options. The set of all possible + /// exception filters is defined by the `exceptionBreakpointFilters` + /// capability. This attribute is only honored by a debug adapter if the + /// corresponding capability `supportsExceptionFilterOptions` is true. The + /// `filter` and `filterOptions` sets are additive. + std::optional<std::vector<ExceptionFilterOptions>> filterOptions; + + /// Configuration options for selected exceptions. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsExceptionOptions` is true. + std::optional<std::vector<ExceptionOptions>> exceptionOptions; +}; + +/// Arguments for `setInstructionBreakpoints` request. +struct SetInstructionBreakpointsArguments { + /// The instruction references of the breakpoints. + std::vector<InstructionBreakpoint> breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetInstructionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setInstructionBreakpoints` request. +struct SetInstructionBreakpointsResponseBody { + /// Information about the breakpoints. The array elements correspond to the + /// elements of the `breakpoints` array. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 8b193287acc19..f10adbac9e41c 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -499,6 +499,66 @@ struct InstructionBreakpoint { bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, llvm::json::Path); +/// An ExceptionFilterOptions is used to specify an exception filter together with a condition for the `setExceptionBreakpoints` request. +struct ExceptionFilterOptions { + /// ID of an exception filter returned by the `exceptionBreakpointFilters` + /// capability. + std::string filterId; + + /// An expression for conditional exceptions. + /// The exception breaks into the debugger if the result of the condition is + /// true. + std::optional<std::string> condition; + + /// The mode of this exception breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional<std::string> mode; +}; +bool fromJSON(const llvm::json::Value &, ExceptionFilterOptions &, + llvm::json::Path); + +/// This enumeration defines all possible conditions when a thrown exception +/// should result in a break. +enum ExceptionBreakMode : unsigned { + /// Never breaks. + eExceptionBreakModeNever, + /// Always breaks. + eExceptionBreakModeAlways, + /// Breaks when the exception is unhandled. + eExceptionBreakModeUnhandled, + /// Breaks if the exception is not handled by user code. + eExceptionBreakModeUserUnhandled +}; +bool fromJSON(const llvm::json::Value &, ExceptionBreakMode &, + llvm::json::Path); + +/// An ExceptionPathSegment represents a segment in a path that is used to +/// match leafs or nodes in a tree of exceptions. +struct ExceptionPathSegment { + /// If false or missing, this segment matches the names provided. Otherwise, + /// it matches anything except the names provided. + std::optional<bool> negate; + + /// Depending on the value of `negate`, the names that should match or not + /// match. + std::vector<std::string> names; +}; +bool fromJSON(const llvm::json::Value &, ExceptionPathSegment &, + llvm::json::Path); + +/// ExceptionOptions assigns configuration options to a set of exceptions. +struct ExceptionOptions { + /// A path that selects a single or multiple exceptions in a tree. If `path` + /// is missing, the whole tree is selected. + /// By convention, the first segment of the path is a category that is used + /// to group exceptions in the UI. + std::optional<std::vector<ExceptionPathSegment>> path; + + /// Condition when a thrown exception should result in a break. + ExceptionBreakMode breakMode; +}; +bool fromJSON(const llvm::json::Value &, ExceptionOptions &, + llvm::json::Path); } // namespace lldb_dap::protocol >From a18da83181ad303ef344f88f61ea00a3306c3b23 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 11:12:31 +0200 Subject: [PATCH 3/6] decouple JSON from DAP Breakpoint classes --- lldb/tools/lldb-dap/Breakpoint.h | 4 ++- lldb/tools/lldb-dap/BreakpointBase.cpp | 10 +++--- lldb/tools/lldb-dap/BreakpointBase.h | 3 +- lldb/tools/lldb-dap/FunctionBreakpoint.cpp | 8 ++--- lldb/tools/lldb-dap/FunctionBreakpoint.h | 3 +- lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 15 ++++---- lldb/tools/lldb-dap/InstructionBreakpoint.h | 4 ++- .../lldb-dap/Protocol/ProtocolRequests.h | 6 ++-- lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 34 ++++++++++--------- lldb/tools/lldb-dap/SourceBreakpoint.cpp | 13 ++++--- lldb/tools/lldb-dap/SourceBreakpoint.h | 3 +- 11 files changed, 55 insertions(+), 48 deletions(-) diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h index 580017125af44..987dbeadd4659 100644 --- a/lldb/tools/lldb-dap/Breakpoint.h +++ b/lldb/tools/lldb-dap/Breakpoint.h @@ -17,7 +17,9 @@ namespace lldb_dap { class Breakpoint : public BreakpointBase { public: - Breakpoint(DAP &d, const llvm::json::Object &obj) : BreakpointBase(d, obj) {} + Breakpoint(DAP &d, const std::optional<std::string> &condition, + const std::optional<std::string> &hit_condition) + : BreakpointBase(d, condition, hit_condition) {} Breakpoint(DAP &d, lldb::SBBreakpoint bp) : BreakpointBase(d), m_bp(bp) {} lldb::break_id_t GetID() const { return m_bp.GetID(); } diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp index 331ce8efee9bc..960dc5fc5ee56 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.cpp +++ b/lldb/tools/lldb-dap/BreakpointBase.cpp @@ -12,11 +12,11 @@ using namespace lldb_dap; -BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj) - : m_dap(d), - m_condition(std::string(GetString(obj, "condition").value_or(""))), - m_hit_condition( - std::string(GetString(obj, "hitCondition").value_or(""))) {} +BreakpointBase::BreakpointBase(DAP &d, + const std::optional<std::string> &condition, + const std::optional<std::string> &hit_condition) + : m_dap(d), m_condition(condition.value_or("")), + m_hit_condition(hit_condition.value_or("")) {} void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) { if (m_condition != request_bp.m_condition) { diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h index 4c13326624831..e7aef44cc9d9a 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -18,7 +18,8 @@ namespace lldb_dap { class BreakpointBase { public: explicit BreakpointBase(DAP &d) : m_dap(d) {} - BreakpointBase(DAP &d, const llvm::json::Object &obj); + BreakpointBase(DAP &d, const std::optional<std::string> &condition, + const std::optional<std::string> &hit_condition); virtual ~BreakpointBase() = default; virtual void SetCondition() = 0; diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp index d87723f7557bd..1ea9cddb9f689 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp @@ -8,15 +8,15 @@ #include "FunctionBreakpoint.h" #include "DAP.h" -#include "JSONUtils.h" #include "lldb/API/SBMutex.h" #include <mutex> namespace lldb_dap { -FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj) - : Breakpoint(d, obj), - m_function_name(std::string(GetString(obj, "name").value_or(""))) {} +FunctionBreakpoint::FunctionBreakpoint( + DAP &d, const protocol::FunctionBreakpoint &breakpoint) + : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition), + m_function_name(breakpoint.name) {} void FunctionBreakpoint::SetBreakpoint() { lldb::SBMutex lock = m_dap.GetAPIMutex(); diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.h b/lldb/tools/lldb-dap/FunctionBreakpoint.h index 7100360cd7ec1..76fbdb3e886a4 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.h +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h @@ -11,12 +11,13 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" namespace lldb_dap { class FunctionBreakpoint : public Breakpoint { public: - FunctionBreakpoint(DAP &dap, const llvm::json::Object &obj); + FunctionBreakpoint(DAP &dap, const protocol::FunctionBreakpoint &breakpoint); /// Set this breakpoint in LLDB as a new breakpoint. void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp index dfdc6319ac9e8..ddae1a8b20243 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp @@ -9,20 +9,19 @@ #include "InstructionBreakpoint.h" #include "DAP.h" -#include "JSONUtils.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBTarget.h" #include "llvm/ADT/StringRef.h" namespace lldb_dap { -InstructionBreakpoint::InstructionBreakpoint(DAP &d, - const llvm::json::Object &obj) - : Breakpoint(d, obj), m_instruction_address_reference(LLDB_INVALID_ADDRESS), - m_offset(GetInteger<int64_t>(obj, "offset").value_or(0)) { - GetString(obj, "instructionReference") - .value_or("") - .getAsInteger(0, m_instruction_address_reference); +InstructionBreakpoint::InstructionBreakpoint( + DAP &d, const protocol::InstructionBreakpoint &breakpoint) + : Breakpoint(d, breakpoint.condition, breakpoint.hitCondition), + m_instruction_address_reference(LLDB_INVALID_ADDRESS), + m_offset(breakpoint.offset.value_or(0)) { + llvm::StringRef instruction_reference(breakpoint.instructionReference); + instruction_reference.getAsInteger(0, m_instruction_address_reference); m_instruction_address_reference += m_offset; } diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h index 6ed980e00d038..a8c8f2113e5eb 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.h +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h @@ -12,6 +12,7 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/lldb-types.h" #include <cstdint> @@ -20,7 +21,8 @@ namespace lldb_dap { /// Instruction Breakpoint class InstructionBreakpoint : public Breakpoint { public: - InstructionBreakpoint(DAP &d, const llvm::json::Object &obj); + InstructionBreakpoint(DAP &d, + const protocol::InstructionBreakpoint &breakpoint); /// Set instruction breakpoint in LLDB as a new breakpoint. void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index ae515bd186c76..b1a395ec4ae3d 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -398,9 +398,9 @@ bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &, /// Response to `setBreakpoints` request. /// Returned is information about each breakpoint created by this request. -/// This includes the actual code location and whether the breakpoint could be verified. -/// The breakpoints returned are in the same order as the elements of the breakpoints -/// (or the deprecated lines) array in the arguments. +/// This includes the actual code location and whether the breakpoint could be +/// verified. The breakpoints returned are in the same order as the elements of +/// the breakpoints (or the deprecated lines) array in the arguments. struct SetBreakpointsResponseBody { /// Information about the breakpoints. /// The array elements are in the same order as the elements of the diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index f10adbac9e41c..216dcd907ae15 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -322,7 +322,9 @@ enum SteppingGranularity : unsigned { bool fromJSON(const llvm::json::Value &, SteppingGranularity &, llvm::json::Path); -/// Information about a breakpoint created in `setBreakpoints`, `setFunctionBreakpoints`, `setInstructionBreakpoints`, or `setDataBreakpoints` requests. +/// Information about a breakpoint created in `setBreakpoints`, +/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or +/// `setDataBreakpoints` requests. struct Breakpoint { /// A machine-readable explanation of why a breakpoint may not be verified. enum class Reason : unsigned { @@ -343,8 +345,8 @@ struct Breakpoint { bool verified; /// A message about the state of the breakpoint. - /// This is shown to the user and can be used to explain why a breakpoint could - /// not be verified. + /// This is shown to the user and can be used to explain why a breakpoint + /// could not be verified. std::optional<std::string> message; /// The source where the breakpoint is located. @@ -362,10 +364,9 @@ struct Breakpoint { std::optional<uint32_t> endLine; /// End position of the source range covered by the breakpoint. It is measured - /// in UTF-16 code units and the client capability `columnsStartAt1` determines - /// whether it is 0- or 1-based. - /// If no end line is given, then the end column is assumed to be in the start - /// line. + /// in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. If no end line is given, then the + /// end column is assumed to be in the start line. std::optional<uint32_t> endColumn; /// A memory reference to where the breakpoint is set. @@ -373,7 +374,7 @@ struct Breakpoint { /// The offset from the instruction reference. /// This can be negative. - std::optional<int64_t> offset; + std::optional<int32_t> offset; /// A machine-readable explanation of why a breakpoint may not be verified. If /// a breakpoint is verified or a specific reason is not known, the adapter @@ -382,7 +383,8 @@ struct Breakpoint { }; llvm::json::Value toJSON(const Breakpoint &); -/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` request +/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` +/// request struct SourceBreakpoint { /// The source line of the breakpoint or logpoint. uint32_t line; @@ -419,8 +421,7 @@ struct SourceBreakpoint { /// `breakpointModes` the debug adapter advertised in its `Capabilities`. std::optional<std::string> mode; }; -bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, - llvm::json::Path); +bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path); /// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. struct FunctionBreakpoint { @@ -441,7 +442,8 @@ struct FunctionBreakpoint { bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &, llvm::json::Path); -/// This enumeration defines all possible access types for data breakpoints. Values: ‘read’, ‘write’, ‘readWrite’ +/// This enumeration defines all possible access types for data breakpoints. +/// Values: ‘read’, ‘write’, ‘readWrite’ enum DataBreakpointAccessType : unsigned { eDataBreakpointAccessTypeRead, eDataBreakpointAccessTypeWrite, @@ -479,7 +481,7 @@ struct InstructionBreakpoint { /// The offset from the instruction reference in bytes. /// This can be negative. - std::optional<int64_t> offset; + std::optional<int32_t> offset; /// An expression for conditional breakpoints. /// It is only honored by a debug adapter if the corresponding capability @@ -499,7 +501,8 @@ struct InstructionBreakpoint { bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, llvm::json::Path); -/// An ExceptionFilterOptions is used to specify an exception filter together with a condition for the `setExceptionBreakpoints` request. +/// An ExceptionFilterOptions is used to specify an exception filter together +/// with a condition for the `setExceptionBreakpoints` request. struct ExceptionFilterOptions { /// ID of an exception filter returned by the `exceptionBreakpointFilters` /// capability. @@ -557,8 +560,7 @@ struct ExceptionOptions { /// Condition when a thrown exception should result in a break. ExceptionBreakMode breakMode; }; -bool fromJSON(const llvm::json::Value &, ExceptionOptions &, - llvm::json::Path); +bool fromJSON(const llvm::json::Value &, ExceptionOptions &, llvm::json::Path); } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp index a7e00cae36fbc..4581c995b4260 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp +++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp @@ -26,13 +26,12 @@ namespace lldb_dap { -SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj) - : Breakpoint(dap, obj), - m_log_message(GetString(obj, "logMessage").value_or("").str()), - m_line( - GetInteger<uint64_t>(obj, "line").value_or(LLDB_INVALID_LINE_NUMBER)), - m_column(GetInteger<uint64_t>(obj, "column") - .value_or(LLDB_INVALID_COLUMN_NUMBER)) {} +SourceBreakpoint::SourceBreakpoint(DAP &dap, + const protocol::SourceBreakpoint &breakpoint) + : Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition), + m_log_message(breakpoint.logMessage.value_or("")), + m_line(breakpoint.line), + m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {} void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { lldb::SBMutex lock = m_dap.GetAPIMutex(); diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.h b/lldb/tools/lldb-dap/SourceBreakpoint.h index d01411547d12a..5b15296f861c5 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.h +++ b/lldb/tools/lldb-dap/SourceBreakpoint.h @@ -11,6 +11,7 @@ #include "Breakpoint.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBError.h" #include "llvm/ADT/StringRef.h" #include <cstdint> @@ -21,7 +22,7 @@ namespace lldb_dap { class SourceBreakpoint : public Breakpoint { public: - SourceBreakpoint(DAP &d, const llvm::json::Object &obj); + SourceBreakpoint(DAP &d, const protocol::SourceBreakpoint &breakpoint); // Set this breakpoint in LLDB as a new breakpoint void SetBreakpoint(const llvm::StringRef source_path); >From b7b2454540a1b6f95c2b142768eaaa59bc6be68b Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 11:23:51 +0200 Subject: [PATCH 4/6] forgor exception breakpoint response --- lldb/tools/lldb-dap/BreakpointBase.h | 2 +- .../lldb-dap/Protocol/ProtocolRequests.h | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h index e7aef44cc9d9a..44f3bfa15afc3 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -10,7 +10,7 @@ #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H #include "DAPForward.h" -#include "llvm/ADT/StringRef.h" +#include <optional> #include <string> namespace lldb_dap { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index b1a395ec4ae3d..7bcb8212575f4 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -445,6 +445,34 @@ struct SetExceptionBreakpointsArguments { /// capability `supportsExceptionOptions` is true. std::optional<std::vector<ExceptionOptions>> exceptionOptions; }; +bool fromJSON(const llvm::json::Value &, SetExceptionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setExceptionBreakpoints` request. +/// The response contains an array of Breakpoint objects with information about +/// each exception breakpoint or filter. The Breakpoint objects are in the same +/// order as the elements of the filters, filterOptions, exceptionOptions arrays +/// given as arguments. If both filters and filterOptions are given, the +/// returned array must start with filters information first, followed by +/// filterOptions information. +/// The verified property of a Breakpoint object signals whether the exception +/// breakpoint or filter could be successfully created and whether the condition +/// is valid. In case of an error the message property explains the problem. The +/// id property can be used to introduce a unique ID for the exception +/// breakpoint or filter so that it can be updated subsequently by sending +/// breakpoint events. For backward compatibility both the breakpoints array and +/// the enclosing body are optional. If these elements are missing a client is +/// not able to show problems for individual exception breakpoints or filters. +struct SetExceptionBreakpointsResponseBody { + /// Information about the exception breakpoints or filters. + /// The breakpoints returned are in the same order as the elements of the + /// `filters`, `filterOptions`, `exceptionOptions` arrays in the arguments. + /// If both `filters` and `filterOptions` are given, the returned array must + /// start with `filters` information first, followed by `filterOptions` + /// information. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetExceptionBreakpointsResponseBody &); /// Arguments for `setInstructionBreakpoints` request. struct SetInstructionBreakpointsArguments { @@ -462,6 +490,88 @@ struct SetInstructionBreakpointsResponseBody { }; llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &); +/// Arguments for `dataBreakpointInfo` request. +struct DataBreakpointInfoArguments { + /// Reference to the variable container if the data breakpoint is requested + /// for a child of the container. The `variablesReference` must have been + /// obtained in the current suspended state.See 'Lifetime of Object + /// References' in the Overview section for details. + std::optional<int64_t> variablesReference; + + /// The name of the variable's child to obtain data breakpoint information + /// for. If `variablesReference` isn't specified, this can be an expression, + /// or an address if `asAddress` is also true. + std::string name; + + /// When `name` is an expression, evaluate it in the scope of this stack + /// frame. If not specified, the expression is evaluated in the global scope. + /// When `asAddress` is true, the `frameId` is ignored. + std::optional<int64_t> frameId; + + /// If specified, a debug adapter should return information for the range of + /// memory extending `bytes` number of bytes from the address or variable + /// specified by `name`. Breakpoints set using the resulting data ID should + /// pause on data access anywhere within that range. + /// Clients may set this property only if the `supportsDataBreakpointBytes` + /// capability is true. + std::optional<int64_t> bytes; + + /// If `true`, the `name` is a memory address and the debugger should + /// interpret it as a decimal value, or hex value if it is prefixed with `0x`. + /// Clients may set this property only if the `supportsDataBreakpointBytes` + /// capability is true. + std::optional<bool> asAddress; + + /// The mode of the desired breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional<std::string> mode; +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointInfoArguments &, + llvm::json::Path); + +/// Response to `dataBreakpointInfo` request. +struct DataBreakpointInfoResponseBody { + /// An identifier for the data on which a data breakpoint can be registered + /// with the `setDataBreakpoints` request or null if no data breakpoint is + /// available. If a `variablesReference` or `frameId` is passed, the `dataId` + /// is valid in the current suspended state, otherwise it's valid + /// indefinitely. See 'Lifetime of Object References' in the Overview section + /// for details. Breakpoints set using the `dataId` in the + /// `setDataBreakpoints` request may outlive the lifetime of the associated + /// `dataId`. + std::optional<std::string> dataId; + + /// UI string that describes on what data the breakpoint is set on or why a + /// data breakpoint is not available. + std::string description; + + /// Attribute lists the available access types for a potential data + /// breakpoint. A UI client could surface this information. + std::optional<std::vector<DataBreakpointAccessType>> accessTypes; + + /// Attribute indicates that a potential data breakpoint could be persisted + /// across sessions. + std::optional<bool> canPersist; +}; +llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &); + +/// Arguments for `setDataBreakpoints` request. +struct SetDataBreakpointsArguments { + /// The contents of this array replaces all existing data breakpoints. An + /// empty array clears all data breakpoints. + std::vector<DataBreakpointInfo> breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetDataBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setDataBreakpoints` request. +struct SetDataBreakpointsResponseBody { + /// Information about the data breakpoints. The array elements correspond to + /// the elements of the input argument `breakpoints` array. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &); + } // namespace lldb_dap::protocol #endif >From 68b9504ce7c5620772d84875eeb2bba746614534 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 12:21:10 +0200 Subject: [PATCH 5/6] migrating breakpoint requests --- lldb/tools/lldb-dap/Handler/RequestHandler.h | 60 +++++--- .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 141 ++++++++++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 1 + 3 files changed, 184 insertions(+), 18 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index fa3d76ed4a125..8bfe42d9d3fde 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -339,64 +339,88 @@ class StepOutRequestHandler : public RequestHandler<protocol::StepOutArguments, llvm::Error Run(const protocol::StepOutArguments &args) const override; }; -class SetBreakpointsRequestHandler : public LegacyRequestHandler { +class SetBreakpointsRequestHandler + : public RequestHandler< + protocol::SetBreakpointsArguments, + llvm::Expected<protocol::SetBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setBreakpoints"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureConditionalBreakpoints, protocol::eAdapterFeatureHitConditionalBreakpoints}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::SetBreakpointsResponseBody> + Run(const protocol::SetBreakpointsArguments &args) const override; }; -class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetExceptionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetExceptionBreakpointsArguments, + llvm::Expected<protocol::SetExceptionBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setExceptionBreakpoints"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureExceptionOptions}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::SetExceptionBreakpointsResponseBody> + Run(const protocol::SetExceptionBreakpointsArguments &args) const override; }; -class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetFunctionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetFunctionBreakpointsArguments, + llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureFunctionBreakpoints}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::SetFunctionBreakpointsResponseBody> + Run(const protocol::SetFunctionBreakpointsArguments &args) const override; }; -class DataBreakpointInfoRequestHandler : public LegacyRequestHandler { +class DataBreakpointInfoRequestHandler + : public RequestHandler< + protocol::DataBreakpointInfoArguments, + llvm::Expected<protocol::DataBreakpointInfoResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::DataBreakpointInfoResponseBody> + Run(const protocol::DataBreakpointInfoArguments &args) const override; }; -class SetDataBreakpointsRequestHandler : public LegacyRequestHandler { +class SetDataBreakpointsRequestHandler + : public RequestHandler< + protocol::SetDataBreakpointsArguments, + llvm::Expected<protocol::SetDataBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; } - void operator()(const llvm::json::Object &request) const override; FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureDataBreakpoints}; } + llvm::Expected<protocol::SetDataBreakpointsResponseBody> + Run(const protocol::SetDataBreakpointsArguments &args) const override; }; -class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler { +class SetInstructionBreakpointsRequestHandler + : public RequestHandler< + protocol::SetInstructionBreakpointsArguments, + llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "setInstructionBreakpoints"; } - void operator()(const llvm::json::Object &request) const override; FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureInstructionBreakpoints}; } + llvm::Expected<protocol::SetInstructionBreakpointsResponseBody> + Run(const protocol::SetInstructionBreakpointsArguments &args) const override; }; class CompileUnitsRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index e64998c4ca488..eaad009150497 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -43,6 +43,17 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) { O.map("sourceReference", S.sourceReference); } +llvm::json::Value toJSON(const Source &S) { + json::Object result{ + {"name", S.name}, + {"path", S.path}, + {"sourceReference", S.sourceReference}, + {"presentationHint", S.presentationHint}, + }; + + return result; +} + json::Value toJSON(const ExceptionBreakpointsFilter &EBF) { json::Object result{{"filter", EBF.filter}, {"label", EBF.label}}; @@ -254,4 +265,134 @@ bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG, return true; } +json::Value toJSON(const Breakpoint &BP) { + json::Object result{{"verified", BP.verified}}; + + if (BP.id) + result.insert({"id", *BP.id}); + if (BP.message) + result.insert({"message", *BP.message}); + if (BP.source) + result.insert({"source", *BP.source}); + if (BP.line) + result.insert({"line", *BP.line}); + if (BP.column) + result.insert({"column", *BP.column}); + if (BP.endLine) + result.insert({"endLine", *BP.endLine}); + if (BP.endColumn) + result.insert({"endColumn", *BP.endColumn}); + if (BP.instructionReference) + result.insert({"instructionReference", *BP.instructionReference}); + if (BP.offset) + result.insert({"offset", *BP.offset}); + if (BP.reason) { + switch (*BP.reason) { + case Breakpoint::Reason::eBreakpointReasonPending: + result.insert({"reason", "pending"}); + break; + case Breakpoint::Reason::eBreakpointReasonFailed: + result.insert({"reason", "failed"}); + break; + } + } + + return result; +} + +bool fromJSON(const llvm::json::Value &Params, ExceptionBreakMode &EBM, + llvm::json::Path P) { + auto rawMode = Params.getAsString(); + if (!rawMode) { + P.report("expected a string"); + return false; + } + std::optional<ExceptionBreakMode> mode = + StringSwitch<std::optional<ExceptionBreakMode>>(*rawMode) + .Case("never", eExceptionBreakModeNever) + .Case("always", eExceptionBreakModeAlways) + .Case("unhandled", eExceptionBreakModeUnhandled) + .Case("userUnhandled", eExceptionBreakModeUserUnhandled) + .Default(std::nullopt); + if (!mode) { + P.report("unexpected ExceptionBreakMode value"); + return false; + } + EBM = *mode; + return true; +} + +bool fromJSON(const llvm::json::Value &Params, ExceptionPathSegment &EPS, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("negate", EPS.negate) && O.map("names", EPS.names); +} + +bool fromJSON(const llvm::json::Value &Params, ExceptionOptions &EO, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("path", EO.path) && O.map("breakMode", EO.breakMode); +} + +bool fromJSON(const llvm::json::Value &Params, ExceptionFilterOptions &EFO, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("filterId", EFO.filterId) && + O.map("condition", EFO.condition) && O.map("mode", EFO.mode); +} + +bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("line", SB.line) && O.map("column", SB.column) && + O.map("condition", SB.condition) && + O.map("hitCondition", SB.hitCondition) && + O.map("logMessage", SB.logMessage) && O.map("mode", SB.mode); +} + +bool fromJSON(const llvm::json::Value &Params, FunctionBreakpoint &FB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("name", FB.name) && O.map("condition", FB.condition) && + O.map("hitCondition", FB.hitCondition); +} + +bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT, + llvm::json::Path P) { + auto rawAccessType = Params.getAsString(); + if (!rawAccessType) { + P.report("expected a string"); + return false; + } + std::optional<DataBreakpointAccessType> accessType = + StringSwitch<std::optional<DataBreakpointAccessType>>(*rawAccessType) + .Case("read", eDataBreakpointAccessTypeRead) + .Case("write", eDataBreakpointAccessTypeWrite) + .Case("readWrite", eDataBreakpointAccessTypeReadWrite) + .Default(std::nullopt); + if (!accessType) { + P.report("unexpected value, expected 'read', 'write', or 'readWrite'"); + return false; + } + DBAT = *accessType; + return true; +} + +bool fromJSON(const llvm::json::Value &Params, DataBreakpointInfo &DBI, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("dataId", DBI.dataId) && + O.map("accessType", DBI.accessType) && + O.map("condition", DBI.condition) && + O.map("hitCondition", DBI.hitCondition); +} + +bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("instructionReference", IB.instructionReference) && + O.map("offset", IB.offset) && O.map("condition", IB.condition) && + O.map("hitCondition", IB.hitCondition) && O.map("mode", IB.mode); +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 216dcd907ae15..ff288a1be776b 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -302,6 +302,7 @@ struct Source { // unsupported keys: origin, sources, adapterData, checksums }; bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path); +llvm::json::Value toJSON(const Source &); /// The granularity of one `step` in the stepping requests `next`, `stepIn`, /// `stepOut` and `stepBack`. >From 359e5a38509f6238fb6319c0353a6093770c84b3 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 13:20:34 +0200 Subject: [PATCH 6/6] migrate breakpoints requests handler implementations --- lldb/tools/lldb-dap/Breakpoint.cpp | 22 ++- lldb/tools/lldb-dap/Breakpoint.h | 3 +- lldb/tools/lldb-dap/BreakpointBase.cpp | 2 - lldb/tools/lldb-dap/BreakpointBase.h | 3 +- .../Handler/SetBreakpointsRequestHandler.cpp | 177 ++++-------------- .../SetDataBreakpointsRequestHandler.cpp | 98 ++-------- lldb/tools/lldb-dap/JSONUtils.cpp | 165 +++------------- lldb/tools/lldb-dap/JSONUtils.h | 36 +--- lldb/tools/lldb-dap/Watchpoint.cpp | 24 +-- lldb/tools/lldb-dap/Watchpoint.h | 6 +- 10 files changed, 115 insertions(+), 421 deletions(-) diff --git a/lldb/tools/lldb-dap/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp index 5679fd545d53f..26d633d1d172e 100644 --- a/lldb/tools/lldb-dap/Breakpoint.cpp +++ b/lldb/tools/lldb-dap/Breakpoint.cpp @@ -14,7 +14,6 @@ #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBMutex.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/JSON.h" #include <cstddef> #include <cstdint> #include <mutex> @@ -30,13 +29,16 @@ void Breakpoint::SetHitCondition() { m_bp.SetIgnoreCount(hitCount - 1); } -void Breakpoint::CreateJsonObject(llvm::json::Object &object) { +protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() { + protocol::Breakpoint breakpoint; + // Each breakpoint location is treated as a separate breakpoint for VS code. // They don't have the notion of a single breakpoint with multiple locations. if (!m_bp.IsValid()) - return; - object.try_emplace("verified", m_bp.GetNumResolvedLocations() > 0); - object.try_emplace("id", m_bp.GetID()); + return breakpoint; + + breakpoint.verified = m_bp.GetNumResolvedLocations() > 0; + breakpoint.id = m_bp.GetID(); // VS Code DAP doesn't currently allow one breakpoint to have multiple // locations so we just report the first one. If we report all locations // then the IDE starts showing the wrong line numbers and locations for @@ -60,16 +62,18 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) { if (bp_addr.IsValid()) { std::string formatted_addr = "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget())); - object.try_emplace("instructionReference", formatted_addr); + breakpoint.instructionReference = formatted_addr; auto line_entry = bp_addr.GetLineEntry(); const auto line = line_entry.GetLine(); if (line != UINT32_MAX) - object.try_emplace("line", line); + breakpoint.line = line; const auto column = line_entry.GetColumn(); if (column != 0) - object.try_emplace("column", column); - object.try_emplace("source", CreateSource(line_entry)); + breakpoint.column = column; + breakpoint.source = CreateSource(line_entry); } + + return breakpoint; } bool Breakpoint::MatchesName(const char *name) { diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h index 987dbeadd4659..4f98e5537cda5 100644 --- a/lldb/tools/lldb-dap/Breakpoint.h +++ b/lldb/tools/lldb-dap/Breakpoint.h @@ -26,7 +26,8 @@ class Breakpoint : public BreakpointBase { void SetCondition() override; void SetHitCondition() override; - void CreateJsonObject(llvm::json::Object &object) override; + + protocol::Breakpoint ToProtocolBreakpoint() override; bool MatchesName(const char *name); void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp index 960dc5fc5ee56..1a4da92e44d31 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.cpp +++ b/lldb/tools/lldb-dap/BreakpointBase.cpp @@ -7,8 +7,6 @@ //===----------------------------------------------------------------------===// #include "BreakpointBase.h" -#include "JSONUtils.h" -#include "llvm/ADT/StringRef.h" using namespace lldb_dap; diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h index 44f3bfa15afc3..e9cfc112675c3 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -10,6 +10,7 @@ #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include <optional> #include <string> @@ -24,7 +25,7 @@ class BreakpointBase { virtual void SetCondition() = 0; virtual void SetHitCondition() = 0; - virtual void CreateJsonObject(llvm::json::Object &object) = 0; + virtual protocol::Breakpoint ToProtocolBreakpoint() = 0; void UpdateBreakpoint(const BreakpointBase &request_bp); diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp index dc0368852101f..0fc5792d0a7e7 100644 --- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp @@ -9,153 +9,49 @@ #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" +#include <vector> namespace lldb_dap { -// "SetBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "SetBreakpoints request; value of command field is -// 'setBreakpoints'. Sets multiple breakpoints for a single source and -// clears all previous breakpoints in that source. To clear all breakpoint -// for a source, specify an empty array. When a breakpoint is hit, a -// StoppedEvent (event type 'breakpoint') is generated.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "setBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for 'setBreakpoints' request.", -// "properties": { -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source location of the breakpoints; either -// source.path or source.reference must be specified." -// }, -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/SourceBreakpoint" -// }, -// "description": "The code locations of the breakpoints." -// }, -// "lines": { -// "type": "array", -// "items": { -// "type": "integer" -// }, -// "description": "Deprecated: The code locations of the breakpoints." -// }, -// "sourceModified": { -// "type": "boolean", -// "description": "A value of true indicates that the underlying source -// has been modified which results in new breakpoint locations." -// } -// }, -// "required": [ "source" ] -// }, -// "SetBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'setBreakpoints' request. Returned is -// information about each breakpoint created by this request. This includes -// the actual code location and whether the breakpoint could be verified. -// The breakpoints returned are in the same order as the elements of the -// 'breakpoints' (or the deprecated 'lines') in the -// SetBreakpointsArguments.", "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Breakpoint" -// }, -// "description": "Information about the breakpoints. The array -// elements are in the same order as the elements of the -// 'breakpoints' (or the deprecated 'lines') in the -// SetBreakpointsArguments." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// }, -// "SourceBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint or logpoint passed to the -// setBreakpoints request.", "properties": { -// "line": { -// "type": "integer", -// "description": "The source line of the breakpoint or logpoint." -// }, -// "column": { -// "type": "integer", -// "description": "An optional source column of the breakpoint." -// }, -// "condition": { -// "type": "string", -// "description": "An optional expression for conditional breakpoints." -// }, -// "hitCondition": { -// "type": "string", -// "description": "An optional expression that controls how many hits of -// the breakpoint are ignored. The backend is expected to interpret the -// expression as needed." -// }, -// "logMessage": { -// "type": "string", -// "description": "If this attribute exists and is non-empty, the backend -// must not 'break' (stop) but log the message instead. Expressions within -// {} are interpolated." -// } -// }, -// "required": [ "line" ] -// } -void SetBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *source = arguments->getObject("source"); - const auto path = GetString(source, "path").value_or(""); - const auto *breakpoints = arguments->getArray("breakpoints"); - llvm::json::Array response_breakpoints; +/// Sets multiple breakpoints for a single source and clears all previous +/// breakpoints in that source. To clear all breakpoint for a source, specify an +/// empty array. When a breakpoint is hit, a `stopped` event (with reason +/// `breakpoint`) is generated. +llvm::Expected<protocol::SetBreakpointsResponseBody> +SetBreakpointsRequestHandler::Run( + const protocol::SetBreakpointsArguments &args) const { + const auto &source = args.source; + const auto path = source.path.value_or(""); + std::vector<protocol::Breakpoint> response_breakpoints; // Decode the source breakpoint infos for this "setBreakpoints" request SourceBreakpointMap request_bps; // "breakpoints" may be unset, in which case we treat it the same as being set // to an empty array. - if (breakpoints) { - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (bp_obj) { - SourceBreakpoint src_bp(dap, *bp_obj); - std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(), - src_bp.GetColumn()); - request_bps.try_emplace(bp_pos, src_bp); - const auto [iv, inserted] = - dap.source_breakpoints[path].try_emplace(bp_pos, src_bp); - // We check if this breakpoint already exists to update it - if (inserted) - iv->getSecond().SetBreakpoint(path.data()); - else - iv->getSecond().UpdateBreakpoint(src_bp); - AppendBreakpoint(&iv->getSecond(), response_breakpoints, path, - src_bp.GetLine()); - } + if (args.breakpoints) { + for (const auto &bp : *args.breakpoints) { + SourceBreakpoint src_bp(dap, bp); + std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(), + src_bp.GetColumn()); + request_bps.try_emplace(bp_pos, src_bp); + const auto [iv, inserted] = + dap.source_breakpoints[path].try_emplace(bp_pos, src_bp); + // We check if this breakpoint already exists to update it + if (inserted) + iv->getSecond().SetBreakpoint(path.data()); + else + iv->getSecond().UpdateBreakpoint(src_bp); + + protocol::Breakpoint response_bp = iv->getSecond().ToProtocolBreakpoint(); + if (!path.empty() && !response_bp.source) + response_bp.source = CreateSource(path); + if (!response_bp.line) + response_bp.line = src_bp.GetLine(); + if (!response_bp.column) + response_bp.column = src_bp.GetColumn(); + response_breakpoints.push_back(response_bp); } } @@ -174,10 +70,7 @@ void SetBreakpointsRequestHandler::operator()( } } - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetBreakpointsResponseBody{std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp index 365c9f0d722d4..1caaa23bf06f6 100644 --- a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp @@ -8,90 +8,28 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" #include "RequestHandler.h" #include "Watchpoint.h" #include <set> namespace lldb_dap { -// "SetDataBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Replaces all existing data breakpoints with new data -// breakpoints.\nTo clear all data breakpoints, specify an empty -// array.\nWhen a data breakpoint is hit, a `stopped` event (with reason -// `data breakpoint`) is generated.\nClients should only call this request -// if the corresponding capability `supportsDataBreakpoints` is true.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "setDataBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetDataBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetDataBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for `setDataBreakpoints` request.", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/DataBreakpoint" -// }, -// "description": "The contents of this array replaces all existing data -// breakpoints. An empty array clears all data breakpoints." -// } -// }, -// "required": [ "breakpoints" ] -// }, -// "SetDataBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `setDataBreakpoints` request.\nReturned is -// information about each breakpoint created by this request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Breakpoint" -// }, -// "description": "Information about the data breakpoints. The array -// elements correspond to the elements of the input argument -// `breakpoints` array." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void SetDataBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - lldb::SBError error; - FillResponse(request, response); - const auto *arguments = request.getObject("arguments"); - const auto *breakpoints = arguments->getArray("breakpoints"); - llvm::json::Array response_breakpoints; +/// Replaces all existing data breakpoints with new data breakpoints. +/// To clear all data breakpoints, specify an empty array. +/// When a data breakpoint is hit, a stopped event (with reason data breakpoint) +/// is generated. Clients should only call this request if the corresponding +/// capability supportsDataBreakpoints is true. +llvm::Expected<protocol::SetDataBreakpointsResponseBody> +SetDataBreakpointsRequestHandler::Run( + const protocol::SetDataBreakpointsArguments &args) const { + std::vector<protocol::Breakpoint> response_breakpoints; + dap.target.DeleteAllWatchpoints(); std::vector<Watchpoint> watchpoints; - if (breakpoints) { - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (bp_obj) - watchpoints.emplace_back(dap, *bp_obj); - } - } + for (const auto &bp : args.breakpoints) + watchpoints.emplace_back(dap, bp); + // If two watchpoints start at the same address, the latter overwrite the // former. So, we only enable those at first-seen addresses when iterating // backward. @@ -103,12 +41,10 @@ void SetDataBreakpointsRequestHandler::operator()( } } for (auto wp : watchpoints) - AppendBreakpoint(&wp, response_breakpoints); + response_breakpoints.push_back(wp.ToProtocolBreakpoint()); - llvm::json::Object body; - body.try_emplace("breakpoints", std::move(response_breakpoints)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return protocol::SetDataBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..ffa3ef74866e9 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -354,68 +354,21 @@ llvm::json::Value CreateScope(const llvm::StringRef name, return llvm::json::Value(std::move(object)); } -// "Breakpoint": { -// "type": "object", -// "description": "Information about a Breakpoint created in setBreakpoints -// or setFunctionBreakpoints.", -// "properties": { -// "id": { -// "type": "integer", -// "description": "An optional unique identifier for the breakpoint." -// }, -// "verified": { -// "type": "boolean", -// "description": "If true breakpoint could be set (but not necessarily -// at the desired location)." -// }, -// "message": { -// "type": "string", -// "description": "An optional message about the state of the breakpoint. -// This is shown to the user and can be used to explain -// why a breakpoint could not be verified." -// }, -// "source": { -// "$ref": "#/definitions/Source", -// "description": "The source where the breakpoint is located." -// }, -// "line": { -// "type": "integer", -// "description": "The start line of the actual range covered by the -// breakpoint." -// }, -// "column": { -// "type": "integer", -// "description": "An optional start column of the actual range covered -// by the breakpoint." -// }, -// "endLine": { -// "type": "integer", -// "description": "An optional end line of the actual range covered by -// the breakpoint." -// }, -// "endColumn": { -// "type": "integer", -// "description": "An optional end column of the actual range covered by -// the breakpoint. If no end line is given, then the end -// column is assumed to be in the start line." -// } -// }, -// "required": [ "verified" ] -// } -llvm::json::Value CreateBreakpoint(BreakpointBase *bp, - std::optional<llvm::StringRef> request_path, - std::optional<uint32_t> request_line, - std::optional<uint32_t> request_column) { - llvm::json::Object object; - if (request_path) - object.try_emplace("source", CreateSource(*request_path)); - bp->CreateJsonObject(object); +protocol::Breakpoint +CreateBreakpoint(BreakpointBase *bp, + std::optional<llvm::StringRef> request_path, + std::optional<uint32_t> request_line, + std::optional<uint32_t> request_column) { + protocol::Breakpoint breakpoint = bp->ToProtocolBreakpoint(); + if (request_path && !breakpoint.source) + breakpoint.source = CreateSource(*request_path); + // We try to add request_line as a fallback - if (request_line) - object.try_emplace("line", *request_line); - if (request_column) - object.try_emplace("column", *request_column); - return llvm::json::Value(std::move(object)); + if (request_line && !breakpoint.line) + breakpoint.line = *request_line; + if (request_column && !breakpoint.column) + breakpoint.column = *request_column; + return breakpoint; } static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { @@ -567,96 +520,30 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { return filter; } -// "Source": { -// "type": "object", -// "description": "A Source is a descriptor for source code. It is returned -// from the debug adapter as part of a StackFrame and it is -// used by clients when specifying breakpoints.", -// "properties": { -// "name": { -// "type": "string", -// "description": "The short name of the source. Every source returned -// from the debug adapter has a name. When sending a -// source to the debug adapter this name is optional." -// }, -// "path": { -// "type": "string", -// "description": "The path of the source to be shown in the UI. It is -// only used to locate and load the content of the -// source if no sourceReference is specified (or its -// value is 0)." -// }, -// "sourceReference": { -// "type": "number", -// "description": "If sourceReference > 0 the contents of the source must -// be retrieved through the SourceRequest (even if a path -// is specified). A sourceReference is only valid for a -// session, so it must not be used to persist a source." -// }, -// "presentationHint": { -// "type": "string", -// "description": "An optional hint for how to present the source in the -// UI. A value of 'deemphasize' can be used to indicate -// that the source is not available or that it is -// skipped on stepping.", -// "enum": [ "normal", "emphasize", "deemphasize" ] -// }, -// "origin": { -// "type": "string", -// "description": "The (optional) origin of this source: possible values -// 'internal module', 'inlined content from source map', -// etc." -// }, -// "sources": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Source" -// }, -// "description": "An optional list of sources that are related to this -// source. These may be the source that generated this -// source." -// }, -// "adapterData": { -// "type":["array","boolean","integer","null","number","object","string"], -// "description": "Optional data that a debug adapter might want to loop -// through the client. The client should leave the data -// intact and persist it across sessions. The client -// should not interpret the data." -// }, -// "checksums": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/Checksum" -// }, -// "description": "The checksums associated with this file." -// } -// } -// } -llvm::json::Value CreateSource(const lldb::SBFileSpec &file) { - llvm::json::Object object; +protocol::Source CreateSource(const lldb::SBFileSpec &file) { + protocol::Source source; if (file.IsValid()) { const char *name = file.GetFilename(); if (name) - EmplaceSafeString(object, "name", name); + source.name = name; char path[PATH_MAX] = ""; if (file.GetPath(path, sizeof(path)) && - lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) { - EmplaceSafeString(object, "path", std::string(path)); - } + lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) + source.path = path; } - return llvm::json::Value(std::move(object)); + return source; } -llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) { +protocol::Source CreateSource(const lldb::SBLineEntry &line_entry) { return CreateSource(line_entry.GetFileSpec()); } -llvm::json::Value CreateSource(llvm::StringRef source_path) { - llvm::json::Object source; +protocol::Source CreateSource(llvm::StringRef source_path) { + protocol::Source source; llvm::StringRef name = llvm::sys::path::filename(source_path); - EmplaceSafeString(source, "name", name); - EmplaceSafeString(source, "path", source_path); - return llvm::json::Value(std::move(source)); + source.name = name; + source.path = source_path; + return source; } bool ShouldDisplayAssemblySource( diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index d0e20729f4ed9..d16d41b3bc368 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -198,34 +198,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key); void FillResponse(const llvm::json::Object &request, llvm::json::Object &response); -/// Converts \a bp to a JSON value and appends the first valid location to the -/// \a breakpoints array. -/// -/// \param[in] bp -/// A LLDB breakpoint object which will get the first valid location -/// extracted and converted into a JSON object in the \a breakpoints array -/// -/// \param[in] breakpoints -/// A JSON array that will get a llvm::json::Value for \a bp -/// appended to it. -/// -/// \param[in] request_path -/// An optional source path to use when creating the "Source" object of this -/// breakpoint. If not specified, the "Source" object is created from the -/// breakpoint's address' LineEntry. It is useful to ensure the same source -/// paths provided by the setBreakpoints request are returned to the IDE. -/// -/// \param[in] request_line -/// An optional line to use when creating the "Breakpoint" object to append. -/// It is used if the breakpoint has no valid locations. -/// It is useful to ensure the same line -/// provided by the setBreakpoints request are returned to the IDE as a -/// fallback. -void AppendBreakpoint( - BreakpointBase *bp, llvm::json::Array &breakpoints, - std::optional<llvm::StringRef> request_path = std::nullopt, - std::optional<uint32_t> request_line = std::nullopt); - /// Converts breakpoint location to a debug adapter protocol "Breakpoint". /// /// \param[in] bp @@ -253,7 +225,7 @@ void AppendBreakpoint( /// \return /// A "Breakpoint" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value +protocol::Breakpoint CreateBreakpoint(BreakpointBase *bp, std::optional<llvm::StringRef> request_path = std::nullopt, std::optional<uint32_t> request_line = std::nullopt, @@ -323,7 +295,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name, /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(const lldb::SBFileSpec &file); +protocol::Source CreateSource(const lldb::SBFileSpec &file); /// Create a "Source" JSON object as described in the debug adapter definition. /// @@ -334,7 +306,7 @@ llvm::json::Value CreateSource(const lldb::SBFileSpec &file); /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry); +protocol::Source CreateSource(const lldb::SBLineEntry &line_entry); /// Create a "Source" object for a given source path. /// @@ -344,7 +316,7 @@ llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry); /// \return /// A "Source" JSON object that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateSource(llvm::StringRef source_path); +protocol::Source CreateSource(llvm::StringRef source_path); /// Return true if the given line entry should be displayed as assembly. /// diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp index a94cbcdbc4122..72f913d465408 100644 --- a/lldb/tools/lldb-dap/Watchpoint.cpp +++ b/lldb/tools/lldb-dap/Watchpoint.cpp @@ -8,25 +8,24 @@ #include "Watchpoint.h" #include "DAP.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBTarget.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/JSON.h" #include <cstdint> #include <string> namespace lldb_dap { -Watchpoint::Watchpoint(DAP &d, const llvm::json::Object &obj) - : BreakpointBase(d, obj) { - llvm::StringRef dataId = GetString(obj, "dataId").value_or(""); - std::string accessType = GetString(obj, "accessType").value_or("").str(); +Watchpoint::Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint) + : BreakpointBase(d, breakpoint.condition, breakpoint.hitCondition) { + llvm::StringRef dataId = breakpoint.dataId; auto [addr_str, size_str] = dataId.split('/'); llvm::to_integer(addr_str, m_addr, 16); llvm::to_integer(size_str, m_size); - m_options.SetWatchpointTypeRead(accessType != "write"); - if (accessType != "read") + m_options.SetWatchpointTypeRead(breakpoint.accessType != + protocol::eDataBreakpointAccessTypeWrite); + if (breakpoint.accessType != protocol::eDataBreakpointAccessTypeRead) m_options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify); } @@ -38,13 +37,14 @@ void Watchpoint::SetHitCondition() { m_wp.SetIgnoreCount(hitCount - 1); } -void Watchpoint::CreateJsonObject(llvm::json::Object &object) { +protocol::Breakpoint Watchpoint::ToProtocolBreakpoint() { + protocol::Breakpoint breakpoint; if (!m_error.IsValid() || m_error.Fail()) { - object.try_emplace("verified", false); + breakpoint.verified = false; if (m_error.Fail()) - EmplaceSafeString(object, "message", m_error.GetCString()); + breakpoint.message = m_error.GetCString(); } else { - object.try_emplace("verified", true); + breakpoint.verified = true; } } diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h index bf52b41281f29..9e0902d6db5bf 100644 --- a/lldb/tools/lldb-dap/Watchpoint.h +++ b/lldb/tools/lldb-dap/Watchpoint.h @@ -11,6 +11,7 @@ #include "BreakpointBase.h" #include "DAPForward.h" +#include "Protocol/ProtocolTypes.h" #include "lldb/API/SBError.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBWatchpointOptions.h" @@ -21,12 +22,13 @@ namespace lldb_dap { class Watchpoint : public BreakpointBase { public: - Watchpoint(DAP &d, const llvm::json::Object &obj); + Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint); Watchpoint(DAP &d, lldb::SBWatchpoint wp) : BreakpointBase(d), m_wp(wp) {} void SetCondition() override; void SetHitCondition() override; - void CreateJsonObject(llvm::json::Object &object) override; + + protocol::Breakpoint ToProtocolBreakpoint(); void SetWatchpoint(); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits