https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/207029
>From a38f3b96f47a7e674f89e5bec2862248a0f6b063 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 1 Jul 2026 17:35:20 +0100 Subject: [PATCH 1/2] [lldb-dap] Migrate DAP exceptions test. Migrate all the tests in the `lldb-dap/exception` folder. --- .../lldb-dap/exception/TestDAP_exception.py | 27 +++--- .../lldb-dap/exception/asan/TestDAP_asan.py | 28 +++--- .../exception/cpp/TestDAP_exception_cpp.py | 33 ++++--- .../exception/objc/TestDAP_exception_objc.py | 89 +++++++++++-------- .../lldb-dap/exception/ubsan/TestDAP_ubsan.py | 31 ++++--- 5 files changed, 123 insertions(+), 85 deletions(-) diff --git a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py index 15b8956995432..c1657853e8c94 100644 --- a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py +++ b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py @@ -2,24 +2,29 @@ Test exception behavior in DAP with signal. """ -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbdap_testcase +from lldbsuite.test.decorators import skipIfNoSignals +from lldbsuite.test.tools.lldb_dap import lldb_dap_testcase +from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs @skipIfNoSignals -class TestDAP_exception(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_exception(lldb_dap_testcase.DAPTestCaseBase): def test_stopped_description(self): """ Test that exception description is shown correctly in stopped event. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - self.do_continue() + session = self.build_and_create_session() + process_event = session.launch(LaunchArgs(program=program)) - self.verify_stop_exception_info("signal SIGABRT") - exceptionInfo = self.get_exceptionInfo() - self.assertEqual(exceptionInfo["breakMode"], "always") - self.assertEqual(exceptionInfo["description"], "signal SIGABRT") - self.assertEqual(exceptionInfo["exceptionId"], "signal") + stopped_event = session.verify_stopped_on_exception( + expected_description="signal SIGABRT", after=process_event + ) + thread_id = self.expect_not_none(stopped_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + + self.assertEqual(exception_info.breakMode, "always") + description = self.expect_not_none(exception_info.description) + self.assertEqual(description, "signal SIGABRT") + self.assertEqual(exception_info.exceptionId, "signal") diff --git a/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py b/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py index 0ce315d6e01b7..7a91c2d88f413 100644 --- a/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py +++ b/lldb/test/API/tools/lldb-dap/exception/asan/TestDAP_asan.py @@ -2,25 +2,27 @@ Test that we stop at runtime instrumentation locations (asan). """ -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbdap_testcase +from lldbsuite.test.decorators import skipUnlessAddressSanitizer +from lldbsuite.test.tools.lldb_dap import lldb_dap_testcase +from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs -class TestDAP_asan(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_asan(lldb_dap_testcase.DAPTestCaseBase): @skipUnlessAddressSanitizer def test_asan(self): """ Test that we stop at asan. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - self.do_continue() - - self.verify_stop_exception_info("Use of deallocated memory") - exceptionInfo = self.get_exceptionInfo() - self.assertEqual(exceptionInfo["breakMode"], "always") - self.assertRegex( - exceptionInfo["description"], r"fatal_error: heap-use-after-free" + session = self.build_and_create_session() + process_event = session.launch(LaunchArgs(program)) + stop_event = session.verify_stopped_on_exception( + after=process_event, expected_description="Use of deallocated memory" ) - self.assertEqual(exceptionInfo["exceptionId"], "runtime-instrumentation") + + thread_id = self.expect_not_none(stop_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + self.assertEqual(exception_info.breakMode, "always") + description = self.expect_not_none(exception_info.description) + self.assertRegex(description, r"fatal_error: heap-use-after-free") + self.assertEqual(exception_info.exceptionId, "runtime-instrumentation") diff --git a/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py b/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py index 9fcc1d4a0a8e7..bc801ca9d9912 100644 --- a/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py +++ b/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py @@ -2,13 +2,13 @@ Test exception behavior in DAP with c++ throw. """ -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbdap_testcase +from lldbsuite.test.decorators import skipIfWasm, skipIfWindows +from lldbsuite.test.tools.lldb_dap import lldb_dap_testcase +from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs -@skipIfWasm # wasm inferiors are built with -fno-exceptions -class TestDAP_exception_cpp(lldbdap_testcase.DAPTestCaseBase): +@skipIfWasm # wasm inferiors are built with -fno-exceptions. +class TestDAP_exception_cpp(lldb_dap_testcase.DAPTestCaseBase): @skipIfWindows def test_stopped_description(self): """ @@ -16,11 +16,18 @@ def test_stopped_description(self): event. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - self.dap_server.request_continue() - self.verify_stop_exception_info("signal SIGABRT") - exceptionInfo = self.get_exceptionInfo() - self.assertEqual(exceptionInfo["breakMode"], "always") - self.assertIn("signal SIGABRT", exceptionInfo["description"]) - self.assertEqual(exceptionInfo["exceptionId"], "signal") - self.assertIsNotNone(exceptionInfo["details"]) + session = self.build_and_create_session() + process_event = session.launch(LaunchArgs(program=program)) + + stopped_event = session.verify_stopped_on_exception( + expected_description="signal SIGABRT", after=process_event + ) + + thread_id = self.expect_not_none(stopped_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + + self.assertEqual(exception_info.breakMode, "always") + description = self.expect_not_none(exception_info.description) + self.assertIn("signal SIGABRT", description) + self.assertEqual(exception_info.exceptionId, "signal") + self.assertIsNotNone(exception_info.details) diff --git a/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py b/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py index 89f96f428ec23..438039a19dbc9 100644 --- a/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py +++ b/lldb/test/API/tools/lldb-dap/exception/objc/TestDAP_exception_objc.py @@ -2,28 +2,37 @@ Test exception behavior in DAP with obj-c throw. """ -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbdap_testcase +from lldbsuite.test.decorators import skipUnlessDarwin +from lldbsuite.test.tools.lldb_dap import lldb_dap_testcase +from lldbsuite.test.tools.lldb_dap.dap_types import ExceptionFilterOptions, LaunchArgs -class TestDAP_exception_objc(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_exception_objc(lldb_dap_testcase.DAPTestCaseBase): @skipUnlessDarwin def test_stopped_description(self): """ Test that exception description is shown correctly in stopped event. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - self.dap_server.request_continue() - self.verify_stop_exception_info("signal SIGABRT") - exception_info = self.get_exceptionInfo() - self.assertEqual(exception_info["breakMode"], "always") - self.assertIn("signal SIGABRT", exception_info["description"]) - self.assertEqual(exception_info["exceptionId"], "signal") - exception_details = exception_info["details"] - self.assertRegex(exception_details["message"], "SomeReason") - self.assertRegex(exception_details["stackTrace"], "main.m") + session = self.build_and_create_session() + process_event = session.launch(LaunchArgs(program)) + stop_event = session.verify_stopped_on_exception( + after=process_event, expected_description="signal SIGABRT" + ) + + thread_id = self.expect_not_none(stop_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + + self.assertEqual(exception_info.breakMode, "always") + exception_description = self.expect_not_none(exception_info.description) + self.assertIn("signal SIGABRT", exception_description) + self.assertEqual(exception_info.exceptionId, "signal") + + exception_details = self.expect_not_none(exception_info.details) + exception_message = self.expect_not_none(exception_details.message) + self.assertRegex(exception_message, "SomeReason") + stack_trace = self.expect_not_none(exception_details.stackTrace) + self.assertRegex(stack_trace, "main.m") @skipUnlessDarwin def test_break_on_throw_and_catch(self): @@ -31,20 +40,21 @@ def test_break_on_throw_and_catch(self): Test that breakpoints on exceptions work as expected. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + session = self.build_and_create_session() + with session.configure(LaunchArgs(program)) as ctx: + response = session.set_exception_breakpoints( + filters=["objc_catch", "objc_throw"], + filterOptions=[ + ExceptionFilterOptions( + filterId="objc_throw", + condition='[[((NSException *)$arg1) name] isEqual:@"ThrownException"]', + ) + ], + ) + self.assertTrue(response.success) - response = self.dap_server.request_setExceptionBreakpoints( - filter_options=[ - { - "filterId": "objc_throw", - "condition": '[[((NSException *)$arg1) name] isEqual:@"ThrownException"]', - }, - ] - ) - if response: - self.assertTrue(response["success"]) - - self.continue_to_exception_breakpoint( + session.verify_stopped_on_exception( + after=ctx.process_event, expected_description="hit Objective-C exception", expected_text="Objective-C Throw", ) @@ -55,13 +65,20 @@ def test_break_on_throw_and_catch(self): # SBTarget::BreakpointCreateForException(eLanguageObjectiveC, /*catch_bp=*/true, /*throw_bp=*/false); # self.continue_to_exception_breakpoint("Objective-C Catch") - self.do_continue() + stop_event = session.continue_to_exception_breakpoint( + expected_description="signal SIGABRT" + ) + + thread_id = self.expect_not_none(stop_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + + self.assertEqual(exception_info.breakMode, "always") + description = self.expect_not_none(exception_info.description) + self.assertIn("signal SIGABRT", description) + self.assertEqual(exception_info.exceptionId, "signal") - self.verify_stop_exception_info("signal SIGABRT") - exception_info = self.get_exceptionInfo() - self.assertEqual(exception_info["breakMode"], "always") - self.assertIn("signal SIGABRT", exception_info["description"]) - self.assertEqual(exception_info["exceptionId"], "signal") - exception_details = exception_info["details"] - self.assertRegex(exception_details["message"], "SomeReason") - self.assertRegex(exception_details["stackTrace"], "main.m") + exception_details = self.expect_not_none(exception_info.details) + message = self.expect_not_none(exception_details.message) + self.assertRegex(message, "SomeReason") + stack_trace = self.expect_not_none(exception_details.stackTrace) + self.assertRegex(stack_trace, "main.m") diff --git a/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py b/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py index 270c7f0a22a89..fd2d46c730972 100644 --- a/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py +++ b/lldb/test/API/tools/lldb-dap/exception/ubsan/TestDAP_ubsan.py @@ -2,28 +2,35 @@ Test that we stop at runtime instrumentation locations (ubsan). """ -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -import lldbdap_testcase +from lldbsuite.test.decorators import skipUnlessUndefinedBehaviorSanitizer +from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs +from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase -class TestDAP_ubsan(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_ubsan(DAPTestCaseBase): @skipUnlessUndefinedBehaviorSanitizer def test_ubsan(self): """ Test that we stop at ubsan. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) - self.do_continue() + session = self.build_and_create_session() + process_event = session.launch(LaunchArgs(program)) + stop_event = session.verify_stopped_on_exception( + after=process_event, expected_description=r"Out of bounds index" + ) - self.verify_stop_exception_info("Out of bounds index") - exceptionInfo = self.get_exceptionInfo() - self.assertEqual(exceptionInfo["breakMode"], "always") - self.assertRegex(exceptionInfo["description"], r"Out of bounds index") - self.assertEqual(exceptionInfo["exceptionId"], "runtime-instrumentation") + thread_id = self.expect_not_none(stop_event.body.threadId) + exception_info = session.get_exception_info(thread_id) + + self.assertEqual(exception_info.breakMode, "always") + description = self.expect_not_none(exception_info.description) + self.assertRegex(description, r"Out of bounds index") + self.assertEqual(exception_info.exceptionId, "runtime-instrumentation") # FIXME: Check on non macOS platform the stop information location heuristic # may be wrong. enable when we have updated Ubsan stopInfo heuristic. if self.platformIsDarwin(): - self.assertIn("main.c", exceptionInfo["details"]["stackTrace"]) + exception_details = self.expect_not_none(exception_info.details) + stack_trace = self.expect_not_none(exception_details.stackTrace) + self.assertIn("main.c", stack_trace) >From 85ac2a47085eb9811be15c4418f7f5f221019ef3 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Thu, 2 Jul 2026 14:52:05 +0100 Subject: [PATCH 2/2] Add test for cpp throw and catch --- .../test/tools/lldb_dap/session_helpers.py | 5 +- .../exception/cpp/TestDAP_exception_cpp.py | 52 +++++++++++++++++++ .../API/tools/lldb-dap/exception/cpp/main.cpp | 11 ++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/session_helpers.py b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/session_helpers.py index 1e992883d03eb..34eb33f03c450 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/session_helpers.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/session_helpers.py @@ -1119,7 +1119,10 @@ def continue_to_any_breakpoint(self, breakpoint_ids: list[int]): return event def continue_to_exception_breakpoint( - self, *, expected_description: str, expected_text: Optional[str] = None + self, + *, + expected_description: Optional[str] = None, + expected_text: Optional[str] = None, ): continue_response = self.do_continue() return self.verify_stopped_on_exception( diff --git a/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py b/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py index bc801ca9d9912..c42ba06a3fe6b 100644 --- a/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py +++ b/lldb/test/API/tools/lldb-dap/exception/cpp/TestDAP_exception_cpp.py @@ -3,6 +3,7 @@ """ from lldbsuite.test.decorators import skipIfWasm, skipIfWindows +from lldbsuite.test.lldbtest import line_number from lldbsuite.test.tools.lldb_dap import lldb_dap_testcase from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs @@ -31,3 +32,54 @@ def test_stopped_description(self): self.assertIn("signal SIGABRT", description) self.assertEqual(exception_info.exceptionId, "signal") self.assertIsNotNone(exception_info.details) + + @skipIfWindows # cpp exception breakpoint is not supported. + def test_break_on_throw_and_catch(self): + """Test thrown and caught cpp exception breakpoint works.""" + program = self.getBuildArtifact("a.out") + session = self.build_and_create_session() + with session.configure(LaunchArgs(program, args=["1"])) as ctx: + # Check we advertise the c++ throw and catch exception filters. + resp = ctx.init_response + bp_filters = self.expect_not_none(resp.body.exceptionBreakpointFilters) + + init_filters = [ft.filter for ft in bp_filters] + cpp_filters = ["cpp_throw", "cpp_catch"] + for filter in cpp_filters: + self.assertIn(filter, init_filters, "expect cpp_filters is advertised.") + + session.set_exception_breakpoints(filters=cpp_filters) + + stop_event = session.verify_stopped_on_exception(after=ctx.process_event) + thread_ctx = session.thread_context_from(stop_event) + + def verify_stack_trace_contains(function: str, line: int): + frames = [ctx.frame for ctx in thread_ctx.frames()] + for frame in frames: + if frame.name.startswith(function): + frame_line = self.expect_not_none(frame.line) + self.assertEqual(frame_line, line) + return + + msg = f"expected {function=} {line=} in stacktrace:" + for i, frame in enumerate(frames): + msg += f"\n [{i}] {frame.name}:{frame.line}" + self.fail(msg) + + source = self.getSourcePath("main.cpp") + [throw_filter, caught_filter] = cpp_filters + + # Check the thrown exception. + thrown_line = line_number(source, "// thrown_exception") + verify_stack_trace_contains("throw_some_string", thrown_line) + exc_info = session.get_exception_info(thread_ctx.thread_id) + self.assertEqual(throw_filter, exc_info.exceptionId) + + # Check the caught exception. + session.continue_to_exception_breakpoint() + caught_line = line_number(source, "// caught_exception") + verify_stack_trace_contains("main", caught_line) + exc_info = session.get_exception_info(thread_ctx.thread_id) + self.assertEqual(caught_filter, exc_info.exceptionId) + + session.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/exception/cpp/main.cpp b/lldb/test/API/tools/lldb-dap/exception/cpp/main.cpp index 39d89b95319a8..589906d1a08c0 100644 --- a/lldb/test/API/tools/lldb-dap/exception/cpp/main.cpp +++ b/lldb/test/API/tools/lldb-dap/exception/cpp/main.cpp @@ -1,6 +1,17 @@ #include <stdexcept> +void throw_some_string() { + throw "fake message"; // thrown_exception. +} + int main(int argc, char const *argv[]) { + try { + throw_some_string(); + } catch (const char *) { // caught_exception. + if (argc > 1) + return 0; + } + throw std::invalid_argument("throwing exception for testing"); return 0; } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
