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 <jo...@devlieghere.com> Date: Mon, 24 Feb 2025 11:51:28 -0600 Subject: [PATCH 1/3] [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 <jo...@devlieghere.com> Date: Mon, 24 Feb 2025 14:20:15 -0600 Subject: [PATCH 2/3] 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; >From f8b6011d1d08caa250bb13b99200369d064853e2 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jo...@devlieghere.com> Date: Mon, 24 Feb 2025 14:21:23 -0600 Subject: [PATCH 3/3] Fix missing newline in CMakeLists.txt --- lldb/tools/lldb-dap/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index c04b10861a4c5..49809ec1c2592 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -57,9 +57,10 @@ add_lldb_tool(lldb-dap Handler/SetDataBreakpointsRequestHandler.cpp Handler/SetExceptionBreakpointsRequestHandler.cpp Handler/SetFunctionBreakpointsRequestHandler.cpp - Handler/SetInstructionBreakpointsRequestHandler.cpp Handler/StepOutRequestHandler.cpp + Handler/SetInstructionBreakpointsRequestHandler.cpp Handler/StepInRequestHandler.cpp Handler/StepInTargetsRequestHandler.cpp + Handler/StepOutRequestHandler.cpp Handler/TestGetTargetBreakpointsRequestHandler.cpp LINK_LIBS _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits