[Lldb-commits] [lldb] [lldb-dap] Ensure we do not print the close sentinel when closing stdout. (PR #126833)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Wed Feb 12 10:45:07 PST 2025
https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/126833
>From af07cbad6524cfb0509f93565eb86584e4c3576c Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 11 Feb 2025 16:58:20 -0800
Subject: [PATCH 1/2] [lldb-dap] Ensure we do not print the close sentinel when
closing stdout.
The OutputRedirect is always writing the `"\0"` byte as a final stdout message during shutdown. Instead, I adjusted this to detect the sentinel value and break out of the read loop as if we detected EOF.
---
lldb/tools/lldb-dap/OutputRedirector.cpp | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index a23572ab7ae04..c5090338cfd1c 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -10,6 +10,7 @@
#include "DAP.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
+#include <cstring>
#include <system_error>
#if defined(_WIN32)
#include <fcntl.h>
@@ -25,6 +26,10 @@ using llvm::Expected;
using llvm::inconvertibleErrorCode;
using llvm::StringRef;
+namespace {
+char kCloseSentinel[] = "\0";
+} // namespace
+
namespace lldb_dap {
int OutputRedirector::kInvalidDescriptor = -1;
@@ -59,7 +64,10 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
while (!m_stopped) {
ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer));
// EOF detected.
- if (bytes_count == 0)
+ if (bytes_count == 0 ||
+ (bytes_count == sizeof(kCloseSentinel) &&
+ std::memcmp(buffer, kCloseSentinel, sizeof(kCloseSentinel)) == 0 &&
+ m_fd == kInvalidDescriptor))
break;
if (bytes_count == -1) {
// Skip non-fatal errors.
@@ -85,8 +93,7 @@ void OutputRedirector::Stop() {
// Closing the pipe may not be sufficient to wake up the thread in case the
// write descriptor is duplicated (to stdout/err or to another process).
// Write a null byte to ensure the read call returns.
- char buf[] = "\0";
- (void)::write(fd, buf, sizeof(buf));
+ (void)::write(fd, kCloseSentinel, sizeof(kCloseSentinel));
::close(fd);
m_forwarder.join();
}
>From 941df4009f36f15060452a54b152bcc535a1e5e1 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 12 Feb 2025 10:43:45 -0800
Subject: [PATCH 2/2] Applying reviewer suggestions and updating the existing
tests to ensure output produced by the debuggee is not affected by the output
redirection.
---
.../tools/lldb-dap/output/TestDAP_output.py | 24 ++++++++++++------
lldb/test/API/tools/lldb-dap/output/main.c | 3 +++
lldb/tools/lldb-dap/OutputRedirector.cpp | 25 +++++++------------
lldb/tools/lldb-dap/OutputRedirector.h | 1 -
4 files changed, 29 insertions(+), 24 deletions(-)
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 02c34ba10321b..eae7629409844 100644
--- a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
+++ b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
@@ -10,23 +10,33 @@
class TestDAP_output(lldbdap_testcase.DAPTestCaseBase):
@skipIfWindows
def test_output(self):
+ """
+ Test output handling for the running process.
+ """
program = self.getBuildArtifact("a.out")
- self.build_and_launch(program)
+ self.build_and_launch(
+ program,
+ exitCommands=[
+ # Ensure that output produced by lldb itself is not consumed by the OutputRedirector.
+ "?script print('out\\0\\0', end='\\r\\n', file=sys.stdout)",
+ "?script print('err\\0\\0', end='\\r\\n', file=sys.stderr)",
+ ],
+ )
source = "main.c"
lines = [line_number(source, "// breakpoint 1")]
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.continue_to_breakpoints(breakpoint_ids)
-
+
# Ensure partial messages are still sent.
output = self.collect_stdout(timeout_secs=1.0, pattern="abcdef")
- self.assertTrue(output and len(output) > 0, "expect no program output")
+ self.assertTrue(output and len(output) > 0, "expect program stdout")
self.continue_to_exit()
-
+
output += self.get_stdout(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
- self.assertTrue(output and len(output) > 0, "expect no program output")
+ self.assertTrue(output and len(output) > 0, "expect program stdout")
self.assertIn(
- "abcdefghi\r\nhello world\r\n",
+ "abcdefghi\r\nhello world\r\nfinally\0\0out\0\0\r\nerr\0\0",
output,
- 'full output not found in: ' + output,
+ "full stdout not found in: " + repr(output),
)
diff --git a/lldb/test/API/tools/lldb-dap/output/main.c b/lldb/test/API/tools/lldb-dap/output/main.c
index 0cfcf604aa68f..226cb607a1234 100644
--- a/lldb/test/API/tools/lldb-dap/output/main.c
+++ b/lldb/test/API/tools/lldb-dap/output/main.c
@@ -8,5 +8,8 @@ int main() {
printf("def");
printf("ghi\n");
printf("hello world\n"); // breakpoint 1
+ // Ensure the OutputRedirector does not consume the programs \0\0 output.
+ char buf[] = "finally\0";
+ write(STDOUT_FILENO, buf, sizeof(buf));
return 0;
}
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index c5090338cfd1c..dddef8f6f2b43 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -19,16 +19,9 @@
#include <unistd.h>
#endif
-using lldb_private::Pipe;
-using llvm::createStringError;
-using llvm::Error;
-using llvm::Expected;
-using llvm::inconvertibleErrorCode;
-using llvm::StringRef;
+using namespace llvm;
-namespace {
-char kCloseSentinel[] = "\0";
-} // namespace
+static constexpr auto kCloseSentinel = StringLiteral::withInnerNUL("\0");
namespace lldb_dap {
@@ -63,12 +56,6 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
char buffer[OutputBufferSize];
while (!m_stopped) {
ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer));
- // EOF detected.
- if (bytes_count == 0 ||
- (bytes_count == sizeof(kCloseSentinel) &&
- std::memcmp(buffer, kCloseSentinel, sizeof(kCloseSentinel)) == 0 &&
- m_fd == kInvalidDescriptor))
- break;
if (bytes_count == -1) {
// Skip non-fatal errors.
if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
@@ -76,6 +63,12 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
break;
}
+ StringRef data(buffer, bytes_count);
+ if (m_stopped)
+ data.consume_back(kCloseSentinel);
+ if (data.empty())
+ break;
+
callback(StringRef(buffer, bytes_count));
}
::close(read_fd);
@@ -93,7 +86,7 @@ void OutputRedirector::Stop() {
// Closing the pipe may not be sufficient to wake up the thread in case the
// write descriptor is duplicated (to stdout/err or to another process).
// Write a null byte to ensure the read call returns.
- (void)::write(fd, kCloseSentinel, sizeof(kCloseSentinel));
+ (void)::write(fd, kCloseSentinel.data(), kCloseSentinel.size());
::close(fd);
m_forwarder.join();
}
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
index d2bd39797f3d7..a47ea96f71f14 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ b/lldb/tools/lldb-dap/OutputRedirector.h
@@ -9,7 +9,6 @@
#ifndef LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
#define LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
-#include "lldb/Host/Pipe.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <atomic>
More information about the lldb-commits
mailing list