https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/213234
>From 846371b17aa17d3f057d73a10143113821f0124c Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 29 Jul 2026 21:26:01 +0100 Subject: [PATCH] [lldb-dap] Migrate extended stackTrace and source test Migrated tests - TestDAP_extendedStackTrace.py - TestDAP_source.py - TestDAP_source_x86.py --- .../lldbsuite/test/tools/lldb_dap/types.py | 2 +- .../TestDAP_extendedStackTrace.py | 147 +++++++++-------- .../tools/lldb-dap/source/TestDAP_source.py | 148 +++++++----------- lldb/test/API/tools/lldb-dap/source/main.c | 4 +- .../stackTrace-x86/TestDAP_source_x86.py | 57 +++---- 5 files changed, 164 insertions(+), 194 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 19bd0d9dc26e5..d75a1ea8fb3b9 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb_dap/types.py @@ -653,7 +653,7 @@ class Source: checksums: Optional[List[Checksum]] = None def __post_init__(self): - if not self.name and not self.path and not self.sourceReference: + if not self.name and not self.path and self.sourceReference is None: raise ValueError( f"Source requires either name, path, or source_reference. {self}" ) diff --git a/lldb/test/API/tools/lldb-dap/extendedStackTrace/TestDAP_extendedStackTrace.py b/lldb/test/API/tools/lldb-dap/extendedStackTrace/TestDAP_extendedStackTrace.py index 33a3bde3a5a42..3ee3e203c2767 100644 --- a/lldb/test/API/tools/lldb-dap/extendedStackTrace/TestDAP_extendedStackTrace.py +++ b/lldb/test/API/tools/lldb-dap/extendedStackTrace/TestDAP_extendedStackTrace.py @@ -4,122 +4,121 @@ import os -import lldbdap_testcase -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test.lldbplatformutil import * +from lldbsuite.test.decorators import skipUnlessDarwin +from lldbsuite.test.lldbplatformutil import findBacktraceRecordingDylib +from lldbsuite.test.lldbtest import line_number +from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase +from lldbsuite.test.tools.lldb_dap.types import LaunchArgs, StackFrameFormat -class TestDAP_extendedStackTrace(lldbdap_testcase.DAPTestCaseBase): - def build_and_run(self, displayExtendedBacktrace=True): +class TestDAP_extendedStackTrace(DAPTestCaseBase): + def build_and_run_to_breakpoint(self, display_extended_backtrace: bool = True): backtrace_recording_lib = findBacktraceRecordingDylib() if not backtrace_recording_lib: self.skipTest( - "Skipped because libBacktraceRecording.dylib was present on the system." + "Skipped because libBacktraceRecording.dylib was not present on the system." ) - if not os.path.isfile("/usr/lib/system/introspection/libdispatch.dylib"): self.skipTest( "Skipped because introspection libdispatch dylib is not present." ) program = self.getBuildArtifact("a.out") + session = self.build_and_create_session() + source = self.getSourcePath("main.m") + bp_line = line_number(source, "breakpoint 1") - self.build_and_launch( - program, + launch_args = LaunchArgs( + program=program, env=[ "DYLD_LIBRARY_PATH=/usr/lib/system/introspection", - "DYLD_INSERT_LIBRARIES=" + backtrace_recording_lib, + f"DYLD_INSERT_LIBRARIES={backtrace_recording_lib}", ], - displayExtendedBacktrace=displayExtendedBacktrace, + displayExtendedBacktrace=display_extended_backtrace, ) - source = "main.m" - breakpoint = line_number(source, "breakpoint 1") - lines = [breakpoint] + with session.configure(launch_args) as cm: + [bp_id] = session.resolve_source_breakpoints(source, [bp_line]) - breakpoint_ids = self.set_source_breakpoints(source, lines) - self.assertEqual( - len(breakpoint_ids), len(lines), "expect correct number of breakpoints" - ) + stop_event = session.verify_stopped_on_breakpoint(bp_id, after=cm.process_event) + return session, stop_event @skipUnlessDarwin def test_stackTrace(self): - """ - Tests the 'stackTrace' packet on a thread with an extended backtrace. - """ - self.build_and_run() - events = self.continue_to_next_stop() - - stackFrames, totalFrames = self.get_stackFrames_and_totalFramesCount( - threadId=events[0]["body"]["threadId"] - ) - self.assertGreaterEqual(len(stackFrames), 3, "expect >= 3 frames") - self.assertEqual(len(stackFrames), totalFrames) - self.assertEqual(stackFrames[0]["name"], "one") - self.assertEqual(stackFrames[1]["name"], "two") - self.assertEqual(stackFrames[2]["name"], "three") + """Tests the 'stackTrace' packet on a thread with an extended backtrace.""" + session, stop_event = self.build_and_run_to_breakpoint() + thread_id = self.expect_not_none(stop_event.body.threadId) + + response = session.stack_trace(thread_id) + stack_frames = response.body.stackFrames + total_frames = response.body.totalFrames - stackLabels = [ + self.assertGreaterEqual(len(stack_frames), 3, "expect >= 3 frames") + self.assertEqual(len(stack_frames), total_frames) + self.assertEqual(stack_frames[0].name, "one") + self.assertEqual(stack_frames[1].name, "two") + self.assertEqual(stack_frames[2].name, "three") + + stack_labels = [ (i, frame) - for i, frame in enumerate(stackFrames) - if frame.get("presentationHint", "") == "label" + for i, frame in enumerate(stack_frames) + if frame.presentationHint == "label" ] - self.assertEqual(len(stackLabels), 2, "expected two label stack frames") + self.assertEqual(len(stack_labels), 2, "expected two label stack frames") self.assertRegex( - stackLabels[0][1]["name"], + stack_labels[0][1].name, r"Enqueued from com.apple.root.default-qos \(Thread \d\)", ) self.assertRegex( - stackLabels[1][1]["name"], + stack_labels[1][1].name, r"Enqueued from com.apple.main-thread \(Thread \d\)", ) - for i, frame in stackLabels: + for i, frame in stack_labels: # Ensure requesting startFrame+levels across thread backtraces works as expected. - stackFrames, totalFrames = self.get_stackFrames_and_totalFramesCount( - threadId=events[0]["body"]["threadId"], startFrame=i - 1, levels=3 - ) - self.assertEqual(len(stackFrames), 3, "expected 3 frames with levels=3") + response = session.stack_trace(thread_id, startFrame=i - 1, levels=3) + stack_frames = response.body.stackFrames + total_frames = self.expect_not_none(response.body.totalFrames) + self.assertEqual(len(stack_frames), 3, "expected 3 frames with levels=3") self.assertGreaterEqual( - totalFrames, i + 3, "total frames should include a pagination offset" + total_frames, i + 3, "total frames should include a pagination offset" ) - self.assertEqual(stackFrames[1], frame) + self.assertEqual(stack_frames[1], frame) - # Ensure requesting startFrame+levels at the beginning of a thread backtraces works as expected. - stackFrames, totalFrames = self.get_stackFrames_and_totalFramesCount( - threadId=events[0]["body"]["threadId"], startFrame=i, levels=3 - ) - self.assertEqual(len(stackFrames), 3, "expected 3 frames with levels=3") + # Ensure requesting startFrame+levels at the beginning of a thread backtrace works as expected. + response = session.stack_trace(thread_id, startFrame=i, levels=3) + stack_frames = response.body.stackFrames + total_frames = self.expect_not_none(response.body.totalFrames) + self.assertEqual(len(stack_frames), 3, "expected 3 frames with levels=3") self.assertGreaterEqual( - totalFrames, i + 3, "total frames should include a pagination offset" + total_frames, i + 3, "total frames should include a pagination offset" ) - self.assertEqual(stackFrames[0], frame) - - # Ensure requests with startFrame+levels that end precisely on the last frame includes the totalFrames pagination offset. - stackFrames, totalFrames = self.get_stackFrames_and_totalFramesCount( - threadId=events[0]["body"]["threadId"], startFrame=i - 1, levels=1 - ) - self.assertEqual(len(stackFrames), 1, "expected 1 frames with levels=1") + self.assertEqual(stack_frames[0], frame) + + # Ensure requests with startFrame+levels that end precisely on the + # last frame include the totalFrames pagination offset. + response = session.stack_trace(thread_id, startFrame=i - 1, levels=1) + stack_frames = response.body.stackFrames + total_frames = self.expect_not_none(response.body.totalFrames) + self.assertEqual(len(stack_frames), 1, "expected 1 frame with levels=1") self.assertGreaterEqual( - totalFrames, i, "total frames should include a pagination offset" + total_frames, i, "total frames should include a pagination offset" ) @skipUnlessDarwin def test_stackTraceWithFormat(self): - """ - Tests the 'stackTrace' packet on a thread with an extended backtrace using stack trace formats. - """ - self.build_and_run(displayExtendedBacktrace=False) - events = self.continue_to_next_stop() - - stackFrames, _ = self.get_stackFrames_and_totalFramesCount( - threadId=events[0]["body"]["threadId"], format={"includeAll": True} + """Tests the 'stackTrace' packet using stack trace formats.""" + session, stop_event = self.build_and_run_to_breakpoint( + display_extended_backtrace=False ) + thread_id = self.expect_not_none(stop_event.body.threadId) - stackLabels = [ - (i, frame) - for i, frame in enumerate(stackFrames) - if frame.get("presentationHint", "") == "label" - ] + response = session.stack_trace( + thread_id, format=StackFrameFormat(includeAll=True) + ) - self.assertEqual(len(stackLabels), 2, "expected two label stack frames") + stack_labels = [ + frame + for frame in response.body.stackFrames + if frame.presentationHint == "label" + ] + self.assertEqual(len(stack_labels), 2, "expected two label stack frames") diff --git a/lldb/test/API/tools/lldb-dap/source/TestDAP_source.py b/lldb/test/API/tools/lldb-dap/source/TestDAP_source.py index 01c28fe7a568e..c3180b5ece8a4 100644 --- a/lldb/test/API/tools/lldb-dap/source/TestDAP_source.py +++ b/lldb/test/API/tools/lldb-dap/source/TestDAP_source.py @@ -2,111 +2,79 @@ Test lldb-dap source request """ +from lldbsuite.test.decorators import skipIfWindows +from lldbsuite.test.lldbtest import line_number +from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase +from lldbsuite.test.tools.lldb_dap.types import LaunchArgs, Source, SourceArgs -import os -import lldbdap_testcase -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * - - -class TestDAP_source(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_source(DAPTestCaseBase): @skipIfWindows def test_source(self): - """ - Tests the 'source' packet. - """ + """Tests the Source Request.""" program = self.getBuildArtifact("a.out") - self.build_and_launch(program) source = self.getSourcePath("main.c") - breakpoint_line = line_number(source, "breakpoint") + session = self.build_and_create_session() + with session.configure(LaunchArgs(program)) as ctx: + breakpoint_line = line_number(source, "breakpoint") + [bp_id] = session.resolve_source_breakpoints(source, [breakpoint_line]) - lines = [breakpoint_line] - breakpoint_ids = self.set_source_breakpoints(source, lines) - self.assertEqual( - len(breakpoint_ids), len(lines), "expect correct number of breakpoints" + stop_event = session.verify_stopped_on_breakpoint( + bp_id, after=ctx.process_event ) - self.continue_to_breakpoints(breakpoint_ids) - - response = self.dap_server.request_source(sourceReference=0) - self.assertFalse(response["success"], "verify invalid sourceReference fails") - + src_args = SourceArgs(source=Source(sourceReference=0), sourceReference=0) + session.send_request(src_args).error() # Check only source reference in the arguments field. - response = self.dap_server.request_custom("source", {"sourceReference": 0}) - self.assertFalse(response["success"], "expected failed response") - error_format = self.get_dict_value(response, ["body", "error", "format"]) - self.assertIn("unknown source reference", error_format) + resp = session.send_request(SourceArgs(sourceReference=0)).error( + "verify invalid sourceReference fails" + ) + resp_body = self.expect_not_none(resp.body) + error_msg = self.expect_not_none(resp_body.error) + self.assertIn("unknown source reference", error_msg.format) - (stackFrames, totalFrames) = self.get_stackFrames_and_totalFramesCount() - frameCount = len(stackFrames) - self.assertGreaterEqual(frameCount, 3, "verify we got up to main at least") + # Verify the top three frames handler, add and main. + thread_id = session.thread_context_from(stop_event).thread_id + response = session.stack_trace(thread_id) + frames = response.body.stackFrames + self.assertGreaterEqual(len(frames), 3, "verify we got up to main at least.") self.assertEqual( - totalFrames, - frameCount, + len(frames), + response.body.totalFrames, "verify total frames returns a speculative page size", ) - wantFrames = [ - { - "name": "handler", - "line": 8, - "source": { - "name": "main.c", - "path": source, - "containsSourceReference": False, - }, - }, - { - "name": "add", - "source": { - "name": "add", - "path": program + "`add", - "containsSourceReference": True, - }, - }, - { - "name": "main", - "line": 12, - "source": { - "name": "main.c", - "path": source, - "containsSourceReference": False, - }, - }, - ] - for idx, want in enumerate(wantFrames): - got = stackFrames[idx] - name = self.get_dict_value(got, ["name"]) - self.assertEqual(name, want["name"]) - if "line" in want: - line = self.get_dict_value(got, ["line"]) - self.assertEqual(line, want["line"]) + handler_frame, add_frame, main_frame, *_ = frames - wantSource = want["source"] - source_name = self.get_dict_value(got, ["source", "name"]) - self.assertEqual(source_name, wantSource["name"]) + # Verify frame 0 handler. + self.assertEqual(handler_frame.name, "handler") + self.assertEqual(handler_frame.line, line_number(source, "first_frame")) + handler_source = self.expect_not_none(handler_frame.source) + self.assertEqual(handler_source.name, "main.c") + self.assertEqual(handler_source.path, source) + self.assertIsNone(handler_source.sourceReference) + + # Verify frame 1 add. + self.assertEqual(add_frame.name, "add") + add_source = self.expect_not_none(add_frame.source) + self.assertEqual(add_source.name, "add") + self.assertEqual(add_source.path, program + "`add") + + source_ref = self.expect_not_none(add_source.sourceReference) + disasm = session.send_request(SourceArgs(source_ref)).result() + self.assertGreater( + len(disasm.body.content), 0, "verify content returned disassembly" + ) + self.assertEqual( + disasm.body.mimeType, "text/x-lldb.disassembly", "verify mime type returned" + ) - source_path = self.get_dict_value(got, ["source", "path"]) - self.assertEqual(source_path, wantSource["path"]) + # Verify frame 2 main. + self.assertEqual(main_frame.name, "main") + self.assertEqual(main_frame.line, line_number(source, "third_frame")) + main_source = self.expect_not_none(main_frame.source) + self.assertEqual(main_source.name, "main.c") + self.assertEqual(main_source.path, source) + self.assertIsNone(main_source.sourceReference) - if wantSource["containsSourceReference"]: - sourceReference = self.get_dict_value( - got, ["source", "sourceReference"] - ) - response = self.dap_server.request_source( - sourceReference=sourceReference - ) - self.assertTrue(response["success"]) - self.assertGreater( - len(self.get_dict_value(response, ["body", "content"])), - 0, - "verify content returned disassembly", - ) - self.assertEqual( - self.get_dict_value(response, ["body", "mimeType"]), - "text/x-lldb.disassembly", - "verify mime type returned", - ) - else: - self.assertNotIn("sourceReference", got["source"]) + session.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/source/main.c b/lldb/test/API/tools/lldb-dap/source/main.c index 39420dba7874f..60fb0931ba105 100644 --- a/lldb/test/API/tools/lldb-dap/source/main.c +++ b/lldb/test/API/tools/lldb-dap/source/main.c @@ -5,10 +5,10 @@ __attribute__((nodebug)) static void add(int i, int j, void handler(int)) { } static void handler(int result) { - printf("result %d\n", result); // breakpoint + printf("result %d\n", result); // first_frame, breakpoint } int main(int argc, char const *argv[]) { - add(2, 3, handler); + add(2, 3, handler); // third_frame return 0; } diff --git a/lldb/test/API/tools/lldb-dap/stackTrace-x86/TestDAP_source_x86.py b/lldb/test/API/tools/lldb-dap/stackTrace-x86/TestDAP_source_x86.py index 6239e1af53ecd..f6c6e5c9e73d5 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace-x86/TestDAP_source_x86.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace-x86/TestDAP_source_x86.py @@ -2,64 +2,67 @@ Test lldb-dap stack trace containing x86 assembly """ -import lldbdap_testcase from lldbsuite.test import lldbplatformutil from lldbsuite.test.decorators import skipUnlessArch, skipUnlessPlatform from lldbsuite.test.lldbtest import line_number +from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase +from lldbsuite.test.tools.lldb_dap.types import LaunchArgs -class TestDAP_stacktrace_x86(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_stacktrace_x86(DAPTestCaseBase): @skipUnlessArch("x86_64") @skipUnlessPlatform(["linux"] + lldbplatformutil.getDarwinOSTriples()) def test_stacktrace_x86(self): - """ - Tests that lldb-dap steps through correctly and the source lines are correct in x86 assembly. - """ + """Tests that lldb-dap steps through x86 assembly correctly and reports the right source lines.""" program = self.getBuildArtifact("a.out") - self.build_and_launch( + session = self.build_and_create_session() + launch_args = LaunchArgs( program, initCommands=[ "settings set target.process.thread.step-in-avoid-nodebug false" ], ) + with session.configure(launch_args) as ctx: + source = "main.c" + [breakpoint_id] = session.resolve_source_breakpoints( + source, [line_number(source, "// Break here")] + ) - source = "main.c" - breakpoint_ids = self.set_source_breakpoints( - source, - [line_number(source, "// Break here")], + stop_event = session.verify_stopped_on_breakpoint( + breakpoint_id, after=ctx.process_event ) - self.continue_to_breakpoints(breakpoint_ids) - self.stepIn() + thread_ctx = session.thread_context_from(stop_event) + thread_ctx.step_in() - frame = self.get_stackFrames()[0] + frame = thread_ctx.top_frame().frame self.assertEqual( - frame["name"], + frame.name, "no_branch_func", - "verify we are in the no_branch_func function", + "expected to be in the no_branch_func function", ) + self.assertEqual(frame.line, 1, "expected to be at the start of the function") - self.assertEqual(frame["line"], 1, "verify we are at the start of the function") minimum_assembly_lines = ( line_number(source, "Assembly end") - line_number(source, "Assembly start") + 1 ) - self.assertLessEqual( - 10, + self.assertGreaterEqual( minimum_assembly_lines, - "verify we have a reasonable number of assembly lines", + 10, + "expected a reasonable number of assembly lines", ) - for i in range(2, minimum_assembly_lines): - self.stepIn() - frame = self.get_stackFrames()[0] + for expected_line in range(2, minimum_assembly_lines): + thread_ctx.step_in() + top_frame = thread_ctx.top_frame().frame self.assertEqual( - frame["name"], + top_frame.name, "no_branch_func", - "verify we are still in the no_branch_func function", + "expected to still be in the no_branch_func function", ) self.assertEqual( - frame["line"], - i, - f"step in should advance a single line in the function to {i}", + top_frame.line, + expected_line, + f"step-in should advance a single line in the function to {expected_line}", ) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
