[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