[Lldb-commits] [lldb] [lldb-dap] Add missing supportsSingleThreadExecutionRequests in capabilities (PR #200314)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Jun 1 12:53:35 PDT 2026
https://github.com/kusmour updated https://github.com/llvm/llvm-project/pull/200314
>From 646796d84d4b11d32a0a1df652010ca227a6bd35 Mon Sep 17 00:00:00 2001
From: Wanyi Ye <wanyi at meta.com>
Date: Mon, 1 Jun 2026 10:16:03 -0700
Subject: [PATCH 1/2] [lldb] Support single thread for step out
Summary:
Test Plan:
Reviewers:
Subscribers:
Tasks:
Tags:
Differential Revision: https://phabricator.intern.facebook.com/D107144940
---
lldb/include/lldb/API/SBThread.h | 4 ++
.../test/tools/lldb-dap/dap_server.py | 4 +-
.../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++-
lldb/source/API/SBThread.cpp | 39 ++++++++++++++++
.../API/tools/lldb-dap/step/TestDAP_step.py | 45 +++++++++++++++++++
.../Handler/StepOutRequestHandler.cpp | 4 +-
6 files changed, 99 insertions(+), 4 deletions(-)
diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index 97d3b838492fb..5f43f2318d580 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -121,6 +121,10 @@ class LLDB_API SBThread {
void StepOut(SBError &error);
+ void StepOut(lldb::RunMode stop_other_threads);
+
+ void StepOut(lldb::RunMode stop_other_threads, SBError &error);
+
void StepOutOfFrame(SBFrame &frame);
void StepOutOfFrame(SBFrame &frame, SBError &error);
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index c8acf67b23b99..b8d44d3822703 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -1294,10 +1294,12 @@ def request_stepInTargets(self, frameId):
}
return self._send_recv(command_dict)
- def request_stepOut(self, threadId):
+ def request_stepOut(self, threadId, singleThread=None):
if self.exit_status is not None:
raise ValueError("request_stepOut called after process exited")
args_dict = {"threadId": threadId}
+ if singleThread is not None:
+ args_dict["singleThread"] = singleThread
command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict}
return self._send_recv(command_dict)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 714a8c228a76b..22562308614a2 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -443,8 +443,11 @@ def stepOver(
return self.dap_server.wait_for_stopped()
return None
- def stepOut(self, threadId=None, waitForStop=True):
- self.dap_server.request_stepOut(threadId=threadId)
+ def stepOut(self, threadId=None, waitForStop=True, singleThread=None):
+ response = self.dap_server.request_stepOut(
+ threadId=threadId, singleThread=singleThread
+ )
+ self.assertTrue(response["success"])
if waitForStop:
return self.dap_server.wait_for_stopped()
return None
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index fafb533771e56..9e3e553a426f3 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -621,6 +621,45 @@ void SBThread::StepOut(SBError &error) {
error = Status::FromErrorString(new_plan_status.AsCString());
}
+void SBThread::StepOut(lldb::RunMode stop_other_threads) {
+ LLDB_INSTRUMENT_VA(this, stop_other_threads);
+
+ SBError error; // Ignored
+ StepOut(stop_other_threads, error);
+}
+
+void SBThread::StepOut(lldb::RunMode stop_other_threads, SBError &error) {
+ LLDB_INSTRUMENT_VA(this, stop_other_threads, error);
+
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
+
+ if (!exe_ctx->HasThreadScope()) {
+ error = Status::FromErrorString("this SBThread object is invalid");
+ return;
+ }
+
+ bool abort_other_plans = false;
+
+ Thread *thread = exe_ctx->GetThreadPtr();
+
+ const LazyBool avoid_no_debug = eLazyBoolCalculate;
+ Status new_plan_status;
+ ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut(
+ abort_other_plans, nullptr, false,
+ stop_other_threads == eOnlyThisThread, eVoteYes, eVoteNoOpinion, 0,
+ new_plan_status, avoid_no_debug));
+
+ if (new_plan_status.Success())
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
+ else
+ error = Status::FromErrorString(new_plan_status.AsCString());
+}
+
void SBThread::StepOutOfFrame(SBFrame &sb_frame) {
LLDB_INSTRUMENT_VA(this, sb_frame);
diff --git a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
index 5c055f679aedc..31c0ea4ad1669 100644
--- a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
+++ b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
@@ -84,6 +84,51 @@ def test_step(self):
# only step one thread that is at the breakpoint and stop
break
+ @skipIfWindows
+ def test_step_out_single_thread(self):
+ """
+ Tests step out with singleThread=true correctly completes the step.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+ source = "main.cpp"
+ breakpoint1_line = line_number(source, "// breakpoint 1")
+ lines = [breakpoint1_line]
+ breakpoint_ids = self.set_source_breakpoints(source, lines)
+ self.assertEqual(
+ len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+ )
+ self.continue_to_breakpoints(breakpoint_ids)
+ threads = self.dap_server.get_threads()
+ for thread in threads:
+ if "reason" in thread:
+ reason = thread["reason"]
+ if reason == "breakpoint":
+ tid = thread["id"]
+ x1 = self.get_local_as_int("x", threadId=tid)
+ (src1, line1) = self.get_source_and_line(threadId=tid)
+
+ # Step into the recursive call first so we have
+ # a frame to step out of.
+ self.stepIn(threadId=tid, waitForStop=True)
+ x2 = self.get_local_as_int("x", threadId=tid)
+ (src2, line2) = self.get_source_and_line(threadId=tid)
+ self.assertEqual(x1, x2 + 1, "verify step in variable")
+
+ # Now step out with singleThread and verify we
+ # return to the caller frame.
+ self.stepOut(
+ threadId=tid, waitForStop=True, singleThread=True
+ )
+ x3 = self.get_local_as_int("x", threadId=tid)
+ (src3, line3) = self.get_source_and_line(threadId=tid)
+ self.assertEqual(x1, x3, "verify step out variable")
+ self.assertGreaterEqual(
+ line3, line1, "verify step out line"
+ )
+ self.assertEqual(src1, src3, "verify step out source")
+ break
+
def test_step_over_inlined_function(self):
"""
Test stepping over when the program counter is in another file.
diff --git a/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp
index 61bde76e08a3d..f573f2fe1e25b 100644
--- a/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp
@@ -41,7 +41,9 @@ Error StepOutRequestHandler::Run(const StepOutArguments &arguments) const {
// "threadCausedFocus" boolean value in the "stopped" events.
dap.focus_tid = thread.GetThreadID();
lldb::SBError error;
- thread.StepOut(error);
+ thread.StepOut(arguments.singleThread.value_or(false) ? eOnlyThisThread
+ : eOnlyDuringStepping,
+ error);
return ToError(error);
}
>From 6b82ac40d92960bd352a048c472dea097a345053 Mon Sep 17 00:00:00 2001
From: Wanyi Ye <wanyi at meta.com>
Date: Thu, 28 May 2026 16:50:57 -0700
Subject: [PATCH 2/2] [lldb-dap] Add missing
supportsSingleThreadExecutionRequests in capabilities
Summary:
Test Plan:
Reviewers:
Subscribers:
Tasks:
Tags:
Differential Revision: https://phabricator.intern.facebook.com/D107144941
---
lldb/docs/use/lldbdap.md | 2 +-
lldb/tools/lldb-dap/DAP.cpp | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/docs/use/lldbdap.md b/lldb/docs/use/lldbdap.md
index a4c151a2ff39c..a684b52afd31a 100644
--- a/lldb/docs/use/lldbdap.md
+++ b/lldb/docs/use/lldbdap.md
@@ -109,7 +109,7 @@ for more information.
| supportsSteppingGranularity | Y | The debug adapter supports stepping granularities (argument `granularity`) for the stepping requests. |
| supportsInstructionBreakpoints | Y | The debug adapter supports adding breakpoints based on instruction references. |
| supportsExceptionFilterOptions | N | The debug adapter supports `filterOptions` as an argument on the `setExceptionBreakpoints` request. |
-| supportsSingleThreadExecutionRequests | N | The debug adapter supports the `singleThread` property on the execution requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, `stepBack`). |
+| supportsSingleThreadExecutionRequests | Y | The debug adapter supports the `singleThread` property on the execution requests (lldb-dap supported: `continue`, `next`, `stepIn`; lldb-dap not yet supported: `stepOut`, `reverseContinue`, `stepBack`). |
| supportsDataBreakpointBytes | Y | The debug adapter supports the `asAddress` and `bytes` fields in the `dataBreakpointInfo` request. |
| breakpointModes | `[]` | Modes of breakpoints supported by the debug adapter, such as 'hardware' or 'software'. If present, the client may allow the user to select a mode and include it in its `setBreakpoints` request. Clients may present the first applicable mode in this array as the 'default' mode in gestures that set breakpoints. |
| supportsANSIStyling | Y | The debug adapter supports ANSI escape sequences in styling of `OutputEvent.output` and `Variable.value` fields. |
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index cb34c7b2fd1e8..81388eae2a275 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -1220,6 +1220,7 @@ protocol::Capabilities DAP::GetCapabilities() {
// Supported capabilities that are not specific to a single request.
capabilities.supportedFeatures = {
protocol::eAdapterFeatureLogPoints,
+ protocol::eAdapterFeatureSingleThreadExecutionRequests,
protocol::eAdapterFeatureSteppingGranularity,
protocol::eAdapterFeatureValueFormattingOptions,
};
More information about the lldb-commits
mailing list