Skip to content

Commit ea4ce2a

Browse files
committed
[lldb-dap] Refactor breakpoint related request handlers (NFC)
1 parent 63600d1 commit ea4ce2a

10 files changed

+1081
-875
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 6 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,14 @@ 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 Handler/StepOutRequestHandler.cpp
5561
Handler/StepInRequestHandler.cpp
5662
Handler/StepInTargetsRequestHandler.cpp
5763
Handler/TestGetTargetBreakpointsRequestHandler.cpp
58-
Handler/StepOutRequestHandler.cpp
5964

6065
LINK_LIBS
6166
liblldb
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 = 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.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,59 @@ bool RequestHandler::HasInstructionGranularity(
232232
return false;
233233
}
234234

235+
lldb::SBValueList *
236+
RequestHandler::GetTopLevelScope(int64_t variablesReference) {
237+
switch (variablesReference) {
238+
case VARREF_LOCALS:
239+
return &dap.variables.locals;
240+
case VARREF_GLOBALS:
241+
return &dap.variables.globals;
242+
case VARREF_REGS:
243+
return &dap.variables.registers;
244+
default:
245+
return nullptr;
246+
}
247+
}
248+
249+
lldb::SBValue RequestHandler::FindVariable(uint64_t variablesReference,
250+
llvm::StringRef name) {
251+
lldb::SBValue variable;
252+
if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
253+
bool is_duplicated_variable_name = name.contains(" @");
254+
// variablesReference is one of our scopes, not an actual variable it is
255+
// asking for a variable in locals or globals or registers
256+
int64_t end_idx = top_scope->GetSize();
257+
// Searching backward so that we choose the variable in closest scope
258+
// among variables of the same name.
259+
for (int64_t i = end_idx - 1; i >= 0; --i) {
260+
lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
261+
std::string variable_name = CreateUniqueVariableNameForDisplay(
262+
curr_variable, is_duplicated_variable_name);
263+
if (variable_name == name) {
264+
variable = curr_variable;
265+
break;
266+
}
267+
}
268+
} else {
269+
// This is not under the globals or locals scope, so there are no duplicated
270+
// names.
271+
272+
// We have a named item within an actual variable so we need to find it
273+
// withing the container variable by name.
274+
lldb::SBValue container = dap.variables.GetVariable(variablesReference);
275+
variable = container.GetChildMemberWithName(name.data());
276+
if (!variable.IsValid()) {
277+
if (name.starts_with("[")) {
278+
llvm::StringRef index_str(name.drop_front(1));
279+
uint64_t index = 0;
280+
if (!index_str.consumeInteger(0, index)) {
281+
if (index_str == "]")
282+
variable = container.GetChildAtIndex(index);
283+
}
284+
}
285+
}
286+
}
287+
return variable;
288+
}
289+
235290
} // namespace lldb_dap

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class RequestHandler {
5252
// Check if the step-granularity is `instruction`.
5353
bool HasInstructionGranularity(const llvm::json::Object &request);
5454

55+
lldb::SBValueList *GetTopLevelScope(int64_t variablesReference);
56+
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name);
57+
5558
/// @}
5659

5760
DAP &dap;
@@ -162,6 +165,50 @@ class StepOutRequestHandler : public RequestHandler {
162165
void operator()(const llvm::json::Object &request) override;
163166
};
164167

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

0 commit comments

Comments
 (0)