-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb-dap] Refactor breakpoint related request handlers (NFC) #128550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb-dap] Refactor breakpoint related request handlers (NFC) #128550
Conversation
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesContinuation of the work started in #128262. Builds on top of #128549. Patch is 126.29 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128550.diff 17 Files Affected:
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 73762af5c2fd7..c04b10861a4c5 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -39,16 +39,28 @@ add_lldb_tool(lldb-dap
Handler/AttachRequestHandler.cpp
Handler/BreakpointLocationsHandler.cpp
+ Handler/CompileUnitsRequestHandler.cpp
Handler/CompletionsHandler.cpp
Handler/ConfigurationDoneRequestHandler.cpp
Handler/ContinueRequestHandler.cpp
+ Handler/DataBreakpointInfoRequestHandler.cpp
Handler/DisconnectRequestHandler.cpp
Handler/EvaluateRequestHandler.cpp
Handler/ExceptionInfoRequestHandler.cpp
Handler/InitializeRequestHandler.cpp
Handler/LaunchRequestHandler.cpp
+ Handler/ModulesRequestHandler.cpp
+ 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
LINK_LIBS
liblldb
diff --git a/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp
new file mode 100644
index 0000000000000..c541d1cd039c8
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp
@@ -0,0 +1,80 @@
+//===-- CompileUnitsRequestHandler.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 {
+
+// "compileUnitsRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Compile Unit request; value of command field is
+// 'compileUnits'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "compileUnits" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/compileUnitRequestArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "compileUnitsRequestArguments": {
+// "type": "object",
+// "description": "Arguments for 'compileUnits' request.",
+// "properties": {
+// "moduleId": {
+// "type": "string",
+// "description": "The ID of the module."
+// }
+// },
+// "required": [ "moduleId" ]
+// },
+// "compileUnitsResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'compileUnits' request.",
+// "properties": {
+// "body": {
+// "description": "Response to 'compileUnits' request. Array of
+// paths of compile units."
+// }
+// }
+// }]
+// }
+void CompileUnitsRequestHandler::operator()(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ llvm::json::Object body;
+ llvm::json::Array units;
+ const auto *arguments = request.getObject("arguments");
+ std::string module_id = std::string(GetString(arguments, "moduleId"));
+ int num_modules = dap.target.GetNumModules();
+ for (int i = 0; i < num_modules; i++) {
+ auto curr_module = dap.target.GetModuleAtIndex(i);
+ if (module_id == curr_module.GetUUIDString()) {
+ int num_units = curr_module.GetNumCompileUnits();
+ for (int j = 0; j < num_units; j++) {
+ auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
+ units.emplace_back(CreateCompileUnit(curr_unit));
+ }
+ body.try_emplace("compileUnits", std::move(units));
+ break;
+ }
+ }
+ 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/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/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
new file mode 100644
index 0000000000000..f72faa7be8963
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
@@ -0,0 +1,58 @@
+//===-- ModulesRequestHandler.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 {
+
+// "modulesRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Modules request; value of command field is
+// 'modules'.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "modules" ]
+// },
+// },
+// "required": [ "command" ]
+// }]
+// },
+// "modulesResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'modules' request.",
+// "properties": {
+// "body": {
+// "description": "Response to 'modules' request. Array of
+// module objects."
+// }
+// }
+// }]
+// }
+void ModulesRequestHandler::operator()(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+
+ llvm::json::Array modules;
+ for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
+ lldb::SBModule module = dap.target.GetModuleAtIndex(i);
+ modules.emplace_back(CreateModule(dap.target, module));
+ }
+
+ llvm::json::Object body;
+ body.try_emplace("modules", std::move(modules));
+ 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/NextRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
new file mode 100644
index 0000000000000..695703fe301b3
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
@@ -0,0 +1,79 @@
+//===-- NextRequestHandler.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 {
+
+// "NextRequest": {
+// "allOf": [ { "$ref": "#/definitions/Request" }, {
+// "type": "object",
+// "description": "Next request; value of command field is 'next'. The
+// request starts the debuggee to run again for one step.
+// The debug adapter first sends the NextResponse and then
+// a StoppedEvent (event type 'step') after the step has
+// completed.",
+// "properties": {
+// "command": {
+// "type": "string",
+// "enum": [ "next" ]
+// },
+// "arguments": {
+// "$ref": "#/definitions/NextArguments"
+// }
+// },
+// "required": [ "command", "arguments" ]
+// }]
+// },
+// "NextArguments": {
+// "type": "object",
+// "description": "Arguments for 'next' request.",
+// "properties": {
+// "threadId": {
+// "type": "integer",
+// "description": "Execute 'next' for this thread."
+// },
+// "granularity": {
+// "$ref": "#/definitions/SteppingGranularity",
+// "description": "Stepping granularity. If no granularity is specified, a
+// granularity of `statement` is assumed."
+// }
+// },
+// "required": [ "threadId" ]
+// },
+// "NextResponse": {
+// "allOf": [ { "$ref": "#/definitions/Response" }, {
+// "type": "object",
+// "description": "Response to 'next' request. This is just an
+// acknowledgement, so no body field is required."
+// }]
+// }
+void NextRequestHandler::operator()(const llvm::json::Object &request) {
+ llvm::json::Object response;
+ FillResponse(request, response);
+ const auto *arguments = request.getObject("arguments");
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
+ if (thread.IsValid()) {
+ // Remember the thread ID that caused the resume so we can set the
+ // "threadCausedFocus" boolean value in the "stopped" events.
+ dap.focus_tid = thread.GetThreadID();
+ if (HasInstructionGranularity(*arguments)) {
+ thread.StepInstruction(/*step_over=*/true);
+ } else {
+ thread.StepOver();
+ }
+ } else {
+ response["success"] = llvm::json::Value(false);
+ }
+ 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 c09ddf55dd5e9..5d7d00bec9bd1 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -225,4 +225,66 @@ void RequestHandler::PrintWelcomeMessage() {
#endif
}
+bool RequestHandler::HasInstructionGranularity(
+ const llvm::json::Object &request) {
+ if (std::optional<llvm::StringRef> value = request.getString("granularity"))
+ return value == "instruction";
+ 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 9bc8e60dbb858..a30e0dcc2bd04 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one question about some helpers.
lldb::SBValueList *GetTopLevelScope(int64_t variablesReference); | ||
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these be moved to the lldb_dap::DAP
or lldb_dap::Variables
?
ea4ce2a
to
8f39c77
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/195/builds/5306 Here is the relevant piece of the build log for the reference
|
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
…28550) Continuation of the work started in llvm#128262. Builds on top of llvm#128549.
… handlers (llvm#128551) Continuation of the work started in llvm#128262. Builds on top of llvm#128550.
Continuation of the work started in #128262. Builds on top of #128549.