From 5d046d4f0ca79750b5f79c1f46cf9d4202bd69c8 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 30 Oct 2025 21:03:29 -0700 Subject: [PATCH] [lldb-dap] Retry unbuffered reads. We need to use an unbuffered socket / pipe to the lldb-dap subprocess to make sure that when we perform a select, there is no partial messages in the file buffer. If there are partial messages in the file buffer we could end up doing a read that crosses the buffer size and results in a hang until a new message is sent, that may or may not occur. As a result, when we perform a read we can get less than the requested number of bytes. We need to retry the read operation if this occurs. --- .../lldbsuite/test/tools/lldb-dap/dap_server.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 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 8f3652172dfdf..5000a8ec0a0ce 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 @@ -220,6 +220,11 @@ def _read_packet( followed by the JSON bytes from self.recv. Returns None on EOF. """ + # NOTE: We open the socket or pipe to the subprocess in an unbuffered + # mode to ensure we do not end up with a partial message in the buffer + # when we perform our select. Otherwise, we may run into a case where we + # attempt a read and the buffer is partially full but a new message is + # not sent to fill in the requested buffer size. ready = self.selector.select(timeout) if not ready: warnings.warn( @@ -243,10 +248,16 @@ def _read_packet( if separator != "": Exception("malformed DAP content header, unexpected line: " + separator) # Read JSON bytes - json_str = self.recv.read(length).decode() + # NOTE: Because the read channel is unbuffered we may receive less + # than the requested number of bytes. In, which case we need to + # perform a new read. + json_str = b"" + while len(json_str) < length: + json_str += self.recv.read(length - len(json_str)) + if self.trace_file: self.trace_file.write( - "%s from adapter:\n%s\n" % (time.time(), json_str) + "%s from adapter:\n%s\n" % (time.time(), json_str.decode()) ) # Decode the JSON bytes into a python dictionary return json.loads(json_str)