Index: include/lldb/Host/Pipe.h =================================================================== --- include/lldb/Host/Pipe.h +++ include/lldb/Host/Pipe.h @@ -12,8 +12,16 @@ #if defined(_WIN32) #include "lldb/Host/windows/PipeWindows.h" +namespace lldb_private +{ +typedef PipeWindows Pipe; +} #else #include "lldb/Host/posix/PipePosix.h" +namespace lldb_private +{ +typedef PipePosix Pipe; +} #endif #endif // liblldb_Host_Pipe_h_ Index: include/lldb/Host/PipeBase.h =================================================================== --- /dev/null +++ include/lldb/Host/PipeBase.h @@ -0,0 +1,48 @@ +//===-- PipeBase.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_PipeBase_h_ +#define liblldb_Host_PipeBase_h_ + +#include +#include + +#include "lldb/Core/Error.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_private +{ +class PipeBase +{ + public: + virtual ~PipeBase() {} + + virtual Error CreateNew(bool child_process_inherit) = 0; + virtual Error CreateNew(bool child_process_inherit, llvm::StringRef name) = 0; + virtual Error OpenAsReader(bool child_process_inherit, llvm::StringRef name) = 0; + virtual Error OpenAsWriter(bool child_process_inherit, llvm::StringRef name) = 0; + + virtual bool CanRead() const = 0; + virtual bool CanWrite() const = 0; + + virtual int GetReadFileDescriptor() const = 0; + virtual int GetWriteFileDescriptor() const = 0; + virtual int ReleaseReadFileDescriptor() = 0; + virtual int ReleaseWriteFileDescriptor() = 0; + + // Close both descriptors + virtual void Close() = 0; + + virtual Error Write(const void *buf, size_t size, size_t &bytes_written) = 0; + virtual Error Read(void *buf, size_t size, size_t &bytes_read) = 0; + virtual Error ReadWithTimeout(void *buf, size_t size, const std::chrono::milliseconds &timeout, size_t &bytes_read) = 0; +}; +} + +#endif \ No newline at end of file Index: include/lldb/Host/windows/PipeWindows.h =================================================================== --- include/lldb/Host/windows/PipeWindows.h +++ include/lldb/Host/windows/PipeWindows.h @@ -10,6 +10,7 @@ #ifndef liblldb_Host_windows_PipeWindows_h_ #define liblldb_Host_windows_PipeWindows_h_ +#include "lldb/Host/PipeBase.h" #include "lldb/Host/windows/windows.h" namespace lldb_private @@ -22,55 +23,49 @@ /// /// A class that abstracts the LLDB core from host pipe functionality. //---------------------------------------------------------------------- -class Pipe +class PipeWindows : public PipeBase { public: - Pipe(); + PipeWindows(); + ~PipeWindows() override; - ~Pipe(); + Error CreateNew(bool child_process_inherit) override; + Error CreateNew(bool child_process_inherit, llvm::StringRef name) override; + Error OpenAsReader(bool child_process_inherit, llvm::StringRef name) override; + Error OpenAsWriter(bool child_process_inherit, llvm::StringRef name) override; - bool Open(bool read_overlapped = false, bool write_overlapped = false); + bool CanRead() const override; + bool CanWrite() const override; - bool IsValid() const; + int GetReadFileDescriptor() const override; + int GetWriteFileDescriptor() const override; + int ReleaseReadFileDescriptor() override; + int ReleaseWriteFileDescriptor() override; - bool ReadDescriptorIsValid() const; + void Close() override; - bool WriteDescriptorIsValid() const; + Error Write(const void *buf, size_t size, size_t &bytes_written) override; + Error Read(void *buf, size_t size, size_t &bytes_read) override; + Error ReadWithTimeout(void *buf, size_t size, const std::chrono::milliseconds &timeout, size_t &bytes_read) override; - int GetReadFileDescriptor() const; + // PipeWindows specific methods. These allow access to the underlying OS handle. + HANDLE GetReadNativeHandle(); + HANDLE GetWriteNativeHandle(); - int GetWriteFileDescriptor() const; - - // Close both descriptors - void Close(); - - bool CloseReadFileDescriptor(); - - bool CloseWriteFileDescriptor(); - - int ReleaseReadFileDescriptor(); - - int ReleaseWriteFileDescriptor(); - - HANDLE - GetReadNativeHandle(); - - HANDLE - GetWriteNativeHandle(); - - size_t Read(void *buf, size_t size); + private: + Error OpenNamedPipe(bool child_process_inherit, llvm::StringRef name, bool is_read); - size_t Write(const void *buf, size_t size); + void CloseReadEndpoint(); + void CloseWriteEndpoint(); - private: HANDLE m_read; HANDLE m_write; int m_read_fd; int m_write_fd; - OVERLAPPED *m_read_overlapped; - OVERLAPPED *m_write_overlapped; + OVERLAPPED m_read_overlapped; + OVERLAPPED m_write_overlapped; }; } // namespace lldb_private Index: source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -96,11 +96,12 @@ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); // Make the command file descriptor here: - if (!m_pipe.Open(m_child_processes_inherit)) + Error result = m_pipe.CreateNew(m_child_processes_inherit); + if (!result.Success()) { if (log) log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", static_cast(this), - strerror(errno)); + result.AsCString()); } else { @@ -291,7 +292,9 @@ bool ConnectionFileDescriptor::InterruptRead() { - return m_pipe.Write("i", 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write("i", 1, bytes_written); + return result.Success(); } ConnectionStatus @@ -325,13 +328,13 @@ if (!got_lock) { - if (m_pipe.WriteDescriptorIsValid()) + if (m_pipe.CanWrite()) { - int result; - result = m_pipe.Write("q", 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write("q", 1, bytes_written); if (log) log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, result = %d.", - static_cast(this), m_pipe.GetWriteFileDescriptor(), result); + static_cast(this), m_pipe.GetWriteFileDescriptor(), bytes_written); } else if (log) { Index: source/Host/windows/PipeWindows.cpp =================================================================== --- source/Host/windows/PipeWindows.cpp +++ source/Host/windows/PipeWindows.cpp @@ -17,6 +17,7 @@ #include #include +using namespace lldb; using namespace lldb_private; namespace @@ -24,208 +25,272 @@ std::atomic g_pipe_serial(0); } -Pipe::Pipe() +PipeWindows::PipeWindows() { m_read = INVALID_HANDLE_VALUE; m_write = INVALID_HANDLE_VALUE; m_read_fd = -1; m_write_fd = -1; - - m_read_overlapped = nullptr; - m_write_overlapped = nullptr; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } -Pipe::~Pipe() +PipeWindows::~PipeWindows() { Close(); } -bool -Pipe::Open(bool read_overlapped, bool write_overlapped) +Error +PipeWindows::CreateNew(bool child_process_inherit) { - if (IsValid()) - return true; - uint32_t serial = g_pipe_serial.fetch_add(1); std::string pipe_name; llvm::raw_string_ostream pipe_name_stream(pipe_name); pipe_name_stream << "\\\\.\\Pipe\\lldb.pipe." << ::GetCurrentProcessId() << "." << serial; pipe_name_stream.flush(); - DWORD read_mode = 0; - DWORD write_mode = 0; - if (read_overlapped) - read_mode |= FILE_FLAG_OVERLAPPED; - if (write_overlapped) - write_mode |= FILE_FLAG_OVERLAPPED; - m_read = - ::CreateNamedPipe(pipe_name.c_str(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); + return CreateNew(child_process_inherit, pipe_name.c_str()); +} + +Error +PipeWindows::CreateNew(bool child_process_inherit, llvm::StringRef name) +{ + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + // Always open for overlapped i/o. We implement blocking manually in Read and Write. + DWORD read_mode = FILE_FLAG_OVERLAPPED; + m_read = ::CreateNamedPipe(name.data(), PIPE_ACCESS_INBOUND | read_mode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL); if (INVALID_HANDLE_VALUE == m_read) - return false; - m_write = ::CreateFile(pipe_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, write_mode, NULL); - if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); + + // Open the write end of the pipe. + Error result = OpenNamedPipe(child_process_inherit, name, false); + if (!result.Success()) { - ::CloseHandle(m_read); - m_read = INVALID_HANDLE_VALUE; - return false; + CloseReadEndpoint(); + return result; } - m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); - m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + return result; +} + +Error +PipeWindows::OpenAsReader(bool child_process_inherit, llvm::StringRef name) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(child_process_inherit, name, true); +} - if (read_overlapped) +Error +PipeWindows::OpenAsWriter(bool child_process_inherit, llvm::StringRef name) +{ + if (name.empty()) + return Error(ERROR_INVALID_PARAMETER, eErrorTypeWin32); + + if (CanRead() || CanWrite()) + return Error(ERROR_ALREADY_EXISTS, eErrorTypeWin32); + + return OpenNamedPipe(child_process_inherit, name, false); +} + +Error +PipeWindows::OpenNamedPipe(bool child_process_inherit, llvm::StringRef name, bool is_read) +{ + assert(is_read ? !CanRead() : !CanWrite()); + + SECURITY_ATTRIBUTES attributes = {0}; + attributes.bInheritHandle = child_process_inherit; + + if (is_read) { - m_read_overlapped = new OVERLAPPED; - ZeroMemory(m_read_overlapped, sizeof(OVERLAPPED)); + m_read = ::CreateFile(name.data(), GENERIC_READ, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_read) + return Error(::GetLastError(), eErrorTypeWin32); + + m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY); + + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); + m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); } - if (write_overlapped) + else { - m_write_overlapped = new OVERLAPPED; - ZeroMemory(m_write_overlapped, sizeof(OVERLAPPED)); + m_write = ::CreateFile(name.data(), GENERIC_WRITE, 0, &attributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE == m_write) + return Error(::GetLastError(), eErrorTypeWin32); + + m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY); + + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } - return true; + + return Error(); } int -Pipe::GetReadFileDescriptor() const +PipeWindows::GetReadFileDescriptor() const { return m_read_fd; } int -Pipe::GetWriteFileDescriptor() const +PipeWindows::GetWriteFileDescriptor() const { return m_write_fd; } int -Pipe::ReleaseReadFileDescriptor() +PipeWindows::ReleaseReadFileDescriptor() { + if (!CanRead()) + return -1; int result = m_read_fd; m_read_fd = -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); m_read = INVALID_HANDLE_VALUE; - if (m_read_overlapped) - { - delete m_read_overlapped; - m_read_overlapped = nullptr; - } + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); return result; } int -Pipe::ReleaseWriteFileDescriptor() +PipeWindows::ReleaseWriteFileDescriptor() { + if (!CanWrite()) + return -1; int result = m_write_fd; m_write_fd = -1; m_write = INVALID_HANDLE_VALUE; - if (m_write_overlapped) - { - delete m_write_overlapped; - m_write_overlapped = nullptr; - } + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); return result; } void -Pipe::Close() +PipeWindows::CloseReadEndpoint() { - CloseReadFileDescriptor(); - CloseWriteFileDescriptor(); -} + if (!CanRead()) + return; -bool -Pipe::ReadDescriptorIsValid() const -{ - return m_read_fd != -1; + if (m_read_overlapped.hEvent) + ::CloseHandle(m_read_overlapped.hEvent); + _close(m_read_fd); + m_read = INVALID_HANDLE_VALUE; + m_read_fd = -1; + ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped)); } -bool -Pipe::WriteDescriptorIsValid() const +void +PipeWindows::CloseWriteEndpoint() { - return m_write_fd != -1; + if (!CanWrite()) + return; + + _close(m_write_fd); + m_write = INVALID_HANDLE_VALUE; + m_write_fd = -1; + ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped)); } -bool -Pipe::IsValid() const +void +PipeWindows::Close() { - return ReadDescriptorIsValid() && WriteDescriptorIsValid(); + CloseReadEndpoint(); + CloseWriteEndpoint(); } bool -Pipe::CloseReadFileDescriptor() +PipeWindows::CanRead() const { - if (ReadDescriptorIsValid()) - { - int err; - err = _close(m_read_fd); - m_read_fd = -1; - m_read = INVALID_HANDLE_VALUE; - if (m_read_overlapped) - { - delete m_read_overlapped; - m_read_overlapped = nullptr; - } - return err == 0; - } - return true; + return (m_read != INVALID_HANDLE_VALUE); } bool -Pipe::CloseWriteFileDescriptor() +PipeWindows::CanWrite() const { - if (WriteDescriptorIsValid()) - { - int err; - err = _close(m_write_fd); - m_write_fd = -1; - m_write = INVALID_HANDLE_VALUE; - if (m_write_overlapped) - { - delete m_write_overlapped; - m_write_overlapped = nullptr; - } - return err == 0; - } - return true; + return (m_write != INVALID_HANDLE_VALUE); } HANDLE -Pipe::GetReadNativeHandle() +PipeWindows::GetReadNativeHandle() { return m_read; } HANDLE -Pipe::GetWriteNativeHandle() +PipeWindows::GetWriteNativeHandle() { return m_write; } -size_t -Pipe::Read(void *buf, size_t num_bytes) +Error +PipeWindows::Read(void *buf, size_t size, size_t &bytes_read) { - if (ReadDescriptorIsValid()) - { - DWORD bytes_read = 0; - ::ReadFile(m_read, buf, num_bytes, &bytes_read, m_read_overlapped); - if (m_read_overlapped) - GetOverlappedResult(m_read, m_read_overlapped, &bytes_read, TRUE); - return bytes_read; - } - return 0; // Return 0 since errno won't be set if we didn't call read + return ReadWithTimeout(buf, size, std::chrono::milliseconds::zero(), bytes_read); } -size_t -Pipe::Write(const void *buf, size_t num_bytes) +Error +PipeWindows::ReadWithTimeout(void *buf, size_t size, const std::chrono::milliseconds &duration, size_t &bytes_read) { - if (WriteDescriptorIsValid()) + if (!CanRead()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + bytes_read = 0; + DWORD sys_bytes_read = size; + BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read, &m_read_overlapped); + if (!result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + DWORD timeout = (duration == std::chrono::milliseconds::zero()) ? INFINITE : duration.count(); + DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout); + if (wait_result != WAIT_OBJECT_0) { - DWORD bytes_written = 0; - ::WriteFile(m_write, buf, num_bytes, &bytes_written, m_read_overlapped); - if (m_write_overlapped) - GetOverlappedResult(m_write, m_write_overlapped, &bytes_written, TRUE); - return bytes_written; + // The operation probably failed. However, if it timed out, we need to cancel the I/O. + // Between the time we returned from WaitForSingleObject and the time we call CancelIoEx, + // the operation may complete. If that hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. + // If that happens, the original operation should be considered to have been successful. + bool failed = true; + DWORD failure_error = ::GetLastError(); + if (wait_result == WAIT_TIMEOUT) + { + BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped); + if (!cancel_result && GetLastError() == ERROR_NOT_FOUND) + failed = false; + } + if (failed) + return Error(failure_error, eErrorTypeWin32); } - return 0; // Return 0 since errno won't be set if we didn't call write + + // Now we call GetOverlappedResult setting bWait to false, since we've already waited + // as long as we're willing to. + if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE)) + return Error(::GetLastError(), eErrorTypeWin32); + + bytes_read = sys_bytes_read; + return Error(); +} + +Error +PipeWindows::Write(const void *buf, size_t num_bytes, size_t &bytes_written) +{ + if (!CanWrite()) + return Error(ERROR_INVALID_HANDLE, eErrorTypeWin32); + + DWORD sys_bytes_written = 0; + BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written, &m_write_overlapped); + if (!write_result && GetLastError() != ERROR_IO_PENDING) + return Error(::GetLastError(), eErrorTypeWin32); + + BOOL result = GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_written, TRUE); + if (!result) + return Error(::GetLastError(), eErrorTypeWin32); + return Error(); } Index: source/Interpreter/ScriptInterpreterPython.cpp =================================================================== --- source/Interpreter/ScriptInterpreterPython.cpp +++ source/Interpreter/ScriptInterpreterPython.cpp @@ -603,18 +603,14 @@ // Set output to a temporary file so we can forward the results on to the result object Pipe pipe; -#if defined(_WIN32) - // By default Windows does not create a pipe object that can be used for a non-blocking read. - // We must explicitly request it. Furthermore, we can't use an fd for non-blocking read - // operations, and must use the native os HANDLE. - if (pipe.Open(true, false)) + Error pipe_result = pipe.CreateNew(false); + if (pipe_result.Success()) { +#if defined(_WIN32) lldb::file_t read_file = pipe.GetReadNativeHandle(); pipe.ReleaseReadFileDescriptor(); std::unique_ptr conn_ap(new ConnectionGenericFile(read_file, true)); #else - if (pipe.Open()) - { std::unique_ptr conn_ap(new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(), true)); #endif if (conn_ap->IsConnected()) Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -4927,9 +4927,10 @@ bool OpenPipes () { - if (m_pipe.IsValid()) + if (m_pipe.CanRead() && m_pipe.CanWrite()) return true; - return m_pipe.Open(); + Error result = m_pipe.CreateNew(false); + return result.Success(); } void @@ -5039,7 +5040,8 @@ Cancel () { char ch = 'q'; // Send 'q' for quit - m_pipe.Write (&ch, 1); + size_t bytes_written = 0; + m_pipe.Write(&ch, 1, bytes_written); } virtual bool @@ -5053,7 +5055,9 @@ if (m_active) { char ch = 'i'; // Send 'i' for interrupt - return m_pipe.Write (&ch, 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write(&ch, 1, bytes_written); + return result.Success(); } else {