Author: Ely Ronnen Date: 2025-05-10T00:05:59+02:00 New Revision: 8630c22083e3ebab5955c0c46caa89b59f283fdb
URL: https://github.com/llvm/llvm-project/commit/8630c22083e3ebab5955c0c46caa89b59f283fdb DIFF: https://github.com/llvm/llvm-project/commit/8630c22083e3ebab5955c0c46caa89b59f283fdb.diff LOG: [lldb-dap] migrate set breakpoint requests (#137448) - Migrate set breakpoint requests to use typed RequestHandler - `SetBreakpointsRequestHandler` - `SetDataBreakpointsRequestHandler` - `SetFunctionBreakpointsRequestHandler` - `SetInstructionBreakpointsRequestHandler` - `DataBreakpointInfoRequestHandler` - Decouple JSON from lldb-dap `Breakpoint` classes Added: Modified: lldb/tools/lldb-dap/Breakpoint.cpp lldb/tools/lldb-dap/Breakpoint.h lldb/tools/lldb-dap/BreakpointBase.cpp lldb/tools/lldb-dap/BreakpointBase.h lldb/tools/lldb-dap/DAP.cpp lldb/tools/lldb-dap/DAP.h lldb/tools/lldb-dap/FunctionBreakpoint.cpp lldb/tools/lldb-dap/FunctionBreakpoint.h lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp lldb/tools/lldb-dap/Handler/RequestHandler.h lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp lldb/tools/lldb-dap/InstructionBreakpoint.cpp lldb/tools/lldb-dap/InstructionBreakpoint.h lldb/tools/lldb-dap/JSONUtils.cpp lldb/tools/lldb-dap/JSONUtils.h lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp lldb/tools/lldb-dap/Protocol/ProtocolRequests.h lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp lldb/tools/lldb-dap/Protocol/ProtocolTypes.h lldb/tools/lldb-dap/SourceBreakpoint.cpp lldb/tools/lldb-dap/SourceBreakpoint.h lldb/tools/lldb-dap/Watchpoint.cpp lldb/tools/lldb-dap/Watchpoint.h llvm/include/llvm/Support/JSON.h Removed: ################################################################################ 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 580017125af44..c4f1fa291f181 100644 --- a/lldb/tools/lldb-dap/Breakpoint.h +++ b/lldb/tools/lldb-dap/Breakpoint.h @@ -17,14 +17,16 @@ 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(); } 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 331ce8efee9bc..1a4da92e44d31 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.cpp +++ b/lldb/tools/lldb-dap/BreakpointBase.cpp @@ -7,16 +7,14 @@ //===----------------------------------------------------------------------===// #include "BreakpointBase.h" -#include "JSONUtils.h" -#include "llvm/ADT/StringRef.h" 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..e9cfc112675c3 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -10,7 +10,8 @@ #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H #include "DAPForward.h" -#include "llvm/ADT/StringRef.h" +#include "Protocol/ProtocolTypes.h" +#include <optional> #include <string> namespace lldb_dap { @@ -18,12 +19,13 @@ 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; 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/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index f6754b1f8d7a3..80c150018f0b3 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -47,6 +47,7 @@ #include <chrono> #include <condition_variable> #include <cstdarg> +#include <cstdint> #include <cstdio> #include <fstream> #include <future> @@ -552,9 +553,7 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) { return target.GetProcess().GetThreadByID(tid); } -lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { - const uint64_t frame_id = - GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX); +lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) { lldb::SBProcess process = target.GetProcess(); // Upper 32 bits is the thread index ID lldb::SBThread thread = @@ -563,6 +562,12 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id)); } +lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) { + const auto frame_id = + GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX); + return GetLLDBFrame(frame_id); +} + llvm::json::Value DAP::CreateTopLevelScopes() { llvm::json::Array scopes; scopes.emplace_back( @@ -1602,7 +1607,7 @@ void DAP::EventThread() { // avoids sending paths that should be source mapped. Note that // CreateBreakpoint doesn't apply source mapping and certain // implementation ignore the source part of this event anyway. - llvm::json::Value source_bp = CreateBreakpoint(&bp); + llvm::json::Value source_bp = bp.ToProtocolBreakpoint(); source_bp.getAsObject()->erase("source"); llvm::json::Object body; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index afeda8d81efb0..ecde222c9263c 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -275,6 +275,7 @@ struct DAP { lldb::SBThread GetLLDBThread(lldb::tid_t id); lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments); + lldb::SBFrame GetLLDBFrame(uint64_t frame_id); lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments); llvm::json::Value CreateTopLevelScopes(); 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/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp index 4d920f8556254..8cb25d0603449 100644 --- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp @@ -8,144 +8,50 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" +#include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "llvm/ADT/StringExtras.h" +#include <optional> namespace lldb_dap { -// "DataBreakpointInfoRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Obtains information on a possible data breakpoint that -// could be set on an expression or variable.\nClients should only call this -// request if the corresponding capability `supportsDataBreakpoints` is -// true.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "dataBreakpointInfo" ] -// }, -// "arguments": { -// "$ref": "#/definitions/DataBreakpointInfoArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "DataBreakpointInfoArguments": { -// "type": "object", -// "description": "Arguments for `dataBreakpointInfo` request.", -// "properties": { -// "variablesReference": { -// "type": "integer", -// "description": "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." -// }, -// "name": { -// "type": "string", -// "description": "The name of the variable's child to obtain data -// breakpoint information for.\nIf `variablesReference` isn't specified, -// this can be an expression." -// }, -// "frameId": { -// "type": "integer", -// "description": "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 `variablesReference` is specified, this property -// has no effect." -// } -// }, -// "required": [ "name" ] -// }, -// "DataBreakpointInfoResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `dataBreakpointInfo` request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "dataId": { -// "type": [ "string", "null" ], -// "description": "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`." -// }, -// "description": { -// "type": "string", -// "description": "UI string that describes on what data the -// breakpoint is set on or why a data breakpoint is not available." -// }, -// "accessTypes": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/DataBreakpointAccessType" -// }, -// "description": "Attribute lists the available access types for a -// potential data breakpoint. A UI client could surface this -// information." -// }, -// "canPersist": { -// "type": "boolean", -// "description": "Attribute indicates that a potential data -// breakpoint could be persisted across sessions." -// } -// }, -// "required": [ "dataId", "description" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void DataBreakpointInfoRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - llvm::json::Object body; - lldb::SBError error; - llvm::json::Array accessTypes{"read", "write", "readWrite"}; - const auto *arguments = request.getObject("arguments"); - const auto variablesReference = - GetInteger<uint64_t>(arguments, "variablesReference").value_or(0); - llvm::StringRef name = GetString(arguments, "name").value_or(""); - lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); - lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name); +/// Obtains information on a possible data breakpoint that could be set on an +/// expression or variable. Clients should only call this request if the +/// corresponding capability supportsDataBreakpoints is true. +llvm::Expected<protocol::DataBreakpointInfoResponseBody> +DataBreakpointInfoRequestHandler::Run( + const protocol::DataBreakpointInfoArguments &args) const { + protocol::DataBreakpointInfoResponseBody response; + lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX)); + lldb::SBValue variable = dap.variables.FindVariable( + args.variablesReference.value_or(0), args.name); std::string addr, size; + bool is_data_ok = true; if (variable.IsValid()) { lldb::addr_t load_addr = variable.GetLoadAddress(); size_t byte_size = variable.GetByteSize(); if (load_addr == LLDB_INVALID_ADDRESS) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "does not exist in memory, its location is " + - std::string(variable.GetLocation())); + is_data_ok = false; + response.description = "does not exist in memory, its location is " + + std::string(variable.GetLocation()); } else if (byte_size == 0) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", "variable size is 0"); + is_data_ok = false; + response.description = "variable size is 0"; } else { addr = llvm::utohexstr(load_addr); size = llvm::utostr(byte_size); } - } else if (variablesReference == 0 && frame.IsValid()) { - lldb::SBValue value = frame.EvaluateExpression(name.data()); + } else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) { + lldb::SBValue value = frame.EvaluateExpression(args.name.c_str()); if (value.GetError().Fail()) { lldb::SBError error = value.GetError(); const char *error_cstr = error.GetCString(); - body.try_emplace("dataId", nullptr); - body.try_emplace("description", error_cstr && error_cstr[0] - ? std::string(error_cstr) - : "evaluation failed"); + is_data_ok = false; + response.description = error_cstr && error_cstr[0] + ? std::string(error_cstr) + : "evaluation failed"; } else { uint64_t load_addr = value.GetValueAsUnsigned(); lldb::SBData data = value.GetPointeeData(); @@ -159,32 +65,31 @@ void DataBreakpointInfoRequestHandler::operator()( // request if SBProcess::GetMemoryRegionInfo returns error. if (err.Success()) { if (!(region.IsReadable() || region.IsWritable())) { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "memory region for address " + addr + - " has no read or write permissions"); + is_data_ok = false; + response.description = "memory region for address " + addr + + " has no read or write permissions"; } } } else { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", - "unable to get byte size for expression: " + - name.str()); + is_data_ok = false; + response.description = + "unable to get byte size for expression: " + args.name; } } } else { - body.try_emplace("dataId", nullptr); - body.try_emplace("description", "variable not found: " + name.str()); + is_data_ok = false; + response.description = "variable not found: " + args.name; } - if (!body.getObject("dataId")) { - body.try_emplace("dataId", addr + "/" + size); - body.try_emplace("accessTypes", std::move(accessTypes)); - body.try_emplace("description", - size + " bytes at " + addr + " " + name.str()); + if (is_data_ok) { + response.dataId = addr + "/" + size; + response.accessTypes = {protocol::eDataBreakpointAccessTypeRead, + protocol::eDataBreakpointAccessTypeWrite, + protocol::eDataBreakpointAccessTypeReadWrite}; + response.description = size + " bytes at " + addr + " " + args.name; } - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + + return response; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index db7f05cb1f113..b0002440cf72e 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -15,7 +15,6 @@ #include "Protocol/ProtocolBase.h" #include "Protocol/ProtocolRequests.h" #include "Protocol/ProtocolTypes.h" -#include "lldb/API/SBError.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -349,15 +348,19 @@ 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 { @@ -370,43 +373,59 @@ class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) 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/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp index dc0368852101f..86e090b66afe9 100644 --- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp @@ -9,153 +9,52 @@ #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(); + + // Use the path from the request if it is set + if (!path.empty()) + 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 +73,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/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp index c45dc0d0d6553..1367aa3e864d9 100644 --- a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp @@ -8,116 +8,35 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" #include "RequestHandler.h" namespace lldb_dap { -// "SetFunctionBreakpointsRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "SetFunctionBreakpoints request; value of command field is -// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears -// all previous function breakpoints. To clear all function breakpoint, -// specify an empty array. When a function breakpoint is hit, a StoppedEvent -// (event type 'function breakpoint') is generated.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "setFunctionBreakpoints" ] -// }, -// "arguments": { -// "$ref": "#/definitions/SetFunctionBreakpointsArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "SetFunctionBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for 'setFunctionBreakpoints' request.", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/FunctionBreakpoint" -// }, -// "description": "The function names of the breakpoints." -// } -// }, -// "required": [ "breakpoints" ] -// }, -// "FunctionBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint passed to the -// setFunctionBreakpoints request.", "properties": { -// "name": { -// "type": "string", -// "description": "The name of the function." -// }, -// "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." -// } -// }, -// "required": [ "name" ] -// }, -// "SetFunctionBreakpointsResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to 'setFunctionBreakpoints' request. Returned 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 breakpoints. The array -// elements correspond to the elements of the 'breakpoints' array." -// } -// }, -// "required": [ "breakpoints" ] -// } -// }, -// "required": [ "body" ] -// }] -// } -void SetFunctionBreakpointsRequestHandler::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 function breakpoints with new function breakpoints. +/// To clear all function breakpoints, specify an empty array. +/// When a function breakpoint is hit, a stopped event (with reason function +/// breakpoint) is generated. Clients should only call this request if the +/// corresponding capability supportsFunctionBreakpoints is true. +llvm::Expected<protocol::SetFunctionBreakpointsResponseBody> +SetFunctionBreakpointsRequestHandler::Run( + const protocol::SetFunctionBreakpointsArguments &args) const { + std::vector<protocol::Breakpoint> response_breakpoints; // Disable any function breakpoints that aren't in this request. // There is no call to remove function breakpoints other than calling this // function with a smaller or empty "breakpoints" list. const auto name_iter = dap.function_breakpoints.keys(); llvm::DenseSet<llvm::StringRef> seen(name_iter.begin(), name_iter.end()); - for (const auto &value : *breakpoints) { - const auto *bp_obj = value.getAsObject(); - if (!bp_obj) - continue; - FunctionBreakpoint fn_bp(dap, *bp_obj); - const auto [it, inserted] = dap.function_breakpoints.try_emplace( - fn_bp.GetFunctionName(), dap, *bp_obj); + for (const auto &fb : args.breakpoints) { + FunctionBreakpoint fn_bp(dap, fb); + const auto [it, inserted] = + dap.function_breakpoints.try_emplace(fn_bp.GetFunctionName(), dap, fb); if (inserted) it->second.SetBreakpoint(); else it->second.UpdateBreakpoint(fn_bp); - AppendBreakpoint(&it->second, response_breakpoints); + response_breakpoints.push_back(it->second.ToProtocolBreakpoint()); seen.erase(fn_bp.GetFunctionName()); } @@ -130,10 +49,8 @@ void SetFunctionBreakpointsRequestHandler::operator()( dap.function_breakpoints.erase(name); } - 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::SetFunctionBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp index 4e555ad605a26..48b2ba5a25594 100644 --- a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp @@ -8,207 +8,20 @@ #include "DAP.h" #include "EventHelper.h" -#include "JSONUtils.h" #include "RequestHandler.h" namespace lldb_dap { -// "SetInstructionBreakpointsRequest": { -// "allOf": [ -// {"$ref": "#/definitions/Request"}, -// { -// "type": "object", -// "description" : -// "Replaces all existing instruction breakpoints. Typically, " -// "instruction breakpoints would be set from a disassembly window. " -// "\nTo clear all instruction breakpoints, specify an empty " -// "array.\nWhen an instruction breakpoint is hit, a `stopped` event " -// "(with reason `instruction breakpoint`) is generated.\nClients " -// "should only call this request if the corresponding capability " -// "`supportsInstructionBreakpoints` is true.", -// "properties": { -// "command": { "type": "string", "enum": ["setInstructionBreakpoints"] -// }, "arguments": {"$ref": -// "#/definitions/SetInstructionBreakpointsArguments"} -// }, -// "required": [ "command", "arguments" ] -// } -// ] -// }, -// "SetInstructionBreakpointsArguments": { -// "type": "object", -// "description": "Arguments for `setInstructionBreakpoints` request", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": {"$ref": "#/definitions/InstructionBreakpoint"}, -// "description": "The instruction references of the breakpoints" -// } -// }, -// "required": ["breakpoints"] -// }, -// "SetInstructionBreakpointsResponse": { -// "allOf": [ -// {"$ref": "#/definitions/Response"}, -// { -// "type": "object", -// "description": "Response to `setInstructionBreakpoints` request", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "breakpoints": { -// "type": "array", -// "items": {"$ref": "#/definitions/Breakpoint"}, -// "description": -// "Information about the breakpoints. The array elements -// " "correspond to the elements of the `breakpoints` -// array." -// } -// }, -// "required": ["breakpoints"] -// } -// }, -// "required": ["body"] -// } -// ] -// }, -// "InstructionBreakpoint": { -// "type": "object", -// "description": "Properties of a breakpoint passed to the " -// "`setInstructionBreakpoints` request", -// "properties": { -// "instructionReference": { -// "type": "string", -// "description" : -// "The instruction reference of the breakpoint.\nThis should be a " -// "memory or instruction pointer reference from an -// `EvaluateResponse`, " -// "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`." -// }, -// "offset": { -// "type": "integer", -// "description": "The offset from the instruction reference in " -// "bytes.\nThis can be negative." -// }, -// "condition": { -// "type": "string", -// "description": "An expression for conditional breakpoints.\nIt is only -// " -// "honored by a debug adapter if the corresponding " -// "capability `supportsConditionalBreakpoints` is true." -// }, -// "hitCondition": { -// "type": "string", -// "description": "An expression that controls how many hits of the " -// "breakpoint are ignored.\nThe debug adapter is expected -// " "to interpret the expression as needed.\nThe -// attribute " "is only honored by a debug adapter if the -// corresponding " "capability -// `supportsHitConditionalBreakpoints` is true." -// }, -// "mode": { -// "type": "string", -// "description": "The mode of this breakpoint. If defined, this must be -// " -// "one of the `breakpointModes` the debug adapter " -// "advertised in its `Capabilities`." -// } -// }, -// "required": ["instructionReference"] -// }, -// "Breakpoint": { -// "type": "object", -// "description" : -// "Information about a breakpoint created in `setBreakpoints`, " -// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or " -// "`setDataBreakpoints` requests.", -// "properties": { -// "id": { -// "type": "integer", -// "description" : -// "The identifier for the breakpoint. It is needed if breakpoint -// " "events are used to update or remove breakpoints." -// }, -// "verified": { -// "type": "boolean", -// "description": "If true, the breakpoint could be set (but not " -// "necessarily at the desired location)." -// }, -// "message": { -// "type": "string", -// "description": "A message about the state of the breakpoint.\nThis -// " -// "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" : -// "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." -// }, -// "endLine": { -// "type": "integer", -// "description" : -// "The end line of the actual range covered by the breakpoint." -// }, -// "endColumn": { -// "type": "integer", -// "description" : -// "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.\nIf -// " "no end line is given, then the end column is assumed to be -// in " "the start line." -// }, -// "instructionReference": { -// "type": "string", -// "description": "A memory reference to where the breakpoint is -// set." -// }, -// "offset": { -// "type": "integer", -// "description": "The offset from the instruction reference.\nThis " -// "can be negative." -// }, -// "reason": { -// "type": "string", -// "description" : -// "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. -// Possible " "values include:\n\n- `pending`: Indicates a -// breakpoint might be " "verified in the future, but the adapter -// cannot verify it in the " "current state.\n - `failed`: -// Indicates a breakpoint was not " "able to be verified, and the -// adapter does not believe it can be " "verified without -// intervention.", -// "enum": [ "pending", "failed" ] -// } -// }, -// "required": ["verified"] -// }, -void SetInstructionBreakpointsRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - llvm::json::Array response_breakpoints; - llvm::json::Object body; - FillResponse(request, response); - - const auto *arguments = request.getObject("arguments"); - const auto *breakpoints = arguments->getArray("breakpoints"); +/// Replaces all existing instruction breakpoints. Typically, instruction +/// breakpoints would be set from a disassembly window. To clear all instruction +/// breakpoints, specify an empty array. When an instruction breakpoint is hit, +/// a stopped event (with reason instruction breakpoint) is generated. Clients +/// should only call this request if the corresponding capability +/// supportsInstructionBreakpoints is true. +llvm::Expected<protocol::SetInstructionBreakpointsResponseBody> +SetInstructionBreakpointsRequestHandler::Run( + const protocol::SetInstructionBreakpointsArguments &args) const { + std::vector<protocol::Breakpoint> response_breakpoints; // Disable any instruction breakpoints that aren't in this request. // There is no call to remove instruction breakpoints other than calling this @@ -216,19 +29,16 @@ void SetInstructionBreakpointsRequestHandler::operator()( llvm::DenseSet<lldb::addr_t> seen( llvm::from_range, llvm::make_first_range(dap.instruction_breakpoints)); - for (const auto &bp : *breakpoints) { - const auto *bp_obj = bp.getAsObject(); - if (!bp_obj) - continue; + for (const auto &bp : args.breakpoints) { // Read instruction breakpoint request. - InstructionBreakpoint inst_bp(dap, *bp_obj); + InstructionBreakpoint inst_bp(dap, bp); const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace( - inst_bp.GetInstructionAddressReference(), dap, *bp_obj); + inst_bp.GetInstructionAddressReference(), dap, bp); if (inserted) iv->second.SetBreakpoint(); else iv->second.UpdateBreakpoint(inst_bp); - AppendBreakpoint(&iv->second, response_breakpoints); + response_breakpoints.push_back(iv->second.ToProtocolBreakpoint()); seen.erase(inst_bp.GetInstructionAddressReference()); } @@ -240,9 +50,8 @@ void SetInstructionBreakpointsRequestHandler::operator()( dap.instruction_breakpoints.erase(addr); } - 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::SetInstructionBreakpointsResponseBody{ + std::move(response_breakpoints)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp index 54c11cfa0b791..5f4f016f6a1ef 100644 --- a/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp @@ -20,7 +20,7 @@ void TestGetTargetBreakpointsRequestHandler::operator()( llvm::json::Array response_breakpoints; for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) { auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i)); - AppendBreakpoint(&bp, response_breakpoints); + response_breakpoints.push_back(bp.ToProtocolBreakpoint()); } llvm::json::Object body; body.try_emplace("breakpoints", std::move(response_breakpoints)); 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/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 4409cf5b27e5b..e184e7602ef14 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "JSONUtils.h" -#include "BreakpointBase.h" #include "DAP.h" #include "ExceptionBreakpoint.h" #include "LLDBUtils.h" @@ -354,70 +353,6 @@ 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); - // 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)); -} - static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { uint64_t debug_info_size = 0; llvm::StringRef section_name(section.GetName()); @@ -506,12 +441,6 @@ llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { return llvm::json::Value(std::move(object)); } -void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints, - std::optional<llvm::StringRef> request_path, - std::optional<uint32_t> request_line) { - breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line)); -} - // "Event": { // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { // "type": "object", @@ -567,96 +496,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..7c7cf620f0c06 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -198,67 +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 -/// A LLDB breakpoint object to convert into a JSON value -/// -/// \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 resulting "Breakpoint" object. -/// 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. -/// -/// \param[in] request_column -/// An optional column to use when creating the resulting "Breakpoint" -/// object. It is used if the breakpoint has no valid locations. It is -/// useful to ensure the same column provided by the setBreakpoints request -/// are returned to the IDE as a fallback. -/// -/// \return -/// A "Breakpoint" JSON object with that follows the formal JSON -/// definition outlined by Microsoft. -llvm::json::Value -CreateBreakpoint(BreakpointBase *bp, - std::optional<llvm::StringRef> request_path = std::nullopt, - std::optional<uint32_t> request_line = std::nullopt, - std::optional<uint32_t> request_column = std::nullopt); - /// Converts a LLDB module to a VS Code DAP module for use in "modules" events. /// /// \param[in] target @@ -323,7 +262,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 +273,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 +283,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/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 4204ae7785e63..316e146d43a0f 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -376,4 +376,74 @@ bool fromJSON(const llvm::json::Value &Params, StepOutArguments &SOA, OM.mapOptional("granularity", SOA.granularity); } +bool fromJSON(const llvm::json::Value &Params, SetBreakpointsArguments &SBA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("source", SBA.source) && + O.map("breakpoints", SBA.breakpoints) && O.map("lines", SBA.lines) && + O.map("sourceModified", SBA.sourceModified); +} + +llvm::json::Value toJSON(const SetBreakpointsResponseBody &SBR) { + json::Object result; + result["breakpoints"] = SBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetFunctionBreakpointsArguments &SFBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SFBA.breakpoints); +} + +llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &SFBR) { + json::Object result; + result["breakpoints"] = SFBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetInstructionBreakpointsArguments &SIBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SIBA.breakpoints); +} + +llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &SIBR) { + json::Object result; + result["breakpoints"] = SIBR.breakpoints; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + DataBreakpointInfoArguments &DBIA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("variablesReference", DBIA.variablesReference) && + O.map("name", DBIA.name) && O.map("frameId", DBIA.frameId) && + O.map("bytes", DBIA.bytes) && O.map("asAddress", DBIA.asAddress) && + O.map("mode", DBIA.mode); +} + +llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &DBIRB) { + json::Object result; + result["dataId"] = DBIRB.dataId ? *DBIRB.dataId : llvm::json::Value(nullptr); + result["description"] = DBIRB.description; + if (DBIRB.accessTypes) + result["accessTypes"] = *DBIRB.accessTypes; + if (DBIRB.canPersist) + result["canPersist"] = *DBIRB.canPersist; + return result; +} + +bool fromJSON(const llvm::json::Value &Params, + SetDataBreakpointsArguments &SDBA, llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("breakpoints", SDBA.breakpoints); +} + +llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) { + json::Object result; + result["breakpoints"] = SDBR.breakpoints; + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index 4f17d6e79080d..c6456b4113320 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -559,6 +559,153 @@ struct BreakpointLocationsResponseBody { }; llvm::json::Value toJSON(const BreakpointLocationsResponseBody &); +/// 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 `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 &); + +/// 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<uint64_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 diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 967c1d2321502..2b7419916268b 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -43,6 +43,32 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) { O.map("sourceReference", S.sourceReference); } +llvm::json::Value toJSON(PresentationHint hint) { + switch (hint) { + case ePresentationHintNormal: + return "normal"; + case ePresentationHintEmphasize: + return "emphasize"; + case ePresentationHintDeemphasize: + return "deemphasize"; + } + llvm_unreachable("unhandled presentation hint."); +} + +llvm::json::Value toJSON(const Source &S) { + json::Object result; + if (S.name) + result.insert({"name", *S.name}); + if (S.path) + result.insert({"path", *S.path}); + if (S.sourceReference) + result.insert({"sourceReference", *S.sourceReference}); + if (S.presentationHint) + result.insert({"presentationHint", *S.presentationHint}); + + return result; +} + json::Value toJSON(const ExceptionBreakpointsFilter &EBF) { json::Object result{{"filter", EBF.filter}, {"label", EBF.label}}; @@ -274,4 +300,108 @@ json::Value toJSON(const BreakpointLocation &B) { return result; } +llvm::json::Value toJSON(const BreakpointReason &BR) { + switch (BR) { + case BreakpointReason::eBreakpointReasonPending: + return "pending"; + case BreakpointReason::eBreakpointReasonFailed: + return "failed"; + } + llvm_unreachable("unhandled breakpoint reason."); +} + +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) { + result.insert({"reason", *BP.reason}); + } + + return result; +} + +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; +} + +llvm::json::Value toJSON(const DataBreakpointAccessType &DBAT) { + switch (DBAT) { + case eDataBreakpointAccessTypeRead: + return "read"; + case eDataBreakpointAccessTypeWrite: + return "write"; + case eDataBreakpointAccessTypeReadWrite: + return "readWrite"; + } + llvm_unreachable("unhandled data breakpoint access type."); +} + +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 8ea483d810e02..1f0cb1e0b2d41 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -20,6 +20,7 @@ #ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H #define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H +#include "lldb/lldb-defines.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/JSON.h" #include <cstdint> @@ -273,6 +274,7 @@ enum PresentationHint : unsigned { ePresentationHintEmphasize, ePresentationHintDeemphasize, }; +llvm::json::Value toJSON(PresentationHint hint); /// 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 @@ -302,6 +304,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`. @@ -350,6 +353,187 @@ struct BreakpointLocation { }; llvm::json::Value toJSON(const BreakpointLocation &); +/// A machine-readable explanation of why a breakpoint may not be verified. +enum class BreakpointReason : 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, +}; +llvm::json::Value toJSON(const BreakpointReason &); + +/// Information about a breakpoint created in `setBreakpoints`, +/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or +/// `setDataBreakpoints` requests. +struct Breakpoint { + /// 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 = false; + + /// 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<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 + /// should omit this property. + std::optional<BreakpointReason> 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 = LLDB_INVALID_LINE_NUMBER; + + /// 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); +llvm::json::Value toJSON(const DataBreakpointAccessType &); + +/// 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<int32_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/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); diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp index a94cbcdbc4122..73ed4fdbae1b8 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,14 +37,17 @@ 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; } + + return breakpoint; } void Watchpoint::SetWatchpoint() { diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h index bf52b41281f29..b7fe58fe73501 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() override; void SetWatchpoint(); diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index f1f4f4db709dd..0a6f3f2cb7755 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -781,7 +781,7 @@ inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { Out = *S; return true; } - P.report("expected integer"); + P.report("expected unsigned integer"); return false; } inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits