Skip to content

Commit 539cf82

Browse files
authored
[lldb-dap] Use structured types for stepInTargets request (#144072)
uses the `SendTargetCapabilities` from #142831
1 parent a3d35b8 commit 539cf82

File tree

10 files changed

+225
-136
lines changed

10 files changed

+225
-136
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ def wait_for_terminated(self, timeout: Optional[float] = None):
494494
raise ValueError("didn't get terminated event")
495495
return event_dict
496496

497-
def get_capability(self, key):
497+
def get_capability(self, key: str):
498498
"""Get a value for the given key if it there is a key/value pair in
499499
the capabilities reported by the adapter.
500500
"""

lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,47 @@ def test_basic(self):
7878
leaf_frame = self.dap_server.get_stackFrame()
7979
self.assertIsNotNone(leaf_frame, "expect a leaf frame")
8080
self.assertEqual(step_in_targets[1]["label"], leaf_frame["name"])
81+
82+
@skipIf(archs=no_match(["x86", "x86_64"]))
83+
def test_supported_capability_x86_arch(self):
84+
program = self.getBuildArtifact("a.out")
85+
self.build_and_launch(program)
86+
source = "main.cpp"
87+
bp_lines = [line_number(source, "// set breakpoint here")]
88+
breakpoint_ids = self.set_source_breakpoints(source, bp_lines)
89+
self.assertEqual(
90+
len(breakpoint_ids), len(bp_lines), "expect correct number of breakpoints"
91+
)
92+
self.continue_to_breakpoints(breakpoint_ids)
93+
is_supported = self.dap_server.get_capability("supportsStepInTargetsRequest")
94+
95+
self.assertEqual(
96+
is_supported,
97+
True,
98+
f"expect capability `stepInTarget` is supported with architecture {self.getArchitecture()}",
99+
)
100+
# clear breakpoints.
101+
self.set_source_breakpoints(source, [])
102+
self.continue_to_exit()
103+
104+
@skipIf(archs=["x86", "x86_64"])
105+
def test_supported_capability_other_archs(self):
106+
program = self.getBuildArtifact("a.out")
107+
self.build_and_launch(program)
108+
source = "main.cpp"
109+
bp_lines = [line_number(source, "// set breakpoint here")]
110+
breakpoint_ids = self.set_source_breakpoints(source, bp_lines)
111+
self.assertEqual(
112+
len(breakpoint_ids), len(bp_lines), "expect correct number of breakpoints"
113+
)
114+
self.continue_to_breakpoints(breakpoint_ids)
115+
is_supported = self.dap_server.get_capability("supportsStepInTargetsRequest")
116+
117+
self.assertEqual(
118+
is_supported,
119+
False,
120+
f"expect capability `stepInTarget` is not supported with architecture {self.getArchitecture()}",
121+
)
122+
# clear breakpoints.
123+
self.set_source_breakpoints(source, [])
124+
self.continue_to_exit()

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ void SendTargetBasedCapabilities(DAP &dap) {
4444

4545
protocol::CapabilitiesEventBody body;
4646

47+
const llvm::StringRef target_triple = dap.target.GetTriple();
48+
if (target_triple.starts_with("x86"))
49+
body.capabilities.supportedFeatures.insert(
50+
protocol::eAdapterFeatureStepInTargetsRequest);
51+
4752
// We only support restarting launch requests not attach requests.
4853
if (dap.last_launch_request)
4954
body.capabilities.supportedFeatures.insert(

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,14 +353,15 @@ class StepInRequestHandler : public RequestHandler<protocol::StepInArguments,
353353
llvm::Error Run(const protocol::StepInArguments &args) const override;
354354
};
355355

356-
class StepInTargetsRequestHandler : public LegacyRequestHandler {
356+
class StepInTargetsRequestHandler
357+
: public RequestHandler<
358+
protocol::StepInTargetsArguments,
359+
llvm::Expected<protocol::StepInTargetsResponseBody>> {
357360
public:
358-
using LegacyRequestHandler::LegacyRequestHandler;
361+
using RequestHandler::RequestHandler;
359362
static llvm::StringLiteral GetCommand() { return "stepInTargets"; }
360-
FeatureSet GetSupportedFeatures() const override {
361-
return {protocol::eAdapterFeatureStepInTargetsRequest};
362-
}
363-
void operator()(const llvm::json::Object &request) const override;
363+
llvm::Expected<protocol::StepInTargetsResponseBody>
364+
Run(const protocol::StepInTargetsArguments &args) const override;
364365
};
365366

366367
class StepOutRequestHandler : public RequestHandler<protocol::StepOutArguments,

lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp

Lines changed: 71 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -7,143 +7,85 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "DAP.h"
10-
#include "EventHelper.h"
11-
#include "JSONUtils.h"
10+
#include "Protocol/ProtocolRequests.h"
1211
#include "RequestHandler.h"
1312
#include "lldb/API/SBInstruction.h"
13+
#include "lldb/lldb-defines.h"
1414

15+
using namespace lldb_dap::protocol;
1516
namespace lldb_dap {
1617

17-
// "StepInTargetsRequest": {
18-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
19-
// "type": "object",
20-
// "description": "This request retrieves the possible step-in targets for
21-
// the specified stack frame.\nThese targets can be used in the `stepIn`
22-
// request.\nClients should only call this request if the corresponding
23-
// capability `supportsStepInTargetsRequest` is true.", "properties": {
24-
// "command": {
25-
// "type": "string",
26-
// "enum": [ "stepInTargets" ]
27-
// },
28-
// "arguments": {
29-
// "$ref": "#/definitions/StepInTargetsArguments"
30-
// }
31-
// },
32-
// "required": [ "command", "arguments" ]
33-
// }]
34-
// },
35-
// "StepInTargetsArguments": {
36-
// "type": "object",
37-
// "description": "Arguments for `stepInTargets` request.",
38-
// "properties": {
39-
// "frameId": {
40-
// "type": "integer",
41-
// "description": "The stack frame for which to retrieve the possible
42-
// step-in targets."
43-
// }
44-
// },
45-
// "required": [ "frameId" ]
46-
// },
47-
// "StepInTargetsResponse": {
48-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
49-
// "type": "object",
50-
// "description": "Response to `stepInTargets` request.",
51-
// "properties": {
52-
// "body": {
53-
// "type": "object",
54-
// "properties": {
55-
// "targets": {
56-
// "type": "array",
57-
// "items": {
58-
// "$ref": "#/definitions/StepInTarget"
59-
// },
60-
// "description": "The possible step-in targets of the specified
61-
// source location."
62-
// }
63-
// },
64-
// "required": [ "targets" ]
65-
// }
66-
// },
67-
// "required": [ "body" ]
68-
// }]
69-
// }
70-
void StepInTargetsRequestHandler::operator()(
71-
const llvm::json::Object &request) const {
72-
llvm::json::Object response;
73-
FillResponse(request, response);
74-
const auto *arguments = request.getObject("arguments");
75-
18+
// This request retrieves the possible step-in targets for the specified stack
19+
// frame.
20+
// These targets can be used in the `stepIn` request.
21+
// Clients should only call this request if the corresponding capability
22+
// `supportsStepInTargetsRequest` is true.
23+
llvm::Expected<StepInTargetsResponseBody>
24+
StepInTargetsRequestHandler::Run(const StepInTargetsArguments &args) const {
7625
dap.step_in_targets.clear();
77-
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
78-
if (frame.IsValid()) {
79-
lldb::SBAddress pc_addr = frame.GetPCAddress();
80-
lldb::SBAddress line_end_addr =
81-
pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true);
82-
lldb::SBInstructionList insts = dap.target.ReadInstructions(
83-
pc_addr, line_end_addr, /*flavor_string=*/nullptr);
84-
85-
if (!insts.IsValid()) {
86-
response["success"] = false;
87-
response["message"] = "Failed to get instructions for frame.";
88-
dap.SendJSON(llvm::json::Value(std::move(response)));
89-
return;
90-
}
26+
const lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
27+
if (!frame.IsValid())
28+
return llvm::make_error<DAPError>("Failed to get frame for input frameId.");
29+
30+
lldb::SBAddress pc_addr = frame.GetPCAddress();
31+
lldb::SBAddress line_end_addr =
32+
pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true);
33+
lldb::SBInstructionList insts = dap.target.ReadInstructions(
34+
pc_addr, line_end_addr, /*flavor_string=*/nullptr);
35+
36+
if (!insts.IsValid())
37+
return llvm::make_error<DAPError>("Failed to get instructions for frame.");
38+
39+
StepInTargetsResponseBody body;
40+
const size_t num_insts = insts.GetSize();
41+
for (size_t i = 0; i < num_insts; ++i) {
42+
lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
43+
if (!inst.IsValid())
44+
break;
45+
46+
const lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target);
47+
if (inst_addr == LLDB_INVALID_ADDRESS)
48+
break;
49+
50+
// Note: currently only x86/x64 supports flow kind.
51+
const lldb::InstructionControlFlowKind flow_kind =
52+
inst.GetControlFlowKind(dap.target);
53+
54+
if (flow_kind == lldb::eInstructionControlFlowKindCall) {
55+
56+
const llvm::StringRef call_operand_name = inst.GetOperands(dap.target);
57+
lldb::addr_t call_target_addr = LLDB_INVALID_ADDRESS;
58+
if (call_operand_name.getAsInteger(0, call_target_addr))
59+
continue;
60+
61+
const lldb::SBAddress call_target_load_addr =
62+
dap.target.ResolveLoadAddress(call_target_addr);
63+
if (!call_target_load_addr.IsValid())
64+
continue;
65+
66+
// The existing ThreadPlanStepInRange only accept step in target
67+
// function with debug info.
68+
lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress(
69+
call_target_load_addr, lldb::eSymbolContextFunction);
70+
71+
// The existing ThreadPlanStepInRange only accept step in target
72+
// function with debug info.
73+
llvm::StringRef step_in_target_name;
74+
if (sc.IsValid() && sc.GetFunction().IsValid())
75+
step_in_target_name = sc.GetFunction().GetDisplayName();
76+
77+
// Skip call sites if we fail to resolve its symbol name.
78+
if (step_in_target_name.empty())
79+
continue;
9180

92-
llvm::json::Array step_in_targets;
93-
const auto num_insts = insts.GetSize();
94-
for (size_t i = 0; i < num_insts; ++i) {
95-
lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
96-
if (!inst.IsValid())
97-
break;
98-
99-
lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target);
100-
101-
// Note: currently only x86/x64 supports flow kind.
102-
lldb::InstructionControlFlowKind flow_kind =
103-
inst.GetControlFlowKind(dap.target);
104-
if (flow_kind == lldb::eInstructionControlFlowKindCall) {
105-
// Use call site instruction address as id which is easy to debug.
106-
llvm::json::Object step_in_target;
107-
step_in_target["id"] = inst_addr;
108-
109-
llvm::StringRef call_operand_name = inst.GetOperands(dap.target);
110-
lldb::addr_t call_target_addr;
111-
if (call_operand_name.getAsInteger(0, call_target_addr))
112-
continue;
113-
114-
lldb::SBAddress call_target_load_addr =
115-
dap.target.ResolveLoadAddress(call_target_addr);
116-
if (!call_target_load_addr.IsValid())
117-
continue;
118-
119-
// The existing ThreadPlanStepInRange only accept step in target
120-
// function with debug info.
121-
lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress(
122-
call_target_load_addr, lldb::eSymbolContextFunction);
123-
124-
// The existing ThreadPlanStepInRange only accept step in target
125-
// function with debug info.
126-
std::string step_in_target_name;
127-
if (sc.IsValid() && sc.GetFunction().IsValid())
128-
step_in_target_name = sc.GetFunction().GetDisplayName();
129-
130-
// Skip call sites if we fail to resolve its symbol name.
131-
if (step_in_target_name.empty())
132-
continue;
133-
134-
dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
135-
step_in_target.try_emplace("label", step_in_target_name);
136-
step_in_targets.emplace_back(std::move(step_in_target));
137-
}
81+
StepInTarget target;
82+
target.id = inst_addr;
83+
target.label = step_in_target_name;
84+
dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
85+
body.targets.emplace_back(std::move(target));
13886
}
139-
llvm::json::Object body;
140-
body.try_emplace("targets", std::move(step_in_targets));
141-
response.try_emplace("body", std::move(body));
142-
} else {
143-
response["success"] = llvm::json::Value(false);
144-
response["message"] = "Failed to get frame for input frameId.";
14587
}
146-
dap.SendJSON(llvm::json::Value(std::move(response)));
147-
}
88+
return body;
89+
};
14890

14991
} // namespace lldb_dap

lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,16 @@ bool fromJSON(const json::Value &Params, StepInArguments &SIA, json::Path P) {
368368
OM.mapOptional("granularity", SIA.granularity);
369369
}
370370

371+
bool fromJSON(const llvm::json::Value &Params, StepInTargetsArguments &SITA,
372+
llvm::json::Path P) {
373+
json::ObjectMapper OM(Params, P);
374+
return OM && OM.map("frameId", SITA.frameId);
375+
}
376+
377+
llvm::json::Value toJSON(const StepInTargetsResponseBody &SITR) {
378+
return llvm::json::Object{{"targets", SITR.targets}};
379+
}
380+
371381
bool fromJSON(const json::Value &Params, StepOutArguments &SOA, json::Path P) {
372382
json::ObjectMapper OM(Params, P);
373383
return OM && OM.map("threadId", SOA.threadId) &&

lldb/tools/lldb-dap/Protocol/ProtocolRequests.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,21 @@ bool fromJSON(const llvm::json::Value &, StepInArguments &, llvm::json::Path);
533533
/// body field is required.
534534
using StepInResponse = VoidResponse;
535535

536+
/// Arguments for `stepInTargets` request.
537+
struct StepInTargetsArguments {
538+
/// The stack frame for which to retrieve the possible step-in targets.
539+
uint64_t frameId = LLDB_INVALID_FRAME_ID;
540+
};
541+
bool fromJSON(const llvm::json::Value &, StepInTargetsArguments &,
542+
llvm::json::Path);
543+
544+
/// Response to `stepInTargets` request.
545+
struct StepInTargetsResponseBody {
546+
/// The possible step-in targets of the specified source location.
547+
std::vector<StepInTarget> targets;
548+
};
549+
llvm::json::Value toJSON(const StepInTargetsResponseBody &);
550+
536551
/// Arguments for `stepOut` request.
537552
struct StepOutArguments {
538553
/// Specifies the thread for which to resume execution for one step-out (of

lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,30 @@ llvm::json::Value toJSON(const SteppingGranularity &SG) {
582582
llvm_unreachable("unhandled stepping granularity.");
583583
}
584584

585+
bool fromJSON(const json::Value &Params, StepInTarget &SIT, json::Path P) {
586+
json::ObjectMapper O(Params, P);
587+
return O && O.map("id", SIT.id) && O.map("label", SIT.label) &&
588+
O.mapOptional("line", SIT.line) &&
589+
O.mapOptional("column", SIT.column) &&
590+
O.mapOptional("endLine", SIT.endLine) &&
591+
O.mapOptional("endColumn", SIT.endColumn);
592+
}
593+
594+
llvm::json::Value toJSON(const StepInTarget &SIT) {
595+
json::Object target{{"id", SIT.id}, {"label", SIT.label}};
596+
597+
if (SIT.line != LLDB_INVALID_LINE_NUMBER)
598+
target.insert({"line", SIT.line});
599+
if (SIT.column != LLDB_INVALID_COLUMN_NUMBER)
600+
target.insert({"column", SIT.column});
601+
if (SIT.endLine != LLDB_INVALID_LINE_NUMBER)
602+
target.insert({"endLine", SIT.endLine});
603+
if (SIT.endLine != LLDB_INVALID_COLUMN_NUMBER)
604+
target.insert({"endColumn", SIT.endColumn});
605+
606+
return target;
607+
}
608+
585609
bool fromJSON(const json::Value &Params, Thread &T, json::Path P) {
586610
json::ObjectMapper O(Params, P);
587611
return O && O.map("id", T.id) && O.map("name", T.name);

0 commit comments

Comments
 (0)