Author: John Harrison
Date: 2025-05-16T08:47:01-07:00
New Revision: 087a5d2ec7897cd99d3787820711fec76a8e1792

URL: 
https://github.com/llvm/llvm-project/commit/087a5d2ec7897cd99d3787820711fec76a8e1792
DIFF: 
https://github.com/llvm/llvm-project/commit/087a5d2ec7897cd99d3787820711fec76a8e1792.diff

LOG: [lldb-dap] Adding additional asserts to unit tests. (#140107)

Adding an assert that the 'continue' request succeeds caused a number of
tests to fail. This showed a number of tests that were not specifying if
they should be stopped or not at key points in the test. This is likely
contributing to these tests being flaky since the debugger is not in the
expected state.

Additionally, I spent a little time trying to improve the readability of
the dap_server.py and lldbdap_testcase.py.

Added: 
    

Modified: 
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
    lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
    lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
    lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
    lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
    lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
    lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
    lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
    lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
    lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
    lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
    lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
    lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
    lldb/test/API/tools/lldb-dap/stop-hooks/TestDAP_stop_hooks.py
    lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
    lldb/tools/lldb-dap/DAPError.cpp
    lldb/tools/lldb-dap/DAPError.h
    lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
    lldb/tools/lldb-dap/Handler/RequestHandler.h

Removed: 
    


################################################################################
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 73f7b0e91d57a..d3589e78b6bc7 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
@@ -12,6 +12,13 @@
 import sys
 import threading
 import time
+from typing import Any, Optional, Union, BinaryIO, TextIO
+
+## DAP type references
+Event = dict[str, Any]
+Request = dict[str, Any]
+Response = dict[str, Any]
+ProtocolMessage = Union[Event, Request, Response]
 
 
 def dump_memory(base_addr, data, num_per_line, outfile):
@@ -98,55 +105,40 @@ def dump_dap_log(log_file):
     print("========= END =========", file=sys.stderr)
 
 
-def read_packet_thread(vs_comm, log_file):
-    done = False
-    try:
-        while not done:
-            packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file)
-            # `packet` will be `None` on EOF. We want to pass it down to
-            # handle_recv_packet anyway so the main thread can handle 
unexpected
-            # termination of lldb-dap and stop waiting for new packets.
-            done = not vs_comm.handle_recv_packet(packet)
-    finally:
-        # Wait for the process to fully exit before dumping the log file to
-        # ensure we have the entire log contents.
-        if vs_comm.process is not None:
-            try:
-                # Do not wait forever, some logs are better than none.
-                vs_comm.process.wait(timeout=20)
-            except subprocess.TimeoutExpired:
-                pass
-        dump_dap_log(log_file)
-
-
 class DebugCommunication(object):
-    def __init__(self, recv, send, init_commands, log_file=None):
-        self.trace_file = None
+    def __init__(
+        self,
+        recv: BinaryIO,
+        send: BinaryIO,
+        init_commands: list[str],
+        log_file: Optional[TextIO] = None,
+    ):
+        # For debugging test failures, try setting `trace_file = sys.stderr`.
+        self.trace_file: Optional[TextIO] = None
+        self.log_file = log_file
         self.send = send
         self.recv = recv
-        self.recv_packets = []
+        self.recv_packets: list[Optional[ProtocolMessage]] = []
         self.recv_condition = threading.Condition()
-        self.recv_thread = threading.Thread(
-            target=read_packet_thread, args=(self, log_file)
-        )
+        self.recv_thread = threading.Thread(target=self._read_packet_thread)
         self.process_event_body = None
-        self.exit_status = None
+        self.exit_status: Optional[int] = None
         self.initialize_body = None
-        self.thread_stop_reasons = {}
-        self.progress_events = []
+        self.progress_events: list[Event] = []
         self.reverse_requests = []
         self.sequence = 1
         self.threads = None
+        self.thread_stop_reasons = {}
         self.recv_thread.start()
         self.output_condition = threading.Condition()
-        self.output = {}
+        self.output: dict[str, list[str]] = {}
         self.configuration_done_sent = False
         self.frame_scopes = {}
         self.init_commands = init_commands
         self.disassembled_instructions = {}
 
     @classmethod
-    def encode_content(cls, s):
+    def encode_content(cls, s: str) -> bytes:
         return ("Content-Length: %u\r\n\r\n%s" % (len(s), s)).encode("utf-8")
 
     @classmethod
@@ -156,6 +148,18 @@ def validate_response(cls, command, response):
         if command["seq"] != response["request_seq"]:
             raise ValueError("seq mismatch in response")
 
+    def _read_packet_thread(self):
+        done = False
+        try:
+            while not done:
+                packet = read_packet(self.recv, trace_file=self.trace_file)
+                # `packet` will be `None` on EOF. We want to pass it down to
+                # handle_recv_packet anyway so the main thread can handle 
unexpected
+                # termination of lldb-dap and stop waiting for new packets.
+                done = not self._handle_recv_packet(packet)
+        finally:
+            dump_dap_log(self.log_file)
+
     def get_modules(self):
         module_list = self.request_modules()["body"]["modules"]
         modules = {}
@@ -190,13 +194,13 @@ def collect_output(self, category, timeout_secs, pattern, 
clear=True):
                     break
         return collected_output if collected_output else None
 
-    def enqueue_recv_packet(self, packet):
+    def _enqueue_recv_packet(self, packet: Optional[ProtocolMessage]):
         self.recv_condition.acquire()
         self.recv_packets.append(packet)
         self.recv_condition.notify()
         self.recv_condition.release()
 
-    def handle_recv_packet(self, packet):
+    def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
         """Called by the read thread that is waiting for all incoming packets
         to store the incoming packet in "self.recv_packets" in a thread safe
         way. This function will then signal the "self.recv_condition" to
@@ -205,7 +209,7 @@ def handle_recv_packet(self, packet):
         """
         # If EOF, notify the read thread by enqueuing a None.
         if not packet:
-            self.enqueue_recv_packet(None)
+            self._enqueue_recv_packet(None)
             return False
 
         # Check the packet to see if is an event packet
@@ -235,6 +239,18 @@ def handle_recv_packet(self, packet):
                 # When a new process is attached or launched, remember the
                 # details that are available in the body of the event
                 self.process_event_body = body
+            elif event == "exited":
+                # Process exited, mark the status to indicate the process is 
not
+                # alive.
+                self.exit_status = body["exitCode"]
+            elif event == "continued":
+                # When the process continues, clear the known threads and
+                # thread_stop_reasons.
+                all_threads_continued = body.get("allThreadsContinued", True)
+                tid = body["threadId"]
+                if tid in self.thread_stop_reasons:
+                    del self.thread_stop_reasons[tid]
+                self._process_continued(all_threads_continued)
             elif event == "stopped":
                 # Each thread that stops with a reason will send a
                 # 'stopped' event. We need to remember the thread stop
@@ -252,10 +268,16 @@ def handle_recv_packet(self, packet):
         elif packet_type == "response":
             if packet["command"] == "disconnect":
                 keepGoing = False
-        self.enqueue_recv_packet(packet)
+        self._enqueue_recv_packet(packet)
         return keepGoing
 
-    def send_packet(self, command_dict, set_sequence=True):
+    def _process_continued(self, all_threads_continued: bool):
+        self.threads = None
+        self.frame_scopes = {}
+        if all_threads_continued:
+            self.thread_stop_reasons = {}
+
+    def send_packet(self, command_dict: Request, set_sequence=True):
         """Take the "command_dict" python dictionary and encode it as a JSON
         string and send the contents as a packet to the VSCode debug
         adapter"""
@@ -273,7 +295,12 @@ def send_packet(self, command_dict, set_sequence=True):
             self.send.write(self.encode_content(json_str))
             self.send.flush()
 
-    def recv_packet(self, filter_type=None, filter_event=None, timeout=None):
+    def recv_packet(
+        self,
+        filter_type: Optional[str] = None,
+        filter_event: Optional[Union[str, list[str]]] = None,
+        timeout: Optional[float] = None,
+    ) -> Optional[ProtocolMessage]:
         """Get a JSON packet from the VSCode debug adapter. This function
         assumes a thread that reads packets is running and will deliver
         any received packets by calling handle_recv_packet(...). This
@@ -309,8 +336,6 @@ def recv_packet(self, filter_type=None, filter_event=None, 
timeout=None):
             finally:
                 self.recv_condition.release()
 
-        return None
-
     def send_recv(self, command):
         """Send a command python dictionary as JSON and receive the JSON
         response. Validates that the response is the correct sequence and
@@ -360,47 +385,36 @@ def send_recv(self, command):
 
         return None
 
-    def wait_for_event(self, filter=None, timeout=None):
-        while True:
-            return self.recv_packet(
-                filter_type="event", filter_event=filter, timeout=timeout
-            )
-        return None
-
-    def wait_for_events(self, events, timeout=None):
-        """Wait for a list of events in `events` in any order.
-        Return the events not hit before the timeout expired"""
-        events = events[:]  # Make a copy to avoid modifying the input
-        while events:
-            event_dict = self.wait_for_event(filter=events, timeout=timeout)
-            if event_dict is None:
-                break
-            events.remove(event_dict["event"])
-        return events
+    def wait_for_event(
+        self, filter: Union[str, list[str]], timeout: Optional[float] = None
+    ) -> Optional[Event]:
+        """Wait for the first event that matches the filter."""
+        return self.recv_packet(
+            filter_type="event", filter_event=filter, timeout=timeout
+        )
 
-    def wait_for_stopped(self, timeout=None):
+    def wait_for_stopped(
+        self, timeout: Optional[float] = None
+    ) -> Optional[list[Event]]:
         stopped_events = []
         stopped_event = self.wait_for_event(
             filter=["stopped", "exited"], timeout=timeout
         )
-        exited = False
         while stopped_event:
             stopped_events.append(stopped_event)
             # If we exited, then we are done
             if stopped_event["event"] == "exited":
-                self.exit_status = stopped_event["body"]["exitCode"]
-                exited = True
                 break
             # Otherwise we stopped and there might be one or more 'stopped'
             # events for each thread that stopped with a reason, so keep
             # checking for more 'stopped' events and return all of them
-            stopped_event = self.wait_for_event(filter="stopped", timeout=0.25)
-        if exited:
-            self.threads = []
+            stopped_event = self.wait_for_event(
+                filter=["stopped", "exited"], timeout=0.25
+            )
         return stopped_events
 
-    def wait_for_breakpoint_events(self, timeout=None):
-        breakpoint_events = []
+    def wait_for_breakpoint_events(self, timeout: Optional[float] = None):
+        breakpoint_events: list[Event] = []
         while True:
             event = self.wait_for_event("breakpoint", timeout=timeout)
             if not event:
@@ -408,14 +422,14 @@ def wait_for_breakpoint_events(self, timeout=None):
             breakpoint_events.append(event)
         return breakpoint_events
 
-    def wait_for_exited(self):
-        event_dict = self.wait_for_event("exited")
+    def wait_for_exited(self, timeout: Optional[float] = None):
+        event_dict = self.wait_for_event("exited", timeout=timeout)
         if event_dict is None:
             raise ValueError("didn't get exited event")
         return event_dict
 
-    def wait_for_terminated(self):
-        event_dict = self.wait_for_event("terminated")
+    def wait_for_terminated(self, timeout: Optional[float] = None):
+        event_dict = self.wait_for_event("terminated", timeout)
         if event_dict is None:
             raise ValueError("didn't get terminated event")
         return event_dict
@@ -576,32 +590,30 @@ def replay_packets(self, replay_file_path):
 
     def request_attach(
         self,
-        program=None,
-        pid=None,
-        waitFor=None,
-        trace=None,
-        initCommands=None,
-        preRunCommands=None,
-        stopCommands=None,
-        exitCommands=None,
-        attachCommands=None,
-        terminateCommands=None,
-        coreFile=None,
+        *,
+        program: Optional[str] = None,
+        pid: Optional[int] = None,
+        waitFor=False,
+        initCommands: Optional[list[str]] = None,
+        preRunCommands: Optional[list[str]] = None,
+        attachCommands: Optional[list[str]] = None,
+        postRunCommands: Optional[list[str]] = None,
+        stopCommands: Optional[list[str]] = None,
+        exitCommands: Optional[list[str]] = None,
+        terminateCommands: Optional[list[str]] = None,
+        coreFile: Optional[str] = None,
         stopOnAttach=True,
-        postRunCommands=None,
-        sourceMap=None,
-        gdbRemotePort=None,
-        gdbRemoteHostname=None,
+        sourceMap: Optional[Union[list[tuple[str, str]], dict[str, str]]] = 
None,
+        gdbRemotePort: Optional[int] = None,
+        gdbRemoteHostname: Optional[str] = None,
     ):
         args_dict = {}
         if pid is not None:
             args_dict["pid"] = pid
         if program is not None:
             args_dict["program"] = program
-        if waitFor is not None:
+        if waitFor:
             args_dict["waitFor"] = waitFor
-        if trace:
-            args_dict["trace"] = trace
         args_dict["initCommands"] = self.init_commands
         if initCommands:
             args_dict["initCommands"].extend(initCommands)
@@ -671,7 +683,7 @@ def _process_stopped(self):
         self.threads = None
         self.frame_scopes = {}
 
-    def request_continue(self, threadId=None):
+    def request_continue(self, threadId=None, singleThread=False):
         if self.exit_status is not None:
             raise ValueError("request_continue called after process exited")
         # If we have launched or attached, then the first continue is done by
@@ -681,13 +693,18 @@ def request_continue(self, threadId=None):
         args_dict = {}
         if threadId is None:
             threadId = self.get_thread_id()
-        args_dict["threadId"] = threadId
+        if threadId:
+            args_dict["threadId"] = threadId
+        if singleThread:
+            args_dict["singleThread"] = True
         command_dict = {
             "command": "continue",
             "type": "request",
             "arguments": args_dict,
         }
         response = self.send_recv(command_dict)
+        if response["success"]:
+            self._process_continued(response["body"]["allThreadsContinued"])
         # Caller must still call wait_for_stopped.
         return response
 
@@ -775,7 +792,7 @@ def request_exceptionInfo(self, threadId=None):
         }
         return self.send_recv(command_dict)
 
-    def request_initialize(self, sourceInitFile):
+    def request_initialize(self, sourceInitFile=False):
         command_dict = {
             "command": "initialize",
             "type": "request",
@@ -802,32 +819,32 @@ def request_initialize(self, sourceInitFile):
 
     def request_launch(
         self,
-        program,
-        args=None,
-        cwd=None,
-        env=None,
-        stopOnEntry=False,
+        program: str,
+        *,
+        args: Optional[list[str]] = None,
+        cwd: Optional[str] = None,
+        env: Optional[dict[str, str]] = None,
+        stopOnEntry=True,
         disableASLR=True,
         disableSTDIO=False,
         shellExpandArguments=False,
-        trace=False,
-        initCommands=None,
-        preRunCommands=None,
-        stopCommands=None,
-        exitCommands=None,
-        terminateCommands=None,
-        sourcePath=None,
-        debuggerRoot=None,
-        launchCommands=None,
-        sourceMap=None,
         runInTerminal=False,
-        postRunCommands=None,
         enableAutoVariableSummaries=False,
         displayExtendedBacktrace=False,
         enableSyntheticChildDebugging=False,
-        commandEscapePrefix=None,
-        customFrameFormat=None,
-        customThreadFormat=None,
+        initCommands: Optional[list[str]] = None,
+        preRunCommands: Optional[list[str]] = None,
+        launchCommands: Optional[list[str]] = None,
+        postRunCommands: Optional[list[str]] = None,
+        stopCommands: Optional[list[str]] = None,
+        exitCommands: Optional[list[str]] = None,
+        terminateCommands: Optional[list[str]] = None,
+        sourceMap: Optional[Union[list[tuple[str, str]], dict[str, str]]] = 
None,
+        sourcePath: Optional[str] = None,
+        debuggerRoot: Optional[str] = None,
+        commandEscapePrefix: Optional[str] = None,
+        customFrameFormat: Optional[str] = None,
+        customThreadFormat: Optional[str] = None,
     ):
         args_dict = {"program": program}
         if args:
@@ -842,8 +859,6 @@ def request_launch(
             args_dict["disableSTDIO"] = disableSTDIO
         if shellExpandArguments:
             args_dict["shellExpandArguments"] = shellExpandArguments
-        if trace:
-            args_dict["trace"] = trace
         args_dict["initCommands"] = self.init_commands
         if initCommands:
             args_dict["initCommands"].extend(initCommands)
@@ -1190,7 +1205,8 @@ def request_testGetTargetBreakpoints(self):
 
     def terminate(self):
         self.send.close()
-        # self.recv.close()
+        if self.recv_thread.is_alive():
+            self.recv_thread.join()
 
     def request_setInstructionBreakpoints(self, memory_reference=[]):
         breakpoints = []
@@ -1211,11 +1227,11 @@ def request_setInstructionBreakpoints(self, 
memory_reference=[]):
 class DebugAdapterServer(DebugCommunication):
     def __init__(
         self,
-        executable=None,
-        connection=None,
-        init_commands=[],
-        log_file=None,
-        env=None,
+        executable: Optional[str] = None,
+        connection: Optional[str] = None,
+        init_commands: list[str] = [],
+        log_file: Optional[TextIO] = None,
+        env: Optional[dict[str, str]] = None,
     ):
         self.process = None
         self.connection = None
@@ -1247,7 +1263,14 @@ def __init__(
             )
 
     @classmethod
-    def launch(cls, /, executable, env=None, log_file=None, connection=None):
+    def launch(
+        cls,
+        *,
+        executable: str,
+        env: Optional[dict[str, str]] = None,
+        log_file: Optional[TextIO] = None,
+        connection: Optional[str] = None,
+    ) -> tuple[subprocess.Popen, Optional[str]]:
         adapter_env = os.environ.copy()
         if env is not None:
             adapter_env.update(env)
@@ -1289,26 +1312,29 @@ def launch(cls, /, executable, env=None, log_file=None, 
connection=None):
 
         return (process, connection)
 
-    def get_pid(self):
+    def get_pid(self) -> int:
         if self.process:
             return self.process.pid
         return -1
 
     def terminate(self):
-        super(DebugAdapterServer, self).terminate()
-        if self.process is not None:
-            process = self.process
-            self.process = None
-            try:
-                # When we close stdin it should signal the lldb-dap that no
-                # new messages will arrive and it should shutdown on its own.
-                process.stdin.close()
-                process.wait(timeout=20)
-            except subprocess.TimeoutExpired:
-                process.kill()
-                process.wait()
-            if process.returncode != 0:
-                raise DebugAdapterProcessError(process.returncode)
+        try:
+            if self.process is not None:
+                process = self.process
+                self.process = None
+                try:
+                    # When we close stdin it should signal the lldb-dap that no
+                    # new messages will arrive and it should shutdown on its
+                    # own.
+                    process.stdin.close()
+                    process.wait(timeout=20)
+                except subprocess.TimeoutExpired:
+                    process.kill()
+                    process.wait()
+                if process.returncode != 0:
+                    raise DebugAdapterProcessError(process.returncode)
+        finally:
+            super(DebugAdapterServer, self).terminate()
 
 
 class DebugAdapterError(Exception):

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 c5a7eb76a58c7..d7cf8e2864324 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
@@ -1,5 +1,6 @@
 import os
 import time
+from typing import Optional
 import uuid
 
 import dap_server
@@ -11,10 +12,14 @@
 class DAPTestCaseBase(TestBase):
     # set timeout based on whether ASAN was enabled or not. Increase
     # timeout by a factor of 10 if ASAN is enabled.
-    timeoutval = 10 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
+    DEFAULT_TIMEOUT = 10 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
     NO_DEBUG_INFO_TESTCASE = True
 
-    def create_debug_adapter(self, lldbDAPEnv=None, connection=None):
+    def create_debug_adapter(
+        self,
+        lldbDAPEnv: Optional[dict[str, str]] = None,
+        connection: Optional[str] = None,
+    ):
         """Create the Visual Studio Code debug adapter"""
         self.assertTrue(
             is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -28,7 +33,11 @@ def create_debug_adapter(self, lldbDAPEnv=None, 
connection=None):
             env=lldbDAPEnv,
         )
 
-    def build_and_create_debug_adapter(self, lldbDAPEnv=None, dictionary=None):
+    def build_and_create_debug_adapter(
+        self,
+        lldbDAPEnv: Optional[dict[str, str]] = None,
+        dictionary: Optional[dict] = None,
+    ):
         self.build(dictionary=dictionary)
         self.create_debug_adapter(lldbDAPEnv)
 
@@ -78,13 +87,13 @@ def waitUntil(self, condition_callback):
             time.sleep(0.5)
         return False
 
-    def verify_breakpoint_hit(self, breakpoint_ids):
+    def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
         """Wait for the process we are debugging to stop, and verify we hit
         any breakpoint location in the "breakpoint_ids" array.
         "breakpoint_ids" should be a list of breakpoint ID strings
         (["1", "2"]). The return value from self.set_source_breakpoints()
         or self.set_function_breakpoints() can be passed to this function"""
-        stopped_events = self.dap_server.wait_for_stopped()
+        stopped_events = self.dap_server.wait_for_stopped(timeout)
         for stopped_event in stopped_events:
             if "body" in stopped_event:
                 body = stopped_event["body"]
@@ -110,16 +119,15 @@ def verify_breakpoint_hit(self, breakpoint_ids):
                     match_desc = "breakpoint %s." % (breakpoint_id)
                     if match_desc in description:
                         return
-        self.assertTrue(False, "breakpoint not hit")
+        self.assertTrue(False, f"breakpoint not hit, 
stopped_events={stopped_events}")
 
-    def verify_stop_exception_info(self, expected_description, 
timeout=timeoutval):
+    def verify_stop_exception_info(self, expected_description, 
timeout=DEFAULT_TIMEOUT):
         """Wait for the process we are debugging to stop, and verify the stop
         reason is 'exception' and that the description matches
         'expected_description'
         """
-        stopped_events = self.dap_server.wait_for_stopped(timeout=timeout)
+        stopped_events = self.dap_server.wait_for_stopped(timeout)
         for stopped_event in stopped_events:
-            print("stopped_event", stopped_event)
             if "body" in stopped_event:
                 body = stopped_event["body"]
                 if "reason" not in body:
@@ -263,46 +271,61 @@ def set_global(self, name, value, id=None):
         return self.dap_server.request_setVariable(2, name, str(value), id=id)
 
     def stepIn(
-        self, threadId=None, targetId=None, waitForStop=True, 
granularity="statement"
+        self,
+        threadId=None,
+        targetId=None,
+        waitForStop=True,
+        granularity="statement",
+        timeout=DEFAULT_TIMEOUT,
     ):
         response = self.dap_server.request_stepIn(
             threadId=threadId, targetId=targetId, granularity=granularity
         )
         self.assertTrue(response["success"])
         if waitForStop:
-            return self.dap_server.wait_for_stopped()
+            return self.dap_server.wait_for_stopped(timeout)
         return None
 
-    def stepOver(self, threadId=None, waitForStop=True, 
granularity="statement"):
+    def stepOver(
+        self,
+        threadId=None,
+        waitForStop=True,
+        granularity="statement",
+        timeout=DEFAULT_TIMEOUT,
+    ):
         self.dap_server.request_next(threadId=threadId, 
granularity=granularity)
         if waitForStop:
-            return self.dap_server.wait_for_stopped()
+            return self.dap_server.wait_for_stopped(timeout)
         return None
 
-    def stepOut(self, threadId=None, waitForStop=True):
+    def stepOut(self, threadId=None, waitForStop=True, 
timeout=DEFAULT_TIMEOUT):
         self.dap_server.request_stepOut(threadId=threadId)
         if waitForStop:
-            return self.dap_server.wait_for_stopped()
+            return self.dap_server.wait_for_stopped(timeout)
         return None
 
-    def continue_to_next_stop(self):
-        self.dap_server.request_continue()
-        return self.dap_server.wait_for_stopped()
+    def do_continue(self):  # `continue` is a keyword.
+        resp = self.dap_server.request_continue()
+        self.assertTrue(resp["success"], f"continue request failed: {resp}")
+
+    def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
+        self.do_continue()
+        return self.dap_server.wait_for_stopped(timeout)
 
-    def continue_to_breakpoints(self, breakpoint_ids):
-        self.dap_server.request_continue()
-        self.verify_breakpoint_hit(breakpoint_ids)
+    def continue_to_breakpoints(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+        self.do_continue()
+        self.verify_breakpoint_hit(breakpoint_ids, timeout)
 
-    def continue_to_exception_breakpoint(self, filter_label):
-        self.dap_server.request_continue()
+    def continue_to_exception_breakpoint(self, filter_label, 
timeout=DEFAULT_TIMEOUT):
+        self.do_continue()
         self.assertTrue(
-            self.verify_stop_exception_info(filter_label),
+            self.verify_stop_exception_info(filter_label, timeout),
             'verify we got "%s"' % (filter_label),
         )
 
-    def continue_to_exit(self, exitCode=0):
-        self.dap_server.request_continue()
-        stopped_events = self.dap_server.wait_for_stopped()
+    def continue_to_exit(self, exitCode=0, timeout=DEFAULT_TIMEOUT):
+        self.do_continue()
+        stopped_events = self.dap_server.wait_for_stopped(timeout)
         self.assertEqual(
             len(stopped_events), 1, "stopped_events = 
{}".format(stopped_events)
         )
@@ -330,27 +353,15 @@ def disassemble(self, threadId=None, frameIndex=None):
 
     def attach(
         self,
-        program=None,
-        pid=None,
-        waitFor=None,
-        trace=None,
-        initCommands=None,
-        preRunCommands=None,
-        stopCommands=None,
-        exitCommands=None,
-        attachCommands=None,
-        coreFile=None,
+        *,
         stopOnAttach=True,
         disconnectAutomatically=True,
-        terminateCommands=None,
-        postRunCommands=None,
-        sourceMap=None,
         sourceInitFile=False,
         expectFailure=False,
-        gdbRemotePort=None,
-        gdbRemoteHostname=None,
         sourceBreakpoints=None,
         functionBreakpoints=None,
+        timeout=DEFAULT_TIMEOUT,
+        **kwargs,
     ):
         """Build the default Makefile target, create the DAP debug adapter,
         and attach to the process.
@@ -367,7 +378,7 @@ 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.wait_for_event("initialized", timeout)
 
         # Set source breakpoints as part of the launch sequence.
         if sourceBreakpoints:
@@ -389,64 +400,28 @@ def cleanup():
             )
 
         self.dap_server.request_configurationDone()
-        response = self.dap_server.request_attach(
-            program=program,
-            pid=pid,
-            waitFor=waitFor,
-            trace=trace,
-            initCommands=initCommands,
-            preRunCommands=preRunCommands,
-            stopCommands=stopCommands,
-            exitCommands=exitCommands,
-            attachCommands=attachCommands,
-            terminateCommands=terminateCommands,
-            coreFile=coreFile,
-            stopOnAttach=stopOnAttach,
-            postRunCommands=postRunCommands,
-            sourceMap=sourceMap,
-            gdbRemotePort=gdbRemotePort,
-            gdbRemoteHostname=gdbRemoteHostname,
-        )
+        response = self.dap_server.request_attach(stopOnAttach=stopOnAttach, 
**kwargs)
         if expectFailure:
             return response
         if not (response and response["success"]):
             self.assertTrue(
                 response["success"], "attach failed (%s)" % 
(response["message"])
             )
+        if stopOnAttach:
+            self.dap_server.wait_for_stopped(timeout)
 
     def launch(
         self,
         program=None,
-        args=None,
-        cwd=None,
-        env=None,
-        stopOnEntry=False,
-        disableASLR=False,
-        disableSTDIO=False,
-        shellExpandArguments=False,
-        trace=False,
-        initCommands=None,
-        preRunCommands=None,
-        stopCommands=None,
-        exitCommands=None,
-        terminateCommands=None,
-        sourcePath=None,
-        debuggerRoot=None,
+        *,
         sourceInitFile=False,
-        launchCommands=None,
-        sourceMap=None,
         disconnectAutomatically=True,
-        runInTerminal=False,
-        expectFailure=False,
-        postRunCommands=None,
-        enableAutoVariableSummaries=False,
-        displayExtendedBacktrace=False,
-        enableSyntheticChildDebugging=False,
-        commandEscapePrefix=None,
-        customFrameFormat=None,
-        customThreadFormat=None,
         sourceBreakpoints=None,
         functionBreakpoints=None,
+        expectFailure=False,
+        stopOnEntry=True,
+        timeout=DEFAULT_TIMEOUT,
+        **kwargs,
     ):
         """Sending launch request to dap"""
 
@@ -462,7 +437,7 @@ def cleanup():
 
         # Initialize and launch the program
         self.dap_server.request_initialize(sourceInitFile)
-        self.dap_server.wait_for_event("initialized")
+        self.dap_server.wait_for_event("initialized", timeout)
 
         # Set source breakpoints as part of the launch sequence.
         if sourceBreakpoints:
@@ -487,76 +462,28 @@ def cleanup():
 
         response = self.dap_server.request_launch(
             program,
-            args=args,
-            cwd=cwd,
-            env=env,
             stopOnEntry=stopOnEntry,
-            disableASLR=disableASLR,
-            disableSTDIO=disableSTDIO,
-            shellExpandArguments=shellExpandArguments,
-            trace=trace,
-            initCommands=initCommands,
-            preRunCommands=preRunCommands,
-            stopCommands=stopCommands,
-            exitCommands=exitCommands,
-            terminateCommands=terminateCommands,
-            sourcePath=sourcePath,
-            debuggerRoot=debuggerRoot,
-            launchCommands=launchCommands,
-            sourceMap=sourceMap,
-            runInTerminal=runInTerminal,
-            postRunCommands=postRunCommands,
-            enableAutoVariableSummaries=enableAutoVariableSummaries,
-            displayExtendedBacktrace=displayExtendedBacktrace,
-            enableSyntheticChildDebugging=enableSyntheticChildDebugging,
-            commandEscapePrefix=commandEscapePrefix,
-            customFrameFormat=customFrameFormat,
-            customThreadFormat=customThreadFormat,
+            **kwargs,
         )
 
         if expectFailure:
             return response
-
         if not (response and response["success"]):
             self.assertTrue(
                 response["success"],
                 "launch failed (%s)" % (response["body"]["error"]["format"]),
             )
+        if stopOnEntry:
+            self.dap_server.wait_for_stopped(timeout)
+
         return response
 
     def build_and_launch(
         self,
         program,
-        args=None,
-        cwd=None,
-        env=None,
-        stopOnEntry=False,
-        disableASLR=False,
-        disableSTDIO=False,
-        shellExpandArguments=False,
-        trace=False,
-        initCommands=None,
-        preRunCommands=None,
-        stopCommands=None,
-        exitCommands=None,
-        terminateCommands=None,
-        sourcePath=None,
-        debuggerRoot=None,
-        sourceInitFile=False,
-        runInTerminal=False,
-        disconnectAutomatically=True,
-        postRunCommands=None,
-        lldbDAPEnv=None,
-        enableAutoVariableSummaries=False,
-        displayExtendedBacktrace=False,
-        enableSyntheticChildDebugging=False,
-        commandEscapePrefix=None,
-        customFrameFormat=None,
-        customThreadFormat=None,
-        launchCommands=None,
-        expectFailure=False,
-        sourceBreakpoints=None,
-        functionBreakpoints=None,
+        *,
+        lldbDAPEnv: Optional[dict[str, str]] = None,
+        **kwargs,
     ):
         """Build the default Makefile target, create the DAP debug adapter,
         and launch the process.
@@ -564,38 +491,7 @@ def build_and_launch(
         self.build_and_create_debug_adapter(lldbDAPEnv)
         self.assertTrue(os.path.exists(program), "executable must exist")
 
-        return self.launch(
-            program,
-            args,
-            cwd,
-            env,
-            stopOnEntry,
-            disableASLR,
-            disableSTDIO,
-            shellExpandArguments,
-            trace,
-            initCommands,
-            preRunCommands,
-            stopCommands,
-            exitCommands,
-            terminateCommands,
-            sourcePath,
-            debuggerRoot,
-            sourceInitFile,
-            runInTerminal=runInTerminal,
-            disconnectAutomatically=disconnectAutomatically,
-            postRunCommands=postRunCommands,
-            enableAutoVariableSummaries=enableAutoVariableSummaries,
-            enableSyntheticChildDebugging=enableSyntheticChildDebugging,
-            displayExtendedBacktrace=displayExtendedBacktrace,
-            commandEscapePrefix=commandEscapePrefix,
-            customFrameFormat=customFrameFormat,
-            customThreadFormat=customThreadFormat,
-            launchCommands=launchCommands,
-            expectFailure=expectFailure,
-            sourceBreakpoints=sourceBreakpoints,
-            functionBreakpoints=functionBreakpoints,
-        )
+        return self.launch(program, **kwargs)
 
     def getBuiltinDebugServerTool(self):
         # Tries to find simulation/lldb-server/gdbserver tool path.

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 a9218d3c3dde3..55557e6e0030e 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -2,15 +2,11 @@
 Test lldb-dap attach request
 """
 
-import dap_server
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 import lldbdap_testcase
-import os
-import shutil
 import subprocess
-import tempfile
 import threading
 import time
 
@@ -26,8 +22,6 @@ def spawn_and_wait(program, delay):
 
 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]
@@ -36,7 +30,12 @@ def set_and_hit_breakpoint(self, continueToExit=True):
         self.assertEqual(
             len(breakpoint_ids), len(lines), "expect correct number of 
breakpoints"
         )
-        self.continue_to_breakpoints(breakpoint_ids)
+        # Test binary will sleep for 10s, offset the breakpoint timeout
+        # accordingly.
+        timeout_offset = 10
+        self.continue_to_breakpoints(
+            breakpoint_ids, timeout=timeout_offset + self.DEFAULT_TIMEOUT
+        )
         if continueToExit:
             self.continue_to_exit()
 
@@ -160,7 +159,7 @@ def test_commands(self):
         # Continue after launch and hit the "pause()" call and stop the target.
         # Get output from the console. This should contain both the
         # "stopCommands" that were run after we stop.
-        self.dap_server.request_continue()
+        self.do_continue()
         time.sleep(0.5)
         self.dap_server.request_pause()
         self.dap_server.wait_for_stopped()
@@ -198,9 +197,6 @@ def test_attach_command_process_failures(self):
         )
 
     @skipIfNetBSD  # Hangs on NetBSD as well
-    @skipIf(
-        archs=["arm", "aarch64"]
-    )  # Example of a flaky run 
http://lab.llvm.org:8011/builders/lldb-aarch64-ubuntu/builds/5517/steps/test/logs/stdio
     def test_terminate_commands(self):
         """
         Tests that the "terminateCommands", that can be passed during

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 8581f10cef22a..25f031db5cac5 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
@@ -60,7 +60,7 @@ def test_breakpoint_events(self):
         response = self.dap_server.request_setBreakpoints(
             main_source_path, [main_bp_line]
         )
-        self.assertTrue(response)
+        self.assertTrue(response["success"])
         breakpoints = response["body"]["breakpoints"]
         for breakpoint in breakpoints:
             main_bp_id = breakpoint["id"]
@@ -72,7 +72,7 @@ def test_breakpoint_events(self):
         response = self.dap_server.request_setBreakpoints(
             foo_source_path, [foo_bp1_line]
         )
-        self.assertTrue(response)
+        self.assertTrue(response["success"])
         breakpoints = response["body"]["breakpoints"]
         for breakpoint in breakpoints:
             foo_bp_id = breakpoint["id"]
@@ -81,9 +81,6 @@ def test_breakpoint_events(self):
                 breakpoint["verified"], "expect foo breakpoint to not be 
verified"
             )
 
-        # Make sure we're stopped.
-        self.dap_server.wait_for_stopped()
-
         # Flush the breakpoint events.
         self.dap_server.wait_for_breakpoint_events(timeout=5)
 

diff  --git a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py 
b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
index 479a91208a66c..948c146d4da68 100644
--- a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
+++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
@@ -9,7 +9,7 @@
 import lldbdap_testcase
 
 
-class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
     def send_async_req(self, command: str, arguments={}) -> int:
         seq = self.dap_server.sequence
         self.dap_server.send_packet(
@@ -45,14 +45,13 @@ def test_pending_request(self):
         """
         program = self.getBuildArtifact("a.out")
         self.build_and_launch(program, stopOnEntry=True)
-        self.continue_to_next_stop()
 
         # Use a relatively short timeout since this is only to ensure the
         # following request is queued.
         blocking_seq = self.async_blocking_request(duration=1.0)
         # Use a longer timeout to ensure we catch if the request was 
interrupted
         # properly.
-        pending_seq = self.async_blocking_request(duration=self.timeoutval / 2)
+        pending_seq = 
self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2)
         cancel_seq = self.async_cancel(requestId=pending_seq)
 
         blocking_resp = self.dap_server.recv_packet(filter_type=["response"])
@@ -78,12 +77,11 @@ def test_inflight_request(self):
         """
         program = self.getBuildArtifact("a.out")
         self.build_and_launch(program, stopOnEntry=True)
-        self.continue_to_next_stop()
 
-        blocking_seq = self.async_blocking_request(duration=self.timeoutval / 
2)
+        blocking_seq = 
self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2)
         # Wait for the sleep to start to cancel the inflight request.
         self.collect_console(
-            timeout_secs=self.timeoutval,
+            timeout_secs=self.DEFAULT_TIMEOUT,
             pattern="starting sleep",
         )
         cancel_seq = self.async_cancel(requestId=blocking_seq)

diff  --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py 
b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
index 223258fbdd3dc..ea6b2ea7f28ab 100644
--- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
+++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
@@ -75,11 +75,12 @@ def 
test_command_directive_abort_on_error_attach_commands(self):
         )
         command_abort_on_error = "settings set foo bar"
         program = self.build_and_create_debug_adapter_for_attach()
-        self.attach(
-            program,
+        resp = self.attach(
+            program=program,
             attachCommands=["?!" + command_quiet, "!" + 
command_abort_on_error],
             expectFailure=True,
         )
+        self.assertFalse(resp["success"], "expected 'attach' failure")
         full_output = self.collect_console(
             timeout_secs=1.0,
             pattern=command_abort_on_error,

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 a94288c7a669e..75876c248f86c 100644
--- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
+++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
@@ -43,12 +43,12 @@ 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):
+    def setup_debuggee(self):
         program = self.getBuildArtifact("a.out")
         source = "main.cpp"
         self.build_and_launch(
             program,
-            stopOnEntry=stopOnEntry,
+            stopOnEntry=True,
             sourceBreakpoints=[
                 (
                     source,
@@ -64,7 +64,7 @@ def test_command_completions(self):
         """
         Tests completion requests for lldb commands, within "repl-mode=command"
         """
-        self.setup_debugee()
+        self.setup_debuggee()
         self.continue_to_next_stop()
 
         res = self.dap_server.request_evaluate(
@@ -143,7 +143,7 @@ def test_variable_completions(self):
         """
         Tests completion requests in "repl-mode=variable"
         """
-        self.setup_debugee()
+        self.setup_debuggee()
         self.continue_to_next_stop()
 
         res = self.dap_server.request_evaluate(
@@ -241,7 +241,7 @@ def test_auto_completions(self):
         """
         Tests completion requests in "repl-mode=auto"
         """
-        self.setup_debugee(stopOnEntry=True)
+        self.setup_debuggee()
 
         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 0b428994e7fba..1f810afdbb667 100644
--- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
+++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
@@ -177,7 +177,7 @@ def test_diagnositcs(self):
         )
 
         diagnostics = self.collect_important(
-            timeout_secs=self.timeoutval, pattern="minidump file"
+            timeout_secs=self.DEFAULT_TIMEOUT, pattern="minidump file"
         )
 
         self.assertIn(

diff  --git a/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py 
b/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
index 1896acea15a99..e678c5ee77fdc 100644
--- a/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
+++ b/lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
@@ -2,7 +2,6 @@
 Test lldb-dap coreFile attaching
 """
 
-
 import dap_server
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -19,7 +18,7 @@ def test_core_file(self):
         core_file = os.path.join(current_dir, "linux-x86_64.core")
 
         self.create_debug_adapter()
-        self.attach(exe_file, coreFile=core_file)
+        self.attach(program=exe_file, coreFile=core_file)
 
         expected_frames = [
             {
@@ -51,7 +50,8 @@ def test_core_file(self):
         self.assertEqual(self.get_stackFrames(), expected_frames)
 
         # Resuming should have no effect and keep the process stopped
-        self.continue_to_next_stop()
+        resp = self.dap_server.request_continue()
+        self.assertFalse(resp["success"])
         self.assertEqual(self.get_stackFrames(), expected_frames)
 
         self.dap_server.request_next(threadId=32259)
@@ -67,7 +67,7 @@ def test_core_file_source_mapping_array(self):
         self.create_debug_adapter()
 
         source_map = [["/home/labath/test", current_dir]]
-        self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
+        self.attach(program=exe_file, coreFile=core_file, sourceMap=source_map)
 
         self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
 
@@ -81,6 +81,6 @@ def test_core_file_source_mapping_object(self):
         self.create_debug_adapter()
 
         source_map = {"/home/labath/test": current_dir}
-        self.attach(exe_file, coreFile=core_file, sourceMap=source_map)
+        self.attach(program=exe_file, coreFile=core_file, sourceMap=source_map)
 
         self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])

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 ec7387dabb0c2..f044bcae41892 100644
--- a/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
+++ b/lldb/test/API/tools/lldb-dap/exception/TestDAP_exception.py
@@ -16,6 +16,7 @@ def test_stopped_description(self):
         """
         program = self.getBuildArtifact("a.out")
         self.build_and_launch(program)
+        self.do_continue()
 
         self.assertTrue(self.verify_stop_exception_info("signal SIGABRT"))
         exceptionInfo = self.get_exceptionInfo()

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 7c85f05c1ba45..0063954791fd5 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -75,9 +75,7 @@ def test_termination(self):
         self.dap_server.request_disconnect()
 
         # Wait until the underlying lldb-dap process dies.
-        self.dap_server.process.wait(
-            timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval
-        )
+        self.dap_server.process.wait(timeout=self.DEFAULT_TIMEOUT)
 
         # Check the return code
         self.assertEqual(self.dap_server.process.poll(), 0)
@@ -90,15 +88,16 @@ def test_stopOnEntry(self):
         program = self.getBuildArtifact("a.out")
         self.build_and_launch(program, stopOnEntry=True)
 
-        stopped_events = self.dap_server.wait_for_stopped()
-        for stopped_event in stopped_events:
-            if "body" in stopped_event:
-                body = stopped_event["body"]
-                if "reason" in body:
-                    reason = body["reason"]
-                    self.assertNotEqual(
-                        reason, "breakpoint", 'verify stop isn\'t "main" 
breakpoint'
-                    )
+        self.assertTrue(
+            len(self.dap_server.thread_stop_reasons) > 0,
+            "expected stopped event during launch",
+        )
+        for _, body in self.dap_server.thread_stop_reasons.items():
+            if "reason" in body:
+                reason = body["reason"]
+                self.assertNotEqual(
+                    reason, "breakpoint", 'verify stop isn\'t "main" 
breakpoint'
+                )
 
     @skipIfWindows
     def test_cwd(self):
@@ -393,14 +392,14 @@ def test_commands(self):
         # Get output from the console. This should contain both the
         # "stopCommands" that were run after the first breakpoint was hit
         self.continue_to_breakpoints(breakpoint_ids)
-        output = 
self.get_console(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.verify_commands("stopCommands", output, stopCommands)
 
         # Continue again and hit the second breakpoint.
         # Get output from the console. This should contain both the
         # "stopCommands" that were run after the second breakpoint was hit
         self.continue_to_breakpoints(breakpoint_ids)
-        output = 
self.get_console(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.verify_commands("stopCommands", output, stopCommands)
 
         # Continue until the program exits
@@ -462,21 +461,21 @@ def test_extra_launch_commands(self):
         self.verify_commands("launchCommands", output, launchCommands)
         # Verify the "stopCommands" here
         self.continue_to_next_stop()
-        output = 
self.get_console(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.verify_commands("stopCommands", output, stopCommands)
 
         # Continue and hit the second breakpoint.
         # Get output from the console. This should contain both the
         # "stopCommands" that were run after the first breakpoint was hit
         self.continue_to_next_stop()
-        output = 
self.get_console(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.verify_commands("stopCommands", output, stopCommands)
 
         # Continue until the program exits
         self.continue_to_exit()
         # Get output from the console. This should contain both the
         # "exitCommands" that were run after the second breakpoint was hit
-        output = 
self.get_console(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.verify_commands("exitCommands", output, exitCommands)
 
     def test_failing_launch_commands(self):
@@ -531,7 +530,7 @@ def test_terminate_commands(self):
 
         terminateCommands = ["expr 4+2"]
         self.launch(
-            program=program,
+            program,
             stopOnEntry=True,
             terminateCommands=terminateCommands,
             disconnectAutomatically=False,

diff  --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py 
b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
index 3fc0f752ee39e..b333efd7bfb1f 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -14,7 +14,7 @@ class TestDAP_module(lldbdap_testcase.DAPTestCaseBase):
     def run_test(self, symbol_basename, expect_debug_info_size):
         program_basename = "a.out.stripped"
         program = self.getBuildArtifact(program_basename)
-        self.build_and_launch(program)
+        self.build_and_launch(program, stopOnEntry=True)
         functions = ["foo"]
         breakpoint_ids = self.set_function_breakpoints(functions)
         self.assertEqual(len(breakpoint_ids), len(functions), "expect one 
breakpoint")
@@ -108,7 +108,7 @@ def test_modules_dsym(self):
     @skipIfWindows
     def test_compile_units(self):
         program = self.getBuildArtifact("a.out")
-        self.build_and_launch(program)
+        self.build_and_launch(program, stopOnEntry=True)
         source = "main.cpp"
         main_source_path = self.getSourcePath(source)
         breakpoint1_line = line_number(source, "// breakpoint 1")

diff  --git a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py 
b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
index 49131ad9ecb17..0425b55a5e552 100644
--- a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
+++ b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
@@ -37,14 +37,14 @@ def test_output(self):
         # Disconnecting from the server to ensure any pending IO is flushed.
         self.dap_server.request_disconnect()
 
-        output += 
self.get_stdout(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        output += self.get_stdout(timeout=self.DEFAULT_TIMEOUT)
         self.assertTrue(output and len(output) > 0, "expect program stdout")
         self.assertIn(
             "abcdefghi\r\nhello world\r\nfinally\0\0",
             output,
             "full stdout not found in: " + repr(output),
         )
-        console = self.get_console(timeout=self.timeoutval)
+        console = self.get_console(timeout=self.DEFAULT_TIMEOUT)
         self.assertTrue(console and len(console) > 0, "expect dap messages")
         self.assertIn(
             "out\0\0\r\nerr\0\0\r\n", console, f"full console message not 
found"

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 5f95c7bfb1556..8681b31e8eb1b 100644
--- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
+++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart.py
@@ -22,9 +22,8 @@ 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.verify_breakpoint_hit([bp_A])
-        self.dap_server.request_continue()
-        self.verify_breakpoint_hit([bp_B])
+        self.continue_to_breakpoints([bp_A])
+        self.continue_to_breakpoints([bp_B])
 
         # Make sure i has been modified from its initial value of 0.
         self.assertEqual(
@@ -34,8 +33,9 @@ def test_basic_functionality(self):
         )
 
         # Restart then check we stop back at A and program state has been 
reset.
-        self.dap_server.request_restart()
-        self.verify_breakpoint_hit([bp_A])
+        resp = self.dap_server.request_restart()
+        self.assertTrue(resp["success"])
+        self.continue_to_breakpoints([bp_A])
         self.assertEqual(
             int(self.dap_server.get_local_variable_value("i")),
             0,
@@ -50,27 +50,26 @@ def test_stopOnEntry(self):
         program = self.getBuildArtifact("a.out")
         self.build_and_launch(program, stopOnEntry=True)
         [bp_main] = self.set_function_breakpoints(["main"])
-        self.dap_server.request_configurationDone()
-
         # Once the "configuration done" event is sent, we should get a stopped
         # event immediately because of stopOnEntry.
-        stopped_events = self.dap_server.wait_for_stopped()
-        for stopped_event in stopped_events:
-            if "body" in stopped_event:
-                body = stopped_event["body"]
-                if "reason" in body:
-                    reason = body["reason"]
-                    self.assertNotEqual(
-                        reason, "breakpoint", 'verify stop isn\'t "main" 
breakpoint'
-                    )
+        self.assertTrue(
+            len(self.dap_server.thread_stop_reasons) > 0,
+            "expected stopped event during launch",
+        )
+        for _, body in self.dap_server.thread_stop_reasons.items():
+            if "reason" in body:
+                reason = body["reason"]
+                self.assertNotEqual(
+                    reason, "breakpoint", 'verify stop isn\'t "main" 
breakpoint'
+                )
 
         # Then, if we continue, we should hit the breakpoint at main.
-        self.dap_server.request_continue()
-        self.verify_breakpoint_hit([bp_main])
+        self.continue_to_breakpoints([bp_main])
 
         # Restart and check that we still get a stopped event before reaching
         # main.
-        self.dap_server.request_restart()
+        resp = self.dap_server.request_restart()
+        self.assertTrue(resp["success"])
         stopped_events = self.dap_server.wait_for_stopped()
         for stopped_event in stopped_events:
             if "body" in stopped_event:
@@ -96,8 +95,7 @@ def test_arguments(self):
         [bp_A] = self.set_source_breakpoints("main.c", [line_A])
 
         # Verify we hit A, then B.
-        self.dap_server.request_configurationDone()
-        self.verify_breakpoint_hit([bp_A])
+        self.continue_to_breakpoints([bp_A])
 
         # We don't set any arguments in the initial launch request, so argc
         # should be 1.
@@ -109,7 +107,7 @@ def test_arguments(self):
 
         # Restart with some extra 'args' and check that the new argc reflects
         # the updated launch config.
-        self.dap_server.request_restart(
+        resp = self.dap_server.request_restart(
             restartArguments={
                 "arguments": {
                     "program": program,
@@ -117,6 +115,7 @@ def test_arguments(self):
                 }
             }
         )
+        self.assertTrue(resp["success"])
         self.verify_breakpoint_hit([bp_A])
         self.assertEqual(
             int(self.dap_server.get_local_variable_value("argc")),

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 7e28a5af4331c..33e038408fa34 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
@@ -2,7 +2,6 @@
 Test stop hooks
 """
 
-
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 import lldbdap_testcase
@@ -17,10 +16,6 @@ def test_stop_hooks_before_run(self):
         program = self.getBuildArtifact("a.out")
         preRunCommands = ["target stop-hook add -o help"]
         self.build_and_launch(program, stopOnEntry=True, 
preRunCommands=preRunCommands)
-
-        # The first stop is on entry.
-        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
         # command interpreter is in synchronous mode while lldb-dap expects

diff  --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py 
b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
index 296e4911f4052..340be0b39010d 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -116,7 +116,7 @@ def darwin_dwarf_missing_obj(self, initCommands):
         self.create_debug_adapter()
         self.assertTrue(os.path.exists(program), "executable must exist")
 
-        self.launch(program=program, initCommands=initCommands)
+        self.launch(program, initCommands=initCommands)
 
         functions = ["main"]
         breakpoint_ids = self.set_function_breakpoints(functions)

diff  --git a/lldb/tools/lldb-dap/DAPError.cpp 
b/lldb/tools/lldb-dap/DAPError.cpp
index dcb955af0345f..60347d577f821 100644
--- a/lldb/tools/lldb-dap/DAPError.cpp
+++ b/lldb/tools/lldb-dap/DAPError.cpp
@@ -8,6 +8,7 @@
 
 #include "DAPError.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 #include <system_error>
 
 namespace lldb_dap {
@@ -26,4 +27,12 @@ std::error_code DAPError::convertToErrorCode() const {
   return llvm::inconvertibleErrorCode();
 }
 
+char NotStoppedError::ID;
+
+void NotStoppedError::log(llvm::raw_ostream &OS) const { OS << "not stopped"; }
+
+std::error_code NotStoppedError::convertToErrorCode() const {
+  return llvm::inconvertibleErrorCode();
+}
+
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/DAPError.h b/lldb/tools/lldb-dap/DAPError.h
index 564651b1f587d..4c94bdd6ac3d6 100644
--- a/lldb/tools/lldb-dap/DAPError.h
+++ b/lldb/tools/lldb-dap/DAPError.h
@@ -13,7 +13,7 @@
 
 namespace lldb_dap {
 
-/// An Error that is reported as a DAP Error Message, which may be presented to
+/// An error that is reported as a DAP Error Message, which may be presented to
 /// the user.
 class DAPError : public llvm::ErrorInfo<DAPError> {
 public:
@@ -40,4 +40,13 @@ class DAPError : public llvm::ErrorInfo<DAPError> {
   std::optional<std::string> m_url_label;
 };
 
+/// An error that indicates the current request handler cannot execute because
+/// the process is not stopped.
+class NotStoppedError : public llvm::ErrorInfo<NotStoppedError> {
+public:
+  static char ID;
+  void log(llvm::raw_ostream &OS) const override;
+  std::error_code convertToErrorCode() const override;
+};
+
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
index ca4c9141eca38..361c86421cf1b 100644
--- a/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ContinueRequestHandler.cpp
@@ -31,6 +31,9 @@ ContinueRequestHandler::Run(const ContinueArguments &args) 
const {
   SBProcess process = dap.target.GetProcess();
   SBError error;
 
+  if (!SBDebugger::StateIsStoppedState(process.GetState()))
+    return make_error<NotStoppedError>();
+
   if (args.singleThread)
     dap.GetLLDBThread(args.threadId).Resume(error);
   else
@@ -40,7 +43,7 @@ ContinueRequestHandler::Run(const ContinueArguments &args) 
const {
     return ToError(error);
 
   ContinueResponseBody body;
-  body.allThreadsContinued = args.singleThread;
+  body.allThreadsContinued = !args.singleThread;
   return body;
 }
 

diff  --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h 
b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index eaebaf6619bbd..383f9e24a729a 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -106,11 +106,13 @@ class RequestHandler : public BaseRequestHandler {
       DAP_LOG(dap.log,
               "({0}) malformed request {1}, expected arguments but got none",
               dap.transport.GetClientName(), request.command);
-      response.success = false;
-      response.message = llvm::formatv("arguments required for command '{0}' "
-                                       "but none received",
-                                       request.command)
-                             .str();
+      HandleErrorResponse(
+          llvm::make_error<DAPError>(
+              llvm::formatv("arguments required for command '{0}' "
+                            "but none received",
+                            request.command)
+                  .str()),
+          response);
       dap.Send(response);
       return;
     }
@@ -123,26 +125,21 @@ class RequestHandler : public BaseRequestHandler {
       OS << "invalid arguments for request '" << request.command
          << "': " << llvm::toString(root.getError()) << "\n";
       root.printErrorContext(*request.arguments, OS);
-
-      response.success = false;
-      response.body = ToResponse(llvm::make_error<DAPError>(parse_failure));
-
+      HandleErrorResponse(llvm::make_error<DAPError>(parse_failure), response);
       dap.Send(response);
       return;
     }
 
     if constexpr (std::is_same_v<Resp, llvm::Error>) {
       if (llvm::Error err = Run(arguments)) {
-        response.success = false;
-        response.body = ToResponse(std::move(err));
+        HandleErrorResponse(std::move(err), response);
       } else {
         response.success = true;
       }
     } else {
       Resp body = Run(arguments);
       if (llvm::Error err = body.takeError()) {
-        response.success = false;
-        response.body = ToResponse(std::move(err));
+        HandleErrorResponse(std::move(err), response);
       } else {
         response.success = true;
         response.body = std::move(*body);
@@ -172,26 +169,36 @@ class RequestHandler : public BaseRequestHandler {
   /// error.
   virtual void PostRun() const {};
 
-  protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
-    protocol::ErrorMessage error_message;
-    // Default to showing the user errors unless otherwise specified by a
-    // DAPError.
-    error_message.showUser = true;
-    error_message.sendTelemetry = false;
-    if (llvm::Error unhandled = llvm::handleErrors(
-            std::move(err), [&](const DAPError &E) -> llvm::Error {
-              error_message.format = E.getMessage();
-              error_message.showUser = E.getShowUser();
-              error_message.id = E.convertToErrorCode().value();
-              error_message.url = E.getURL();
-              error_message.urlLabel = E.getURLLabel();
-              return llvm::Error::success();
-            })) {
-      error_message.format = llvm::toString(std::move(unhandled));
-    }
-    protocol::ErrorResponseBody body;
-    body.error = error_message;
-    return body;
+  void HandleErrorResponse(llvm::Error err,
+                           protocol::Response &response) const {
+    response.success = false;
+    llvm::handleAllErrors(
+        std::move(err),
+        [&](const NotStoppedError &err) {
+          response.message = lldb_dap::protocol::eResponseMessageNotStopped;
+        },
+        [&](const DAPError &err) {
+          protocol::ErrorMessage error_message;
+          error_message.sendTelemetry = false;
+          error_message.format = err.getMessage();
+          error_message.showUser = err.getShowUser();
+          error_message.id = err.convertToErrorCode().value();
+          error_message.url = err.getURL();
+          error_message.urlLabel = err.getURLLabel();
+          protocol::ErrorResponseBody body;
+          body.error = error_message;
+          response.body = body;
+        },
+        [&](const llvm::ErrorInfoBase &err) {
+          protocol::ErrorMessage error_message;
+          error_message.showUser = true;
+          error_message.sendTelemetry = false;
+          error_message.format = err.message();
+          error_message.id = err.convertToErrorCode().value();
+          protocol::ErrorResponseBody body;
+          body.error = error_message;
+          response.body = body;
+        });
   }
 };
 


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to