[Lldb-commits] [lldb] [lldb-dap] Retry unbuffered reads. (PR #165823)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Thu Oct 30 21:04:55 PDT 2025
https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/165823
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.
I think this should resolve issue #165784
>From 5d046d4f0ca79750b5f79c1f46cf9d4202bd69c8 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
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)
More information about the lldb-commits
mailing list