llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) <details> <summary>Changes</summary> Continuation of the work started in #<!-- -->128262. --- Patch is 32.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128453.diff 8 Files Affected: - (modified) lldb/tools/lldb-dap/CMakeLists.txt (+4) - (added) lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp (+79) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.cpp (+7) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.h (+32-1) - (added) lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp (+96) - (added) lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp (+149) - (added) lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp (+68) - (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+5-339) ``````````diff diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index 73762af5c2fd7..61271e1a9f2a6 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -47,8 +47,12 @@ add_lldb_tool(lldb-dap Handler/ExceptionInfoRequestHandler.cpp Handler/InitializeRequestHandler.cpp Handler/LaunchRequestHandler.cpp + Handler/NextRequestHandler.cpp Handler/RequestHandler.cpp Handler/RestartRequestHandler.cpp + Handler/StepInRequestHandler.cpp + Handler/StepInTargetsRequestHandler.cpp + Handler/StepOutRequestHandler.cpp LINK_LIBS liblldb diff --git a/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp new file mode 100644 index 0000000000000..695703fe301b3 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp @@ -0,0 +1,79 @@ +//===-- NextRequestHandler.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" + +namespace lldb_dap { + +// "NextRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Next request; value of command field is 'next'. The +// request starts the debuggee to run again for one step. +// The debug adapter first sends the NextResponse and then +// a StoppedEvent (event type 'step') after the step has +// completed.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "next" ] +// }, +// "arguments": { +// "$ref": "#/definitions/NextArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "NextArguments": { +// "type": "object", +// "description": "Arguments for 'next' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'next' for this thread." +// }, +// "granularity": { +// "$ref": "#/definitions/SteppingGranularity", +// "description": "Stepping granularity. If no granularity is specified, a +// granularity of `statement` is assumed." +// } +// }, +// "required": [ "threadId" ] +// }, +// "NextResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'next' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void NextRequestHandler::operator()(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + const auto *arguments = request.getObject("arguments"); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + dap.focus_tid = thread.GetThreadID(); + if (HasInstructionGranularity(*arguments)) { + thread.StepInstruction(/*step_over=*/true); + } else { + thread.StepOver(); + } + } else { + response["success"] = llvm::json::Value(false); + } + dap.SendJSON(llvm::json::Value(std::move(response))); +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index c09ddf55dd5e9..3b1c2b0dc7e31 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -225,4 +225,11 @@ void RequestHandler::PrintWelcomeMessage() { #endif } +bool RequestHandler::HasInstructionGranularity( + const llvm::json::Object &request) { + if (std::optional<llvm::StringRef> value = request.getString("granularity")) + return value == "instruction"; + return false; +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 9bc8e60dbb858..2610a3d21ebc4 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -30,6 +30,7 @@ class RequestHandler { virtual void operator()(const llvm::json::Object &request) = 0; +protected: /// Helpers used by multiple request handlers. /// FIXME: Move these into the DAP class? /// @{ @@ -48,9 +49,11 @@ class RequestHandler { // This way we can reuse the process launching logic for RestartRequest too. lldb::SBError LaunchProcess(const llvm::json::Object &request); + // Check if the step-granularity is `instruction`. + bool HasInstructionGranularity(const llvm::json::Object &request); + /// @} -protected: DAP &dap; }; @@ -131,6 +134,34 @@ class RestartRequestHandler : public RequestHandler { void operator()(const llvm::json::Object &request) override; }; +class NextRequestHandler : public RequestHandler { +public: + using RequestHandler::RequestHandler; + static llvm::StringLiteral getCommand() { return "next"; } + void operator()(const llvm::json::Object &request) override; +}; + +class StepInRequestHandler : public RequestHandler { +public: + using RequestHandler::RequestHandler; + static llvm::StringLiteral getCommand() { return "stepIn"; } + void operator()(const llvm::json::Object &request) override; +}; + +class StepInTargetsRequestHandler : public RequestHandler { +public: + using RequestHandler::RequestHandler; + static llvm::StringLiteral getCommand() { return "stepInTargets"; } + void operator()(const llvm::json::Object &request) override; +}; + +class StepOutRequestHandler : public RequestHandler { +public: + using RequestHandler::RequestHandler; + static llvm::StringLiteral getCommand() { return "stepOut"; } + void operator()(const llvm::json::Object &request) override; +}; + } // namespace lldb_dap #endif diff --git a/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp new file mode 100644 index 0000000000000..f435436734538 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp @@ -0,0 +1,96 @@ +//===-- StepInRequestHandler.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" + +namespace lldb_dap { + +// "StepInRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepIn request; value of command field is 'stepIn'. The +// request starts the debuggee to step into a function/method if possible. +// If it cannot step into a target, 'stepIn' behaves like 'next'. The debug +// adapter first sends the StepInResponse and then a StoppedEvent (event +// type 'step') after the step has completed. If there are multiple +// function/method calls (or other targets) on the source line, the optional +// argument 'targetId' can be used to control into which target the 'stepIn' +// should occur. The list of possible targets for a given source line can be +// retrieved via the 'stepInTargets' request.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepIn" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepInArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepInArguments": { +// "type": "object", +// "description": "Arguments for 'stepIn' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepIn' for this thread." +// }, +// "targetId": { +// "type": "integer", +// "description": "Optional id of the target to step into." +// }, +// "granularity": { +// "$ref": "#/definitions/SteppingGranularity", +// "description": "Stepping granularity. If no granularity is specified, a +// granularity of `statement` is assumed." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepInResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepIn' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void StepInRequestHandler::operator()(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + const auto *arguments = request.getObject("arguments"); + + std::string step_in_target; + uint64_t target_id = GetUnsigned(arguments, "targetId", 0); + auto it = dap.step_in_targets.find(target_id); + if (it != dap.step_in_targets.end()) + step_in_target = it->second; + + const bool single_thread = GetBoolean(arguments, "singleThread", false); + lldb::RunMode run_mode = + single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping; + lldb::SBThread thread = dap.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + dap.focus_tid = thread.GetThreadID(); + if (HasInstructionGranularity(*arguments)) { + thread.StepInstruction(/*step_over=*/false); + } else { + thread.StepInto(step_in_target.c_str(), run_mode); + } + } else { + response["success"] = llvm::json::Value(false); + } + dap.SendJSON(llvm::json::Value(std::move(response))); +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp new file mode 100644 index 0000000000000..e771780711ae9 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp @@ -0,0 +1,149 @@ +//===-- 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) { + 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 diff --git a/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp new file mode 100644 index 0000000000000..d71547d579f1f --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp @@ -0,0 +1,68 @@ +//===-- StepOutRequestHandler.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" + +namespace lldb_dap { + +// "StepOutRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "StepOut request; value of command field is 'stepOut'. The +// request starts the debuggee to run again for one step. The debug adapter +// first sends the StepOutResponse and then a StoppedEvent (event type +// 'step') after the step has completed.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepOut" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepOutArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepOutArguments": { +// "type": "object", +// "description": "Arguments for 'stepOut' request.", +// "properties": { +// "threadId": { +// "type": "integer", +// "description": "Execute 'stepOut' for this thread." +// } +// }, +// "required": [ "threadId" ] +// }, +// "StepOutResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'stepOut' request. This is just an +// acknowledgement, so no body field is required." +// }] +// } +void StepOutRequestHandler::operator()(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + const auto *arguments = request.getObject("arguments"); + lldb::SBThread thread = dap.GetLLDBThread(*arguments); + if (thread.IsValid()) { + // Remember the thread ID that caused the resume so we can set the + // "threadCausedFocus" boolean value in the "stopped" events. + dap.focus_tid = thread.GetThreadID(); + thread.StepOut(); + } else { + response["success"] = llvm::json::Value(false); + } + dap.SendJSON(llvm::json::Value(std::move(response))); +} + +} // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index fcefb4137f227..edbf78678dca7 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -373,77 +373,6 @@ void request_modules(DAP &dap, const llvm::json::Object &request) { dap.SendJSON(llvm::json::Value(std::move(response))); } -// Check if the step-granularity is `instruction` -static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) { - if (std::optional<llvm::StringRef> value = - requestArgs.getString("granularity")) - return value == "instruction"; - return false; -} - -// "NextRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Next request; value of command field is 'next'. The -// request starts the debuggee to run again for one step. -// The debug adapter first sends the NextResponse and then -// a StoppedEvent (event type 'step') after the step has -// completed.", -// "properties": { -// "command": { -// "type": "string", -// "enum": [ "next" ] -// }, -// "arguments": { -// "$ref": "#/definitions/NextArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "NextArguments": { -// "type": "object", -// "description": "Arguments for 'next' request.", -// "properties": { -// "threadId": { -// "type": "integer", -// "description": "Execute 'next' for this thread." -// }, -// "granularity": { -// "$ref": "#/definitions/SteppingGranularity", -// "description": "Stepping granularity. If no granularity is specified, a -// granularity of `statement` is assu... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/128453 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits