Skip to content

Commit adb9ef0

Browse files
authored
[lldb-dap] Partially reverting OutputRedirector changes. (#125136)
I just noticed with these changes lldb-dap was using 200% of my CPU and root causing the issue it seems that lldb_private::Pipe::Read() (without a timeout) is using a timeout of `std::chrono::microseconds::zero()` which ends up setting the SelectHelper timeout to `now + 0` (see https://github.com/llvm/llvm-project/blob/7ceef1b1824073fcfd4724539f5942442da1a9c2/lldb/source/Host/posix/PipePosix.cpp#L314 and https://github.com/llvm/llvm-project/blob/7ceef1b1824073fcfd4724539f5942442da1a9c2/lldb/source/Utility/SelectHelper.cpp#L46) which causes SelectHelper to return immediately with a timeout error. As a result the `lldb_dap::OutputRedirector::RedirectTo()` to turn into a busy loop. Additionally, the 'read' call is waiting until the output buffer is full before returning, which prevents any partial output (see https://github.com/llvm/llvm-project/blob/7ceef1b1824073fcfd4724539f5942442da1a9c2/lldb/source/Host/posix/PipePosix.cpp#L325C9-L326C17). This is not the desired behavior for lldb-dap. Instead we want a write to the FD to result in a callback to send the DAP Output event, which mean we want partial output. To mitigate this, I'm reverting the reading operation to the previous behavior before 873426b but keeping the refactored structure and thread management.
1 parent 012e0a0 commit adb9ef0

File tree

2 files changed

+46
-26
lines changed

2 files changed

+46
-26
lines changed

lldb/tools/lldb-dap/OutputRedirector.cpp

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
//
77
//===----------------------------------------------------------------------===/
88

9+
#include "OutputRedirector.h"
10+
#include "DAP.h"
11+
#include "llvm/ADT/StringRef.h"
912
#include "llvm/Support/Error.h"
1013
#include <system_error>
1114
#if defined(_WIN32)
@@ -15,45 +18,59 @@
1518
#include <unistd.h>
1619
#endif
1720

18-
#include "DAP.h"
19-
#include "OutputRedirector.h"
20-
#include "llvm/ADT/StringRef.h"
21-
2221
using lldb_private::Pipe;
23-
using lldb_private::Status;
2422
using llvm::createStringError;
2523
using llvm::Error;
2624
using llvm::Expected;
25+
using llvm::inconvertibleErrorCode;
2726
using llvm::StringRef;
2827

2928
namespace lldb_dap {
3029

30+
int OutputRedirector::kInvalidDescriptor = -1;
31+
32+
OutputRedirector::OutputRedirector() : m_fd(kInvalidDescriptor) {}
33+
3134
Expected<int> OutputRedirector::GetWriteFileDescriptor() {
32-
if (!m_pipe.CanWrite())
35+
if (m_fd == kInvalidDescriptor)
3336
return createStringError(std::errc::bad_file_descriptor,
3437
"write handle is not open for writing");
35-
return m_pipe.GetWriteFileDescriptor();
38+
return m_fd;
3639
}
3740

3841
Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
39-
Status status = m_pipe.CreateNew(/*child_process_inherit=*/false);
40-
if (status.Fail())
41-
return status.takeError();
42+
assert(m_fd == kInvalidDescriptor && "Output readirector already started.");
43+
int new_fd[2];
4244

43-
m_forwarder = std::thread([this, callback]() {
44-
char buffer[OutputBufferSize];
45-
while (m_pipe.CanRead() && !m_stopped) {
46-
size_t bytes_read;
47-
Status status = m_pipe.Read(&buffer, sizeof(buffer), bytes_read);
48-
if (status.Fail())
49-
continue;
45+
#if defined(_WIN32)
46+
if (::_pipe(new_fd, OutputBufferSize, O_TEXT) == -1) {
47+
#else
48+
if (::pipe(new_fd) == -1) {
49+
#endif
50+
int error = errno;
51+
return createStringError(inconvertibleErrorCode(),
52+
"Couldn't create new pipe %s", strerror(error));
53+
}
5054

51-
// EOF detected
52-
if (bytes_read == 0 || m_stopped)
55+
int read_fd = new_fd[0];
56+
m_fd = new_fd[1];
57+
m_forwarder = std::thread([this, callback, read_fd]() {
58+
char buffer[OutputBufferSize];
59+
while (!m_stopped) {
60+
ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer));
61+
// EOF detected.
62+
if (bytes_count == 0)
63+
break;
64+
if (bytes_count == -1) {
65+
// Skip non-fatal errors.
66+
if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
67+
continue;
5368
break;
69+
}
5470

55-
callback(StringRef(buffer, bytes_read));
71+
callback(StringRef(buffer, bytes_count));
5672
}
73+
::close(read_fd);
5774
});
5875

5976
return Error::success();
@@ -62,14 +79,15 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
6279
void OutputRedirector::Stop() {
6380
m_stopped = true;
6481

65-
if (m_pipe.CanWrite()) {
82+
if (m_fd != kInvalidDescriptor) {
83+
int fd = m_fd;
84+
m_fd = kInvalidDescriptor;
6685
// Closing the pipe may not be sufficient to wake up the thread in case the
6786
// write descriptor is duplicated (to stdout/err or to another process).
6887
// Write a null byte to ensure the read call returns.
6988
char buf[] = "\0";
70-
size_t bytes_written;
71-
m_pipe.Write(buf, sizeof(buf), bytes_written);
72-
m_pipe.CloseWriteFileDescriptor();
89+
::write(fd, buf, sizeof(buf));
90+
::close(fd);
7391
m_forwarder.join();
7492
}
7593
}

lldb/tools/lldb-dap/OutputRedirector.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ namespace lldb_dap {
2020

2121
class OutputRedirector {
2222
public:
23+
static int kInvalidDescriptor;
24+
2325
/// Creates writable file descriptor that will invoke the given callback on
2426
/// each write in a background thread.
2527
///
@@ -33,13 +35,13 @@ class OutputRedirector {
3335

3436
~OutputRedirector() { Stop(); }
3537

36-
OutputRedirector() = default;
38+
OutputRedirector();
3739
OutputRedirector(const OutputRedirector &) = delete;
3840
OutputRedirector &operator=(const OutputRedirector &) = delete;
3941

4042
private:
4143
std::atomic<bool> m_stopped = false;
42-
lldb_private::Pipe m_pipe;
44+
int m_fd;
4345
std::thread m_forwarder;
4446
};
4547

0 commit comments

Comments
 (0)