1 | //===-- StepInTargetsRequestHandler.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/SBInstruction.h" |
14 | |
15 | namespace lldb_dap { |
16 | |
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(K: "arguments" ); |
75 | |
76 | dap.step_in_targets.clear(); |
77 | lldb::SBFrame frame = dap.GetLLDBFrame(arguments: *arguments); |
78 | if (frame.IsValid()) { |
79 | lldb::SBAddress pc_addr = frame.GetPCAddress(); |
80 | lldb::SBAddress line_end_addr = |
81 | pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(include_inlined_functions: true); |
82 | lldb::SBInstructionList insts = dap.target.ReadInstructions( |
83 | start_addr: pc_addr, end_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(json: llvm::json::Value(std::move(response))); |
89 | return; |
90 | } |
91 | |
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(idx: i); |
96 | if (!inst.IsValid()) |
97 | break; |
98 | |
99 | lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(target: dap.target); |
100 | |
101 | // Note: currently only x86/x64 supports flow kind. |
102 | lldb::InstructionControlFlowKind flow_kind = |
103 | inst.GetControlFlowKind(target: 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(target: dap.target); |
110 | lldb::addr_t call_target_addr; |
111 | if (call_operand_name.getAsInteger(Radix: 0, Result&: call_target_addr)) |
112 | continue; |
113 | |
114 | lldb::SBAddress call_target_load_addr = |
115 | dap.target.ResolveLoadAddress(vm_addr: 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 | addr: call_target_load_addr, resolve_scope: 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(Key: inst_addr, Args&: step_in_target_name); |
135 | step_in_target.try_emplace(K: "label" , Args&: step_in_target_name); |
136 | step_in_targets.emplace_back(A: std::move(step_in_target)); |
137 | } |
138 | } |
139 | llvm::json::Object body; |
140 | body.try_emplace(K: "targets" , Args: std::move(step_in_targets)); |
141 | response.try_emplace(K: "body" , Args: std::move(body)); |
142 | } else { |
143 | response["success" ] = llvm::json::Value(false); |
144 | response["message" ] = "Failed to get frame for input frameId." ; |
145 | } |
146 | dap.SendJSON(json: llvm::json::Value(std::move(response))); |
147 | } |
148 | |
149 | } // namespace lldb_dap |
150 | |