Skip to content

[lldb-dap] Migrate 'stepIn' request to well structured types. #137071

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
Apr 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
3 changes: 2 additions & 1 deletion lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "llvm/Support/Error.h"

using namespace llvm;
using namespace lldb;
using namespace lldb_dap::protocol;

namespace lldb_dap {
Expand All @@ -35,7 +36,7 @@ Error NextRequestHandler::Run(const NextArguments &args) const {
if (args.granularity == eSteppingGranularityInstruction) {
thread.StepInstruction(/*step_over=*/true);
} else {
thread.StepOver();
thread.StepOver(args.singleThread ? eOnlyThisThread : eOnlyDuringStepping);
}

return Error::success();
Expand Down
7 changes: 4 additions & 3 deletions lldb/tools/lldb-dap/Handler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,12 @@ class NextRequestHandler
llvm::Error Run(const protocol::NextArguments &args) const override;
};

class StepInRequestHandler : public LegacyRequestHandler {
class StepInRequestHandler : public RequestHandler<protocol::StepInArguments,
protocol::StepInResponse> {
public:
using LegacyRequestHandler::LegacyRequestHandler;
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "stepIn"; }
void operator()(const llvm::json::Object &request) const override;
llvm::Error Run(const protocol::StepInArguments &args) const override;
};

class StepInTargetsRequestHandler : public LegacyRequestHandler {
Expand Down
111 changes: 35 additions & 76 deletions lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,91 +8,50 @@

#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "Protocol/ProtocolRequests.h"
#include "Protocol/ProtocolTypes.h"
#include "RequestHandler.h"

using namespace llvm;
using namespace lldb;
using namespace lldb_dap::protocol;

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) const {
llvm::json::Object response;
FillResponse(request, response);
const auto *arguments = request.getObject("arguments");
// The request resumes the given thread to step into a function/method and
// allows all other threads to run freely by resuming them. If the debug adapter
// supports single thread execution (see capability
// `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument
// to true prevents other suspended threads from resuming. If the request cannot
// step into a target, `stepIn` behaves like the `next` request. The debug
// adapter first sends the response and then a `stopped` event (with reason
// `step`) after the step has completed. If there are multiple function/method
// calls (or other targets) on the source line, the 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.
Error StepInRequestHandler::Run(const StepInArguments &args) const {
SBThread thread = dap.GetLLDBThread(args.threadId);
if (!thread.IsValid())
return make_error<DAPError>("invalid thread");

// 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 (args.granularity == eSteppingGranularityInstruction) {
thread.StepInstruction(/*step_over=*/false);
return Error::success();
}

std::string step_in_target;
const auto target_id =
GetInteger<uint64_t>(arguments, "targetId").value_or(0);
auto it = dap.step_in_targets.find(target_id);
auto it = dap.step_in_targets.find(args.targetId.value_or(0));
if (it != dap.step_in_targets.end())
step_in_target = it->second;

const bool single_thread =
GetBoolean(arguments, "singleThread").value_or(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)));
RunMode run_mode = args.singleThread ? eOnlyThisThread : eOnlyDuringStepping;
thread.StepInto(step_in_target.c_str(), run_mode);
return Error::success();
}

} // namespace lldb_dap
9 changes: 9 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,13 @@ bool fromJSON(const llvm::json::Value &Params, NextArguments &NA,
OM.mapOptional("granularity", NA.granularity);
}

bool fromJSON(const llvm::json::Value &Params, StepInArguments &SIA,
llvm::json::Path P) {
json::ObjectMapper OM(Params, P);
return OM && OM.map("threadId", SIA.threadId) &&
OM.map("targetId", SIA.targetId) &&
OM.mapOptional("singleThread", SIA.singleThread) &&
OM.mapOptional("granularity", SIA.granularity);
}

} // namespace lldb_dap::protocol
22 changes: 22 additions & 0 deletions lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,28 @@ bool fromJSON(const llvm::json::Value &, NextArguments &, llvm::json::Path);
/// body field is required.
using NextResponse = VoidResponse;

/// Arguments for `stepIn` request.
struct StepInArguments {
/// Specifies the thread for which to resume execution for one step-into (of
/// the given granularity).
uint64_t threadId = LLDB_INVALID_THREAD_ID;

/// If this flag is true, all other suspended threads are not resumed.
bool singleThread = false;

/// Id of the target to step into.
std::optional<uint64_t> targetId;

/// Stepping granularity. If no granularity is specified, a granularity of
/// `statement` is assumed.
SteppingGranularity granularity = eSteppingGranularityStatement;
};
bool fromJSON(const llvm::json::Value &, StepInArguments &, llvm::json::Path);

/// Response to `stepIn` request. This is just an acknowledgement, so no
/// body field is required.
using StepInResponse = VoidResponse;

} // namespace lldb_dap::protocol

#endif
Loading