xref: /llvm-project/lldb/tools/lldb-dap/OutputRedirector.cpp (revision 873426bea3dd67d80dd10650e64e91c69796614f)
1 //===-- OutputRedirector.cpp -----------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===/
8 
9 #include "llvm/Support/Error.h"
10 #include <system_error>
11 #if defined(_WIN32)
12 #include <fcntl.h>
13 #include <io.h>
14 #else
15 #include <unistd.h>
16 #endif
17 
18 #include "DAP.h"
19 #include "OutputRedirector.h"
20 #include "llvm/ADT/StringRef.h"
21 
22 using lldb_private::Pipe;
23 using lldb_private::Status;
24 using llvm::createStringError;
25 using llvm::Error;
26 using llvm::Expected;
27 using llvm::StringRef;
28 
29 namespace lldb_dap {
30 
31 Expected<int> OutputRedirector::GetWriteFileDescriptor() {
32   if (!m_pipe.CanWrite())
33     return createStringError(std::errc::bad_file_descriptor,
34                              "write handle is not open for writing");
35   return m_pipe.GetWriteFileDescriptor();
36 }
37 
38 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 
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;
50 
51       // EOF detected
52       if (bytes_read == 0 || m_stopped)
53         break;
54 
55       callback(StringRef(buffer, bytes_read));
56     }
57   });
58 
59   return Error::success();
60 }
61 
62 void OutputRedirector::Stop() {
63   m_stopped = true;
64 
65   if (m_pipe.CanWrite()) {
66     // Closing the pipe may not be sufficient to wake up the thread in case the
67     // write descriptor is duplicated (to stdout/err or to another process).
68     // Write a null byte to ensure the read call returns.
69     char buf[] = "\0";
70     size_t bytes_written;
71     m_pipe.Write(buf, sizeof(buf), bytes_written);
72     m_pipe.CloseWriteFileDescriptor();
73     m_forwarder.join();
74   }
75 }
76 
77 } // namespace lldb_dap
78