[Lldb-commits] [lldb] [lldb-dap] Refactor breakpoint related request handlers (NFC) (PR #128550)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Mon Feb 24 12:20:57 PST 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/128550
>From 95f575e5f58a4d4285377f9f6d0807573da90e44 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 11:51:28 -0600
Subject: [PATCH 1/2] [lldb-dap] Refactor breakpoint related request handlers
(NFC)
---
lldb/tools/lldb-dap/CMakeLists.txt | 7 +-
.../DataBreakpointInfoRequestHandler.cpp | 190 ++++
.../tools/lldb-dap/Handler/RequestHandler.cpp | 55 ++
lldb/tools/lldb-dap/Handler/RequestHandler.h | 47 +
.../Handler/SetBreakpointsRequestHandler.cpp | 182 ++++
.../SetDataBreakpointsRequestHandler.cpp | 114 +++
.../SetExceptionBreakpointsRequestHandler.cpp | 93 ++
.../SetFunctionBreakpointsRequestHandler.cpp | 139 +++
...etInstructionBreakpointsRequestHandler.cpp | 249 +++++
lldb/tools/lldb-dap/lldb-dap.cpp | 880 +-----------------
10 files changed, 1081 insertions(+), 875 deletions(-)
create mode 100644 lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
create mode 100644 lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
create mode 100644 lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
create mode 100644 lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
create mode 100644 lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
create mode 100644 lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 688a2e448f71d..c04b10861a4c5 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -43,6 +43,7 @@ add_lldb_tool(lldb-dap
Handler/CompletionsHandler.cpp
Handler/ConfigurationDoneRequestHandler.cpp
Handler/ContinueRequestHandler.cpp
+ Handler/DataBreakpointInfoRequestHandler.cpp
Handler/DisconnectRequestHandler.cpp
Handler/EvaluateRequestHandler.cpp
Handler/ExceptionInfoRequestHandler.cpp
@@ -52,10 +53,14 @@ add_lldb_tool(lldb-dap
Handler/NextRequestHandler.cpp
Handler/RequestHandler.cpp
Handler/RestartRequestHandler.cpp
+ Handler/SetBreakpointsRequestHandler.cpp
+ Handler/SetDataBreakpointsRequestHandler.cpp
+ Handler/SetExceptionBreakpointsRequestHandler.cpp
+ Handler/SetFunctionBreakpointsRequestHandler.cpp
+ Handler/SetInstructionBreakpointsRequestHandler.cpp Handler/StepOutRequestHandler.cpp
Handler/StepInRequestHandler.cpp
Handler/StepInTargetsRequestHandler.cpp
Handler/TestGetTargetBreakpointsRequestHandler.cpp
- Handler/StepOutRequestHandler.cpp
LINK_LIBS
liblldb
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
new file mode 100644
index 0000000000000..519a9c728e4b3
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -0,0 +1,190 @@
+//===-- DataBreakpointInfoRequestHandler.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBMemoryRegionInfo.h"
+#include "llvm/ADT/StringExtras.h"
+
+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) {
+ 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 =
+ GetUnsigned(arguments, "variablesReference", 0);
+ llvm::StringRef name = GetString(arguments, "name");
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+ lldb::SBValue variable = FindVariable(variablesReference, name);
+ std::string addr, size;
+
+ 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()));
+ } else if (byte_size == 0) {
+ body.try_emplace("dataId", nullptr);
+ body.try_emplace("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());
+ 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");
+ } else {
+ uint64_t load_addr = value.GetValueAsUnsigned();
+ lldb::SBData data = value.GetPointeeData();
+ if (data.IsValid()) {
+ size = llvm::utostr(data.GetByteSize());
+ addr = llvm::utohexstr(load_addr);
+ lldb::SBMemoryRegionInfo region;
+ lldb::SBError err =
+ dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
+ // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
+ // 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");
+ }
+ }
+ } else {
+ body.try_emplace("dataId", nullptr);
+ body.try_emplace("description",
+ "unable to get byte size for expression: " +
+ name.str());
+ }
+ }
+ } else {
+ body.try_emplace("dataId", nullptr);
+ body.try_emplace("description", "variable not found: " + name.str());
+ }
+
+ 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());
+ }
+ response.try_emplace("body", std::move(body));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index f9502e09846d4..de313eb02a24a 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -232,4 +232,59 @@ bool RequestHandler::HasInstructionGranularity(
return false;
}
+lldb::SBValueList *
+RequestHandler::GetTopLevelScope(int64_t variablesReference) {
+ switch (variablesReference) {
+ case VARREF_LOCALS:
+ return &dap.variables.locals;
+ case VARREF_GLOBALS:
+ return &dap.variables.globals;
+ case VARREF_REGS:
+ return &dap.variables.registers;
+ default:
+ return nullptr;
+ }
+}
+
+lldb::SBValue RequestHandler::FindVariable(uint64_t variablesReference,
+ llvm::StringRef name) {
+ lldb::SBValue variable;
+ if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+ bool is_duplicated_variable_name = name.contains(" @");
+ // variablesReference is one of our scopes, not an actual variable it is
+ // asking for a variable in locals or globals or registers
+ int64_t end_idx = top_scope->GetSize();
+ // Searching backward so that we choose the variable in closest scope
+ // among variables of the same name.
+ for (int64_t i = end_idx - 1; i >= 0; --i) {
+ lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
+ std::string variable_name = CreateUniqueVariableNameForDisplay(
+ curr_variable, is_duplicated_variable_name);
+ if (variable_name == name) {
+ variable = curr_variable;
+ break;
+ }
+ }
+ } else {
+ // This is not under the globals or locals scope, so there are no duplicated
+ // names.
+
+ // We have a named item within an actual variable so we need to find it
+ // withing the container variable by name.
+ lldb::SBValue container = dap.variables.GetVariable(variablesReference);
+ variable = container.GetChildMemberWithName(name.data());
+ if (!variable.IsValid()) {
+ if (name.starts_with("[")) {
+ llvm::StringRef index_str(name.drop_front(1));
+ uint64_t index = 0;
+ if (!index_str.consumeInteger(0, index)) {
+ if (index_str == "]")
+ variable = container.GetChildAtIndex(index);
+ }
+ }
+ }
+ }
+ return variable;
+}
+
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 874b600181f43..a30e0dcc2bd04 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -52,6 +52,9 @@ class RequestHandler {
// Check if the step-granularity is `instruction`.
bool HasInstructionGranularity(const llvm::json::Object &request);
+ lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
+ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
+
/// @}
DAP &dap;
@@ -162,6 +165,50 @@ class StepOutRequestHandler : public RequestHandler {
void operator()(const llvm::json::Object &request) override;
};
+class SetBreakpointsRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() { return "setBreakpoints"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+class SetExceptionBreakpointsRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() { return "setExceptionBreakpoints"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+class SetFunctionBreakpointsRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() { return "setFunctionBreakpoints"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+class DataBreakpointInfoRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() { return "dataBreakpointInfo"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+class SetDataBreakpointsRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() { return "setDataBreakpoints"; }
+ void operator()(const llvm::json::Object &request) override;
+};
+
+class SetInstructionBreakpointsRequestHandler : public RequestHandler {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral getCommand() {
+ return "setInstructionBreakpoints";
+ }
+ void operator()(const llvm::json::Object &request) override;
+};
+
class CompileUnitsRequestHandler : public RequestHandler {
public:
using RequestHandler::RequestHandler;
diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..6dbd24c130db6
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
@@ -0,0 +1,182 @@
+//===-- SetBreakpointsRequestHandler.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+
+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) {
+ 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");
+ const auto *breakpoints = arguments->getArray("breakpoints");
+ llvm::json::Array 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.line, src_bp.column);
+ 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.line);
+ }
+ }
+ }
+
+ // Delete any breakpoints in this source file that aren't in the
+ // request_bps set. There is no call to remove breakpoints other than
+ // calling this function with a smaller or empty "breakpoints" list.
+ auto old_src_bp_pos = dap.source_breakpoints.find(path);
+ if (old_src_bp_pos != dap.source_breakpoints.end()) {
+ for (auto &old_bp : old_src_bp_pos->second) {
+ auto request_pos = request_bps.find(old_bp.first);
+ if (request_pos == request_bps.end()) {
+ // This breakpoint no longer exists in this source file, delete it
+ dap.target.BreakpointDelete(old_bp.second.bp.GetID());
+ old_src_bp_pos->second.erase(old_bp.first);
+ }
+ }
+ }
+
+ 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)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..9c2308f7a6bcd
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp
@@ -0,0 +1,114 @@
+//===-- SetDataBreakpointsRequestHandler.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.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) {
+ 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;
+ 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);
+ }
+ }
+ // 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.
+ std::set<lldb::addr_t> addresses;
+ for (auto iter = watchpoints.rbegin(); iter != watchpoints.rend(); ++iter) {
+ if (addresses.count(iter->addr) == 0) {
+ iter->SetWatchpoint();
+ addresses.insert(iter->addr);
+ }
+ }
+ for (auto wp : watchpoints)
+ AppendBreakpoint(&wp, response_breakpoints);
+
+ 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)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..d208525385094
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp
@@ -0,0 +1,93 @@
+//===-- SetExceptionBreakpointsRequestHandler.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+#include "RequestHandler.h"
+#include <set>
+
+namespace lldb_dap {
+
+// "SetExceptionBreakpointsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "SetExceptionBreakpoints request; value of command field
+// is 'setExceptionBreakpoints'. The request configures the debuggers
+// response to thrown exceptions. If an exception is configured to break, a
+// StoppedEvent is fired (event type 'exception').", "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "setExceptionBreakpoints" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/SetExceptionBreakpointsArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "SetExceptionBreakpointsArguments": {
+// "type": "object",
+// "description": "Arguments for 'setExceptionBreakpoints' request.",
+// "properties": {
+// "filters": {
+// "type": "array",
+// "items": {
+// "type": "string"
+// },
+// "description": "IDs of checked exception options. The set of IDs is
+// returned via the 'exceptionBreakpointFilters' capability."
+// },
+// "exceptionOptions": {
+// "type": "array",
+// "items": {
+// "$ref": "#/definitions/ExceptionOptions"
+// },
+// "description": "Configuration options for selected exceptions."
+// }
+// },
+// "required": [ "filters" ]
+// },
+// "SetExceptionBreakpointsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'setExceptionBreakpoints' request. This is
+// just an acknowledgement, so no body field is required."
+// }]
+// }
+void SetExceptionBreakpointsRequestHandler::operator()(
+ const llvm::json::Object &request) {
+ llvm::json::Object response;
+ lldb::SBError error;
+ FillResponse(request, response);
+ const auto *arguments = request.getObject("arguments");
+ const auto *filters = arguments->getArray("filters");
+ // Keep a list of any exception breakpoint filter names that weren't set
+ // so we can clear any exception breakpoints if needed.
+ std::set<std::string> unset_filters;
+ for (const auto &bp : *dap.exception_breakpoints)
+ unset_filters.insert(bp.filter);
+
+ for (const auto &value : *filters) {
+ const auto filter = GetAsString(value);
+ auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
+ if (exc_bp) {
+ exc_bp->SetBreakpoint();
+ unset_filters.erase(std::string(filter));
+ }
+ }
+ for (const auto &filter : unset_filters) {
+ auto *exc_bp = dap.GetExceptionBreakpoint(filter);
+ if (exc_bp)
+ exc_bp->ClearBreakpoint();
+ }
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..e55cfaef8c897
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp
@@ -0,0 +1,139 @@
+//===-- SetFunctionBreakpointsRequestHandler.cpp --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "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) {
+ 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;
+
+ // 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.functionName, dap, *bp_obj);
+ if (inserted)
+ it->second.SetBreakpoint();
+ else
+ it->second.UpdateBreakpoint(fn_bp);
+
+ AppendBreakpoint(&it->second, response_breakpoints);
+ seen.erase(fn_bp.functionName);
+ }
+
+ // Remove any breakpoints that are no longer in our list
+ for (const auto &name : seen) {
+ auto fn_bp = dap.function_breakpoints.find(name);
+ if (fn_bp == dap.function_breakpoints.end())
+ continue;
+ dap.target.BreakpointDelete(fn_bp->second.bp.GetID());
+ 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)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
new file mode 100644
index 0000000000000..636d9b814ab76
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp
@@ -0,0 +1,249 @@
+//===-- SetInstructionBreakpointsRequestHandler.cpp -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "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) {
+ 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");
+
+ // Disable any instruction breakpoints that aren't in this request.
+ // There is no call to remove instruction breakpoints other than calling this
+ // function with a smaller or empty "breakpoints" list.
+ llvm::DenseSet<lldb::addr_t> seen;
+ for (const auto &addr : dap.instruction_breakpoints)
+ seen.insert(addr.first);
+
+ for (const auto &bp : *breakpoints) {
+ const auto *bp_obj = bp.getAsObject();
+ if (!bp_obj)
+ continue;
+ // Read instruction breakpoint request.
+ InstructionBreakpoint inst_bp(dap, *bp_obj);
+ const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
+ inst_bp.instructionAddressReference, dap, *bp_obj);
+ if (inserted)
+ iv->second.SetBreakpoint();
+ else
+ iv->second.UpdateBreakpoint(inst_bp);
+ AppendBreakpoint(&iv->second, response_breakpoints);
+ seen.erase(inst_bp.instructionAddressReference);
+ }
+
+ for (const auto &addr : seen) {
+ auto inst_bp = dap.instruction_breakpoints.find(addr);
+ if (inst_bp == dap.instruction_breakpoints.end())
+ continue;
+ dap.target.BreakpointDelete(inst_bp->second.bp.GetID());
+ 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)));
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 632629d56232c..fd4615897841c 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -18,8 +18,6 @@
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBFile.h"
#include "lldb/API/SBInstruction.h"
-#include "lldb/API/SBListener.h"
-#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBStream.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/MainLoop.h"
@@ -395,636 +393,6 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
dap.SendJSON(llvm::json::Value(std::move(response)));
}
-// "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 request_setBreakpoints(DAP &dap, const llvm::json::Object &request) {
- 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");
- const auto *breakpoints = arguments->getArray("breakpoints");
- llvm::json::Array 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.line, src_bp.column);
- 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.line);
- }
- }
- }
-
- // Delete any breakpoints in this source file that aren't in the
- // request_bps set. There is no call to remove breakpoints other than
- // calling this function with a smaller or empty "breakpoints" list.
- auto old_src_bp_pos = dap.source_breakpoints.find(path);
- if (old_src_bp_pos != dap.source_breakpoints.end()) {
- for (auto &old_bp : old_src_bp_pos->second) {
- auto request_pos = request_bps.find(old_bp.first);
- if (request_pos == request_bps.end()) {
- // This breakpoint no longer exists in this source file, delete it
- dap.target.BreakpointDelete(old_bp.second.bp.GetID());
- old_src_bp_pos->second.erase(old_bp.first);
- }
- }
- }
-
- 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)));
-}
-
-// "SetExceptionBreakpointsRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "SetExceptionBreakpoints request; value of command field
-// is 'setExceptionBreakpoints'. The request configures the debuggers
-// response to thrown exceptions. If an exception is configured to break, a
-// StoppedEvent is fired (event type 'exception').", "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "setExceptionBreakpoints" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/SetExceptionBreakpointsArguments"
-// }
-// },
-// "required": [ "command", "arguments" ]
-// }]
-// },
-// "SetExceptionBreakpointsArguments": {
-// "type": "object",
-// "description": "Arguments for 'setExceptionBreakpoints' request.",
-// "properties": {
-// "filters": {
-// "type": "array",
-// "items": {
-// "type": "string"
-// },
-// "description": "IDs of checked exception options. The set of IDs is
-// returned via the 'exceptionBreakpointFilters' capability."
-// },
-// "exceptionOptions": {
-// "type": "array",
-// "items": {
-// "$ref": "#/definitions/ExceptionOptions"
-// },
-// "description": "Configuration options for selected exceptions."
-// }
-// },
-// "required": [ "filters" ]
-// },
-// "SetExceptionBreakpointsResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to 'setExceptionBreakpoints' request. This is
-// just an acknowledgement, so no body field is required."
-// }]
-// }
-void request_setExceptionBreakpoints(DAP &dap,
- const llvm::json::Object &request) {
- llvm::json::Object response;
- lldb::SBError error;
- FillResponse(request, response);
- const auto *arguments = request.getObject("arguments");
- const auto *filters = arguments->getArray("filters");
- // Keep a list of any exception breakpoint filter names that weren't set
- // so we can clear any exception breakpoints if needed.
- std::set<std::string> unset_filters;
- for (const auto &bp : *dap.exception_breakpoints)
- unset_filters.insert(bp.filter);
-
- for (const auto &value : *filters) {
- const auto filter = GetAsString(value);
- auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
- if (exc_bp) {
- exc_bp->SetBreakpoint();
- unset_filters.erase(std::string(filter));
- }
- }
- for (const auto &filter : unset_filters) {
- auto *exc_bp = dap.GetExceptionBreakpoint(filter);
- if (exc_bp)
- exc_bp->ClearBreakpoint();
- }
- dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "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 request_setFunctionBreakpoints(DAP &dap,
- const llvm::json::Object &request) {
- 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;
-
- // 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.functionName, dap, *bp_obj);
- if (inserted)
- it->second.SetBreakpoint();
- else
- it->second.UpdateBreakpoint(fn_bp);
-
- AppendBreakpoint(&it->second, response_breakpoints);
- seen.erase(fn_bp.functionName);
- }
-
- // Remove any breakpoints that are no longer in our list
- for (const auto &name : seen) {
- auto fn_bp = dap.function_breakpoints.find(name);
- if (fn_bp == dap.function_breakpoints.end())
- continue;
- dap.target.BreakpointDelete(fn_bp->second.bp.GetID());
- 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)));
-}
-
-// "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 request_dataBreakpointInfo(DAP &dap, const llvm::json::Object &request) {
- 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 =
- GetUnsigned(arguments, "variablesReference", 0);
- llvm::StringRef name = GetString(arguments, "name");
- lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
- lldb::SBValue variable = FindVariable(dap, variablesReference, name);
- std::string addr, size;
-
- 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()));
- } else if (byte_size == 0) {
- body.try_emplace("dataId", nullptr);
- body.try_emplace("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());
- 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");
- } else {
- uint64_t load_addr = value.GetValueAsUnsigned();
- lldb::SBData data = value.GetPointeeData();
- if (data.IsValid()) {
- size = llvm::utostr(data.GetByteSize());
- addr = llvm::utohexstr(load_addr);
- lldb::SBMemoryRegionInfo region;
- lldb::SBError err =
- dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
- // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
- // 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");
- }
- }
- } else {
- body.try_emplace("dataId", nullptr);
- body.try_emplace("description",
- "unable to get byte size for expression: " +
- name.str());
- }
- }
- } else {
- body.try_emplace("dataId", nullptr);
- body.try_emplace("description", "variable not found: " + name.str());
- }
-
- 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());
- }
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
-}
-
-// "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 request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
- 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;
- 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);
- }
- }
- // 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.
- std::set<lldb::addr_t> addresses;
- for (auto iter = watchpoints.rbegin(); iter != watchpoints.rend(); ++iter) {
- if (addresses.count(iter->addr) == 0) {
- iter->SetWatchpoint();
- addresses.insert(iter->addr);
- }
- }
- for (auto wp : watchpoints)
- AppendBreakpoint(&wp, response_breakpoints);
-
- 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)));
-}
-
// "SourceRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
@@ -2081,245 +1449,13 @@ void request_readMemory(DAP &dap, const llvm::json::Object &request) {
dap.SendJSON(llvm::json::Value(std::move(response)));
}
-// "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 request_setInstructionBreakpoints(DAP &dap,
- const llvm::json::Object &request) {
- 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");
-
- // Disable any instruction breakpoints that aren't in this request.
- // There is no call to remove instruction breakpoints other than calling this
- // function with a smaller or empty "breakpoints" list.
- llvm::DenseSet<lldb::addr_t> seen;
- for (const auto &addr : dap.instruction_breakpoints)
- seen.insert(addr.first);
-
- for (const auto &bp : *breakpoints) {
- const auto *bp_obj = bp.getAsObject();
- if (!bp_obj)
- continue;
- // Read instruction breakpoint request.
- InstructionBreakpoint inst_bp(dap, *bp_obj);
- const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
- inst_bp.instructionAddressReference, dap, *bp_obj);
- if (inserted)
- iv->second.SetBreakpoint();
- else
- iv->second.UpdateBreakpoint(inst_bp);
- AppendBreakpoint(&iv->second, response_breakpoints);
- seen.erase(inst_bp.instructionAddressReference);
- }
-
- for (const auto &addr : seen) {
- auto inst_bp = dap.instruction_breakpoints.find(addr);
- if (inst_bp == dap.instruction_breakpoints.end())
- continue;
- dap.target.BreakpointDelete(inst_bp->second.bp.GetID());
- 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)));
-}
-
void RegisterRequestCallbacks(DAP &dap) {
dap.RegisterRequest<AttachRequestHandler>();
dap.RegisterRequest<BreakpointLocationsRequestHandler>();
dap.RegisterRequest<CompletionsRequestHandler>();
dap.RegisterRequest<ConfigurationDoneRequestHandler>();
dap.RegisterRequest<ContinueRequestHandler>();
+ dap.RegisterRequest<DataBreakpointInfoRequestHandler>();
dap.RegisterRequest<DisconnectRequestHandler>();
dap.RegisterRequest<EvaluateRequestHandler>();
dap.RegisterRequest<ExceptionInfoRequestHandler>();
@@ -2327,6 +1463,11 @@ void RegisterRequestCallbacks(DAP &dap) {
dap.RegisterRequest<LaunchRequestHandler>();
dap.RegisterRequest<NextRequestHandler>();
dap.RegisterRequest<RestartRequestHandler>();
+ dap.RegisterRequest<SetBreakpointsRequestHandler>();
+ dap.RegisterRequest<SetDataBreakpointsRequestHandler>();
+ dap.RegisterRequest<SetExceptionBreakpointsRequestHandler>();
+ dap.RegisterRequest<SetFunctionBreakpointsRequestHandler>();
+ dap.RegisterRequest<SetInstructionBreakpointsRequestHandler>();
dap.RegisterRequest<StepInRequestHandler>();
dap.RegisterRequest<StepInTargetsRequestHandler>();
dap.RegisterRequest<StepOutRequestHandler>();
@@ -2340,13 +1481,6 @@ void RegisterRequestCallbacks(DAP &dap) {
dap.RegisterRequestCallback("pause", request_pause);
dap.RegisterRequestCallback("scopes", request_scopes);
- dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
- dap.RegisterRequestCallback("setExceptionBreakpoints",
- request_setExceptionBreakpoints);
- dap.RegisterRequestCallback("setFunctionBreakpoints",
- request_setFunctionBreakpoints);
- dap.RegisterRequestCallback("dataBreakpointInfo", request_dataBreakpointInfo);
- dap.RegisterRequestCallback("setDataBreakpoints", request_setDataBreakpoints);
dap.RegisterRequestCallback("setVariable", request_setVariable);
dap.RegisterRequestCallback("source", request_source);
dap.RegisterRequestCallback("stackTrace", request_stackTrace);
@@ -2355,8 +1489,6 @@ void RegisterRequestCallbacks(DAP &dap) {
dap.RegisterRequestCallback("locations", request_locations);
dap.RegisterRequestCallback("disassemble", request_disassemble);
dap.RegisterRequestCallback("readMemory", request_readMemory);
- dap.RegisterRequestCallback("setInstructionBreakpoints",
- request_setInstructionBreakpoints);
}
} // anonymous namespace
>From 8f39c7725b00f3e263868cae4651ab852ac74dcd Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 24 Feb 2025 14:20:15 -0600
Subject: [PATCH 2/2] Move helpers into Variable
---
lldb/tools/lldb-dap/DAP.cpp | 54 +++++++++++++++++
lldb/tools/lldb-dap/DAP.h | 4 ++
.../DataBreakpointInfoRequestHandler.cpp | 2 +-
.../tools/lldb-dap/Handler/RequestHandler.cpp | 55 -----------------
lldb/tools/lldb-dap/Handler/RequestHandler.h | 3 -
lldb/tools/lldb-dap/lldb-dap.cpp | 59 +------------------
6 files changed, 61 insertions(+), 116 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 9b22b60a68d94..c9487dd89b5dc 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -1201,4 +1201,58 @@ DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
return inst_bp;
}
+lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
+ switch (variablesReference) {
+ case VARREF_LOCALS:
+ return &locals;
+ case VARREF_GLOBALS:
+ return &globals;
+ case VARREF_REGS:
+ return ®isters;
+ default:
+ return nullptr;
+ }
+}
+
+lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
+ llvm::StringRef name) {
+ lldb::SBValue variable;
+ if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+ bool is_duplicated_variable_name = name.contains(" @");
+ // variablesReference is one of our scopes, not an actual variable it is
+ // asking for a variable in locals or globals or registers
+ int64_t end_idx = top_scope->GetSize();
+ // Searching backward so that we choose the variable in closest scope
+ // among variables of the same name.
+ for (int64_t i = end_idx - 1; i >= 0; --i) {
+ lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
+ std::string variable_name = CreateUniqueVariableNameForDisplay(
+ curr_variable, is_duplicated_variable_name);
+ if (variable_name == name) {
+ variable = curr_variable;
+ break;
+ }
+ }
+ } else {
+ // This is not under the globals or locals scope, so there are no duplicated
+ // names.
+
+ // We have a named item within an actual variable so we need to find it
+ // withing the container variable by name.
+ lldb::SBValue container = GetVariable(variablesReference);
+ variable = container.GetChildMemberWithName(name.data());
+ if (!variable.IsValid()) {
+ if (name.starts_with("[")) {
+ llvm::StringRef index_str(name.drop_front(1));
+ uint64_t index = 0;
+ if (!index_str.consumeInteger(0, index)) {
+ if (index_str == "]")
+ variable = container.GetChildAtIndex(index);
+ }
+ }
+ }
+ }
+ return variable;
+}
+
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 18de39838f218..ca26ea1b9a5de 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -116,6 +116,10 @@ struct Variables {
/// \return variableReference assigned to this expandable variable.
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
+ lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
+
+ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
+
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
};
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 519a9c728e4b3..0d007ee52e07f 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -119,7 +119,7 @@ void DataBreakpointInfoRequestHandler::operator()(
GetUnsigned(arguments, "variablesReference", 0);
llvm::StringRef name = GetString(arguments, "name");
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
- lldb::SBValue variable = FindVariable(variablesReference, name);
+ lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name);
std::string addr, size;
if (variable.IsValid()) {
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index de313eb02a24a..f9502e09846d4 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -232,59 +232,4 @@ bool RequestHandler::HasInstructionGranularity(
return false;
}
-lldb::SBValueList *
-RequestHandler::GetTopLevelScope(int64_t variablesReference) {
- switch (variablesReference) {
- case VARREF_LOCALS:
- return &dap.variables.locals;
- case VARREF_GLOBALS:
- return &dap.variables.globals;
- case VARREF_REGS:
- return &dap.variables.registers;
- default:
- return nullptr;
- }
-}
-
-lldb::SBValue RequestHandler::FindVariable(uint64_t variablesReference,
- llvm::StringRef name) {
- lldb::SBValue variable;
- if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
- bool is_duplicated_variable_name = name.contains(" @");
- // variablesReference is one of our scopes, not an actual variable it is
- // asking for a variable in locals or globals or registers
- int64_t end_idx = top_scope->GetSize();
- // Searching backward so that we choose the variable in closest scope
- // among variables of the same name.
- for (int64_t i = end_idx - 1; i >= 0; --i) {
- lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
- std::string variable_name = CreateUniqueVariableNameForDisplay(
- curr_variable, is_duplicated_variable_name);
- if (variable_name == name) {
- variable = curr_variable;
- break;
- }
- }
- } else {
- // This is not under the globals or locals scope, so there are no duplicated
- // names.
-
- // We have a named item within an actual variable so we need to find it
- // withing the container variable by name.
- lldb::SBValue container = dap.variables.GetVariable(variablesReference);
- variable = container.GetChildMemberWithName(name.data());
- if (!variable.IsValid()) {
- if (name.starts_with("[")) {
- llvm::StringRef index_str(name.drop_front(1));
- uint64_t index = 0;
- if (!index_str.consumeInteger(0, index)) {
- if (index_str == "]")
- variable = container.GetChildAtIndex(index);
- }
- }
- }
- }
- return variable;
-}
-
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index a30e0dcc2bd04..9ca03ea25971d 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -52,9 +52,6 @@ class RequestHandler {
// Check if the step-granularity is `instruction`.
bool HasInstructionGranularity(const llvm::json::Object &request);
- lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
- lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
-
/// @}
DAP &dap;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index fd4615897841c..b939c231e4d91 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -130,61 +130,6 @@ typedef void (*RequestCallback)(const llvm::json::Object &command);
/// Page size used for reporting addtional frames in the 'stackTrace' request.
constexpr int StackPageSize = 20;
-lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
- switch (variablesReference) {
- case VARREF_LOCALS:
- return &dap.variables.locals;
- case VARREF_GLOBALS:
- return &dap.variables.globals;
- case VARREF_REGS:
- return &dap.variables.registers;
- default:
- return nullptr;
- }
-}
-
-lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference,
- llvm::StringRef name) {
- lldb::SBValue variable;
- if (lldb::SBValueList *top_scope =
- GetTopLevelScope(dap, variablesReference)) {
- bool is_duplicated_variable_name = name.contains(" @");
- // variablesReference is one of our scopes, not an actual variable it is
- // asking for a variable in locals or globals or registers
- int64_t end_idx = top_scope->GetSize();
- // Searching backward so that we choose the variable in closest scope
- // among variables of the same name.
- for (int64_t i = end_idx - 1; i >= 0; --i) {
- lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
- std::string variable_name = CreateUniqueVariableNameForDisplay(
- curr_variable, is_duplicated_variable_name);
- if (variable_name == name) {
- variable = curr_variable;
- break;
- }
- }
- } else {
- // This is not under the globals or locals scope, so there are no duplicated
- // names.
-
- // We have a named item within an actual variable so we need to find it
- // withing the container variable by name.
- lldb::SBValue container = dap.variables.GetVariable(variablesReference);
- variable = container.GetChildMemberWithName(name.data());
- if (!variable.IsValid()) {
- if (name.starts_with("[")) {
- llvm::StringRef index_str(name.drop_front(1));
- uint64_t index = 0;
- if (!index_str.consumeInteger(0, index)) {
- if (index_str == "]")
- variable = container.GetChildAtIndex(index);
- }
- }
- }
- }
- return variable;
-}
-
// Fill in the stack frames of the thread.
//
// Threads stacks may contain runtime specific extended backtraces, when
@@ -741,7 +686,7 @@ void request_setVariable(DAP &dap, const llvm::json::Object &request) {
if (id_value != UINT64_MAX) {
variable = dap.variables.GetVariable(id_value);
} else {
- variable = FindVariable(dap, variablesReference, name);
+ variable = dap.variables.FindVariable(variablesReference, name);
}
if (variable.IsValid()) {
@@ -867,7 +812,7 @@ void request_variables(DAP &dap, const llvm::json::Object &request) {
hex = GetBoolean(format, "hex", false);
if (lldb::SBValueList *top_scope =
- GetTopLevelScope(dap, variablesReference)) {
+ dap.variables.GetTopLevelScope(variablesReference)) {
// variablesReference is one of our scopes, not an actual variable it is
// asking for the list of args, locals or globals.
int64_t start_idx = 0;
More information about the lldb-commits
mailing list