[Lldb-commits] [lldb] f4a0273 - [lldb-dap] Support loading core files through attachCommands (#202785)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jun 11 05:21:54 PDT 2026
Author: Alexandre Perez
Date: 2026-06-11T05:21:49-07:00
New Revision: f4a027364add48b3522ead105494187ded58047f
URL: https://github.com/llvm/llvm-project/commit/f4a027364add48b3522ead105494187ded58047f
DIFF: https://github.com/llvm/llvm-project/commit/f4a027364add48b3522ead105494187ded58047f.diff
LOG: [lldb-dap] Support loading core files through attachCommands (#202785)
The `attachCommands` attach option lets users bootstrap a session with
arbitrary LLDB commands, but a command that loaded a core (e.g. `target
create --core`) produced a broken session:
`ConfigurationDoneRequestHandler` would call `process.Continue()` on the
core and fail, because the non-live-session handling was keyed on the
`coreFile` attach argument rather than on the actual resulting process.
This teaches `AttachRequestHandler` to detect, after the attach commands
run, whether the selected process was loaded from a core via the
`SBProcess:: IsLiveDebugSession()` API added in #203111. When it is a
core, it sets `stop_at_entry` and clears `is_live_session`, mirroring
what the `coreFile` key does.
Added:
Modified:
lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
Removed:
################################################################################
diff --git a/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py b/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
index 7fc275c0c702b..7c1bbad6afaba 100644
--- a/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
+++ b/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
@@ -9,6 +9,51 @@
import lldbdap_testcase
import os
+# The expected backtrace when loading the bundled linux-x86_64.core. Shared by
+# the tests that load this core through
diff erent mechanisms (the "coreFile"
+# attach key and "attachCommands") so we can assert they behave identically.
+EXPECTED_CORE_FRAMES = [
+ {
+ "column": 0,
+ "id": 524288,
+ "line": 4,
+ "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+ "name": "bar",
+ "source": {
+ "name": "main.c",
+ "path": "/home/labath/test/main.c",
+ "presentationHint": "deemphasize",
+ },
+ "instructionPointerReference": "0x40011C",
+ },
+ {
+ "column": 0,
+ "id": 524289,
+ "line": 10,
+ "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+ "name": "foo",
+ "source": {
+ "name": "main.c",
+ "path": "/home/labath/test/main.c",
+ "presentationHint": "deemphasize",
+ },
+ "instructionPointerReference": "0x400142",
+ },
+ {
+ "column": 0,
+ "id": 524290,
+ "line": 16,
+ "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+ "name": "_start",
+ "source": {
+ "name": "main.c",
+ "path": "/home/labath/test/main.c",
+ "presentationHint": "deemphasize",
+ },
+ "instructionPointerReference": "0x40015F",
+ },
+]
+
class TestDAP_coreFile(lldbdap_testcase.DAPTestCaseBase):
@skipIfLLVMTargetMissing("X86")
@@ -21,47 +66,7 @@ def test_core_file(self):
self.attach(program=exe_file, coreFile=core_file)
self.dap_server.request_configurationDone()
- expected_frames = [
- {
- "column": 0,
- "id": 524288,
- "line": 4,
- "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
- "name": "bar",
- "source": {
- "name": "main.c",
- "path": "/home/labath/test/main.c",
- "presentationHint": "deemphasize",
- },
- "instructionPointerReference": "0x40011C",
- },
- {
- "column": 0,
- "id": 524289,
- "line": 10,
- "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
- "name": "foo",
- "source": {
- "name": "main.c",
- "path": "/home/labath/test/main.c",
- "presentationHint": "deemphasize",
- },
- "instructionPointerReference": "0x400142",
- },
- {
- "column": 0,
- "id": 524290,
- "line": 16,
- "moduleId": "01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
- "name": "_start",
- "source": {
- "name": "main.c",
- "path": "/home/labath/test/main.c",
- "presentationHint": "deemphasize",
- },
- "instructionPointerReference": "0x40015F",
- },
- ]
+ expected_frames = EXPECTED_CORE_FRAMES
self.assertEqual(self.get_stackFrames(), expected_frames)
@@ -73,6 +78,51 @@ def test_core_file(self):
self.dap_server.request_next(threadId=32259)
self.assertEqual(self.get_stackFrames(), expected_frames)
+ @skipIfLLVMTargetMissing("X86")
+ def test_core_file_attach_commands(self):
+ """Loading a core through "attachCommands" (e.g. `target create --core`)
+ should behave identically to using the "coreFile" attach key: the
+ session stops with the real crash reason and cannot be resumed."""
+ current_dir = os.path.dirname(__file__)
+ exe_file = os.path.join(current_dir, "linux-x86_64.out")
+ core_file = os.path.join(current_dir, "linux-x86_64.core")
+
+ self.create_debug_adapter()
+ # Bootstrap the core target purely through a custom attach command,
+ # mirroring how the "coreFile" key passes the same program.
+ self.attach(
+ program=exe_file,
+ attachCommands=['target create --core "%s" "%s"' % (core_file, exe_file)],
+ )
+
+ # configurationDone must succeed: a core is a non-live session, so the
+ # adapter must not try to resume it (resuming a core fails).
+ resp = self.dap_server.request_configurationDone()
+ self.assertTrue(
+ resp["success"],
+ "configurationDone should succeed for a core loaded via attachCommands",
+ )
+
+ # The backtrace must match the "coreFile" attach key exactly.
+ self.assertEqual(self.get_stackFrames(), EXPECTED_CORE_FRAMES)
+
+ # The stop must be reported with the real crash reason, not "entry".
+ self.dap_server.wait_for_stopped()
+ found_exception = any(
+ body.get("reason") == "exception"
+ for body in self.dap_server.thread_stop_reasons.values()
+ )
+ self.assertTrue(
+ found_exception,
+ f"Expected a thread stopped with reason 'exception', got: "
+ f"{self.dap_server.thread_stop_reasons}",
+ )
+
+ # Resuming should have no effect and keep the process stopped.
+ resp = self.dap_server.request_continue()
+ self.assertFalse(resp["success"])
+ self.assertEqual(self.get_stackFrames(), EXPECTED_CORE_FRAMES)
+
def test_wrong_core_file(self):
exe_file = self.getSourcePath("linux-x86_64.out")
wrong_core_file = self.getSourcePath("main.c")
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index c4cfbcbd3d73c..b6947c81986f1 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -109,6 +109,15 @@ Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
if (!dap.target.GetProcess().IsValid())
return make_error<DAPError>(
"attachCommands failed to attach to a process");
+
+ // If the attach commands produced a non-live session (e.g. a core file
+ // loaded via `target create --core`), report the session's stop reason
+ // instead of trying to resume it, matching the behavior of the
+ // `coreFile` attach key.
+ if (!dap.target.GetProcess().IsLiveDebugSession()) {
+ dap.stop_at_entry = true;
+ dap.is_live_session = false;
+ }
} else if (!args.coreFile.empty()) {
dap.target.LoadCore(args.coreFile.data(), error);
} else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) {
More information about the lldb-commits
mailing list