https://github.com/eronnen updated https://github.com/llvm/llvm-project/pull/137448
>From dc8388b0027c75dba465c72009ac0c25750ae877 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sat, 26 Apr 2025 10:25:21 +0200 Subject: [PATCH 1/2] adding breakpoints protocol types add all breakpoint requests JSON types decouple JSON from DAP Breakpoint classes forgor exception breakpoint response migrating breakpoint requests migrate breakpoints requests handler implementations remove CreateBreakpoint data breakpoint requests serialization fix GetFrame fix data breakpoint return exception breakpoints list restore exception breakpoints --- lldb/tools/lldb-dap/Breakpoint.cpp | 22 +- lldb/tools/lldb-dap/Breakpoint.h | 6 +- lldb/tools/lldb-dap/BreakpointBase.cpp | 12 +- lldb/tools/lldb-dap/BreakpointBase.h | 8 +- lldb/tools/lldb-dap/DAP.cpp | 11 +- lldb/tools/lldb-dap/DAP.h | 1 + lldb/tools/lldb-dap/FunctionBreakpoint.cpp | 8 +- lldb/tools/lldb-dap/FunctionBreakpoint.h | 3 +- .../DataBreakpointInfoRequestHandler.cpp | 175 ++++---------- .../Handler/InitializeRequestHandler.cpp | 2 +- lldb/tools/lldb-dap/Handler/RequestHandler.h | 51 ++-- .../Handler/SetBreakpointsRequestHandler.cpp | 180 +++----------- .../SetDataBreakpointsRequestHandler.cpp | 98 ++------ .../SetFunctionBreakpointsRequestHandler.cpp | 115 ++------- ...etInstructionBreakpointsRequestHandler.cpp | 223 ++---------------- ...TestGetTargetBreakpointsRequestHandler.cpp | 2 +- lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 15 +- lldb/tools/lldb-dap/InstructionBreakpoint.h | 4 +- lldb/tools/lldb-dap/JSONUtils.cpp | 161 +------------ lldb/tools/lldb-dap/JSONUtils.h | 67 +----- .../lldb-dap/Protocol/ProtocolRequests.cpp | 70 ++++++ .../lldb-dap/Protocol/ProtocolRequests.h | 147 ++++++++++++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 127 ++++++++++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 181 ++++++++++++++ lldb/tools/lldb-dap/SourceBreakpoint.cpp | 13 +- lldb/tools/lldb-dap/SourceBreakpoint.h | 3 +- lldb/tools/lldb-dap/Watchpoint.cpp | 26 +- lldb/tools/lldb-dap/Watchpoint.h | 6 +- llvm/include/llvm/Support/JSON.h | 8 + 29 files changed, 790 insertions(+), 955 deletions(-) diff --git a/lldb/tools/lldb-dap/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp index 5679fd545d53f..26d633d1d172e 100644 --- a/lldb/tools/lldb-dap/Breakpoint.cpp +++ b/lldb/tools/lldb-dap/Breakpoint.cpp @@ -14,7 +14,6 @@ #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBMutex.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/JSON.h" #include <cstddef> #include <cstdint> #include <mutex> @@ -30,13 +29,16 @@ void Breakpoint::SetHitCondition() { m_bp.SetIgnoreCount(hitCount - 1); } -void Breakpoint::CreateJsonObject(llvm::json::Object &object) { +protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() { + protocol::Breakpoint breakpoint; + // Each breakpoint location is treated as a separate breakpoint for VS code. // They don't have the notion of a single breakpoint with multiple locations. if (!m_bp.IsValid()) - return; - object.try_emplace("verified", m_bp.GetNumResolvedLocations() > 0); - object.try_emplace("id", m_bp.GetID()); + return breakpoint; + + breakpoint.verified = m_bp.GetNumResolvedLocations() > 0; + breakpoint.id = m_bp.GetID(); // VS Code DAP doesn't currently allow one breakpoint to have multiple // locations so we just report the first one. If we report all locations // then the IDE starts showing the wrong line numbers and locations for @@ -60,16 +62,18 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) { if (bp_addr.IsValid()) { std::string formatted_addr = "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget())); - object.try_emplace("instructionReference", formatted_addr); + breakpoint.instructionReference = formatted_addr; auto line_entry = bp_addr.GetLineEntry(); const auto line = line_entry.GetLine(); if (line != UINT32_MAX) - object.try_emplace("line", line); + breakpoint.line = line; const auto column = line_entry.GetColumn(); if (column != 0) - object.try_emplace("column", column); - object.try_emplace("source", CreateSource(line_entry)); + breakpoint.column = column; + breakpoint.source = CreateSource(line_entry); } + + return breakpoint; } bool Breakpoint::MatchesName(const char *name) { diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h index 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 55d49667b6398..50e5fd9dd6147 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -45,6 +45,7 @@ #include <chrono> #include <condition_variable> #include <cstdarg> +#include <cstdint> #include <cstdio> #include <fstream> #include <future> @@ -514,9 +515,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 = @@ -525,6 +524,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( diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..d785f071414db 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/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index 7d8ac676ba935..9614d2876b2d4 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -212,7 +212,7 @@ static void EventThreadFunction(DAP &dap) { // 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/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index fa3d76ed4a125..279e70dbbf555 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" @@ -339,15 +338,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 { @@ -360,43 +363,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 61fea66490c30..1d204ebaee381 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -301,4 +301,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 33f93cc38799a..91301ed09c33f 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -377,6 +377,153 @@ bool fromJSON(const llvm::json::Value &, StepOutArguments &, llvm::json::Path); /// body field is required. using StepOutResponse = VoidResponse; +/// Arguments for `setBreakpoints` request. +struct SetBreakpointsArguments { + /// The source location of the breakpoints; either `source.path` or + /// `source.sourceReference` must be specified. + Source source; + + /// The code locations of the breakpoints. + std::optional<std::vector<SourceBreakpoint>> breakpoints; + + /// Deprecated: The code locations of the breakpoints. + std::optional<std::vector<uint32_t>> lines; + + /// A value of true indicates that the underlying source has been modified + /// which results in new breakpoint locations. + std::optional<bool> sourceModified; +}; +bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +/// This includes the actual code location and whether the breakpoint could be +/// verified. The breakpoints returned are in the same order as the elements of +/// the breakpoints (or the deprecated lines) array in the arguments. +struct SetBreakpointsResponseBody { + /// Information about the breakpoints. + /// The array elements are in the same order as the elements of the + /// `breakpoints` (or the deprecated `lines`) array in the arguments. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetBreakpointsResponseBody &); + +/// Arguments for `setFunctionBreakpoints` request. +struct SetFunctionBreakpointsArguments { + /// The function names of the breakpoints. + std::vector<FunctionBreakpoint> breakpoints; +}; +bool fromJSON(const llvm::json::Value &, SetFunctionBreakpointsArguments &, + llvm::json::Path); + +/// Response to `setFunctionBreakpoints` request. +/// Returned is information about each breakpoint created by this request. +struct SetFunctionBreakpointsResponseBody { + /// Information about the breakpoints. The array elements correspond to the + /// elements of the `breakpoints` array. + std::vector<Breakpoint> breakpoints; +}; +llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &); + +/// Arguments for `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 e64998c4ca488..5d1868c7238a4 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); } +static llvm::json::Value ToString(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", ToString(*S.presentationHint)}); + + return result; +} + json::Value toJSON(const ExceptionBreakpointsFilter &EBF) { json::Object result{{"filter", EBF.filter}, {"label", EBF.label}}; @@ -254,4 +280,105 @@ bool fromJSON(const llvm::json::Value &Params, SteppingGranularity &SG, return true; } +json::Value toJSON(const Breakpoint &BP) { + json::Object result{{"verified", BP.verified}}; + + if (BP.id) + result.insert({"id", *BP.id}); + if (BP.message) + result.insert({"message", *BP.message}); + if (BP.source) + result.insert({"source", *BP.source}); + if (BP.line) + result.insert({"line", *BP.line}); + if (BP.column) + result.insert({"column", *BP.column}); + if (BP.endLine) + result.insert({"endLine", *BP.endLine}); + if (BP.endColumn) + result.insert({"endColumn", *BP.endColumn}); + if (BP.instructionReference) + result.insert({"instructionReference", *BP.instructionReference}); + if (BP.offset) + result.insert({"offset", *BP.offset}); + if (BP.reason) { + switch (*BP.reason) { + case Breakpoint::Reason::eBreakpointReasonPending: + result.insert({"reason", "pending"}); + break; + case Breakpoint::Reason::eBreakpointReasonFailed: + result.insert({"reason", "failed"}); + break; + } + } + + return result; +} + +bool fromJSON(const llvm::json::Value &Params, 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 54941f24efbd9..9d4a75f5f29f8 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -302,6 +302,7 @@ struct Source { // unsupported keys: origin, sources, adapterData, checksums }; bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path); +llvm::json::Value toJSON(const Source &); /// The granularity of one `step` in the stepping requests `next`, `stepIn`, /// `stepOut` and `stepBack`. @@ -322,6 +323,186 @@ enum SteppingGranularity : unsigned { bool fromJSON(const llvm::json::Value &, SteppingGranularity &, llvm::json::Path); +/// Information about a breakpoint created in `setBreakpoints`, +/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or +/// `setDataBreakpoints` requests. +struct Breakpoint { + /// A machine-readable explanation of why a breakpoint may not be verified. + enum class Reason : unsigned { + /// Indicates a breakpoint might be verified in the future, but + /// the adapter cannot verify it in the current state. + eBreakpointReasonPending, + /// Indicates a breakpoint was not able to be verified, and the + /// adapter does not believe it can be verified without intervention. + eBreakpointReasonFailed, + }; + + /// The identifier for the breakpoint. It is needed if breakpoint events are + /// used to update or remove breakpoints. + std::optional<int> id; + + /// If true, the breakpoint could be set (but not necessarily at the desired + /// location). + bool verified; + + /// A message about the state of the breakpoint. + /// This is shown to the user and can be used to explain why a breakpoint + /// could not be verified. + std::optional<std::string> message; + + /// The source where the breakpoint is located. + std::optional<Source> source; + + /// The start line of the actual range covered by the breakpoint. + std::optional<uint32_t> line; + + /// Start position of the source range covered by the breakpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional<uint32_t> column; + + /// The end line of the actual range covered by the breakpoint. + std::optional<uint32_t> endLine; + + /// End position of the source range covered by the breakpoint. It is measured + /// in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. If no end line is given, then the + /// end column is assumed to be in the start line. + std::optional<uint32_t> endColumn; + + /// A memory reference to where the breakpoint is set. + std::optional<std::string> instructionReference; + + /// The offset from the instruction reference. + /// This can be negative. + std::optional<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<Reason> reason; +}; +llvm::json::Value toJSON(const Breakpoint &); + +/// Properties of a breakpoint or logpoint passed to the `setBreakpoints` +/// request +struct SourceBreakpoint { + /// The source line of the breakpoint or logpoint. + uint32_t line; + + /// Start position within source line of the breakpoint or logpoint. It is + /// measured in UTF-16 code units and the client capability `columnsStartAt1` + /// determines whether it is 0- or 1-based. + std::optional<uint32_t> column; + + /// The expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional<std::string> condition; + + /// The expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + /// If both this property and `condition` are specified, `hitCondition` should + /// be evaluated only if the `condition` is met, and the debug adapter should + /// stop only if both conditions are met. + std::optional<std::string> hitCondition; + + /// If this attribute exists and is non-empty, the debug adapter must not + /// 'break' (stop) + /// but log the message instead. Expressions within `{}` are interpolated. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsLogPoints` is true. + /// If either `hitCondition` or `condition` is specified, then the message + /// should only be logged if those conditions are met. + std::optional<std::string> logMessage; + + /// The mode of this breakpoint. If defined, this must be one of the + /// `breakpointModes` the debug adapter advertised in its `Capabilities`. + std::optional<std::string> mode; +}; +bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path); + +/// Properties of a breakpoint passed to the `setFunctionBreakpoints` request. +struct FunctionBreakpoint { + /// The name of the function. + std::string name; + + /// An expression for conditional breakpoints. + /// It is only honored by a debug adapter if the corresponding capability + /// `supportsConditionalBreakpoints` is true. + std::optional<std::string> condition; + + /// An expression that controls how many hits of the breakpoint are ignored. + /// The debug adapter is expected to interpret the expression as needed. + /// The attribute is only honored by a debug adapter if the corresponding + /// capability `supportsHitConditionalBreakpoints` is true. + std::optional<std::string> hitCondition; +}; +bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &, + llvm::json::Path); + +/// This enumeration defines all possible access types for data breakpoints. +/// Values: ‘read’, ‘write’, ‘readWrite’ +enum DataBreakpointAccessType : unsigned { + eDataBreakpointAccessTypeRead, + eDataBreakpointAccessTypeWrite, + eDataBreakpointAccessTypeReadWrite +}; +bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &, + llvm::json::Path); +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 7f7f5f6228763..0a6f3f2cb7755 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -776,6 +776,14 @@ inline bool fromJSON(const Value &E, bool &Out, Path P) { P.report("expected boolean"); return false; } +inline bool fromJSON(const Value &E, unsigned int &Out, Path P) { + if (auto S = E.getAsInteger()) { + Out = *S; + return true; + } + P.report("expected unsigned integer"); + return false; +} inline bool fromJSON(const Value &E, uint64_t &Out, Path P) { if (auto S = E.getAsUINT64()) { Out = *S; >From a0fe0dd2f540ad6aea890cfec67492f8be667ef7 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sun, 27 Apr 2025 23:31:14 +0200 Subject: [PATCH 2/2] add default values --- lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp | 4 ++-- lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index 5d1868c7238a4..d185b0d65ed8a 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -43,7 +43,7 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) { O.map("sourceReference", S.sourceReference); } -static llvm::json::Value ToString(PresentationHint hint) { +llvm::json::Value toJSON(PresentationHint hint) { switch (hint) { case ePresentationHintNormal: return "normal"; @@ -64,7 +64,7 @@ llvm::json::Value toJSON(const Source &S) { if (S.sourceReference) result.insert({"sourceReference", *S.sourceReference}); if (S.presentationHint) - result.insert({"presentationHint", ToString(*S.presentationHint)}); + result.insert({"presentationHint", *S.presentationHint}); return result; } diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 9d4a75f5f29f8..9159d4d3adaf0 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 @@ -343,7 +345,7 @@ struct Breakpoint { /// If true, the breakpoint could be set (but not necessarily at the desired /// location). - bool verified; + 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 @@ -388,7 +390,7 @@ llvm::json::Value toJSON(const Breakpoint &); /// request struct SourceBreakpoint { /// The source line of the breakpoint or logpoint. - uint32_t line; + 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` _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits