https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/144616
…sistency. (#143818)" This reverts commit 362b9d78b4ee9107da2b5e90b3764b0f0fa610fe. Buildbots using python3.10 are running into errors from this change. >From 117982fb34200d307f9ce4c0e1907a441a05247e Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Tue, 17 Jun 2025 16:00:20 -0700 Subject: [PATCH] Revert "[lldb-dap] Refactoring DebugCommunication to improve test consistency. (#143818)" This reverts commit 362b9d78b4ee9107da2b5e90b3764b0f0fa610fe. Buildbots using python3.10 are running into errors from this change. --- .../test/tools/lldb-dap/dap_server.py | 875 +++++++----------- .../test/tools/lldb-dap/lldbdap_testcase.py | 79 +- .../breakpoint/TestDAP_setBreakpoints.py | 5 +- .../tools/lldb-dap/cancel/TestDAP_cancel.py | 10 +- .../tools/lldb-dap/launch/TestDAP_launch.py | 12 +- .../tools/lldb-dap/module/TestDAP_module.py | 2 +- .../tools/lldb-dap/output/TestDAP_output.py | 4 +- 7 files changed, 397 insertions(+), 590 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 23178a215206e..6d32491eaa5e9 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 @@ -10,124 +10,17 @@ import subprocess import signal import sys -from dataclasses import dataclass import threading import time -from typing import ( - IO, - Any, - Callable, - Dict, - List, - Optional, - Tuple, - TypeVar, - Generic, - TypedDict, - Union, - BinaryIO, - TextIO, - Literal, - cast, -) +from typing import Any, Optional, Union, BinaryIO, TextIO ## DAP type references - -T = TypeVar("T") -Te = TypeVar("Te") # Generic type for event body -Ta = TypeVar("Ta") # Generic type for request arguments -Tb = TypeVar("Tb") # Generic type for response body - - -class Event(Generic[Te], TypedDict): - type: Literal["event"] - seq: int - event: str - body: Optional[Te] - - -class Request(Generic[Ta], TypedDict, total=False): - type: Literal["request"] - seq: int - command: str - arguments: Ta - - -class Response(Generic[Tb], TypedDict): - type: Literal["response"] - seq: int - request_seq: int - success: bool - command: str - message: Optional[str] - body: Optional[Tb] - - +Event = dict[str, Any] +Request = dict[str, Any] +Response = dict[str, Any] ProtocolMessage = Union[Event, Request, Response] -class AttachOrLaunchArguments(TypedDict, total=False): - stopOnEntry: bool - disableASLR: bool - disableSTDIO: bool - enableAutoVariableSummaries: bool - displayExtendedBacktrace: bool - enableSyntheticChildDebugging: bool - initCommands: List[str] - preRunCommands: List[str] - postRunCommands: List[str] - stopCommands: List[str] - exitCommands: List[str] - terminateCommands: List[str] - sourceMap: Union[List[Tuple[str, str]], Dict[str, str]] - sourcePath: str - debuggerRoot: str - commandEscapePrefix: str - customFrameFormat: str - customThreadFormat: str - - -class LaunchArguments(AttachOrLaunchArguments, total=False): - program: str - args: List[str] - cwd: str - env: Dict[str, str] - shellExpandArguments: bool - runInTerminal: bool - launchCommands: List[str] - - -# Using the function form of TypedDict to allow for hyphenated keys. -AttachGdbServer = TypedDict( - "AttachGdbServer", {"gdb-remote-port": int, "gdb-remote-hostname": str}, total=False -) - - -class AttachArguments(AttachGdbServer, AttachOrLaunchArguments, total=False): - program: str - pid: int - waitFor: bool - attachCommands: List[str] - coreFile: str - - -class BreakpointData(TypedDict, total=False): - column: int - condition: str - hitCondition: str - logMessage: str - mode: str - - -class SourceBreakpoint(BreakpointData): - line: int - - -class Breakpoint(TypedDict, total=False): - id: int - verified: bool - - def dump_memory(base_addr, data, num_per_line, outfile): data_len = len(data) hex_string = binascii.hexlify(data) @@ -165,9 +58,7 @@ def dump_memory(base_addr, data, num_per_line, outfile): outfile.write("\n") -def read_packet( - f: IO[bytes], trace_file: Optional[IO[str]] = None -) -> Optional[ProtocolMessage]: +def read_packet(f, verbose=False, trace_file=None): """Decode a JSON packet that starts with the content length and is followed by the JSON bytes from a file 'f'. Returns None on EOF. """ @@ -179,20 +70,32 @@ def read_packet( prefix = "Content-Length: " if line.startswith(prefix): # Decode length of JSON bytes + if verbose: + print('content: "%s"' % (line)) length = int(line[len(prefix) :]) + if verbose: + print('length: "%u"' % (length)) # Skip empty line - line = f.readline().decode() + line = f.readline() + if verbose: + print('empty: "%s"' % (line)) # Read JSON bytes json_str = f.read(length) + if verbose: + print('json: "%s"' % (json_str)) if trace_file: - trace_file.write(f"from adapter:\n{json_str!r}\n") + trace_file.write("from adapter:\n%s\n" % (json_str)) # Decode the JSON bytes into a python dictionary return json.loads(json_str) raise Exception("unexpected malformed message from lldb-dap: " + line) -def dump_dap_log(log_file: Optional[str]) -> None: +def packet_type_is(packet, packet_type): + return "type" in packet and packet["type"] == packet_type + + +def dump_dap_log(log_file): print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr) if log_file is None: print("no log file available", file=sys.stderr) @@ -202,30 +105,34 @@ def dump_dap_log(log_file: Optional[str]) -> None: print("========= END =========", file=sys.stderr) -@dataclass -class Source: - path: Optional[str] - source_reference: Optional[int] - - @property - def name(self) -> Optional[str]: - if not self.path: - return None - return os.path.basename(self.path) - +class Source(object): def __init__( self, path: Optional[str] = None, source_reference: Optional[int] = None ): - if path is None and source_reference is None: + self._name = None + self._path = None + self._source_reference = None + + if path is not None: + self._name = os.path.basename(path) + self._path = path + elif source_reference is not None: + self._source_reference = source_reference + else: raise ValueError("Either path or source_reference must be provided") - self.path = path - self.source_reference = source_reference + def __str__(self): + return f"Source(name={self.name}, path={self.path}), source_reference={self.source_reference})" - def to_DAP(self) -> dict: - if self.path: - return {"path": self.path, "name": self.name} - return {"sourceReference": self.source_reference} + def as_dict(self): + source_dict = {} + if self._name is not None: + source_dict["name"] = self._name + if self._path is not None: + source_dict["path"] = self._path + if self._source_reference is not None: + source_dict["sourceReference"] = self._source_reference + return source_dict class NotSupportedError(KeyError): @@ -237,7 +144,7 @@ def __init__( self, recv: BinaryIO, send: BinaryIO, - init_commands: List[str], + init_commands: list[str], log_file: Optional[TextIO] = None, ): # For debugging test failures, try setting `trace_file = sys.stderr`. @@ -245,50 +152,35 @@ def __init__( self.log_file = log_file self.send = send self.recv = recv - # Packets that have been received and processed but have not yet been - # requested by a test case. - self._pending_packets: List[Optional[ProtocolMessage]] = [] - # Received packets that have not yet been processed. - self._recv_packets: List[Optional[ProtocolMessage]] = [] - # Used as a mutex for _recv_packets and for notify when _recv_packets - # changes. - self._recv_condition = threading.Condition() - self._recv_thread = threading.Thread(target=self._read_packet_thread) - - # session state - self.init_commands = init_commands + self.recv_packets: list[Optional[ProtocolMessage]] = [] + self.recv_condition = threading.Condition() + self.recv_thread = threading.Thread(target=self._read_packet_thread) + self.process_event_body = None self.exit_status: Optional[int] = None - self.capabilities: Optional[Dict] = None - self.initialized: bool = False - self.configuration_done_sent: bool = False - self.process_event_body: Optional[Dict] = None - self.terminated: bool = False - self.events: List[Event] = [] - self.progress_events: List[Event] = [] - self.reverse_requests: List[Request] = [] - self.module_events: List[Dict] = [] - self.sequence: int = 1 - self.output: Dict[str, str] = {} - - # debuggee state - self.threads: Optional[dict] = None - self.thread_stop_reasons: Dict[str, Any] = {} - self.frame_scopes: Dict[str, Any] = {} - # keyed by breakpoint id - self.resolved_breakpoints: Dict[str, bool] = {} - - # trigger enqueue thread - self._recv_thread.start() + self.capabilities: dict[str, Any] = {} + 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: dict[str, list[str]] = {} + self.configuration_done_sent = False + self.initialized = False + self.frame_scopes = {} + self.init_commands = init_commands + self.resolved_breakpoints = {} @classmethod def encode_content(cls, s: str) -> bytes: return ("Content-Length: %u\r\n\r\n%s" % (len(s), s)).encode("utf-8") @classmethod - def validate_response(cls, request: Request, response: Response) -> None: - if request["command"] != response["command"]: + def validate_response(cls, command, response): + if command["command"] != response["command"]: raise ValueError("command mismatch in response") - if request["seq"] != response["request_seq"]: + if command["seq"] != response["request_seq"]: raise ValueError("seq mismatch in response") def _read_packet_thread(self): @@ -297,323 +189,262 @@ def _read_packet_thread(self): 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. + # 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 _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool: - """Handles an incoming packet. + def get_modules(self): + module_list = self.request_modules()["body"]["modules"] + modules = {} + for module in module_list: + modules[module["name"]] = module + return modules - 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 - indicate a new packet is available. + def get_output(self, category, timeout=0.0, clear=True): + self.output_condition.acquire() + output = None + if category in self.output: + output = self.output[category] + if clear: + del self.output[category] + elif timeout != 0.0: + self.output_condition.wait(timeout) + if category in self.output: + output = self.output[category] + if clear: + del self.output[category] + self.output_condition.release() + return output - Args: - packet: A new packet to store. + def collect_output(self, category, timeout_secs, pattern, clear=True): + end_time = time.time() + timeout_secs + collected_output = "" + while end_time > time.time(): + output = self.get_output(category, timeout=0.25, clear=clear) + if output: + collected_output += output + if pattern is not None and pattern in output: + break + return collected_output if collected_output else None + + 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() - Returns: - True if the caller should keep calling this function for more - packets. - """ - with self._recv_condition: - self._recv_packets.append(packet) - self._recv_condition.notify() - # packet is None on EOF - return packet is not None and not ( - packet["type"] == "response" and packet["command"] == "disconnect" - ) - - def _recv_packet( - self, - *, - predicate: Optional[Callable[[ProtocolMessage], bool]] = None, - timeout: Optional[float] = None, - ) -> Optional[ProtocolMessage]: - """Processes received packets from the adapter. - - Updates the DebugCommunication stateful properties based on the received - packets in the order they are received. - - NOTE: The only time the session state properties should be updated is - during this call to ensure consistency during tests. - - Args: - predicate: - Optional, if specified, returns the first packet that matches - the given predicate. - timeout: - Optional, if specified, processes packets until either the - timeout occurs or the predicate matches a packet, whichever - occurs first. - - Returns: - The first matching packet for the given predicate, if specified, - otherwise None. + 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 + indicate a new packet is available. Returns True if the caller + should keep calling this function for more packets. """ - assert ( - threading.current_thread != self._recv_thread - ), "Must not be called from the _recv_thread" - - def process_until_match(): - self._process_recv_packets() - for i, packet in enumerate(self._pending_packets): - if packet is None: - # We need to return a truthy value to break out of the - # wait_for, use `EOFError` as an indicator of EOF. - return EOFError() - if predicate and predicate(packet): - self._pending_packets.pop(i) - return packet - - with self._recv_condition: - packet = self._recv_condition.wait_for(process_until_match, timeout) - return None if isinstance(packet, EOFError) else packet - - def _process_recv_packets(self) -> None: - """Process received packets, updating the session state.""" - with self._recv_condition: - for packet in self._recv_packets: - # Handle events that may modify any stateful properties of - # the DAP session. - if packet and packet["type"] == "event": - self._handle_event(packet) - elif packet and packet["type"] == "request": - # Handle reverse requests and keep processing. - self._handle_reverse_request(packet) - # Move the packet to the pending queue. - self._pending_packets.append(packet) - self._recv_packets.clear() - - def _handle_event(self, packet: Event) -> None: - """Handle any events that modify debug session state we track.""" - event = packet["event"] - body: Optional[Dict] = packet.get("body", None) - - if event == "output" and body: - # Store any output we receive so clients can retrieve it later. - category = body["category"] - output = body["output"] - if category in self.output: - self.output[category] += output - else: - self.output[category] = output - elif event == "initialized": - self.initialized = True - elif event == "process": - # 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" and body: - # Process exited, mark the status to indicate the process is not - # alive. - self.exit_status = body["exitCode"] - elif event == "continued" and body: - # 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" and body: - # Each thread that stops with a reason will send a - # 'stopped' event. We need to remember the thread stop - # reasons since the 'threads' command doesn't return - # that information. - self._process_stopped() - tid = body["threadId"] - self.thread_stop_reasons[tid] = body - elif event.startswith("progress"): - # Progress events come in as 'progressStart', 'progressUpdate', - # and 'progressEnd' events. Keep these around in case test - # cases want to verify them. - self.progress_events.append(packet) - elif event == "breakpoint" and body: - # Breakpoint events are sent when a breakpoint is resolved - self._update_verified_breakpoints([body["breakpoint"]]) - elif event == "capabilities" and body: - if self.capabilities is None: - self.capabilities = {} - # Update the capabilities with new ones from the event. - self.capabilities.update(body["capabilities"]) - - def _handle_reverse_request(self, request: Request) -> None: - if request in self.reverse_requests: - return - self.reverse_requests.append(request) - arguments = request.get("arguments") - if request["command"] == "runInTerminal" and arguments is not None: - in_shell = arguments.get("argsCanBeInterpretedByShell", False) - proc = subprocess.Popen( - arguments["args"], - env=arguments.get("env", {}), - cwd=arguments["cwd"], - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - shell=in_shell, - ) - body = {} - if in_shell: - body["shellProcessId"] = proc.pid - else: - body["processId"] = proc.pid - self.send_packet( - { - "type": "response", - "seq": 0, - "request_seq": request["seq"], - "success": True, - "command": "runInTerminal", - "message": None, - "body": body, - } - ) - elif request["command"] == "startDebugging": - self.send_packet( - { - "type": "response", - "seq": 0, - "request_seq": request["seq"], - "success": True, - "message": None, - "command": "startDebugging", - "body": {}, - } - ) - else: - desc = 'unknown reverse request "%s"' % (request["command"]) - raise ValueError(desc) + # If EOF, notify the read thread by enqueuing a None. + if not packet: + self._enqueue_recv_packet(None) + return False + + # Check the packet to see if is an event packet + keepGoing = True + packet_type = packet["type"] + if packet_type == "event": + event = packet["event"] + body = None + if "body" in packet: + body = packet["body"] + # Handle the event packet and cache information from these packets + # as they come in + if event == "output": + # Store any output we receive so clients can retrieve it later. + category = body["category"] + output = body["output"] + self.output_condition.acquire() + if category in self.output: + self.output[category] += output + else: + self.output[category] = output + self.output_condition.notify() + self.output_condition.release() + # no need to add 'output' event packets to our packets list + return keepGoing + elif event == "initialized": + self.initialized = True + elif event == "process": + # 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 + # reasons since the 'threads' command doesn't return + # that information. + self._process_stopped() + tid = body["threadId"] + self.thread_stop_reasons[tid] = body + elif event.startswith("progress"): + # Progress events come in as 'progressStart', 'progressUpdate', + # and 'progressEnd' events. Keep these around in case test + # cases want to verify them. + self.progress_events.append(packet) + elif event == "breakpoint": + # Breakpoint events are sent when a breakpoint is resolved + self._update_verified_breakpoints([body["breakpoint"]]) + elif event == "capabilities": + # Update the capabilities with new ones from the event. + self.capabilities.update(body["capabilities"]) + + elif packet_type == "response": + if packet["command"] == "disconnect": + keepGoing = False + self._enqueue_recv_packet(packet) + return keepGoing def _process_continued(self, all_threads_continued: bool): self.frame_scopes = {} if all_threads_continued: self.thread_stop_reasons = {} - def _update_verified_breakpoints(self, breakpoints: list[Breakpoint]): - for bp in breakpoints: - # If no id is set, we cannot correlate the given breakpoint across - # requests, ignore it. - if "id" not in bp: - continue - - self.resolved_breakpoints[str(bp["id"])] = bp.get("verified", False) - - def _send_recv(self, request: Request[Ta]) -> Optional[Response[Tb]]: - """Send a command python dictionary as JSON and receive the JSON - response. Validates that the response is the correct sequence and - command in the reply. Any events that are received are added to the - events list in this object""" - seq = self.send_packet(request) - response = self.receive_response(seq) - if response is None: - raise ValueError(f"no response for {request!r}") - self.validate_response(request, response) - return response + def _update_verified_breakpoints(self, breakpoints: list[Event]): + for breakpoint in breakpoints: + if "id" in breakpoint: + self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get( + "verified", False + ) - def send_packet(self, packet: ProtocolMessage) -> int: + 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. - - Returns the seq of the packet.""" - # Set the seq for requests. - if packet["type"] == "request": - packet["seq"] = self.sequence + adapter""" + # Set the sequence ID for this command automatically + if set_sequence: + command_dict["seq"] = self.sequence self.sequence += 1 - else: - packet["seq"] = 0 - # Encode our command dictionary as a JSON string - json_str = json.dumps(packet, separators=(",", ":")) - + json_str = json.dumps(command_dict, separators=(",", ":")) if self.trace_file: self.trace_file.write("to adapter:\n%s\n" % (json_str)) - length = len(json_str) if length > 0: # Send the encoded JSON packet and flush the 'send' file self.send.write(self.encode_content(json_str)) self.send.flush() - return packet["seq"] - - def receive_response(self, seq: int) -> Optional[Response]: - """Waits for the a response with the associated request_sec.""" - - def predicate(p: ProtocolMessage): - return p["type"] == "response" and p["request_seq"] == seq - - return cast(Optional[Response], self._recv_packet(predicate=predicate)) - - def get_modules(self): - modules = {} - resp = self.request_modules() - if resp["success"]: - module_list = resp["body"]["modules"] - for module in module_list: - modules[module["name"]] = module - else: - raise ValueError(f"request_modules failed: {resp!r}") - return modules - - def get_output(self, category: str, clear=True) -> str: - output = "" - if category in self.output: - output = self.output.get(category, "") - if clear: - del self.output[category] - return output - - def collect_output( + def recv_packet( self, - category: str, - timeout_secs: float, - pattern: Optional[str] = None, - clear=True, - ) -> str: - """Collect output from 'output' events. - - Args: - category: The category to collect. - timeout_secs: The max duration for collecting output. - pattern: - Optional, if set, return once this pattern is detected in the - collected output. - - Returns: - The collected output. - """ - deadline = time.monotonic() + timeout_secs - output = self.get_output(category, clear) - while deadline >= time.monotonic() and ( - pattern is None or pattern not in output - ): - event = self.wait_for_event(["output"], timeout=deadline - time.monotonic()) - if not event: # Timeout or EOF - break - output += self.get_output(category, clear=clear) - return output + 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 + function will wait for the packet to arrive and return it when + it does.""" + while True: + try: + self.recv_condition.acquire() + packet = None + while True: + for i, curr_packet in enumerate(self.recv_packets): + if not curr_packet: + raise EOFError + packet_type = curr_packet["type"] + if filter_type is None or packet_type in filter_type: + if filter_event is None or ( + packet_type == "event" + and curr_packet["event"] in filter_event + ): + packet = self.recv_packets.pop(i) + break + if packet: + break + # Sleep until packet is received + len_before = len(self.recv_packets) + self.recv_condition.wait(timeout) + len_after = len(self.recv_packets) + if len_before == len_after: + return None # Timed out + return packet + except EOFError: + return None + finally: + self.recv_condition.release() + + 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 + command in the reply. Any events that are received are added to the + events list in this object""" + self.send_packet(command) + done = False + while not done: + response_or_request = self.recv_packet(filter_type=["response", "request"]) + if response_or_request is None: + desc = 'no response for "%s"' % (command["command"]) + raise ValueError(desc) + if response_or_request["type"] == "response": + self.validate_response(command, response_or_request) + return response_or_request + else: + self.reverse_requests.append(response_or_request) + if response_or_request["command"] == "runInTerminal": + subprocess.Popen( + response_or_request["arguments"]["args"], + env=response_or_request["arguments"]["env"], + ) + self.send_packet( + { + "type": "response", + "request_seq": response_or_request["seq"], + "success": True, + "command": "runInTerminal", + "body": {}, + }, + ) + elif response_or_request["command"] == "startDebugging": + self.send_packet( + { + "type": "response", + "request_seq": response_or_request["seq"], + "success": True, + "command": "startDebugging", + "body": {}, + }, + ) + else: + desc = 'unknown reverse request "%s"' % ( + response_or_request["command"] + ) + raise ValueError(desc) + + return None def wait_for_event( - self, filter: List[str] = [], timeout: Optional[float] = None + self, filter: Union[str, list[str]], timeout: Optional[float] = None ) -> Optional[Event]: """Wait for the first event that matches the filter.""" - - def predicate(p: ProtocolMessage): - return p["type"] == "event" and p["event"] in filter - - return cast( - Optional[Event], self._recv_packet(predicate=predicate, timeout=timeout) + return self.recv_packet( + filter_type="event", filter_event=filter, timeout=timeout ) def wait_for_stopped( self, timeout: Optional[float] = None - ) -> Optional[List[Event]]: + ) -> Optional[list[Event]]: stopped_events = [] stopped_event = self.wait_for_event( filter=["stopped", "exited"], timeout=timeout @@ -632,9 +463,9 @@ def wait_for_stopped( return stopped_events def wait_for_breakpoint_events(self, timeout: Optional[float] = None): - breakpoint_events: List[Event] = [] + breakpoint_events: list[Event] = [] while True: - event = self.wait_for_event(["breakpoint"], timeout=timeout) + event = self.wait_for_event("breakpoint", timeout=timeout) if not event: break breakpoint_events.append(event) @@ -645,26 +476,20 @@ def wait_for_breakpoints_to_be_verified( ): """Wait for all breakpoints to be verified. Return all unverified breakpoints.""" while any(id not in self.resolved_breakpoints for id in breakpoint_ids): - breakpoint_event = self.wait_for_event(["breakpoint"], timeout=timeout) + breakpoint_event = self.wait_for_event("breakpoint", timeout=timeout) if breakpoint_event is None: break - return [ - id - for id in breakpoint_ids - if id not in self.resolved_breakpoints and not self.resolved_breakpoints[id] - ] + return [id for id in breakpoint_ids if id not in self.resolved_breakpoints] def wait_for_exited(self, timeout: Optional[float] = None): - event_dict = self.wait_for_event(["exited"], timeout=timeout) + 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, timeout: Optional[float] = None): - if self.terminated: - raise ValueError("already terminated") - event_dict = self.wait_for_event(["terminated"], timeout) + event_dict = self.wait_for_event("terminated", timeout) if event_dict is None: raise ValueError("didn't get terminated event") return event_dict @@ -699,10 +524,12 @@ def get_stackFrame(self, frameIndex=0, threadId=None): if threadId is None: threadId = self.get_thread_id() if threadId is None: + print("invalid threadId") return None response = self.request_stackTrace(threadId, startFrame=frameIndex, levels=1) if response: return response["body"]["stackFrames"][0] + print("invalid response") return None def get_completions(self, text, frameId=None): @@ -840,7 +667,7 @@ def request_attach( gdbRemotePort: Optional[int] = None, gdbRemoteHostname: Optional[str] = None, ): - args_dict: AttachArguments = {} + args_dict = {} if pid is not None: args_dict["pid"] = pid if program is not None: @@ -872,12 +699,8 @@ def request_attach( args_dict["gdb-remote-port"] = gdbRemotePort if gdbRemoteHostname is not None: args_dict["gdb-remote-hostname"] = gdbRemoteHostname - command_dict: Request = { - "command": "attach", - "type": "request", - "arguments": args_dict, - } - return self._send_recv(command_dict) + command_dict = {"command": "attach", "type": "request", "arguments": args_dict} + return self.send_recv(command_dict) def request_breakpointLocations( self, file_path, line, end_line=None, column=None, end_column=None @@ -899,7 +722,7 @@ def request_breakpointLocations( "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_configurationDone(self): command_dict = { @@ -907,7 +730,7 @@ def request_configurationDone(self): "type": "request", "arguments": {}, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if response: self.configuration_done_sent = True self.request_threads() @@ -936,7 +759,7 @@ def request_continue(self, threadId=None, singleThread=False): "type": "request", "arguments": args_dict, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if response["success"]: self._process_continued(response["body"]["allThreadsContinued"]) # Caller must still call wait_for_stopped. @@ -953,7 +776,7 @@ def request_restart(self, restartArguments=None): if restartArguments: command_dict["arguments"] = restartArguments - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) # Caller must still call wait_for_stopped. return response @@ -969,7 +792,7 @@ def request_disconnect(self, terminateDebuggee=None): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_disassemble( self, @@ -989,7 +812,7 @@ def request_disassemble( "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict)["body"]["instructions"] + return self.send_recv(command_dict)["body"]["instructions"] def request_readMemory(self, memoryReference, offset, count): args_dict = { @@ -1002,7 +825,7 @@ def request_readMemory(self, memoryReference, offset, count): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None): stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId) @@ -1018,7 +841,7 @@ def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_exceptionInfo(self, threadId=None): if threadId is None: @@ -1029,7 +852,7 @@ def request_exceptionInfo(self, threadId=None): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_initialize(self, sourceInitFile=False): command_dict = { @@ -1050,7 +873,7 @@ def request_initialize(self, sourceInitFile=False): "$__lldb_sourceInitFile": sourceInitFile, }, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if response: if "body" in response: self.capabilities = response["body"] @@ -1085,7 +908,7 @@ def request_launch( customFrameFormat: Optional[str] = None, customThreadFormat: Optional[str] = None, ): - args_dict: LaunchArguments = {"program": program} + args_dict = {"program": program} if args: args_dict["args"] = args if cwd: @@ -1132,19 +955,15 @@ def request_launch( args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace if commandEscapePrefix is not None: args_dict["commandEscapePrefix"] = commandEscapePrefix - command_dict: Request = { - "command": "launch", - "type": "request", - "arguments": args_dict, - } - return self._send_recv(command_dict) + command_dict = {"command": "launch", "type": "request", "arguments": args_dict} + return self.send_recv(command_dict) def request_next(self, threadId, granularity="statement"): if self.exit_status is not None: raise ValueError("request_continue called after process exited") args_dict = {"threadId": threadId, "granularity": granularity} command_dict = {"command": "next", "type": "request", "arguments": args_dict} - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_stepIn(self, threadId, targetId, granularity="statement"): if self.exit_status is not None: @@ -1157,7 +976,7 @@ def request_stepIn(self, threadId, targetId, granularity="statement"): "granularity": granularity, } command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict} - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_stepInTargets(self, frameId): if self.exit_status is not None: @@ -1169,14 +988,14 @@ def request_stepInTargets(self, frameId): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_stepOut(self, threadId): if self.exit_status is not None: raise ValueError("request_stepOut called after process exited") args_dict = {"threadId": threadId} command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict} - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_pause(self, threadId=None): if self.exit_status is not None: @@ -1185,47 +1004,49 @@ def request_pause(self, threadId=None): threadId = self.get_thread_id() args_dict = {"threadId": threadId} command_dict = {"command": "pause", "type": "request", "arguments": args_dict} - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_scopes(self, frameId): args_dict = {"frameId": frameId} command_dict = {"command": "scopes", "type": "request", "arguments": args_dict} - return self._send_recv(command_dict) + return self.send_recv(command_dict) - def request_setBreakpoints( - self, - source: Union[Source, str], - line_array: Optional[List[int]], - data: Optional[List[BreakpointData]] = None, - ): + def request_setBreakpoints(self, source: Source, line_array, data=None): """data is array of parameters for breakpoints in line_array. Each parameter object is 1:1 mapping with entries in line_entry. It contains optional location/hitCondition/logMessage parameters. """ - if isinstance(source, str): - source = Source(path=source) args_dict = { - "source": source.to_DAP(), + "source": source.as_dict(), "sourceModified": False, } - if line_array: + if line_array is not None: args_dict["lines"] = line_array breakpoints = [] for i, line in enumerate(line_array): - breakpoint_data: BreakpointData = {} + breakpoint_data = None if data is not None and i < len(data): breakpoint_data = data[i] - bp: SourceBreakpoint = {"line": line, **breakpoint_data} + bp = {"line": line} + if breakpoint_data is not None: + if breakpoint_data.get("condition"): + bp["condition"] = breakpoint_data["condition"] + if breakpoint_data.get("hitCondition"): + bp["hitCondition"] = breakpoint_data["hitCondition"] + if breakpoint_data.get("logMessage"): + bp["logMessage"] = breakpoint_data["logMessage"] + if breakpoint_data.get("column"): + bp["column"] = breakpoint_data["column"] breakpoints.append(bp) args_dict["breakpoints"] = breakpoints - command_dict: Request = { + command_dict = { "command": "setBreakpoints", "type": "request", "arguments": args_dict, } - response = self._send_recv(command_dict) - if response and response["success"] and response["body"]: + response = self.send_recv(command_dict) + if response["success"]: self._update_verified_breakpoints(response["body"]["breakpoints"]) return response @@ -1240,7 +1061,7 @@ def request_setExceptionBreakpoints( "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=None): breakpoints = [] @@ -1257,7 +1078,7 @@ def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=Non "type": "request", "arguments": args_dict, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if response["success"]: self._update_verified_breakpoints(response["body"]["breakpoints"]) return response @@ -1278,7 +1099,7 @@ def request_dataBreakpointInfo( "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_setDataBreakpoint(self, dataBreakpoints): """dataBreakpoints is a list of dictionary with following fields: @@ -1295,7 +1116,7 @@ def request_setDataBreakpoint(self, dataBreakpoints): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_compileUnits(self, moduleId): args_dict = {"moduleId": moduleId} @@ -1304,7 +1125,7 @@ def request_compileUnits(self, moduleId): "type": "request", "arguments": args_dict, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) return response def request_completions(self, text, frameId=None): @@ -1316,10 +1137,10 @@ def request_completions(self, text, frameId=None): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_modules(self): - return self._send_recv({"command": "modules", "type": "request"}) + return self.send_recv({"command": "modules", "type": "request"}) def request_stackTrace( self, threadId=None, startFrame=None, levels=None, format=None, dump=False @@ -1338,7 +1159,7 @@ def request_stackTrace( "type": "request", "arguments": args_dict, } - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if dump: for idx, frame in enumerate(response["body"]["stackFrames"]): name = frame["name"] @@ -1364,7 +1185,7 @@ def request_source(self, sourceReference): "sourceReference": sourceReference, }, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_threads(self): """Request a list of all threads and combine any information from any @@ -1372,7 +1193,7 @@ def request_threads(self): thread actually stopped. Returns an array of thread dictionaries with information about all threads""" command_dict = {"command": "threads", "type": "request", "arguments": {}} - response = self._send_recv(command_dict) + response = self.send_recv(command_dict) if not response["success"]: self.threads = None return response @@ -1412,7 +1233,7 @@ def request_variables( "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_setVariable(self, containingVarRef, name, value, id=None): args_dict = { @@ -1427,7 +1248,7 @@ def request_setVariable(self, containingVarRef, name, value, id=None): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_locations(self, locationReference): args_dict = { @@ -1438,7 +1259,7 @@ def request_locations(self, locationReference): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def request_testGetTargetBreakpoints(self): """A request packet used in the LLDB test suite to get all currently @@ -1450,12 +1271,12 @@ def request_testGetTargetBreakpoints(self): "type": "request", "arguments": {}, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) def terminate(self): self.send.close() - if self._recv_thread.is_alive(): - self._recv_thread.join() + if self.recv_thread.is_alive(): + self.recv_thread.join() def request_setInstructionBreakpoints(self, memory_reference=[]): breakpoints = [] @@ -1470,7 +1291,7 @@ def request_setInstructionBreakpoints(self, memory_reference=[]): "type": "request", "arguments": args_dict, } - return self._send_recv(command_dict) + return self.send_recv(command_dict) class DebugAdapterServer(DebugCommunication): 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 8778b51e7c360..3b54d598c3509 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,6 +1,6 @@ import os import time -from typing import Optional, Callable +from typing import Optional import uuid import dap_server @@ -121,19 +121,11 @@ def wait_for_breakpoints_to_resolve( f"Expected to resolve all breakpoints. Unresolved breakpoint ids: {unresolved_breakpoints}", ) - def wait_until( - self, - predicate: Callable[[], bool], - delay: float = 0.5, - timeout: float = DEFAULT_TIMEOUT, - ) -> bool: - """Repeatedly run the predicate until either the predicate returns True - or a timeout has occurred.""" - deadline = time.monotonic() + timeout - while deadline > time.monotonic(): - if predicate(): + def waitUntil(self, condition_callback): + for _ in range(20): + if condition_callback(): return True - time.sleep(delay) + time.sleep(0.5) return False def assertCapabilityIsSet(self, key: str, msg: Optional[str] = None) -> None: @@ -152,7 +144,6 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT): "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""" - breakpoint_ids = [str(i) for i in breakpoint_ids] stopped_events = self.dap_server.wait_for_stopped(timeout) for stopped_event in stopped_events: if "body" in stopped_event: @@ -164,16 +155,22 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT): and body["reason"] != "instruction breakpoint" ): continue - if "hitBreakpointIds" not in body: + if "description" not in body: continue - hit_breakpoint_ids = body["hitBreakpointIds"] - for bp in hit_breakpoint_ids: - if str(bp) in breakpoint_ids: + # Descriptions for breakpoints will be in the form + # "breakpoint 1.1", so look for any description that matches + # ("breakpoint 1.") in the description field as verification + # that one of the breakpoint locations was hit. DAP doesn't + # allow breakpoints to have multiple locations, but LLDB does. + # So when looking at the description we just want to make sure + # the right breakpoint matches and not worry about the actual + # location. + description = body["description"] + for breakpoint_id in breakpoint_ids: + match_desc = f"breakpoint {breakpoint_id}." + if match_desc in description: return - self.assertTrue( - False, - f"breakpoint not hit, wanted breakpoint_ids={breakpoint_ids} stopped_events={stopped_events}", - ) + self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}") def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEOUT): """Wait for the process we are debugging to stop, and verify the stop @@ -208,9 +205,7 @@ def verify_commands(self, flavor, output, commands): found = True break self.assertTrue( - found, - "verify '%s' found in console output for '%s' in %s" - % (cmd, flavor, output), + found, "verify '%s' found in console output for '%s'" % (cmd, flavor) ) def get_dict_value(self, d, key_path): @@ -282,30 +277,26 @@ def get_source_and_line(self, threadId=None, frameIndex=0): return (source["path"], stackFrame["line"]) return ("", 0) - def get_stdout(self): - return self.dap_server.get_output("stdout") + def get_stdout(self, timeout=0.0): + return self.dap_server.get_output("stdout", timeout=timeout) - def get_console(self): - return self.dap_server.get_output("console") + def get_console(self, timeout=0.0): + return self.dap_server.get_output("console", timeout=timeout) - def get_important(self): - return self.dap_server.get_output("important") + def get_important(self, timeout=0.0): + return self.dap_server.get_output("important", timeout=timeout) - def collect_stdout(self, timeout_secs: float, pattern: Optional[str] = None) -> str: + def collect_stdout(self, timeout_secs, pattern=None): return self.dap_server.collect_output( "stdout", timeout_secs=timeout_secs, pattern=pattern ) - def collect_console( - self, timeout_secs: float, pattern: Optional[str] = None - ) -> str: + def collect_console(self, timeout_secs, pattern=None): return self.dap_server.collect_output( "console", timeout_secs=timeout_secs, pattern=pattern ) - def collect_important( - self, timeout_secs: float, pattern: Optional[str] = None - ) -> str: + def collect_important(self, timeout_secs, pattern=None): return self.dap_server.collect_output( "important", timeout_secs=timeout_secs, pattern=pattern ) @@ -364,7 +355,7 @@ def stepOut(self, threadId=None, waitForStop=True, timeout=DEFAULT_TIMEOUT): return self.dap_server.wait_for_stopped(timeout) return None - def do_continue(self) -> None: # `continue` is a keyword. + def do_continue(self): # `continue` is a keyword. resp = self.dap_server.request_continue() self.assertTrue(resp["success"], f"continue request failed: {resp}") @@ -372,14 +363,10 @@ def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT): self.do_continue() return self.dap_server.wait_for_stopped(timeout) - def continue_to_breakpoint( - self, breakpoint_id: int, timeout: Optional[float] = DEFAULT_TIMEOUT - ) -> None: - self.continue_to_breakpoints([breakpoint_id], timeout) + def continue_to_breakpoint(self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT): + self.continue_to_breakpoints((breakpoint_id), timeout) - def continue_to_breakpoints( - self, breakpoint_ids: list[int], timeout: Optional[float] = DEFAULT_TIMEOUT - ) -> None: + def continue_to_breakpoints(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT): self.do_continue() self.verify_breakpoint_hit(breakpoint_ids, timeout) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index a6eeee3a02543..831edd6494c1e 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -78,7 +78,7 @@ def test_source_map(self): self.assertFalse(breakpoint["verified"]) self.assertEqual(other_basename, breakpoint["source"]["name"]) self.assertEqual(new_other_path, breakpoint["source"]["path"]) - other_breakpoint_id = str(breakpoint["id"]) + other_breakpoint_id = breakpoint["id"] self.dap_server.request_continue() self.verify_breakpoint_hit([other_breakpoint_id]) @@ -379,8 +379,7 @@ def test_column_breakpoints(self): self.assertEqual(breakpoint["line"], loop_line) self.assertEqual(breakpoint["column"], columns[index]) self.assertTrue(breakpoint["verified"], "expect breakpoint verified") - self.assertIn("id", breakpoint, "expected breakpoint id") - breakpoint_ids.append(str(breakpoint["id"])) + breakpoint_ids.append(breakpoint["id"]) # Continue to the first breakpoint, self.continue_to_breakpoints([breakpoint_ids[0]]) 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 c750cff071a80..824ed8fe3bb97 100644 --- a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py +++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py @@ -54,18 +54,18 @@ def test_pending_request(self): pending_seq = self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2) cancel_seq = self.async_cancel(requestId=pending_seq) - blocking_resp = self.dap_server.receive_response(blocking_seq) + blocking_resp = self.dap_server.recv_packet(filter_type=["response"]) self.assertEqual(blocking_resp["request_seq"], blocking_seq) self.assertEqual(blocking_resp["command"], "evaluate") self.assertEqual(blocking_resp["success"], True) - pending_resp = self.dap_server.receive_response(pending_seq) + pending_resp = self.dap_server.recv_packet(filter_type=["response"]) self.assertEqual(pending_resp["request_seq"], pending_seq) self.assertEqual(pending_resp["command"], "evaluate") self.assertEqual(pending_resp["success"], False) self.assertEqual(pending_resp["message"], "cancelled") - cancel_resp = self.dap_server.receive_response(cancel_seq) + cancel_resp = self.dap_server.recv_packet(filter_type=["response"]) self.assertEqual(cancel_resp["request_seq"], cancel_seq) self.assertEqual(cancel_resp["command"], "cancel") self.assertEqual(cancel_resp["success"], True) @@ -86,13 +86,13 @@ def test_inflight_request(self): ) cancel_seq = self.async_cancel(requestId=blocking_seq) - blocking_resp = self.dap_server.receive_response(blocking_seq) + blocking_resp = self.dap_server.recv_packet(filter_type=["response"]) self.assertEqual(blocking_resp["request_seq"], blocking_seq) self.assertEqual(blocking_resp["command"], "evaluate") self.assertEqual(blocking_resp["success"], False) self.assertEqual(blocking_resp["message"], "cancelled") - cancel_resp = self.dap_server.receive_response(cancel_seq) + cancel_resp = self.dap_server.recv_packet(filter_type=["response"]) self.assertEqual(cancel_resp["request_seq"], cancel_seq) self.assertEqual(cancel_resp["command"], "cancel") self.assertEqual(cancel_resp["success"], True) 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 c29e0d3fa7b81..ae8142ae4f484 100644 --- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py +++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py @@ -191,7 +191,7 @@ def test_disableSTDIO(self): self.continue_to_exit() # Now get the STDOUT and verify our program argument is correct output = self.get_stdout() - self.assertEqual(output, "", "expect no program output") + self.assertEqual(output, None, "expect no program output") @skipIfWindows @skipIfLinux # shell argument expansion doesn't seem to work on Linux @@ -392,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() + 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() + output = self.get_console(timeout=self.DEFAULT_TIMEOUT) self.verify_commands("stopCommands", output, stopCommands) # Continue until the program exits @@ -461,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() + 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() + 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() + output = self.get_console(timeout=self.DEFAULT_TIMEOUT) self.verify_commands("exitCommands", output, exitCommands) def test_failing_launch_commands(self): 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 b1823e4c8b1c3..4fc221668a8ee 100644 --- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py +++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py @@ -54,7 +54,7 @@ def checkSymbolsLoadedWithSize(): return symbol_regex.match(program_module["symbolStatus"]) if expect_debug_info_size: - self.wait_until(checkSymbolsLoadedWithSize) + self.waitUntil(checkSymbolsLoadedWithSize) active_modules = self.dap_server.get_modules() program_module = active_modules[program_basename] self.assertEqual(program_basename, program_module["name"]) 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 4fcde623e3829..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() + 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() + 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" _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits