Skip to content

[lldb-dap] Refactor stepping related request handlers (NFC) #128453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lldb/tools/lldb-dap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
79 changes: 79 additions & 0 deletions lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions lldb/tools/lldb-dap/Handler/RequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,11 @@ void RequestHandler::PrintWelcomeMessage() {
#endif
}

bool RequestHandler::HasInstructionGranularity(
const llvm::json::Object &arguments) {
if (std::optional<llvm::StringRef> value = arguments.getString("granularity"))
return value == "instruction";
return false;
}

} // namespace lldb_dap
33 changes: 32 additions & 1 deletion lldb/tools/lldb-dap/Handler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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?
/// @{
Expand All @@ -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;
};

Expand Down Expand Up @@ -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
96 changes: 96 additions & 0 deletions lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading