//===-- StepInTargetsRequestHandler.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/SBInstruction.h" namespace lldb_dap { // "StepInTargetsRequest": { // "allOf": [ { "$ref": "#/definitions/Request" }, { // "type": "object", // "description": "This request retrieves the possible step-in targets for // the specified stack frame.\nThese targets can be used in the `stepIn` // request.\nClients should only call this request if the corresponding // capability `supportsStepInTargetsRequest` is true.", "properties": { // "command": { // "type": "string", // "enum": [ "stepInTargets" ] // }, // "arguments": { // "$ref": "#/definitions/StepInTargetsArguments" // } // }, // "required": [ "command", "arguments" ] // }] // }, // "StepInTargetsArguments": { // "type": "object", // "description": "Arguments for `stepInTargets` request.", // "properties": { // "frameId": { // "type": "integer", // "description": "The stack frame for which to retrieve the possible // step-in targets." // } // }, // "required": [ "frameId" ] // }, // "StepInTargetsResponse": { // "allOf": [ { "$ref": "#/definitions/Response" }, { // "type": "object", // "description": "Response to `stepInTargets` request.", // "properties": { // "body": { // "type": "object", // "properties": { // "targets": { // "type": "array", // "items": { // "$ref": "#/definitions/StepInTarget" // }, // "description": "The possible step-in targets of the specified // source location." // } // }, // "required": [ "targets" ] // } // }, // "required": [ "body" ] // }] // } void StepInTargetsRequestHandler::operator()( const llvm::json::Object &request) const { llvm::json::Object response; FillResponse(request, response); const auto *arguments = request.getObject("arguments"); dap.step_in_targets.clear(); lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); if (frame.IsValid()) { lldb::SBAddress pc_addr = frame.GetPCAddress(); lldb::SBAddress line_end_addr = pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true); lldb::SBInstructionList insts = dap.target.ReadInstructions( pc_addr, line_end_addr, /*flavor_string=*/nullptr); if (!insts.IsValid()) { response["success"] = false; response["message"] = "Failed to get instructions for frame."; dap.SendJSON(llvm::json::Value(std::move(response))); return; } llvm::json::Array step_in_targets; const auto num_insts = insts.GetSize(); for (size_t i = 0; i < num_insts; ++i) { lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); if (!inst.IsValid()) break; lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target); // Note: currently only x86/x64 supports flow kind. lldb::InstructionControlFlowKind flow_kind = inst.GetControlFlowKind(dap.target); if (flow_kind == lldb::eInstructionControlFlowKindCall) { // Use call site instruction address as id which is easy to debug. llvm::json::Object step_in_target; step_in_target["id"] = inst_addr; llvm::StringRef call_operand_name = inst.GetOperands(dap.target); lldb::addr_t call_target_addr; if (call_operand_name.getAsInteger(0, call_target_addr)) continue; lldb::SBAddress call_target_load_addr = dap.target.ResolveLoadAddress(call_target_addr); if (!call_target_load_addr.IsValid()) continue; // The existing ThreadPlanStepInRange only accept step in target // function with debug info. lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress( call_target_load_addr, lldb::eSymbolContextFunction); // The existing ThreadPlanStepInRange only accept step in target // function with debug info. std::string step_in_target_name; if (sc.IsValid() && sc.GetFunction().IsValid()) step_in_target_name = sc.GetFunction().GetDisplayName(); // Skip call sites if we fail to resolve its symbol name. if (step_in_target_name.empty()) continue; dap.step_in_targets.try_emplace(inst_addr, step_in_target_name); step_in_target.try_emplace("label", step_in_target_name); step_in_targets.emplace_back(std::move(step_in_target)); } } llvm::json::Object body; body.try_emplace("targets", std::move(step_in_targets)); response.try_emplace("body", std::move(body)); } else { response["success"] = llvm::json::Value(false); response["message"] = "Failed to get frame for input frameId."; } dap.SendJSON(llvm::json::Value(std::move(response))); } } // namespace lldb_dap