https://github.com/da-viper created 
https://github.com/llvm/llvm-project/pull/217403

- TestDAP_save_core.py
- TestDAP_coreFile.py

There is a bug in the number of modules reported from the core file in 
TestDAP_save_core.py. The test previously assertsTrue instead of assertsEqual.

>From 7a3aee8c0637722024ae2d5b1bfc079a6285c772 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <[email protected]>
Date: Fri, 24 Jul 2026 21:24:19 +0100
Subject: [PATCH] [lldb-dap] Migrate core file test

- TestDAP_save_core.py
- TestDAP_coreFile.py

There is a bug in the number of modules reported from the core file
in TestDAP_save_core.py. The test previously assertsTrue instead of
assertsEqual.
---
 .../lldbsuite/test/tools/lldb_dap/types.py    |   8 +-
 .../lldb-dap/coreFile/TestDAP_coreFile.py     | 283 +++++++++---------
 .../lldb-dap/save-core/TestDAP_save_core.py   | 117 ++++----
 3 files changed, 199 insertions(+), 209 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
index d75a1ea8fb3b9..94980cb081f73 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py
@@ -1080,7 +1080,7 @@ class InitializeArgs:
     supportsStartDebuggingRequest: Optional[bool] = None
     supportsANSIStyling: Optional[bool] = None
     sourceInitFile: bool = field(
-        metadata={"alias": "$__lldbSourceInitFile"}, default=False
+        metadata={"alias": "$__lldb_sourceInitFile"}, default=False
     )
 
     command_ = "initialize"
@@ -1118,9 +1118,9 @@ class LaunchArgs:
     env: Optional[Union[Dict[str, str], List[str]]] = None
     detachOnError: Optional[bool] = None
     disableASLR: bool = False
-    disableSTDIO: bool = False
+    disableSTDIO: Optional[bool] = None
     shellExpandArguments: bool = False
-    console: Console = Console.INTERNAL
+    console: Optional[Console] = None
     stdio: Optional[List[Optional[str]]] = None
 
     # Configurations.
@@ -1128,7 +1128,7 @@ class LaunchArgs:
     enableAutoVariableSummaries: bool = False
     enableSyntheticChildDebugging: bool = False
     displayExtendedBacktrace: bool = False
-    stopOnEntry: bool = False
+    stopOnEntry: Optional[bool] = None
     timeout: Optional[float] = None
     commandEscapePrefix: Optional[str] = None
     customFrameFormat: Optional[str] = None
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 7c1bbad6afaba..94bf30956e7a8 100644
--- a/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
+++ b/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
@@ -2,197 +2,184 @@
 Test lldb-dap coreFile attaching
 """
 
-import dap_server
 from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-import lldbdap_testcase
-import os
+from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase
+from lldbsuite.test.tools.lldb_dap.types import (
+    AttachArgs,
+    ContinueArgs,
+    NextArgs,
+    Source,
+    StackFrame,
+)
 
 # The expected backtrace when loading the bundled linux-x86_64.core. Shared by
 # the tests that load this core through different 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",
-    },
+    StackFrame(
+        column=0,
+        id=524288,
+        line=4,
+        moduleId="01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+        name="bar",
+        source=Source(
+            name="main.c",
+            path="/home/labath/test/main.c",
+            presentationHint="deemphasize",
+        ),
+        instructionPointerReference="0x40011C",
+    ),
+    StackFrame(
+        column=0,
+        id=524289,
+        line=10,
+        moduleId="01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+        name="foo",
+        source=Source(
+            name="main.c",
+            path="/home/labath/test/main.c",
+            presentationHint="deemphasize",
+        ),
+        instructionPointerReference="0x400142",
+    ),
+    StackFrame(
+        column=0,
+        id=524290,
+        line=16,
+        moduleId="01DF54A6-045E-657D-3F8F-FB9CE1118789-14F8BD6D",
+        name="_start",
+        source=Source(
+            name="main.c",
+            path="/home/labath/test/main.c",
+            presentationHint="deemphasize",
+        ),
+        instructionPointerReference="0x40015F",
+    ),
 ]
 
 
-class TestDAP_coreFile(lldbdap_testcase.DAPTestCaseBase):
-    @skipIfLLVMTargetMissing("X86")
+@skipIfLLVMTargetMissing("X86")
+class TestDAP_coreFile(DAPTestCaseBase):
     def test_core_file(self):
-        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()
-        self.attach(program=exe_file, coreFile=core_file)
-        self.dap_server.request_configurationDone()
+        exe_file = self.getSourcePath("linux-x86_64.out")
+        core_file = self.getSourcePath("linux-x86_64.core")
 
-        expected_frames = EXPECTED_CORE_FRAMES
+        session = self.create_session()
+        process_event = session.attach(AttachArgs(program=exe_file, 
coreFile=core_file))
+        stop_event = session.wait_for_stopped_event(after=process_event)
+        thread_id = self.expect_not_none(stop_event.body.threadId)
 
-        self.assertEqual(self.get_stackFrames(), expected_frames)
+        frames = session.stack_trace(thread_id).body.stackFrames
+        self.assertEqual(frames, EXPECTED_CORE_FRAMES)
 
-        # 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_frames)
+        # Resuming a core process should fail. the process stays stopped
+        # with the same backtrace.
+        session.send_request(ContinueArgs(thread_id)).error()
+        frames = session.stack_trace(thread_id).body.stackFrames
+        self.assertEqual(frames, EXPECTED_CORE_FRAMES)
 
-        self.dap_server.request_next(threadId=32259)
-        self.assertEqual(self.get_stackFrames(), expected_frames)
+        # Same for step-over.
+        session.send_request(NextArgs(threadId=thread_id)).error()
+        frames = session.stack_trace(thread_id).body.stackFrames
+        self.assertEqual(frames, EXPECTED_CORE_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")
+        exe_file = self.getSourcePath("linux-x86_64.out")
+        core_file = self.getSourcePath("linux-x86_64.core")
 
-        self.create_debug_adapter()
+        session = self.create_session()
         # 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",
+        process_event = session.attach(
+            AttachArgs(
+                program=exe_file,
+                attachCommands=[f"target create --core '{core_file}' 
'{exe_file}"],
+            )
         )
 
-        # 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}",
-        )
+        stop_event = session.verify_stopped_on_exception(after=process_event)
+        thread_id = self.expect_not_none(stop_event.body.threadId)
 
-        # 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)
+        # The backtrace must match the "coreFile" attach key exactly.
+        frames = session.stack_trace(thread_id).body.stackFrames
+        self.assertEqual(frames, EXPECTED_CORE_FRAMES)
+
+        # Resuming should fail.
+        session.send_request(ContinueArgs(thread_id)).error()
+        frames = session.stack_trace(thread_id).body.stackFrames
+        self.assertEqual(frames, EXPECTED_CORE_FRAMES)
 
     def test_wrong_core_file(self):
+        """Attaching with a file that isn't a real core should fail cleanly
+        during configurationDone rather than crashing the adapter."""
         exe_file = self.getSourcePath("linux-x86_64.out")
         wrong_core_file = self.getSourcePath("main.c")
 
-        self.create_debug_adapter()
-        resp = self.attach_and_configurationDone(
-            program=exe_file, coreFile=wrong_core_file
+        session = self.create_session()
+        session.initialize_sequence(session.initialize_args)
+        pending_attach = session.send_request(
+            AttachArgs(program=exe_file, coreFile=wrong_core_file)
         )
-        self.assertIsNotNone(resp)
-        self.assertFalse(resp["success"], "Expected failure in response 
{resp!r}")
-        error_msg = resp["body"]["error"]["format"]
+        session.verify_configuration_done(expected_success=False)
 
-        # attach may fail for mutilple reasons.
-        self.assertEqual(error_msg, "Failed to create the process")
+        resp = pending_attach.error()
+        resp_error = self.expect_not_none(resp.body and resp.body.error)
+        self.assertEqual(resp_error.format, "Failed to create the process")
 
-    @skipIfLLVMTargetMissing("X86")
     def test_core_file_stopped_reason(self):
-        """Test that the stopped event for a core file reports the actual crash
+        """The stopped event for a core file should report the actual crash
         reason (e.g. 'exception') rather than 'entry'."""
-        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()
-        self.attach(program=exe_file, coreFile=core_file)
-        self.dap_server.request_configurationDone()
-        self.dap_server.wait_for_stopped()
-
-        # Core files should report the actual crash reason, not 'entry'.
-        stop_reasons = self.dap_server.thread_stop_reasons
-        self.assertGreater(len(stop_reasons), 0, "Expected at least one 
stopped thread")
-
-        # Find any thread with a stop reason — the crashing thread should
-        # report 'exception' with a description about the signal.
-        found_exception = False
-        for tid, body in stop_reasons.items():
-            if body.get("reason") == "exception":
-                found_exception = True
-                self.assertIn("description", body)
-                break
-        self.assertTrue(
-            found_exception,
-            f"Expected at least one thread with stop reason 'exception', "
-            f"got: {stop_reasons}",
-        )
+        exe_file = self.getSourcePath("linux-x86_64.out")
+        core_file = self.getSourcePath("linux-x86_64.core")
 
-    @skipIfLLVMTargetMissing("X86")
-    def test_core_file_source_mapping_array(self):
-        """Test that sourceMap property is correctly applied when loading a 
core"""
-        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")
+        session = self.create_session()
+        process_event = session.attach(AttachArgs(program=exe_file, 
coreFile=core_file))
 
-        self.create_debug_adapter()
+        stop_event = session.verify_stopped_on_exception(after=process_event)
+        self.assertIsNotNone(stop_event.body.description, "expect a stop 
description.")
 
-        source_map = [["/home/labath/test", current_dir]]
-        self.attach(program=exe_file, coreFile=core_file, sourceMap=source_map)
+    def test_core_file_source_mapping_array(self):
+        """Test that sourceMap property is correctly applied when loading a 
core"""
+        exe_file = self.getSourcePath("linux-x86_64.out")
+        core_file = self.getSourcePath("linux-x86_64.core")
+        current_dir = self.getSourceDir()
+
+        session = self.create_session()
+        process_event = session.attach(
+            AttachArgs(
+                program=exe_file,
+                coreFile=core_file,
+                sourceMap=[("/home/labath/test", current_dir)],
+            )
+        )
 
-        self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
+        stop_event = session.verify_stopped_on_exception(after=process_event)
+        top_frame = session.top_frame_from(stop_event).frame
+        top_source = self.expect_not_none(top_frame.source and 
top_frame.source.path)
+        self.assertIn(current_dir, top_source)
 
-    @skipIfLLVMTargetMissing("X86")
     def test_core_file_source_mapping_object(self):
         """Test that sourceMap property is correctly applied when loading a 
core"""
-        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()
-
-        source_map = {"/home/labath/test": current_dir}
-        self.attach(program=exe_file, coreFile=core_file, sourceMap=source_map)
+        exe_file = self.getSourcePath("linux-x86_64.out")
+        core_file = self.getSourcePath("linux-x86_64.core")
+        current_dir = self.getSourceDir()
+
+        session = self.create_session()
+        process_event = session.attach(
+            AttachArgs(
+                program=exe_file,
+                coreFile=core_file,
+                sourceMap={"/home/labath/test": current_dir},
+            )
+        )
 
-        self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
+        stop_event = session.verify_stopped_on_exception(after=process_event)
+        top_frame = session.top_frame_from(stop_event).frame
+        top_source = self.expect_not_none(top_frame.source and 
top_frame.source.path)
+        self.assertIn(current_dir, top_source)
diff --git a/lldb/test/API/tools/lldb-dap/save-core/TestDAP_save_core.py 
b/lldb/test/API/tools/lldb-dap/save-core/TestDAP_save_core.py
index 77c1e47914a39..a8239ff4a082f 100644
--- a/lldb/test/API/tools/lldb-dap/save-core/TestDAP_save_core.py
+++ b/lldb/test/API/tools/lldb-dap/save-core/TestDAP_save_core.py
@@ -1,73 +1,76 @@
 """
-Test saving core minidump from lldb-dap
+Test lldb-dap saving a core file and attaching to it.
 """
 
-import dap_server
+from pathlib import Path
+
 from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-import lldbdap_testcase
-from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbtest import line_number
+from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase
+from lldbsuite.test.tools.lldb_dap.types import AttachArgs, LaunchArgs
+
 
+class TestDAP_save_core(DAPTestCaseBase):
+    SHARED_BUILD_TESTCASE = False
 
-class TestDAP_save_core(lldbdap_testcase.DAPTestCaseBase):
     @skipUnlessArch("x86_64")
-    @skipUnlessPlatform(["linux"])
-    def test_save_core(self):
-        """
-        Tests saving core minidump from lldb-dap.
-        """
-        program = self.getBuildArtifact("a.out")
-        self.build_and_launch(program)
+    @requireLinux
+    def test_save_and_reload_core(self):
+        """Save minidump cores in every supported `--style` from a single
+        live stop, then re-attach to each and verify the frame, thread
+        count, and module count all round-trip."""
+
+        session = self.build_and_create_session()
         source = "main.cpp"
-        # source_path = os.path.join(os.getcwd(), source)
-        breakpoint1_line = line_number(source, "// breakpoint 1")
-        lines = [breakpoint1_line]
-        # Set breakpoint in the thread function so we can step the threads
-        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)
+        program = self.getBuildArtifact("a.out")
+        breakpoint_line = line_number(source, "// breakpoint 1")
 
-        # Getting dap stack trace may trigger __lldb_caller_function JIT 
module to be created.
-        self.get_stackFrames(startFrame=0)
+        with session.configure(LaunchArgs(program)) as cm:
+            [bp_id] = session.resolve_source_breakpoints(source, 
[breakpoint_line])
 
-        modules = self.dap_server.get_modules()
-        thread_count = len(self.dap_server.get_threads())
+        stop_event = session.verify_stopped_on_breakpoint(bp_id, 
after=cm.process_event)
 
-        core_stack = self.getBuildArtifact("core.stack.dmp")
-        core_dirty = self.getBuildArtifact("core.dirty.dmp")
-        core_full = self.getBuildArtifact("core.full.dmp")
+        # Snapshot the live state so we can assert the reloaded core matches.
+        thread_count = len(session.get_threads())
+        module_count = len(session.get_modules())
 
-        base_command = "`process save-core --plugin-name=minidump "
-        self.dap_server.request_evaluate(
-            base_command + " --style=stack '%s'" % (core_stack), context="repl"
-        )
+        core_styles = ["stack", "modified-memory", "full"]
+        top_frame = session.top_frame_from(stop_event)
+        for style in core_styles:
+            path = Path(self.getBuildArtifact(f"core.{style}.dmp"))
+            self.assertFalse(path.exists(), f"stale core file: {path}")
 
-        self.assertTrue(os.path.isfile(core_stack))
-        self.verify_core_file(core_stack, len(modules), thread_count)
+            save_core = "process save-core --plugin-name=minidump"
+            top_frame.evaluate(f"`{save_core} --style={style} {path}", 
context="repl")
+            self.assertTrue(path.is_file(), f"{style} core file is a file")
 
-        self.dap_server.request_evaluate(
-            base_command + " --style=modified-memory '%s'" % (core_dirty),
-            context="repl",
-        )
-        self.assertTrue(os.path.isfile(core_dirty))
-        self.verify_core_file(core_dirty, len(modules), thread_count)
+            with self.subTest(style=style):
+                self.verify_core(style, path, module_count, thread_count)
+
+        session.continue_to_exit(exitCode=3)
+
+    def verify_core(
+        self, style: str, core_path: Path, module_count: int, thread_count: int
+    ):
+        """Attach to a saved core and verify the reloaded process state
+        matches what was captured live: current frame, thread count,
+        module count."""
+        session = 
self.create_session(adapter=self.create_stdio_debug_adapter())
+        process_event = session.attach(AttachArgs(coreFile=str(core_path)))
 
-        self.dap_server.request_evaluate(
-            base_command + " --style=full '%s'" % (core_full), context="repl"
+        stop_event = session.verify_stopped_on_exception(after=process_event)
+        top_frame = session.top_frame_from(stop_event).frame
+        self.assertTrue(
+            top_frame.name.startswith("function"),
+            "expected to stop inside `function`",
         )
-        self.assertTrue(os.path.isfile(core_full))
-        self.verify_core_file(core_full, len(modules), thread_count)
-
-    def verify_core_file(self, core_path, expected_module_count, 
expected_thread_count):
-        # To verify, we'll launch with the mini dump
-        target = self.dbg.CreateTarget(None)
-        process = target.LoadCore(core_path)
-
-        # check if the core is in desired state
-        self.assertTrue(process, PROCESS_IS_VALID)
-        self.assertTrue(process.GetProcessInfo().IsValid())
-        self.assertNotEqual(target.GetTriple().find("linux"), -1)
-        self.assertTrue(target.GetNumModules(), expected_module_count)
-        self.assertEqual(process.GetNumThreads(), expected_thread_count)
+
+        expected_line = line_number("main.cpp", "// breakpoint 1")
+        self.assertEqual(top_frame.line, expected_line)
+
+        core_thread_count = len(session.get_threads())
+        core_module_count = len(session.get_modules())
+
+        self.assertEqual(core_thread_count, thread_count)
+        if style != "full":  # FIXME: There is a bug on linux
+            self.assertEqual(core_module_count, module_count)

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to