Skip to content

Commit d362319

Browse files
authored
[lldb-dap] Refactor breakpoint related request handlers (NFC) (#128550)
Continuation of the work started in #128262. Builds on top of #128549.
1 parent 1b15a89 commit d362319

11 files changed

+1084
-932
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ add_lldb_tool(lldb-dap
4343
Handler/CompletionsHandler.cpp
4444
Handler/ConfigurationDoneRequestHandler.cpp
4545
Handler/ContinueRequestHandler.cpp
46+
Handler/DataBreakpointInfoRequestHandler.cpp
4647
Handler/DisconnectRequestHandler.cpp
4748
Handler/EvaluateRequestHandler.cpp
4849
Handler/ExceptionInfoRequestHandler.cpp
@@ -52,10 +53,15 @@ add_lldb_tool(lldb-dap
5253
Handler/NextRequestHandler.cpp
5354
Handler/RequestHandler.cpp
5455
Handler/RestartRequestHandler.cpp
56+
Handler/SetBreakpointsRequestHandler.cpp
57+
Handler/SetDataBreakpointsRequestHandler.cpp
58+
Handler/SetExceptionBreakpointsRequestHandler.cpp
59+
Handler/SetFunctionBreakpointsRequestHandler.cpp
60+
Handler/SetInstructionBreakpointsRequestHandler.cpp
5561
Handler/StepInRequestHandler.cpp
5662
Handler/StepInTargetsRequestHandler.cpp
57-
Handler/TestGetTargetBreakpointsRequestHandler.cpp
5863
Handler/StepOutRequestHandler.cpp
64+
Handler/TestGetTargetBreakpointsRequestHandler.cpp
5965

6066
LINK_LIBS
6167
liblldb

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,58 @@ DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
12011201
return inst_bp;
12021202
}
12031203

1204+
lldb::SBValueList *Variables::GetTopLevelScope(int64_t variablesReference) {
1205+
switch (variablesReference) {
1206+
case VARREF_LOCALS:
1207+
return &locals;
1208+
case VARREF_GLOBALS:
1209+
return &globals;
1210+
case VARREF_REGS:
1211+
return &registers;
1212+
default:
1213+
return nullptr;
1214+
}
1215+
}
1216+
1217+
lldb::SBValue Variables::FindVariable(uint64_t variablesReference,
1218+
llvm::StringRef name) {
1219+
lldb::SBValue variable;
1220+
if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
1221+
bool is_duplicated_variable_name = name.contains(" @");
1222+
// variablesReference is one of our scopes, not an actual variable it is
1223+
// asking for a variable in locals or globals or registers
1224+
int64_t end_idx = top_scope->GetSize();
1225+
// Searching backward so that we choose the variable in closest scope
1226+
// among variables of the same name.
1227+
for (int64_t i = end_idx - 1; i >= 0; --i) {
1228+
lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
1229+
std::string variable_name = CreateUniqueVariableNameForDisplay(
1230+
curr_variable, is_duplicated_variable_name);
1231+
if (variable_name == name) {
1232+
variable = curr_variable;
1233+
break;
1234+
}
1235+
}
1236+
} else {
1237+
// This is not under the globals or locals scope, so there are no duplicated
1238+
// names.
1239+
1240+
// We have a named item within an actual variable so we need to find it
1241+
// withing the container variable by name.
1242+
lldb::SBValue container = GetVariable(variablesReference);
1243+
variable = container.GetChildMemberWithName(name.data());
1244+
if (!variable.IsValid()) {
1245+
if (name.starts_with("[")) {
1246+
llvm::StringRef index_str(name.drop_front(1));
1247+
uint64_t index = 0;
1248+
if (!index_str.consumeInteger(0, index)) {
1249+
if (index_str == "]")
1250+
variable = container.GetChildAtIndex(index);
1251+
}
1252+
}
1253+
}
1254+
}
1255+
return variable;
1256+
}
1257+
12041258
} // namespace lldb_dap

lldb/tools/lldb-dap/DAP.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ struct Variables {
116116
/// \return variableReference assigned to this expandable variable.
117117
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
118118

119+
lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
120+
121+
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
122+
119123
/// Clear all scope variables and non-permanent expandable variables.
120124
void Clear();
121125
};
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//===-- DataBreakpointInfoRequestHandler.cpp ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAP.h"
10+
#include "EventHelper.h"
11+
#include "JSONUtils.h"
12+
#include "RequestHandler.h"
13+
#include "lldb/API/SBMemoryRegionInfo.h"
14+
#include "llvm/ADT/StringExtras.h"
15+
16+
namespace lldb_dap {
17+
18+
// "DataBreakpointInfoRequest": {
19+
// "allOf": [ { "$ref": "#/definitions/Request" }, {
20+
// "type": "object",
21+
// "description": "Obtains information on a possible data breakpoint that
22+
// could be set on an expression or variable.\nClients should only call this
23+
// request if the corresponding capability `supportsDataBreakpoints` is
24+
// true.", "properties": {
25+
// "command": {
26+
// "type": "string",
27+
// "enum": [ "dataBreakpointInfo" ]
28+
// },
29+
// "arguments": {
30+
// "$ref": "#/definitions/DataBreakpointInfoArguments"
31+
// }
32+
// },
33+
// "required": [ "command", "arguments" ]
34+
// }]
35+
// },
36+
// "DataBreakpointInfoArguments": {
37+
// "type": "object",
38+
// "description": "Arguments for `dataBreakpointInfo` request.",
39+
// "properties": {
40+
// "variablesReference": {
41+
// "type": "integer",
42+
// "description": "Reference to the variable container if the data
43+
// breakpoint is requested for a child of the container. The
44+
// `variablesReference` must have been obtained in the current suspended
45+
// state. See 'Lifetime of Object References' in the Overview section for
46+
// details."
47+
// },
48+
// "name": {
49+
// "type": "string",
50+
// "description": "The name of the variable's child to obtain data
51+
// breakpoint information for.\nIf `variablesReference` isn't specified,
52+
// this can be an expression."
53+
// },
54+
// "frameId": {
55+
// "type": "integer",
56+
// "description": "When `name` is an expression, evaluate it in the scope
57+
// of this stack frame. If not specified, the expression is evaluated in
58+
// the global scope. When `variablesReference` is specified, this property
59+
// has no effect."
60+
// }
61+
// },
62+
// "required": [ "name" ]
63+
// },
64+
// "DataBreakpointInfoResponse": {
65+
// "allOf": [ { "$ref": "#/definitions/Response" }, {
66+
// "type": "object",
67+
// "description": "Response to `dataBreakpointInfo` request.",
68+
// "properties": {
69+
// "body": {
70+
// "type": "object",
71+
// "properties": {
72+
// "dataId": {
73+
// "type": [ "string", "null" ],
74+
// "description": "An identifier for the data on which a data
75+
// breakpoint can be registered with the `setDataBreakpoints`
76+
// request or null if no data breakpoint is available. If a
77+
// `variablesReference` or `frameId` is passed, the `dataId` is
78+
// valid in the current suspended state, otherwise it's valid
79+
// indefinitely. See 'Lifetime of Object References' in the Overview
80+
// section for details. Breakpoints set using the `dataId` in the
81+
// `setDataBreakpoints` request may outlive the lifetime of the
82+
// associated `dataId`."
83+
// },
84+
// "description": {
85+
// "type": "string",
86+
// "description": "UI string that describes on what data the
87+
// breakpoint is set on or why a data breakpoint is not available."
88+
// },
89+
// "accessTypes": {
90+
// "type": "array",
91+
// "items": {
92+
// "$ref": "#/definitions/DataBreakpointAccessType"
93+
// },
94+
// "description": "Attribute lists the available access types for a
95+
// potential data breakpoint. A UI client could surface this
96+
// information."
97+
// },
98+
// "canPersist": {
99+
// "type": "boolean",
100+
// "description": "Attribute indicates that a potential data
101+
// breakpoint could be persisted across sessions."
102+
// }
103+
// },
104+
// "required": [ "dataId", "description" ]
105+
// }
106+
// },
107+
// "required": [ "body" ]
108+
// }]
109+
// }
110+
void DataBreakpointInfoRequestHandler::operator()(
111+
const llvm::json::Object &request) {
112+
llvm::json::Object response;
113+
FillResponse(request, response);
114+
llvm::json::Object body;
115+
lldb::SBError error;
116+
llvm::json::Array accessTypes{"read", "write", "readWrite"};
117+
const auto *arguments = request.getObject("arguments");
118+
const auto variablesReference =
119+
GetUnsigned(arguments, "variablesReference", 0);
120+
llvm::StringRef name = GetString(arguments, "name");
121+
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
122+
lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name);
123+
std::string addr, size;
124+
125+
if (variable.IsValid()) {
126+
lldb::addr_t load_addr = variable.GetLoadAddress();
127+
size_t byte_size = variable.GetByteSize();
128+
if (load_addr == LLDB_INVALID_ADDRESS) {
129+
body.try_emplace("dataId", nullptr);
130+
body.try_emplace("description",
131+
"does not exist in memory, its location is " +
132+
std::string(variable.GetLocation()));
133+
} else if (byte_size == 0) {
134+
body.try_emplace("dataId", nullptr);
135+
body.try_emplace("description", "variable size is 0");
136+
} else {
137+
addr = llvm::utohexstr(load_addr);
138+
size = llvm::utostr(byte_size);
139+
}
140+
} else if (variablesReference == 0 && frame.IsValid()) {
141+
lldb::SBValue value = frame.EvaluateExpression(name.data());
142+
if (value.GetError().Fail()) {
143+
lldb::SBError error = value.GetError();
144+
const char *error_cstr = error.GetCString();
145+
body.try_emplace("dataId", nullptr);
146+
body.try_emplace("description", error_cstr && error_cstr[0]
147+
? std::string(error_cstr)
148+
: "evaluation failed");
149+
} else {
150+
uint64_t load_addr = value.GetValueAsUnsigned();
151+
lldb::SBData data = value.GetPointeeData();
152+
if (data.IsValid()) {
153+
size = llvm::utostr(data.GetByteSize());
154+
addr = llvm::utohexstr(load_addr);
155+
lldb::SBMemoryRegionInfo region;
156+
lldb::SBError err =
157+
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
158+
// Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
159+
// request if SBProcess::GetMemoryRegionInfo returns error.
160+
if (err.Success()) {
161+
if (!(region.IsReadable() || region.IsWritable())) {
162+
body.try_emplace("dataId", nullptr);
163+
body.try_emplace("description",
164+
"memory region for address " + addr +
165+
" has no read or write permissions");
166+
}
167+
}
168+
} else {
169+
body.try_emplace("dataId", nullptr);
170+
body.try_emplace("description",
171+
"unable to get byte size for expression: " +
172+
name.str());
173+
}
174+
}
175+
} else {
176+
body.try_emplace("dataId", nullptr);
177+
body.try_emplace("description", "variable not found: " + name.str());
178+
}
179+
180+
if (!body.getObject("dataId")) {
181+
body.try_emplace("dataId", addr + "/" + size);
182+
body.try_emplace("accessTypes", std::move(accessTypes));
183+
body.try_emplace("description",
184+
size + " bytes at " + addr + " " + name.str());
185+
}
186+
response.try_emplace("body", std::move(body));
187+
dap.SendJSON(llvm::json::Value(std::move(response)));
188+
}
189+
190+
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,50 @@ class StepOutRequestHandler : public RequestHandler {
162162
void operator()(const llvm::json::Object &request) override;
163163
};
164164

165+
class SetBreakpointsRequestHandler : public RequestHandler {
166+
public:
167+
using RequestHandler::RequestHandler;
168+
static llvm::StringLiteral getCommand() { return "setBreakpoints"; }
169+
void operator()(const llvm::json::Object &request) override;
170+
};
171+
172+
class SetExceptionBreakpointsRequestHandler : public RequestHandler {
173+
public:
174+
using RequestHandler::RequestHandler;
175+
static llvm::StringLiteral getCommand() { return "setExceptionBreakpoints"; }
176+
void operator()(const llvm::json::Object &request) override;
177+
};
178+
179+
class SetFunctionBreakpointsRequestHandler : public RequestHandler {
180+
public:
181+
using RequestHandler::RequestHandler;
182+
static llvm::StringLiteral getCommand() { return "setFunctionBreakpoints"; }
183+
void operator()(const llvm::json::Object &request) override;
184+
};
185+
186+
class DataBreakpointInfoRequestHandler : public RequestHandler {
187+
public:
188+
using RequestHandler::RequestHandler;
189+
static llvm::StringLiteral getCommand() { return "dataBreakpointInfo"; }
190+
void operator()(const llvm::json::Object &request) override;
191+
};
192+
193+
class SetDataBreakpointsRequestHandler : public RequestHandler {
194+
public:
195+
using RequestHandler::RequestHandler;
196+
static llvm::StringLiteral getCommand() { return "setDataBreakpoints"; }
197+
void operator()(const llvm::json::Object &request) override;
198+
};
199+
200+
class SetInstructionBreakpointsRequestHandler : public RequestHandler {
201+
public:
202+
using RequestHandler::RequestHandler;
203+
static llvm::StringLiteral getCommand() {
204+
return "setInstructionBreakpoints";
205+
}
206+
void operator()(const llvm::json::Object &request) override;
207+
};
208+
165209
class CompileUnitsRequestHandler : public RequestHandler {
166210
public:
167211
using RequestHandler::RequestHandler;

0 commit comments

Comments
 (0)