https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/138981
This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 >From 8c7a01465891c83f7cc99ee95b0852c56e0f150f Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jo...@devlieghere.com> Date: Tue, 6 May 2025 15:58:44 -0700 Subject: [PATCH 1/2] [lldb-dap] Change the launch sequence (#138219) This PR changes how we treat the launch sequence in lldb-dap. - Send the initialized event after we finish handling the initialize request, rather than after we finish attaching or launching. - Delay handling the launch and attach request until we have handled the configurationDone request. The latter is now largely a NO-OP and only exists to signal lldb-dap that it can handle the launch and attach requests. - Delay handling the initial threads requests until we have handled the launch or attach request. - Make all attaching and launching synchronous, including when we have attach or launch commands. This removes the need to synchronize between the request and event thread. Background: https://discourse.llvm.org/t/reliability-of-the-lldb-dap-tests/86125 --- .../test/tools/lldb-dap/dap_server.py | 65 +++++----- .../test/tools/lldb-dap/lldbdap_testcase.py | 7 ++ .../tools/lldb-dap/attach/TestDAP_attach.py | 2 + .../attach/TestDAP_attachByPortNum.py | 8 +- .../TestDAP_breakpointEvents.py | 61 +++------- .../completions/TestDAP_completions.py | 6 +- .../tools/lldb-dap/console/TestDAP_console.py | 2 +- .../lldb-dap/disconnect/TestDAP_disconnect.py | 6 +- .../lldb-dap/evaluate/TestDAP_evaluate.py | 5 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 4 +- .../lldb-dap/progress/TestDAP_Progress.py | 2 +- .../repl-mode/TestDAP_repl_mode_detection.py | 2 +- .../tools/lldb-dap/restart/TestDAP_restart.py | 1 - .../restart/TestDAP_restart_runInTerminal.py | 1 - .../lldb-dap/stop-hooks/TestDAP_stop_hooks.py | 2 +- lldb/tools/lldb-dap/DAP.cpp | 39 ++++-- lldb/tools/lldb-dap/DAP.h | 8 +- lldb/tools/lldb-dap/EventHelper.cpp | 2 +- .../lldb-dap/Handler/AttachRequestHandler.cpp | 115 ++++++++++-------- .../ConfigurationDoneRequestHandler.cpp | 14 +-- .../Handler/InitializeRequestHandler.cpp | 44 +++---- .../lldb-dap/Handler/LaunchRequestHandler.cpp | 7 +- .../tools/lldb-dap/Handler/RequestHandler.cpp | 67 ++++++---- lldb/tools/lldb-dap/Handler/RequestHandler.h | 1 + 24 files changed, 252 insertions(+), 219 deletions(-) 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 6d9ab770684f1..e10342b72f4f0 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 @@ -132,7 +132,6 @@ def __init__(self, recv, send, init_commands, log_file=None): self.exit_status = None self.initialize_body = None self.thread_stop_reasons = {} - self.breakpoint_events = [] self.progress_events = [] self.reverse_requests = [] self.module_events = [] @@ -244,13 +243,6 @@ def handle_recv_packet(self, packet): self._process_stopped() tid = body["threadId"] self.thread_stop_reasons[tid] = body - elif event == "breakpoint": - # Breakpoint events come in when a breakpoint has locations - # added or removed. Keep track of them so we can look for them - # in tests. - self.breakpoint_events.append(packet) - # no need to add 'breakpoint' event packets to our packets list - return keepGoing elif event.startswith("progress"): # Progress events come in as 'progressStart', 'progressUpdate', # and 'progressEnd' events. Keep these around in case test @@ -412,6 +404,15 @@ def wait_for_stopped(self, timeout=None): self.threads = [] return stopped_events + def wait_for_breakpoint_events(self, timeout=None): + breakpoint_events = [] + while True: + event = self.wait_for_event("breakpoint", timeout=timeout) + if not event: + break + breakpoint_events.append(event) + return breakpoint_events + def wait_for_exited(self): event_dict = self.wait_for_event("exited") if event_dict is None: @@ -591,6 +592,7 @@ def request_attach( attachCommands=None, terminateCommands=None, coreFile=None, + stopOnAttach=True, postRunCommands=None, sourceMap=None, gdbRemotePort=None, @@ -620,6 +622,8 @@ def request_attach( args_dict["attachCommands"] = attachCommands if coreFile: args_dict["coreFile"] = coreFile + if stopOnAttach: + args_dict["stopOnEntry"] = stopOnAttach if postRunCommands: args_dict["postRunCommands"] = postRunCommands if sourceMap: @@ -632,7 +636,7 @@ def request_attach( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_breakpointLocations( @@ -666,10 +670,6 @@ def request_configurationDone(self): response = self.send_recv(command_dict) if response: self.configuration_done_sent = True - # Client requests the baseline of currently existing threads after - # a successful launch or attach. - # Kick off the threads request that follows - self.request_threads() return response def _process_stopped(self): @@ -887,7 +887,7 @@ def request_launch( response = self.send_recv(command_dict) if response["success"]: - self.wait_for_events(["process", "initialized"]) + self.wait_for_event("process") return response def request_next(self, threadId, granularity="statement"): @@ -1325,6 +1325,26 @@ def attach_options_specified(options): def run_vscode(dbg, args, options): dbg.request_initialize(options.sourceInitFile) + + if options.sourceBreakpoints: + source_to_lines = {} + for file_line in options.sourceBreakpoints: + (path, line) = file_line.split(":") + if len(path) == 0 or len(line) == 0: + print('error: invalid source with line "%s"' % (file_line)) + + else: + if path in source_to_lines: + source_to_lines[path].append(int(line)) + else: + source_to_lines[path] = [int(line)] + for source in source_to_lines: + dbg.request_setBreakpoints(source, source_to_lines[source]) + if options.funcBreakpoints: + dbg.request_setFunctionBreakpoints(options.funcBreakpoints) + + dbg.request_configurationDone() + if attach_options_specified(options): response = dbg.request_attach( program=options.program, @@ -1353,23 +1373,6 @@ def run_vscode(dbg, args, options): ) if response["success"]: - if options.sourceBreakpoints: - source_to_lines = {} - for file_line in options.sourceBreakpoints: - (path, line) = file_line.split(":") - if len(path) == 0 or len(line) == 0: - print('error: invalid source with line "%s"' % (file_line)) - - else: - if path in source_to_lines: - source_to_lines[path].append(int(line)) - else: - source_to_lines[path] = [int(line)] - for source in source_to_lines: - dbg.request_setBreakpoints(source, source_to_lines[source]) - if options.funcBreakpoints: - dbg.request_setFunctionBreakpoints(options.funcBreakpoints) - dbg.request_configurationDone() dbg.wait_for_stopped() else: if "message" in response: 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 2c14bb35162b5..958c7268c0c72 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 @@ -340,6 +340,7 @@ def attach( exitCommands=None, attachCommands=None, coreFile=None, + stopOnAttach=True, disconnectAutomatically=True, terminateCommands=None, postRunCommands=None, @@ -364,6 +365,8 @@ def cleanup(): self.addTearDownHook(cleanup) # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, pid=pid, @@ -376,6 +379,7 @@ def cleanup(): attachCommands=attachCommands, terminateCommands=terminateCommands, coreFile=coreFile, + stopOnAttach=stopOnAttach, postRunCommands=postRunCommands, sourceMap=sourceMap, gdbRemotePort=gdbRemotePort, @@ -434,6 +438,9 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) + self.dap_server.wait_for_event("initialized") + self.dap_server.request_configurationDone() + response = self.dap_server.request_launch( program, args=args, diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index f48d5a7db3c50..741c011a3d692 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -27,6 +27,8 @@ def spawn_and_wait(program, delay): @skip class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py index 7f93b9f2a3a22..7250e67ebcd8c 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attachByPortNum.py @@ -18,17 +18,17 @@ import socket -@skip class TestDAP_attachByPortNum(lldbdap_testcase.DAPTestCaseBase): default_timeout = 20 def set_and_hit_breakpoint(self, continueToExit=True): + self.dap_server.wait_for_stopped() + source = "main.c" - main_source_path = os.path.join(os.getcwd(), source) - breakpoint1_line = line_number(main_source_path, "// breakpoint 1") + 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(main_source_path, lines) + breakpoint_ids = self.set_source_breakpoints(source, lines) self.assertEqual( len(breakpoint_ids), len(lines), "expect correct number of breakpoints" ) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py index e5590e1b332a0..8581f10cef22a 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py @@ -81,52 +81,27 @@ def test_breakpoint_events(self): breakpoint["verified"], "expect foo breakpoint to not be verified" ) - # Get the stop at the entry point - self.continue_to_next_stop() + # Make sure we're stopped. + self.dap_server.wait_for_stopped() - # We are now stopped at the entry point to the program. Shared - # libraries are not loaded yet (at least on macOS they aren't) and only - # the breakpoint in the main executable should be resolved. - self.assertEqual(len(self.dap_server.breakpoint_events), 1) - event = self.dap_server.breakpoint_events[0] - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertEqual(breakpoint["id"], main_bp_id) - self.assertTrue(breakpoint["verified"], "main breakpoint should be resolved") - - # Clear the list of breakpoint events so we don't see this one again. - self.dap_server.breakpoint_events.clear() + # Flush the breakpoint events. + self.dap_server.wait_for_breakpoint_events(timeout=5) # Continue to the breakpoint self.continue_to_breakpoints(dap_breakpoint_ids) - # When the process launches, we first expect to see both the main and - # foo breakpoint as unresolved. - for event in self.dap_server.breakpoint_events[:2]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertFalse(breakpoint["verified"], "breakpoint should be unresolved") + verified_breakpoint_ids = [] + unverified_breakpoint_ids = [] + for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5): + breakpoint = breakpoint_event["body"]["breakpoint"] + id = breakpoint["id"] + if breakpoint["verified"]: + verified_breakpoint_ids.append(id) + else: + unverified_breakpoint_ids.append(id) - # Then, once the dynamic loader has given us a load address, they - # should show up as resolved again. - for event in self.dap_server.breakpoint_events[3:]: - body = event["body"] - self.assertEqual( - body["reason"], "changed", "breakpoint event should say changed" - ) - breakpoint = body["breakpoint"] - self.assertIn(str(breakpoint["id"]), dap_breakpoint_ids) - self.assertTrue(breakpoint["verified"], "breakpoint should be resolved") - self.assertNotIn( - "source", - breakpoint, - "breakpoint event should not return a source object", - ) - self.assertIn("line", breakpoint, "breakpoint event should have line") + self.assertIn(main_bp_id, unverified_breakpoint_ids) + self.assertIn(foo_bp_id, unverified_breakpoint_ids) + + self.assertIn(main_bp_id, verified_breakpoint_ids) + self.assertIn(foo_bp_id, verified_breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 210e591bff426..455ac84168baf 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -44,9 +44,9 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self): + def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=stopOnEntry) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") @@ -235,7 +235,7 @@ def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ - self.setup_debugee() + self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index b07c4f871d73b..65a1bc04c7cd7 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -167,7 +167,7 @@ def test_exit_status_message_ok(self): def test_diagnositcs(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) core = self.getBuildArtifact("minidump.core") self.yaml2obj("minidump.yaml", core) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 0cb792d662a80..09e3f62f0eead 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -31,7 +31,7 @@ def test_launch(self): created. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program, disconnectAutomatically=False) + self.build_and_launch(program, stopOnEntry=True, disconnectAutomatically=False) # We set a breakpoint right before the side effect file is created self.set_source_breakpoints( @@ -39,7 +39,11 @@ def test_launch(self): ) self.continue_to_next_stop() + # verify we haven't produced the side effect file yet + self.assertFalse(os.path.exists(program + ".side_effect")) + self.dap_server.request_disconnect() + # verify we didn't produce the side effect file time.sleep(1) self.assertFalse(os.path.exists(program + ".side_effect")) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index d97fda730c46a..19b682dfcd22d 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -10,6 +10,7 @@ from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * + # DAP tests are flakey, see https://github.com/llvm/llvm-project/issues/137660. @skip class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @@ -42,7 +43,9 @@ def run_test_evaluate_expressions( self.context = context program = self.getBuildArtifact("a.out") self.build_and_launch( - program, enableAutoVariableSummaries=enableAutoVariableSummaries + program, + enableAutoVariableSummaries=enableAutoVariableSummaries, + stopOnEntry=True, ) source = "main.cpp" self.set_source_breakpoints( diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 931456299e03e..604a41678500c 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -88,8 +88,8 @@ def test_stopOnEntry(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch(program, stopOnEntry=True) - self.set_function_breakpoints(["main"]) - stopped_events = self.continue_to_next_stop() + + stopped_events = self.dap_server.wait_for_stopped() for stopped_event in stopped_events: if "body" in stopped_event: body = stopped_event["body"] diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py index fee63655de0da..0f94b50c31fba 100755 --- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py +++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py @@ -50,7 +50,7 @@ def verify_progress_events( @skipIfWindows def test(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py") self.dap_server.request_evaluate( f"`command script import {progress_emitter}", context="repl" diff --git a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py index c6f59949d668e..81edcdf4bd0f9 100644 --- a/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py +++ b/lldb/test/API/tools/lldb-dap/repl-mode/TestDAP_repl_mode_detection.py @@ -20,7 +20,7 @@ def assertEvaluate(self, expression, regex): def test_completions(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py index 36fa0bd40183f..5f95c7bfb1556 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py @@ -22,7 +22,6 @@ def test_basic_functionality(self): [bp_A, bp_B] = self.set_source_breakpoints("main.c", [line_A, line_B]) # Verify we hit A, then B. - self.dap_server.request_configurationDone() self.verify_breakpoint_hit([bp_A]) self.dap_server.request_continue() self.verify_breakpoint_hit([bp_B]) diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py index a94c9860c1508..eed769a5a0cc6 100644 --- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py +++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_runInTerminal.py @@ -74,7 +74,6 @@ def test_stopOnEntry(self): program = self.getBuildArtifact("a.out") self.build_and_launch(program, runInTerminal=True, stopOnEntry=True) [bp_main] = self.set_function_breakpoints(["main"]) - self.dap_server.request_configurationDone() # When using stopOnEntry, configurationDone doesn't result in a running # process, we should immediately get a stopped event instead. diff --git a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py index 70c11a63a79f7..7e28a5af4331c 100644 --- a/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py +++ b/lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py @@ -19,7 +19,7 @@ def test_stop_hooks_before_run(self): self.build_and_launch(program, stopOnEntry=True, preRunCommands=preRunCommands) # The first stop is on entry. - self.continue_to_next_stop() + self.dap_server.wait_for_stopped() breakpoint_ids = self.set_function_breakpoints(["main"]) # This request hangs if the race happens, because, in that case, the diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4b631484c9fab..62c60cc3a9b3b 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -84,8 +84,8 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode, : log(log), transport(transport), broadcaster("lldb-dap"), exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), - restarting_process_id(LLDB_INVALID_PROCESS_ID), - configuration_done_sent(false), waiting_for_run_in_terminal(false), + restarting_process_id(LLDB_INVALID_PROCESS_ID), configuration_done(false), + waiting_for_run_in_terminal(false), progress_event_reporter( [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }), reverse_request_seq(0), repl_mode(default_repl_mode) { @@ -893,10 +893,19 @@ llvm::Error DAP::Loop() { return errWrapper; } + // The launch sequence is special and we need to carefully handle + // packets in the right order. Until we've handled configurationDone, + bool add_to_pending_queue = false; + if (const protocol::Request *req = - std::get_if<protocol::Request>(&*next); - req && req->command == "disconnect") { - disconnecting = true; + std::get_if<protocol::Request>(&*next)) { + llvm::StringRef command = req->command; + if (command == "disconnect") + disconnecting = true; + if (!configuration_done) + add_to_pending_queue = + command != "initialize" && command != "configurationDone" && + command != "disconnect" && !command.ends_with("Breakpoints"); } const std::optional<CancelArguments> cancel_args = @@ -924,7 +933,8 @@ llvm::Error DAP::Loop() { { std::lock_guard<std::mutex> guard(m_queue_mutex); - m_queue.push_back(std::move(*next)); + auto &queue = add_to_pending_queue ? m_pending_queue : m_queue; + queue.push_back(std::move(*next)); } m_queue_cv.notify_one(); } @@ -938,16 +948,19 @@ llvm::Error DAP::Loop() { StopEventHandlers(); }); - while (!disconnecting || !m_queue.empty()) { + while (true) { std::unique_lock<std::mutex> lock(m_queue_mutex); m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); }); - if (m_queue.empty()) + if (disconnecting && m_queue.empty()) break; Message next = m_queue.front(); m_queue.pop_front(); + // Unlock while we're processing the event. + lock.unlock(); + if (!HandleObject(next)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "unhandled packet"); @@ -1219,6 +1232,16 @@ void DAP::SetConfiguration(const protocol::Configuration &config, SetThreadFormat(*configuration.customThreadFormat); } +void DAP::SetConfigurationDone() { + { + std::lock_guard<std::mutex> guard(m_queue_mutex); + std::copy(m_pending_queue.begin(), m_pending_queue.end(), + std::front_inserter(m_queue)); + configuration_done = true; + } + m_queue_cv.notify_all(); +} + void DAP::SetFrameFormat(llvm::StringRef format) { if (format.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 88eedb0860cf1..b581ae759b1bc 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -188,7 +188,7 @@ struct DAP { // shutting down the entire adapter. When we're restarting, we keep the id of // the old process here so we can detect this case and keep running. lldb::pid_t restarting_process_id; - bool configuration_done_sent; + bool configuration_done; llvm::StringMap<std::unique_ptr<BaseRequestHandler>> request_handlers; bool waiting_for_run_in_terminal; ProgressEventReporter progress_event_reporter; @@ -251,6 +251,8 @@ struct DAP { /// Configures the debug adapter for launching/attaching. void SetConfiguration(const protocol::Configuration &confing, bool is_attach); + void SetConfigurationDone(); + /// Configure source maps based on the current `DAPConfiguration`. void ConfigureSourceMaps(); @@ -417,8 +419,10 @@ struct DAP { lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); } private: - std::mutex m_queue_mutex; + /// Queue for all incoming messages. std::deque<protocol::Message> m_queue; + std::deque<protocol::Message> m_pending_queue; + std::mutex m_queue_mutex; std::condition_variable m_queue_cv; std::mutex m_cancelled_requests_mutex; diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 2c659f39f4b66..ed2d8700c26b0 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -222,7 +222,7 @@ void SendContinuedEvent(DAP &dap) { // If the focus thread is not set then we haven't reported any thread status // to the client, so nothing to report. - if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) { + if (!dap.configuration_done || dap.focus_tid == LLDB_INVALID_THREAD_ID) { return; } diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp index 7a0f091128e4a..5dc9c3f9772e3 100644 --- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp @@ -133,61 +133,70 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { dap.SendOutput(OutputType::Console, llvm::StringRef(attach_msg, attach_msg_len)); } - if (attachCommands.empty()) { - // No "attachCommands", just attach normally. - // Disable async events so the attach will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - - if (core_file.empty()) { - if ((pid != LLDB_INVALID_PROCESS_ID) && - (gdb_remote_port != invalid_port)) { - // If both pid and port numbers are specified. - error.SetErrorString("The user can't specify both pid and port"); - } else if (gdb_remote_port != invalid_port) { - // If port is specified and pid is not. - lldb::SBListener listener = dap.debugger.GetListener(); - - // If the user hasn't provided the hostname property, default localhost - // being used. - std::string connect_url = - llvm::formatv("connect://{0}:", gdb_remote_hostname); - connect_url += std::to_string(gdb_remote_port); - dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", - error); + if (attachCommands.empty()) { + // No "attachCommands", just attach normally. + if (core_file.empty()) { + if ((pid != LLDB_INVALID_PROCESS_ID) && + (gdb_remote_port != invalid_port)) { + // If both pid and port numbers are specified. + error.SetErrorString("The user can't specify both pid and port"); + } else if (gdb_remote_port != invalid_port) { + // If port is specified and pid is not. + lldb::SBListener listener = dap.debugger.GetListener(); + + // If the user hasn't provided the hostname property, default + // localhost being used. + std::string connect_url = + llvm::formatv("connect://{0}:", gdb_remote_hostname); + connect_url += std::to_string(gdb_remote_port); + dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote", + error); + } else { + // Attach by pid or process name. + lldb::SBAttachInfo attach_info; + if (pid != LLDB_INVALID_PROCESS_ID) + attach_info.SetProcessID(pid); + else if (dap.configuration.program.has_value()) + attach_info.SetExecutable(dap.configuration.program->data()); + attach_info.SetWaitForLaunch(wait_for, false /*async*/); + dap.target.Attach(attach_info, error); + } } else { - // Attach by pid or process name. - lldb::SBAttachInfo attach_info; - if (pid != LLDB_INVALID_PROCESS_ID) - attach_info.SetProcessID(pid); - else if (dap.configuration.program.has_value()) - attach_info.SetExecutable(dap.configuration.program->data()); - attach_info.SetWaitForLaunch(wait_for, false /*async*/); - dap.target.Attach(attach_info, error); + dap.target.LoadCore(core_file.data(), error); } } else { - dap.target.LoadCore(core_file.data(), error); - } - } else { - // We have "attachCommands" that are a set of commands that are expected - // to execute the commands after which a process should be created. If there - // is no valid process after running these commands, we have failed. - if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { - response["success"] = false; - EmplaceSafeString(response, "message", llvm::toString(std::move(err))); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; + // We have "attachCommands" that are a set of commands that are expected + // to execute the commands after which a process should be created. If + // there is no valid process after running these commands, we have failed. + if (llvm::Error err = dap.RunAttachCommands(attachCommands)) { + response["success"] = false; + EmplaceSafeString(response, "message", llvm::toString(std::move(err))); + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); } - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - - // Make sure the process is attached and stopped before proceeding as the - // the launch commands are not run using the synchronous mode. - error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); } + // Make sure the process is attached and stopped. + error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds)); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + if (error.Success() && core_file.empty()) { auto attached_pid = dap.target.GetProcess().GetProcessID(); if (attached_pid == LLDB_INVALID_PROCESS_ID) { @@ -206,9 +215,17 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const { } dap.SendJSON(llvm::json::Value(std::move(response))); + + // FIXME: Move this into PostRun. if (error.Success()) { - SendProcessEvent(dap, Attach); - dap.SendJSON(CreateEventObject("initialized")); + if (dap.target.GetProcess().IsValid()) { + SendProcessEvent(dap, Attach); + + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp index f39bbdefdbb95..802c28d7b8904 100644 --- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp @@ -47,21 +47,11 @@ namespace lldb_dap { void ConfigurationDoneRequestHandler::operator()( const llvm::json::Object &request) const { + dap.SetConfigurationDone(); + llvm::json::Object response; FillResponse(request, response); dap.SendJSON(llvm::json::Value(std::move(response))); - dap.configuration_done_sent = true; - if (dap.stop_at_entry) - SendThreadStoppedEvent(dap); - else { - // Client requests the baseline of currently existing threads after - // a successful launch or attach by sending a 'threads' request - // right after receiving the configurationDone response. - // Obtain the list of threads before we resume the process - dap.initial_thread_list = - GetThreads(dap.target.GetProcess(), dap.thread_format); - dap.target.GetProcess().Continue(); - } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index ce34c52bcc334..aa947d3cb5ab9 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -140,43 +140,28 @@ static void EventThreadFunction(DAP &dap) { lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event); if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) { auto state = lldb::SBProcess::GetStateFromEvent(event); + + DAP_LOG(dap.log, "State = {0}", state); switch (state) { + case lldb::eStateConnected: + case lldb::eStateDetached: case lldb::eStateInvalid: - // Not a state event - break; case lldb::eStateUnloaded: break; - case lldb::eStateConnected: - break; case lldb::eStateAttaching: - break; - case lldb::eStateLaunching: - break; - case lldb::eStateStepping: - break; case lldb::eStateCrashed: - break; - case lldb::eStateDetached: - break; - case lldb::eStateSuspended: - break; + case lldb::eStateLaunching: case lldb::eStateStopped: - // We launch and attach in synchronous mode then the first stop - // event will not be delivered. If we use "launchCommands" during a - // launch or "attachCommands" during an attach we might some process - // stop events which we do not want to send an event for. We will - // manually send a stopped event in request_configurationDone(...) - // so don't send any before then. - if (dap.configuration_done_sent) { - // Only report a stopped event if the process was not - // automatically restarted. - if (!lldb::SBProcess::GetRestartedFromEvent(event)) { - SendStdOutStdErr(dap, process); - SendThreadStoppedEvent(dap); - } + case lldb::eStateSuspended: + // Only report a stopped event if the process was not + // automatically restarted. + if (!lldb::SBProcess::GetRestartedFromEvent(event)) { + SendStdOutStdErr(dap, process); + SendThreadStoppedEvent(dap); } break; case lldb::eStateRunning: + case lldb::eStateStepping: dap.WillContinue(); SendContinuedEvent(dap); break; @@ -284,6 +269,7 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run( // Do not source init files until in/out/err are configured. dap.debugger = lldb::SBDebugger::Create(false); dap.debugger.SetInputFile(dap.in); + dap.target = dap.debugger.GetDummyTarget(); llvm::Expected<int> out_fd = dap.out.GetWriteFileDescriptor(); if (!out_fd) @@ -338,4 +324,8 @@ llvm::Expected<InitializeResponseBody> InitializeRequestHandler::Run( return dap.GetCapabilities(); } +void InitializeRequestHandler::PostRun() const { + dap.SendJSON(CreateEventObject("initialized")); +} + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp index 3e4532e754ec6..7e0e76935dd02 100644 --- a/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/LaunchRequestHandler.cpp @@ -71,9 +71,12 @@ void LaunchRequestHandler::PostRun() const { if (dap.target.GetProcess().IsValid()) { // Attach happens when launching with runInTerminal. SendProcessEvent(dap, dap.is_attach ? Attach : Launch); - } - dap.SendJSON(CreateEventObject("initialized")); + if (dap.stop_at_entry) + SendThreadStoppedEvent(dap); + else + dap.target.GetProcess().Continue(); + } } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp index 7a75cd93abc19..282c5f4ab15a5 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp @@ -8,6 +8,7 @@ #include "Handler/RequestHandler.h" #include "DAP.h" +#include "EventHelper.h" #include "Handler/ResponseHandler.h" #include "JSONUtils.h" #include "LLDBUtils.h" @@ -162,7 +163,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { dap.target.GetProcess().Continue(); // Now that the actual target is just starting (i.e. exec was just invoked), - // we return the debugger to its async state. + // we return the debugger to its sync state. scope_sync_mode.reset(); // If sending the notification failed, the launcher should be dead by now and @@ -238,35 +239,47 @@ llvm::Error BaseRequestHandler::LaunchProcess( launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | lldb::eLaunchFlagStopAtEntry); - if (arguments.runInTerminal) { - if (llvm::Error err = RunInTerminal(dap, arguments)) - return err; - } else if (launchCommands.empty()) { - lldb::SBError error; - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously + { + // Perform the launch in synchronous mode so that we don't have to worry + // about process state changes during the launch. ScopeSyncMode scope_sync_mode(dap.debugger); - dap.target.Launch(launch_info, error); - if (error.Fail()) - return llvm::make_error<DAPError>(error.GetCString()); - } else { - // Set the launch info so that run commands can access the configured - // launch details. - dap.target.SetLaunchInfo(launch_info); - if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) - return err; - - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - dap.target = dap.debugger.GetSelectedTarget(); - // Make sure the process is launched and stopped at the entry point before - // proceeding as the launch commands are not run using the synchronous - // mode. - lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); - if (error.Fail()) - return llvm::make_error<DAPError>(error.GetCString()); + + if (arguments.runInTerminal) { + if (llvm::Error err = RunInTerminal(dap, arguments)) + return err; + } else if (launchCommands.empty()) { + lldb::SBError error; + dap.target.Launch(launch_info, error); + if (error.Fail()) + return llvm::make_error<DAPError>(error.GetCString()); + } else { + // Set the launch info so that run commands can access the configured + // launch details. + dap.target.SetLaunchInfo(launch_info); + if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) + return err; + + // The custom commands might have created a new target so we should use + // the selected target after these commands are run. + dap.target = dap.debugger.GetSelectedTarget(); + } } + // Make sure the process is launched and stopped at the entry point before + // proceeding. + lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout); + if (error.Fail()) + return llvm::make_error<DAPError>(error.GetCString()); + + // Clients can request a baseline of currently existing threads after + // we acknowledge the configurationDone request. + // Client requests the baseline of currently existing threads after + // a successful or attach by sending a 'threads' request + // right after receiving the configurationDone response. + // Obtain the list of threads before we resume the process + dap.initial_thread_list = + GetThreads(dap.target.GetProcess(), dap.thread_format); + return llvm::Error::success(); } diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index 37cc902e1c98e..9e9cfb13d77b8 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -282,6 +282,7 @@ class InitializeRequestHandler static llvm::StringLiteral GetCommand() { return "initialize"; } llvm::Expected<protocol::InitializeResponseBody> Run(const protocol::InitializeRequestArguments &args) const override; + void PostRun() const override; }; class LaunchRequestHandler >From 3e7b2457705d73c22c2607e02416f1ad9e57bedc Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jo...@devlieghere.com> Date: Wed, 7 May 2025 15:26:01 -0700 Subject: [PATCH 2/2] Update more tests --- .../test/tools/lldb-dap/lldbdap_testcase.py | 48 +++++++++++++++++++ .../completions/TestDAP_completions.py | 22 +++++---- .../tools/lldb-dap/console/TestDAP_console.py | 9 ++-- .../console/TestDAP_redirection_to_console.py | 4 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 2 + .../lldb-dap/send-event/TestDAP_sendEvent.py | 7 +-- .../lldb-dap/stackTrace/TestDAP_stackTrace.py | 2 +- .../TestDAP_stackTraceDisassemblyDisplay.py | 2 +- .../startDebugging/TestDAP_startDebugging.py | 3 +- .../children/TestDAP_variables_children.py | 4 +- 10 files changed, 79 insertions(+), 24 deletions(-) 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 958c7268c0c72..c5a7eb76a58c7 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 @@ -349,6 +349,8 @@ def attach( expectFailure=False, gdbRemotePort=None, gdbRemoteHostname=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and attach to the process. @@ -366,6 +368,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_attach( program=program, @@ -423,6 +445,8 @@ def launch( commandEscapePrefix=None, customFrameFormat=None, customThreadFormat=None, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Sending launch request to dap""" @@ -439,6 +463,26 @@ def cleanup(): # Initialize and launch the program self.dap_server.request_initialize(sourceInitFile) self.dap_server.wait_for_event("initialized") + + # Set source breakpoints as part of the launch sequence. + if sourceBreakpoints: + for source_path, lines in sourceBreakpoints: + response = self.dap_server.request_setBreakpoints(source_path, lines) + self.assertTrue( + response["success"], + "setBreakpoints failed (%s)" % (response), + ) + + # Set function breakpoints as part of the launch sequence. + if functionBreakpoints: + response = self.dap_server.request_setFunctionBreakpoints( + functionBreakpoints + ) + self.assertTrue( + response["success"], + "setFunctionBreakpoint failed (%s)" % (response), + ) + self.dap_server.request_configurationDone() response = self.dap_server.request_launch( @@ -511,6 +555,8 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + sourceBreakpoints=None, + functionBreakpoints=None, ): """Build the default Makefile target, create the DAP debug adapter, and launch the process. @@ -547,6 +593,8 @@ def build_and_launch( customThreadFormat=customThreadFormat, launchCommands=launchCommands, expectFailure=expectFailure, + sourceBreakpoints=sourceBreakpoints, + functionBreakpoints=functionBreakpoints, ) def getBuiltinDebugServerTool(self): diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py index 455ac84168baf..a94288c7a669e 100644 --- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py +++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py @@ -2,7 +2,6 @@ Test lldb-dap completions request """ - import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil @@ -32,6 +31,7 @@ variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} + # Older version of libcxx produce slightly different typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) @@ -43,16 +43,22 @@ def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) - def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, stopOnEntry=stopOnEntry) - source = "main.cpp" - breakpoint1_line = line_number(source, "// breakpoint 1") - breakpoint2_line = line_number(source, "// breakpoint 2") - - self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line]) + self.build_and_launch( + program, + stopOnEntry=stopOnEntry, + sourceBreakpoints=[ + ( + source, + [ + line_number(source, "// breakpoint 1"), + line_number(source, "// breakpoint 2"), + ], + ), + ], + ) def test_command_completions(self): """ diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py index 65a1bc04c7cd7..8642e317f9b3a 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py @@ -19,6 +19,7 @@ def get_subprocess(root_process, process_name): self.assertTrue(False, "No subprocess with name %s found" % process_name) + class TestDAP_console(lldbdap_testcase.DAPTestCaseBase): def check_lldb_command( self, lldb_command, contains_string, assert_msg, command_escape_prefix="`" @@ -52,7 +53,7 @@ def test_scopes_variables_setVariable_evaluate(self): character. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") lines = [breakpoint1_line] @@ -81,7 +82,7 @@ def test_scopes_variables_setVariable_evaluate(self): def test_custom_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="::") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="::") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -96,7 +97,7 @@ def test_custom_escape_prefix(self): def test_empty_escape_prefix(self): program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") source = "main.cpp" breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) @@ -113,7 +114,7 @@ def test_empty_escape_prefix(self): def test_exit_status_message_sigterm(self): source = "main.cpp" program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint1_line = line_number(source, "// breakpoint 1") breakpoint_ids = self.set_source_breakpoints(source, [breakpoint1_line]) self.continue_to_breakpoints(breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py index e367c327d4295..23500bd6fe586 100644 --- a/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py +++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_redirection_to_console.py @@ -16,7 +16,9 @@ def test(self): """ program = self.getBuildArtifact("a.out") self.build_and_launch( - program, lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""} + program, + stopOnEntry=True, + lldbDAPEnv={"LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION": ""}, ) source = "main.cpp" diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py index 604a41678500c..1af661958dc09 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -15,6 +15,7 @@ # Despite the test program printing correctly. See # https://github.com/llvm/llvm-project/issues/137599. + class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_default(self): @@ -357,6 +358,7 @@ def test_commands(self): terminateCommands = ["expr 4+2"] self.build_and_launch( program, + stopOnEntry=True, initCommands=initCommands, preRunCommands=preRunCommands, postRunCommands=postRunCommands, diff --git a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py index ce262be161861..64cec70aa923b 100644 --- a/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py +++ b/lldb/test/API/tools/lldb-dap/send-event/TestDAP_sendEvent.py @@ -16,12 +16,14 @@ def test_send_event(self): """ program = self.getBuildArtifact("a.out") source = "main.c" + breakpoint_line = line_number(source, "// breakpoint") custom_event_body = { "key": 321, "arr": [True], } self.build_and_launch( program, + sourceBreakpoints=[(source, [breakpoint_line])], stopCommands=[ "lldb-dap send-event my-custom-event-no-body", "lldb-dap send-event my-custom-event '{}'".format( @@ -30,11 +32,6 @@ def test_send_event(self): ], ) - breakpoint_line = line_number(source, "// breakpoint") - - self.set_source_breakpoints(source, [breakpoint_line]) - self.continue_to_next_stop() - custom_event = self.dap_server.wait_for_event( filter=["my-custom-event-no-body"] ) diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py index 4e2a76cf76980..edf4adae14a3b 100644 --- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py +++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py @@ -61,7 +61,7 @@ def test_stackTrace(self): Tests the 'stackTrace' packet and all its variants. """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) source = "main.c" self.source_path = os.path.join(os.getcwd(), source) self.recurse_end = line_number(source, "recurse end") diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py index 08c225b3cada4..963d711978534 100644 --- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py +++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py @@ -37,7 +37,7 @@ def build_and_run_until_breakpoint(self): breakpoint_line = line_number(other_source_file, "// Break here") program = self.getBuildArtifact("a.out") - self.build_and_launch(program, commandEscapePrefix="") + self.build_and_launch(program, stopOnEntry=True, commandEscapePrefix="") breakpoint_ids = self.set_source_breakpoints( other_source_file, [breakpoint_line] diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py index fd452d91e472b..e37cd36d7f283 100644 --- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py +++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py @@ -2,7 +2,6 @@ Test lldb-dap start-debugging reverse requests. """ - from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbdap_testcase @@ -16,7 +15,7 @@ def test_startDebugging(self): """ program = self.getBuildArtifact("a.out") source = "main.c" - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) breakpoint_line = line_number(source, "// breakpoint") diff --git a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py index a9371e5c5fe68..eb09649f387d7 100644 --- a/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py +++ b/lldb/test/API/tools/lldb-dap/variables/children/TestDAP_variables_children.py @@ -13,13 +13,13 @@ def test_get_num_children(self): program = self.getBuildArtifact("a.out") self.build_and_launch( program, + stopOnEntry=True, preRunCommands=[ "command script import '%s'" % self.getSourcePath("formatter.py") ], ) source = "main.cpp" breakpoint1_line = line_number(source, "// break here") - lines = [breakpoint1_line] breakpoint_ids = self.set_source_breakpoints( source, [line_number(source, "// break here")] @@ -47,7 +47,7 @@ def test_return_variable_with_children(self): Test the stepping out of a function with return value show the children correctly """ program = self.getBuildArtifact("a.out") - self.build_and_launch(program) + self.build_and_launch(program, stopOnEntry=True) function_name = "test_return_variable_with_children" breakpoint_ids = self.set_function_breakpoints([function_name]) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits