-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb-dap] Test Gardening, improving DebugCommunication. #141689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Improving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness. Specifically, this refactored the `DebugCommunication._handle_recv_packet` function to ensure consistency with the reader thread when handling state changes and improved the `DebugCommunication._recv_packet` helper to make it easier to follow by adding some additional helpers.
@llvm/pr-subscribers-lldb Author: John Harrison (ashgti) ChangesImproving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness. Specifically, this refactored the Patch is 44.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/141689.diff 3 Files Affected:
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 a028381a0a4f9..cc235f4fe8b1a 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,13 +12,52 @@
import sys
import threading
import time
-from typing import Any, Optional, Union, BinaryIO, TextIO
+from typing import (
+ Any,
+ Optional,
+ Union,
+ BinaryIO,
+ TextIO,
+ TypedDict,
+ Literal,
+ Callable,
+ TypeVar,
+)
## DAP type references
-Event = dict[str, Any]
-Request = dict[str, Any]
-Response = dict[str, Any]
+
+
+class Event(TypedDict):
+ type: Literal["event"]
+ seq: Literal[0]
+ event: str
+ body: Optional[dict]
+
+
+class Request(TypedDict):
+ type: Literal["request"]
+ seq: int
+ command: str
+ arguments: Optional[dict]
+
+
+class Response(TypedDict):
+ type: Literal["response"]
+ seq: Literal[0]
+ request_seq: int
+ success: bool
+ command: str
+ message: Optional[str]
+ body: Optional[dict]
+
+
+_T = TypeVar("_T")
ProtocolMessage = Union[Event, Request, Response]
+# An internal type used for tracking protocol messages and an EOF sentinel
+# value. 'None' cannot easily be used as a sentinel because it is a falsey
+# value. When returned outside of DebugCommunication an EOFError is typically
+# converted into 'None'.
+_InternalProtocolMessage = Union[Event, Request, Response, EOFError]
def dump_memory(base_addr, data, num_per_line, outfile):
@@ -58,44 +97,42 @@ def dump_memory(base_addr, data, num_per_line, outfile):
outfile.write("\n")
-def read_packet(f, verbose=False, trace_file=None):
+def read_packet(
+ f: BinaryIO, verbose=False, trace_file=None
+) -> _InternalProtocolMessage:
"""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.
"""
- line = f.readline().decode("utf-8")
+ line = f.readline().decode()
if len(line) == 0:
- return None # EOF.
+ return EOFError() # EOF.
# Watch for line that starts with the prefix
prefix = "Content-Length: "
if line.startswith(prefix):
# Decode length of JSON bytes
if verbose:
- print('content: "%s"' % (line))
+ print("content:", line)
length = int(line[len(prefix) :])
if verbose:
- print('length: "%u"' % (length))
+ print("length:", length)
# Skip empty line
- line = f.readline()
+ line = f.readline().decode()
if verbose:
- print('empty: "%s"' % (line))
+ print("empty:", line)
# Read JSON bytes
- json_str = f.read(length)
+ json_str = f.read(length).decode()
if verbose:
- print('json: "%s"' % (json_str))
+ print("json:", json_str)
if trace_file:
- trace_file.write("from adapter:\n%s\n" % (json_str))
+ trace_file.write(f"from adapter:\n{json_str}\n")
# Decode the JSON bytes into a python dictionary
return json.loads(json_str)
raise Exception("unexpected malformed message from lldb-dap: " + line)
-def packet_type_is(packet, packet_type):
- return "type" in packet and packet["type"] == packet_type
-
-
-def dump_dap_log(log_file):
+def dump_dap_log(log_file: str):
print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr)
if log_file is None:
print("no log file available", file=sys.stderr)
@@ -124,8 +161,8 @@ def __init__(
def __str__(self):
return f"Source(name={self.name}, path={self.path}), source_reference={self.source_reference})"
- def as_dict(self):
- source_dict = {}
+ def as_dict(self) -> dict:
+ source_dict: dict[str, Any] = {}
if self._name is not None:
source_dict["name"] = self._name
if self._path is not None:
@@ -141,38 +178,42 @@ def __init__(
recv: BinaryIO,
send: BinaryIO,
init_commands: list[str],
- log_file: Optional[TextIO] = None,
+ log_file: Optional[str] = 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: list[Optional[ProtocolMessage]] = []
+ self.recv_packets: list[_InternalProtocolMessage] = []
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.init_commands = init_commands
self.initialize_body = None
+ self.initialized = False
+ self.configuration_done_sent = False
+ self.process_event_body: Optional[dict] = None
+ self.terminated = False
self.progress_events: list[Event] = []
- self.reverse_requests = []
+ self.reverse_requests: list[Request] = []
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
+
+ # debuggee state
+ self.threads = None
+ self.thread_stop_reasons: dict[str, Any] = {}
+ self.frame_scopes: dict[str, Any] = {}
+
+ self.recv_thread.start()
@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, command, response):
+ def validate_response(cls, command: Request, response: Response):
if command["command"] != response["command"]:
raise ValueError("command mismatch in response")
if command["seq"] != response["request_seq"]:
@@ -224,84 +265,91 @@ 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: Optional[ProtocolMessage]):
- self.recv_condition.acquire()
+ def _enqueue_recv_packet(self, packet: Union[ProtocolMessage, EOFError]):
self.recv_packets.append(packet)
self.recv_condition.notify()
- self.recv_condition.release()
- def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
+ def _handle_recv_packet(self, packet: _InternalProtocolMessage) -> 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.
"""
- # 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 packet_type == "response":
- if packet["command"] == "disconnect":
- keepGoing = False
- self._enqueue_recv_packet(packet)
- return keepGoing
+ # Hold the recv_condition for consistency of debugger state.
+ with self.recv_condition:
+ if isinstance(packet, EOFError):
+ self._enqueue_recv_packet(packet)
+ return False
+
+ keep_going = True
+
+ # Check the packet to see if is an event packet
+ if packet["type"] == "event" and "event" in packet:
+ event = packet["event"]
+ body = packet.get("body")
+ # Handle the event packet and cache DAP stateful information from
+ # these packets as they come in.
+ if event == "output" and body is not None:
+ # 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 keep_going
+ elif event == "initialized":
+ self.initialized = True
+ elif event == "process" and body is not None:
+ # 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 == "terminated":
+ # If we get the 'terminated' event then lldb-dap has exited
+ # itself.
+ self.terminated = True
+ elif event == "exited" and body is not None:
+ # Process exited, mark the status to indicate the process is not
+ # alive.
+ self.exit_status = body.get("exitCode", 0)
+ elif event == "continued" and body is not None:
+ # 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 is not None:
+ # 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 packet["type"] == "response":
+ if packet["command"] == "disconnect":
+ keep_going = False
+
+ elif packet["type"] == "request":
+ # Handle reverse requests and keep processing.
+ self._handle_reverse_request(packet)
+ return keep_going
+
+ self._enqueue_recv_packet(packet)
+ return keep_going
def _process_continued(self, all_threads_continued: bool):
self.threads = None
@@ -309,14 +357,63 @@ def _process_continued(self, all_threads_continued: bool):
if all_threads_continued:
self.thread_stop_reasons = {}
- def send_packet(self, command_dict: Request, set_sequence=True):
+ def _handle_reverse_request(self, request: Request):
+ 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)
+
+ def send_packet(self, command_dict: ProtocolMessage) -> int:
"""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"""
+ adapter."""
+ seq = 0
# Set the sequence ID for this command automatically
- if set_sequence:
- command_dict["seq"] = self.sequence
+ if command_dict["type"] == "request":
+ seq = command_dict["seq"] = self.sequence
self.sequence += 1
+ else:
+ command_dict["seq"] = 0
# Encode our command dictionary as a JSON string
json_str = json.dumps(command_dict, separators=(",", ":"))
if self.trace_file:
@@ -326,104 +423,70 @@ def send_packet(self, command_dict: Request, set_sequence=True):
# Send the encoded JSON packet and flush the 'send' file
self.send.write(self.encode_content(json_str))
self.send.flush()
+ return seq
- def recv_packet(
+ def receive_response(
self,
- filter_type: Optional[str] = None,
- filter_event: Optional[Union[str, list[str]]] = None,
+ seq: int,
+ ) -> Optional[Response]:
+ """Waits for the a response with the associated request_sec."""
+
+ def predicate(p: Response):
+ return p["type"] == "response" and p["request_seq"] == seq
+
+ return self._recv_packet(predicate=predicate)
+
+ def _recv_packet(
+ self,
+ *,
+ predicate: Callable[[_T], bool],
timeout: Optional[float] = None,
- ) -> Optional[ProtocolMessage]:
+ ) -> Optional[_T]:
"""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:
+ with self.recv_condition:
+
+ def _predicate():
+ return next(
+ filter(
+ lambda p: isinstance(p, EOFError) or predicate(p),
+ self.recv_packets,
+ ),
+ None,
+ )
+
+ packet = self.recv_condition.wait_for(_predicate, timeout=timeout)
+ if packet is None: # Timeout
+ return None
+ self.recv_packets.remove(packet)
+ if isinstance(packet, EOFError):
return None
- finally:
- self.recv_condition.release()
+ return packet
- def send_recv(self, command):
+ def _send_recv(self, command: Request) -> Optional[Response]:
"""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 receive...
[truncated]
|
This was split out from #140777 to make a smaller commit. |
if command_dict["type"] == "request": | ||
seq = command_dict["seq"] = self.sequence | ||
self.sequence += 1 | ||
else: | ||
command_dict["seq"] = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if command_dict["type"] == "request": | |
seq = command_dict["seq"] = self.sequence | |
self.sequence += 1 | |
else: | |
command_dict["seq"] = 0 | |
if command_dict["type"] == "request": | |
seq = self.sequence | |
self.sequence += 1 | |
command_dict["seq"] = seq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with a slightly different version, LMKWYT
"command": "setBreakpoints", | ||
"type": "request", | ||
"seq": 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we use self.sequence
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The send_packet
method will set the seq to the correct value but I need to have this defined or the type checker complains that the 'seq' key is missing.
…yped. To validate the types: ``` $ pip3 install mypy $ mypy --python-version 3.8 \ lldb/packages/Python/lldbsuite/**/*.py \ lldb/test/API/tools/lldb-dap/**/*.py ```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I think the types and classes will help a lot in the long run.
I also filed #141877 to see about running a python type checker as part of the CI job. |
Looks like my tests failed on the buildkit linux host, looking into why python3.10 failed. They passed on the 'CI Checks / Build and Test Linux' but that has python 3.12. |
…g the 'Generic[T]' and making the body/arguments field a plain 'dict'.
Okay, the latest version of this worked on buildkit for linux. I'll submit this, but if we get reports of it failing with other specific versions of python we may need to tweak things a bit. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/23448 Here is the relevant piece of the build log for the reference
|
Looking into the failure on the buildbot. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/18540 Here is the relevant piece of the build log for the reference
|
I'm surprised by some of these failures, since it passed on buildkite and the premerge check that are also on linux x86_64. Trying to understand the reason for these failures. |
same I ran it on my computer x86_64 linux fedora 42 no issues. I will update the |
Improving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness. Specifically, this refactored the `DebugCommunication._handle_recv_packet` function to ensure consistency with the reader thread when handling state changes and improved the `DebugCommunication._recv_packet` helper to make it easier to follow by adding some additional helpers.
The test for disassembly is failing because the test is wrong. The memory reference can have a corresponding source location. if there is debuginfo installed on the computer or it is downloaded with debuginfod. llvm-project/lldb/test/API/tools/lldb-dap/disassemble/TestDAP_disassemble.py Lines 30 to 34 in 84a69a0
|
Improving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness. Specifically, this refactored the `DebugCommunication._handle_recv_packet` function to ensure consistency with the reader thread when handling state changes and improved the `DebugCommunication._recv_packet` helper to make it easier to follow by adding some additional helpers.
This PR seemingly made |
Also have tests failing on Arm and AArch64 Linux: |
Sounds like the failure we see:
We have
We don't have httplib installed though and don't see the URLs env var (https://documentation.ubuntu.com/server/explanation/debugging/about-debuginfod/index.html). So we could be getting the location from a dev or debug info package from apt. |
Sorry, the above failure is a different assumption. Our disassembly test failure is:
We have the libc debug symbols installed:
Could the test work with a function marked with (perhaps that will still have a source location) |
…1689)" This reverts commit 8a49db3. Due to failures on Arm and AArch64 Linux: https://lab.llvm.org/buildbot/#/builders/59/builds/18540 https://lab.llvm.org/buildbot/#/builders/18/builds/16759 File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 22, in assertEvaluateFailure self.assertNotIn( AssertionError: 'result' unexpectedly found in {'memoryReference': '0xFFFFF7CB3060', 'result': '0x0000000000000000', 'type': 'int *', 'variablesReference': 7} FAIL: test_generic_evaluate_expressions (TestDAP_evaluate.TestDAP_evaluate) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 228, in test_generic_evaluate_expressions self.run_test_evaluate_expressions(enableAutoVariableSummaries=False) File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 117, in run_test_evaluate_expressions self.assertEvaluateFailure("list") # local variable of a_function File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 22, in assertEvaluateFailure self.assertNotIn( AssertionError: 'result' unexpectedly found in {'memoryReference': '0xFFFFF7CB3060', 'result': '0x0000000000000000', 'type': 'int *', 'variablesReference': 7} Config=aarch64-/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang The second one is because our bots have the libc debug info package installed, the first, no idea.
Reverted, please take a look and fix. Reproducing should be possible by installing the libc debug package, not sure what's going on with the other failure. Let me know if you need me to investigate it locally. |
Changing the test for disassemble to not depend on source location. |
doh... I'll fix this when I make a new attempt at merging this PR in.
Taking a look. I'll rebase this on #142129 and see if that fixes it. |
Improving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness. Specifically, this refactored the `DebugCommunication._handle_recv_packet` function to ensure consistency with the reader thread when handling state changes and improved the `DebugCommunication._recv_packet` helper to make it easier to follow by adding some additional helpers.
…lvm#142030) Show the expected value in the error message so we can see the expected value without searching through the log messages. Related llvm#141689
…m#141689)" This reverts commit 8a49db3. Due to failures on Arm and AArch64 Linux: https://lab.llvm.org/buildbot/#/builders/59/builds/18540 https://lab.llvm.org/buildbot/#/builders/18/builds/16759 File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 22, in assertEvaluateFailure self.assertNotIn( AssertionError: 'result' unexpectedly found in {'memoryReference': '0xFFFFF7CB3060', 'result': '0x0000000000000000', 'type': 'int *', 'variablesReference': 7} FAIL: test_generic_evaluate_expressions (TestDAP_evaluate.TestDAP_evaluate) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 228, in test_generic_evaluate_expressions self.run_test_evaluate_expressions(enableAutoVariableSummaries=False) File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 117, in run_test_evaluate_expressions self.assertEvaluateFailure("list") # local variable of a_function File "/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py", line 22, in assertEvaluateFailure self.assertNotIn( AssertionError: 'result' unexpectedly found in {'memoryReference': '0xFFFFF7CB3060', 'result': '0x0000000000000000', 'type': 'int *', 'variablesReference': 7} Config=aarch64-/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang The second one is because our bots have the libc debug info package installed, the first, no idea.
Improving the readability and correctness of DebugCommunication by adding type annotations to many parts of the library and trying to improve the implementation of a few key areas of the code to better handle correctness.
Specifically, this refactored the
DebugCommunication._handle_recv_packet
function to ensure consistency with the reader thread when handling state changes and improved theDebugCommunication._recv_packet
helper to make it easier to follow by adding some additional helpers.