diff options
Diffstat (limited to 'lldb/source')
| -rw-r--r-- | lldb/source/Host/common/Editline.cpp | 10 | ||||
| -rw-r--r-- | lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp | 99 | ||||
| -rw-r--r-- | lldb/source/Host/posix/PipePosix.cpp | 162 | ||||
| -rw-r--r-- | lldb/source/Target/Process.cpp | 26 | ||||
| -rw-r--r-- | lldb/source/Utility/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | lldb/source/Utility/SelectHelper.cpp | 294 |
6 files changed, 383 insertions, 209 deletions
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 133a9a76dd9..d23a481d168 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -20,6 +20,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/SelectHelper.h" using namespace lldb_private; using namespace lldb_private::line_editor; @@ -155,11 +156,10 @@ IsInputPending (FILE * file) // instead use some kind of yet-to-be-created abstraction that select-like functionality on // non-socket objects. const int fd = fileno (file); - fd_set fds; - FD_ZERO (&fds); - FD_SET (fd, &fds); - timeval timeout = { 0, 0 }; - return select (fd + 1, &fds, NULL, NULL, &timeout); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::microseconds(0)); + select_helper.FDSetRead(fd); + return select_helper.Select().Success(); } namespace lldb_private diff --git a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 5d6a8938e00..a6d0958c8c6 100644 --- a/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ b/lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -20,6 +20,7 @@ #include "lldb/Host/SocketAddress.h" #include "lldb/Host/Socket.h" #include "lldb/Host/StringConvert.h" +#include "lldb/Utility/SelectHelper.h" // C Includes #include <errno.h> @@ -572,7 +573,7 @@ ConnectionFileDescriptor::GetURI() return m_uri; } -// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// This ConnectionFileDescriptor::BytesAvailable() uses select() via SelectHelper // // PROS: // - select is consistent across most unix platforms @@ -586,11 +587,6 @@ ConnectionFileDescriptor::GetURI() // be used or a new version of ConnectionFileDescriptor::BytesAvailable() // should be written for the system that is running into the limitations. -#if defined(__APPLE__) -#define FD_SET_DATA(fds) fds.data() -#else -#define FD_SET_DATA(fds) &fds -#endif ConnectionStatus ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr) @@ -602,21 +598,6 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void *>(this), timeout_usec); - struct timeval *tv_ptr; - struct timeval tv; - if (timeout_usec == UINT32_MAX) - { - // Infinite wait... - tv_ptr = nullptr; - } - else - { - TimeValue time_value; - time_value.OffsetWithMicroSeconds(timeout_usec); - tv.tv_sec = time_value.seconds(); - tv.tv_usec = time_value.microseconds(); - tv_ptr = &tv; - } // Make a copy of the file descriptors to make sure we don't // have another thread change these values out from under us @@ -624,8 +605,14 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); const int pipe_fd = m_pipe.GetReadFileDescriptor(); + if (handle != IOObject::kInvalidHandleValue) { + SelectHelper select_helper; + if (timeout_usec != UINT32_MAX) + select_helper.SetTimeout(std::chrono::microseconds(timeout_usec)); + + select_helper.FDSetRead(handle); #if defined(_MSC_VER) // select() won't accept pipes on Windows. The entire Windows codepath needs to be // converted over to using WaitForMultipleObjects and event HANDLEs, but for now at least @@ -633,62 +620,14 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr const bool have_pipe_fd = false; #else const bool have_pipe_fd = pipe_fd >= 0; -#if !defined(__APPLE__) - assert(handle < FD_SETSIZE); - if (have_pipe_fd) - assert(pipe_fd < FD_SETSIZE); -#endif #endif + if (have_pipe_fd) + select_helper.FDSetRead(pipe_fd); + while (handle == m_read_sp->GetWaitableHandle()) { - const int nfds = std::max<int>(handle, pipe_fd) + 1; -#if defined(__APPLE__) - llvm::SmallVector<fd_set, 1> read_fds; - read_fds.resize((nfds / FD_SETSIZE) + 1); - for (size_t i = 0; i < read_fds.size(); ++i) - FD_ZERO(&read_fds[i]); -// FD_SET doesn't bounds check, it just happily walks off the end -// but we have taken care of making the extra storage with our -// SmallVector of fd_set objects -#else - fd_set read_fds; - FD_ZERO(&read_fds); -#endif - FD_SET(handle, FD_SET_DATA(read_fds)); - if (have_pipe_fd) - FD_SET(pipe_fd, FD_SET_DATA(read_fds)); - Error error; - - if (log) - { - if (have_pipe_fd) - log->Printf( - "%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", - static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr)); - else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", - static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr)); - } - - const int num_set_fds = ::select(nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); - if (num_set_fds < 0) - error.SetErrorToErrno(); - else - error.Clear(); - - if (log) - { - if (have_pipe_fd) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) " - "=> %d, error = %s", - static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr), num_set_fds, - error.AsCString()); - else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => " - "%d, error = %s", - static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString()); - } + Error error = select_helper.Select(); if (error_ptr) *error_ptr = error; @@ -704,6 +643,9 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr default: // Other unknown error return eConnectionStatusError; + case ETIMEDOUT: + return eConnectionStatusTimedOut; + case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO @@ -713,15 +655,12 @@ ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr break; // Lets keep reading to until we timeout } } - else if (num_set_fds == 0) - { - return eConnectionStatusTimedOut; - } - else if (num_set_fds > 0) + else { - if (FD_ISSET(handle, FD_SET_DATA(read_fds))) + if (select_helper.FDIsSetRead(handle)) return eConnectionStatusSuccess; - if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) + + if (select_helper.FDIsSetRead(pipe_fd)) { // There is an interrupt or exit command in the command pipe // Read the data from that pipe: diff --git a/lldb/source/Host/posix/PipePosix.cpp b/lldb/source/Host/posix/PipePosix.cpp index 353faae1628..6695bbfbac7 100644 --- a/lldb/source/Host/posix/PipePosix.cpp +++ b/lldb/source/Host/posix/PipePosix.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/posix/PipePosix.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Utility/SelectHelper.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" @@ -65,73 +66,6 @@ Now() return std::chrono::steady_clock::now(); } -Error -SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout) -{ - Error error; - fd_set fds; - bool done = false; - - using namespace std::chrono; - - const auto finish_time = Now() + timeout; - - while (!done) - { - struct timeval tv = {0, 0}; - if (timeout != microseconds::zero()) - { - const auto remaining_dur = duration_cast<microseconds>(finish_time - Now()); - if (remaining_dur.count() <= 0) - { - error.SetErrorString("timeout exceeded"); - break; - } - const auto dur_secs = duration_cast<seconds>(remaining_dur); - const auto dur_usecs = remaining_dur % seconds(1); - - tv.tv_sec = dur_secs.count(); - tv.tv_usec = dur_usecs.count(); - } - else - tv.tv_sec = 1; - - FD_ZERO(&fds); - FD_SET(handle, &fds); - - const auto retval = ::select(handle + 1, - (is_read) ? &fds : nullptr, - (is_read) ? nullptr : &fds, - nullptr, &tv); - if (retval == -1) - { - if (errno == EINTR) - continue; - error.SetErrorToErrno(); - break; - } - if (retval == 0) - { - error.SetErrorString("timeout exceeded"); - break; - } - if (!FD_ISSET(handle, &fds)) - { - error.SetErrorString("invalid state"); - break; - } - - error = io_handler(done); - if (error.Fail()) - { - if (error.GetError() == EINTR) - continue; - break; - } - } - return error; -} - } PipePosix::PipePosix() @@ -383,27 +317,33 @@ PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microsecon if (!CanRead()) return Error(EINVAL, eErrorTypePOSIX); - auto handle = GetReadFileDescriptor(); - return SelectIO(handle, - true, - [=, &bytes_read](bool &done) - { - Error error; - auto result = ::read(handle, - reinterpret_cast<char*>(buf) + bytes_read, - size - bytes_read); - if (result != -1) - { - bytes_read += result; - if (bytes_read == size || result == 0) - done = true; - } - else - error.SetErrorToErrno(); - - return error; - }, - timeout); + const int fd = GetReadFileDescriptor(); + + SelectHelper select_helper; + select_helper.SetTimeout(timeout); + select_helper.FDSetRead(fd); + + Error error; + while (error.Success()) + { + error = select_helper.Select(); + if (error.Success()) + { + auto result = ::read(fd, reinterpret_cast<char*>(buf) + bytes_read, size - bytes_read); + if (result != -1) + { + bytes_read += result; + if (bytes_read == size || result == 0) + break; + } + else + { + error.SetErrorToErrno(); + break; + } + } + } + return error; } Error @@ -413,25 +353,29 @@ PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) if (!CanWrite()) return Error(EINVAL, eErrorTypePOSIX); - auto handle = GetWriteFileDescriptor(); - return SelectIO(handle, - false, - [=, &bytes_written](bool &done) - { - Error error; - auto result = ::write(handle, - reinterpret_cast<const char*>(buf) + bytes_written, - size - bytes_written); - if (result != -1) - { - bytes_written += result; - if (bytes_written == size) - done = true; - } - else - error.SetErrorToErrno(); - - return error; - }, - std::chrono::microseconds::zero()); + const int fd = GetWriteFileDescriptor(); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::seconds(0)); + select_helper.FDSetWrite(fd); + + Error error; + while (error.Success()) + { + error = select_helper.Select(); + if (error.Success()) + { + auto result = ::write(fd, reinterpret_cast<const char*>(buf) + bytes_written, size - bytes_written); + if (result != -1) + { + bytes_written += result; + if (bytes_written == size) + break; + } + else + { + error.SetErrorToErrno(); + } + } + } + return error; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 98f9ea13129..94e4f961064 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -62,6 +62,7 @@ #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/SelectHelper.h" using namespace lldb; using namespace lldb_private; @@ -4925,25 +4926,20 @@ public: m_is_running = true; while (!GetIsDone()) { - fd_set read_fdset; - FD_ZERO (&read_fdset); - FD_SET (read_fd, &read_fdset); - FD_SET (pipe_read_fd, &read_fdset); - const int nfds = std::max<int>(read_fd, pipe_read_fd) + 1; - int num_set_fds = select(nfds, &read_fdset, nullptr, nullptr, nullptr); - - if (num_set_fds < 0) - { - const int select_errno = errno; + SelectHelper select_helper; + select_helper.FDSetRead(read_fd); + select_helper.FDSetRead(pipe_read_fd); + Error error = select_helper.Select(); - if (select_errno != EINTR) - SetIsDone(true); + if (error.Fail()) + { + SetIsDone(true); } - else if (num_set_fds > 0) + else { char ch = 0; size_t n; - if (FD_ISSET (read_fd, &read_fdset)) + if (select_helper.FDIsSetRead(read_fd)) { n = 1; if (m_read_file.Read(&ch, n).Success() && n == 1) @@ -4954,7 +4950,7 @@ public: else SetIsDone(true); } - if (FD_ISSET (pipe_read_fd, &read_fdset)) + if (select_helper.FDIsSetRead(pipe_read_fd)) { size_t bytes_read; // Consume the interrupt byte diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index aa4ad030135..90aff225fe3 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -10,6 +10,7 @@ add_lldb_library(lldbUtility PseudoTerminal.cpp Range.cpp RegisterNumber.cpp + SelectHelper.cpp SharingPtr.cpp StringExtractor.cpp StringExtractorGDBRemote.cpp diff --git a/lldb/source/Utility/SelectHelper.cpp b/lldb/source/Utility/SelectHelper.cpp new file mode 100644 index 00000000000..7d616d5745a --- /dev/null +++ b/lldb/source/Utility/SelectHelper.cpp @@ -0,0 +1,294 @@ +//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +// C Includes +#include <errno.h> +#if defined(_WIN32) +// Define NOMINMAX to avoid macros that conflict with std::min and std::max +#define NOMINMAX +#include <winsock2.h> +#else +#include <sys/select.h> +#endif + +// C++ Includes +#include <algorithm> + +// Other libraries and framework includes +#include "llvm/ADT/SmallVector.h" + +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/LLDBAssert.h" + +SelectHelper::SelectHelper() : + m_fd_map(), + m_end_time() // Infinite timeout unless SelectHelper::SetTimeout() gets called +{ +} + +void +SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) +{ + using namespace std::chrono; + m_end_time = steady_clock::time_point(steady_clock::now() + timeout); +} + +void +SelectHelper::FDSetRead(int fd) +{ + m_fd_map[fd].read_set = true; +} + +void +SelectHelper::FDSetWrite(int fd) +{ + m_fd_map[fd].write_set = true; +} + +void +SelectHelper::FDSetError(int fd) +{ + m_fd_map[fd].error_set = true; +} + +bool +SelectHelper::FDIsSetRead(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.read_is_set; + else + return false; +} + +bool +SelectHelper::FDIsSetWrite(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.write_is_set; + else + return false; +} + +bool +SelectHelper::FDIsSetError(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.error_is_set; + else + return false; +} + +lldb_private::Error +SelectHelper::Select() +{ + lldb_private::Error error; + + int max_read_fd = -1; + int max_write_fd = -1; + int max_error_fd = -1; + int max_fd = -1; + for (auto &pair : m_fd_map) + { + pair.second.PrepareForSelect(); + const int fd = pair.first; +#if !defined(__APPLE__) + lldbassert(fd < FD_SETSIZE); + if (fd >= FD_SETSIZE) + { + error.SetErrorStringWithFormat("%i is too large for select()", fd); + return error; + } +#endif + if (pair.second.read_set) + { + max_read_fd = std::max<int>(fd, max_read_fd); + max_fd = std::max<int>(fd, max_fd); + } + if (pair.second.write_set) + { + max_write_fd = std::max<int>(fd, max_write_fd); + max_fd = std::max<int>(fd, max_fd); + } + if (pair.second.error_set) + { + max_error_fd = std::max<int>(fd, max_error_fd); + max_fd = std::max<int>(fd, max_fd); + } + } + + if (max_fd == -1) + { + error.SetErrorString("no valid file descriptors"); + return error; + } + + const int nfds = max_fd + 1; + fd_set *read_fdset_ptr = nullptr; + fd_set *write_fdset_ptr = nullptr; + fd_set *error_fdset_ptr = nullptr; + //---------------------------------------------------------------------- + // Initialize and zero out the fdsets + //---------------------------------------------------------------------- +#if defined(__APPLE__) + llvm::SmallVector<fd_set, 1> read_fdset; + llvm::SmallVector<fd_set, 1> write_fdset; + llvm::SmallVector<fd_set, 1> error_fdset; + + if (max_read_fd >= 0) + { + read_fdset.resize((nfds / FD_SETSIZE) + 1); + read_fdset_ptr = read_fdset.data(); + } + if (max_write_fd >= 0) + { + write_fdset.resize((nfds / FD_SETSIZE) + 1); + write_fdset_ptr = write_fdset.data(); + } + if (max_error_fd >= 0) + { + error_fdset.resize((nfds / FD_SETSIZE) + 1); + error_fdset_ptr = error_fdset.data(); + } + for (auto &fd_set : read_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : write_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : error_fdset) + FD_ZERO(&fd_set); +#else + fd_set read_fdset; + fd_set write_fdset; + fd_set error_fdset; + + if (max_read_fd >= 0) + { + FD_ZERO(&read_fdset); + read_fdset_ptr = &read_fdset; + } + if (max_write_fd >= 0) + { + FD_ZERO(&write_fdset); + write_fdset_ptr = &write_fdset; + } + if (max_error_fd >= 0) + { + FD_ZERO(&error_fdset); + error_fdset_ptr = &error_fdset; + } +#endif + //---------------------------------------------------------------------- + // Set the FD bits in the fdsets for read/write/error + //---------------------------------------------------------------------- + for (auto &pair : m_fd_map) + { + const int fd = pair.first; + + if (pair.second.read_set) + FD_SET(fd, read_fdset_ptr); + + if (pair.second.write_set) + FD_SET(fd, write_fdset_ptr); + + if (pair.second.error_set) + FD_SET(fd, error_fdset_ptr); + } + + //---------------------------------------------------------------------- + // Setup our timeout time value if needed + //---------------------------------------------------------------------- + struct timeval *tv_ptr = nullptr; + struct timeval tv = {0, 0}; + + while (1) + { + using namespace std::chrono; + //------------------------------------------------------------------ + // Setup out relative timeout based on the end time if we have one + //------------------------------------------------------------------ + if (m_end_time.hasValue()) + { + tv_ptr = &tv; + const auto remaining_dur = duration_cast<microseconds>(m_end_time.getValue() - steady_clock::now()); + if (remaining_dur.count() > 0) + { + // Wait for a specific amount of time + const auto dur_secs = duration_cast<seconds>(remaining_dur); + const auto dur_usecs = remaining_dur % seconds(1); + tv.tv_sec = dur_secs.count(); + tv.tv_usec = dur_usecs.count(); + } + else + { + // Just poll once with no timeout + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr); + if (num_set_fds < 0) + { + // We got an error + error.SetErrorToErrno(); + if (error.GetError() == EINTR) + { + error.Clear(); + continue; // Keep calling select if we get EINTR + } + else + return error; + } + else if (num_set_fds == 0) + { + // Timeout + error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX); + error.SetErrorString("timed out"); + return error; + } + else + { + // One or more descriptors were set, update the FDInfo::select_is_set mask + // so users can ask the SelectHelper class so clients can call one of: + + for (auto &pair : m_fd_map) + { + const int fd = pair.first; + + if (pair.second.read_set) + { + if (FD_ISSET(fd, read_fdset_ptr)) + pair.second.read_is_set = true; + } + if (pair.second.write_set) + { + if (FD_ISSET(fd, write_fdset_ptr)) + pair.second.write_is_set = true; + } + if (pair.second.error_set) + { + if (FD_ISSET(fd, error_fdset_ptr)) + pair.second.error_is_set = true; + } + } + break; + } + } + return error; +} |

