Index: include/lldb/Core/ConnectionFileDescriptor.h =================================================================== --- include/lldb/Core/ConnectionFileDescriptor.h +++ include/lldb/Core/ConnectionFileDescriptor.h @@ -7,113 +7,9 @@ // //===----------------------------------------------------------------------===// -#ifndef liblldb_ConnectionFileDescriptor_h_ -#define liblldb_ConnectionFileDescriptor_h_ +#ifndef liblldb_Core_ConnectionFileDescriptor_h_ +#define liblldb_Core_ConnectionFileDescriptor_h_ -// C++ Includes -#include -#include +#include "lldb/Core/posix/ConnectionFileDescriptorPosix.h" -#include "lldb/lldb-forward.h" - -// Other libraries and framework includes -// Project includes -#include "lldb/Core/Connection.h" -#include "lldb/Host/Mutex.h" -#include "lldb/Host/Pipe.h" -#include "lldb/Host/Predicate.h" -#include "lldb/Host/IOObject.h" - -namespace lldb_private { - -class Error; -class Socket; -class SocketAddress; - -class ConnectionFileDescriptor : - public Connection -{ -public: - - ConnectionFileDescriptor (); - - ConnectionFileDescriptor (int fd, bool owns_fd); - - virtual - ~ConnectionFileDescriptor (); - - virtual bool - IsConnected () const; - - virtual lldb::ConnectionStatus - Connect (const char *s, Error *error_ptr); - - virtual lldb::ConnectionStatus - Disconnect (Error *error_ptr); - - virtual size_t - Read (void *dst, - size_t dst_len, - uint32_t timeout_usec, - lldb::ConnectionStatus &status, - Error *error_ptr); - - virtual size_t - Write (const void *src, - size_t src_len, - lldb::ConnectionStatus &status, - Error *error_ptr); - - lldb::ConnectionStatus - BytesAvailable (uint32_t timeout_usec, Error *error_ptr); - - bool - InterruptRead (); - - lldb::IOObjectSP GetReadObject() { return m_read_sp; } - const lldb::IOObjectSP GetReadObject() const { return m_read_sp; } - - uint16_t GetListeningPort(uint32_t timeout_sec); - -protected: - - void - OpenCommandPipe (); - - void - CloseCommandPipe (); - - lldb::ConnectionStatus - SocketListen (const char *host_and_port, Error *error_ptr); - - lldb::ConnectionStatus - ConnectTCP (const char *host_and_port, Error *error_ptr); - - lldb::ConnectionStatus - ConnectUDP (const char *args, Error *error_ptr); - - lldb::ConnectionStatus - NamedSocketConnect (const char *socket_name, Error *error_ptr); - - lldb::ConnectionStatus - NamedSocketAccept (const char *socket_name, Error *error_ptr); - - lldb::IOObjectSP m_read_sp; - lldb::IOObjectSP m_write_sp; - - Predicate m_port_predicate; // Used when binding to port zero to wait for the thread - // that creates the socket, binds and listens to resolve - // the port number. - - Pipe m_pipe; - Mutex m_mutex; - std::atomic m_shutting_down; // This marks that we are shutting down so if we get woken up from - // BytesAvailable to disconnect, we won't try to read again. - bool m_waiting_for_accept; -private: - DISALLOW_COPY_AND_ASSIGN (ConnectionFileDescriptor); -}; - -} // namespace lldb_private - -#endif // liblldb_ConnectionFileDescriptor_h_ +#endif Index: include/lldb/Core/posix/ConnectionFileDescriptorPosix.h =================================================================== --- /dev/null +++ include/lldb/Core/posix/ConnectionFileDescriptorPosix.h @@ -0,0 +1,104 @@ +//===-- ConnectionFileDescriptorPosix.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_Core_posix_ConnectionFileDescriptorPosix_h_ +#define liblldb_Core_posix_ConnectionFileDescriptorPosix_h_ + +// C++ Includes +#include +#include + +#include "lldb/lldb-forward.h" + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/IOObject.h" + +namespace lldb_private +{ + +class Error; +class Socket; +class SocketAddress; + +class ConnectionFileDescriptor : public Connection +{ + public: + ConnectionFileDescriptor(); + + ConnectionFileDescriptor(int fd, bool owns_fd); + + virtual ~ConnectionFileDescriptor(); + + virtual bool IsConnected() const; + + virtual lldb::ConnectionStatus Connect(const char *s, Error *error_ptr); + + virtual lldb::ConnectionStatus Disconnect(Error *error_ptr); + + virtual size_t Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr); + + virtual size_t Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr); + + lldb::ConnectionStatus BytesAvailable(uint32_t timeout_usec, Error *error_ptr); + + bool InterruptRead(); + + lldb::IOObjectSP + GetReadObject() + { + return m_read_sp; + } + const lldb::IOObjectSP + GetReadObject() const + { + return m_read_sp; + } + + uint16_t GetListeningPort(uint32_t timeout_sec); + + protected: + void OpenCommandPipe(); + + void CloseCommandPipe(); + + lldb::ConnectionStatus SocketListen(const char *host_and_port, Error *error_ptr); + + lldb::ConnectionStatus ConnectTCP(const char *host_and_port, Error *error_ptr); + + lldb::ConnectionStatus ConnectUDP(const char *args, Error *error_ptr); + + lldb::ConnectionStatus NamedSocketConnect(const char *socket_name, Error *error_ptr); + + lldb::ConnectionStatus NamedSocketAccept(const char *socket_name, Error *error_ptr); + + lldb::IOObjectSP m_read_sp; + lldb::IOObjectSP m_write_sp; + + Predicate m_port_predicate; // Used when binding to port zero to wait for the thread + // that creates the socket, binds and listens to resolve + // the port number. + + Pipe m_pipe; + Mutex m_mutex; + std::atomic m_shutting_down; // This marks that we are shutting down so if we get woken up from + // BytesAvailable to disconnect, we won't try to read again. + bool m_waiting_for_accept; + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectionFileDescriptor); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionFileDescriptor_h_ Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -270,7 +270,6 @@ 2689003113353E0400698AC0 /* Broadcaster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */; }; 2689003213353E0400698AC0 /* Communication.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6E10F1B85900F91463 /* Communication.cpp */; }; 2689003313353E0400698AC0 /* Connection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E6F10F1B85900F91463 /* Connection.cpp */; }; - 2689003413353E0400698AC0 /* ConnectionFileDescriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */; }; 2689003513353E0400698AC0 /* ConstString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9410F1B85900F91463 /* ConstString.cpp */; }; 2689003613353E0400698AC0 /* DataBufferHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */; }; 2689003713353E0400698AC0 /* DataBufferMemoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */; }; @@ -583,6 +582,7 @@ 26FFC19C14FC072100087D58 /* DYLDRendezvous.h in Headers */ = {isa = PBXBuildFile; fileRef = 26FFC19614FC072100087D58 /* DYLDRendezvous.h */; }; 26FFC19D14FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FFC19714FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp */; }; 26FFC19E14FC072100087D58 /* DynamicLoaderPOSIXDYLD.h in Headers */ = {isa = PBXBuildFile; fileRef = 26FFC19814FC072100087D58 /* DynamicLoaderPOSIXDYLD.h */; }; + 3F6A92D419DB42E1005F2E60 /* ConnectionFileDescriptorPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F6A92D319DB42E1005F2E60 /* ConnectionFileDescriptorPosix.cpp */; }; 3FDFDDBD199C3A06009756A7 /* FileAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */; }; 3FDFDDBF199D345E009756A7 /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDBE199D345E009756A7 /* FileCache.cpp */; }; 3FDFDDC6199D37ED009756A7 /* FileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FDFDDC5199D37ED009756A7 /* FileSystem.cpp */; }; @@ -1466,7 +1466,6 @@ 26BC7D5510F1B77400F91463 /* ClangForward.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClangForward.h; path = include/lldb/Core/ClangForward.h; sourceTree = ""; }; 26BC7D5610F1B77400F91463 /* Communication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Communication.h; path = include/lldb/Core/Communication.h; sourceTree = ""; }; 26BC7D5710F1B77400F91463 /* Connection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Connection.h; path = include/lldb/Core/Connection.h; sourceTree = ""; }; - 26BC7D5810F1B77400F91463 /* ConnectionFileDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionFileDescriptor.h; path = include/lldb/Core/ConnectionFileDescriptor.h; sourceTree = ""; }; 26BC7D5910F1B77400F91463 /* DataBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataBuffer.h; path = include/lldb/Core/DataBuffer.h; sourceTree = ""; }; 26BC7D5A10F1B77400F91463 /* DataExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataExtractor.h; path = include/lldb/Core/DataExtractor.h; sourceTree = ""; }; 26BC7D5B10F1B77400F91463 /* DataBufferHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataBufferHeap.h; path = include/lldb/Core/DataBufferHeap.h; sourceTree = ""; }; @@ -1568,7 +1567,6 @@ 26BC7E6D10F1B85900F91463 /* Broadcaster.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Broadcaster.cpp; path = source/Core/Broadcaster.cpp; sourceTree = ""; }; 26BC7E6E10F1B85900F91463 /* Communication.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Communication.cpp; path = source/Core/Communication.cpp; sourceTree = ""; }; 26BC7E6F10F1B85900F91463 /* Connection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Connection.cpp; path = source/Core/Connection.cpp; sourceTree = ""; }; - 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConnectionFileDescriptor.cpp; path = source/Core/ConnectionFileDescriptor.cpp; sourceTree = ""; }; 26BC7E7110F1B85900F91463 /* DataExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataExtractor.cpp; path = source/Core/DataExtractor.cpp; sourceTree = ""; }; 26BC7E7210F1B85900F91463 /* DataBufferHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataBufferHeap.cpp; path = source/Core/DataBufferHeap.cpp; sourceTree = ""; }; 26BC7E7310F1B85900F91463 /* DataBufferMemoryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataBufferMemoryMap.cpp; path = source/Core/DataBufferMemoryMap.cpp; sourceTree = ""; }; @@ -1769,6 +1767,9 @@ 26FFC19614FC072100087D58 /* DYLDRendezvous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DYLDRendezvous.h; sourceTree = ""; }; 26FFC19714FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicLoaderPOSIXDYLD.cpp; sourceTree = ""; }; 26FFC19814FC072100087D58 /* DynamicLoaderPOSIXDYLD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicLoaderPOSIXDYLD.h; sourceTree = ""; }; + 3F6A92D319DB42E1005F2E60 /* ConnectionFileDescriptorPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ConnectionFileDescriptorPosix.cpp; path = source/Core/posix/ConnectionFileDescriptorPosix.cpp; sourceTree = ""; }; + 3F6A92D519DB42F8005F2E60 /* ConnectionFileDescriptorPosix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionFileDescriptorPosix.h; path = include/lldb/Core/posix/ConnectionFileDescriptorPosix.h; sourceTree = ""; }; + 3F6A92D719DB4343005F2E60 /* ConnectionFileDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ConnectionFileDescriptor.h; path = include/lldb/Core/ConnectionFileDescriptor.h; sourceTree = ""; }; 3FDFD6C3199C396E009756A7 /* FileAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileAction.h; path = include/lldb/Target/FileAction.h; sourceTree = ""; }; 3FDFDDBC199C3A06009756A7 /* FileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileAction.cpp; path = source/Target/FileAction.cpp; sourceTree = ""; }; 3FDFDDBE199D345E009756A7 /* FileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileCache.cpp; path = source/Host/common/FileCache.cpp; sourceTree = ""; }; @@ -3204,6 +3205,7 @@ 26BC7C1010F1B34800F91463 /* Core */ = { isa = PBXGroup; children = ( + 3F6A8B2A19DB4293005F2E60 /* posix */, 26BC7D5010F1B77400F91463 /* Address.h */, 26BC7E6910F1B85900F91463 /* Address.cpp */, 26BC7D5110F1B77400F91463 /* AddressRange.h */, @@ -3225,8 +3227,7 @@ 26BC7E6E10F1B85900F91463 /* Communication.cpp */, 26BC7D5710F1B77400F91463 /* Connection.h */, 26BC7E6F10F1B85900F91463 /* Connection.cpp */, - 26BC7D5810F1B77400F91463 /* ConnectionFileDescriptor.h */, - 26BC7E7010F1B85900F91463 /* ConnectionFileDescriptor.cpp */, + 3F6A92D719DB4343005F2E60 /* ConnectionFileDescriptor.h */, 2671A0CD134825F6003A87BB /* ConnectionMachPort.h */, 2671A0CF13482601003A87BB /* ConnectionMachPort.cpp */, 266603CC1345B5C0004DA8B6 /* ConnectionSharedMemory.h */, @@ -4074,6 +4075,15 @@ path = "POSIX-DYLD"; sourceTree = ""; }; + 3F6A8B2A19DB4293005F2E60 /* posix */ = { + isa = PBXGroup; + children = ( + 3F6A92D519DB42F8005F2E60 /* ConnectionFileDescriptorPosix.h */, + 3F6A92D319DB42E1005F2E60 /* ConnectionFileDescriptorPosix.cpp */, + ); + name = posix; + sourceTree = ""; + }; 3FDFDDC4199D37BE009756A7 /* posix */ = { isa = PBXGroup; children = ( @@ -5004,7 +5014,6 @@ 49DCF702170E70120092F75E /* Materializer.cpp in Sources */, 2689003213353E0400698AC0 /* Communication.cpp in Sources */, 2689003313353E0400698AC0 /* Connection.cpp in Sources */, - 2689003413353E0400698AC0 /* ConnectionFileDescriptor.cpp in Sources */, 2689003513353E0400698AC0 /* ConstString.cpp in Sources */, 2689003613353E0400698AC0 /* DataBufferHeap.cpp in Sources */, 2689003713353E0400698AC0 /* DataBufferMemoryMap.cpp in Sources */, @@ -5266,6 +5275,7 @@ 26BD407F135D2AE000237D80 /* FileLineResolver.cpp in Sources */, 26A7A035135E6E4200FB369E /* OptionValue.cpp in Sources */, 9A22A161135E30370024DDC3 /* EmulateInstructionARM.cpp in Sources */, + 3F6A92D419DB42E1005F2E60 /* ConnectionFileDescriptorPosix.cpp in Sources */, 9A22A163135E30370024DDC3 /* EmulationStateARM.cpp in Sources */, 9A4F35101368A51A00823F52 /* StreamAsynchronousIO.cpp in Sources */, 2692BA15136610C100F9E14D /* UnwindAssemblyInstEmulation.cpp in Sources */, Index: source/Core/CMakeLists.txt =================================================================== --- source/Core/CMakeLists.txt +++ source/Core/CMakeLists.txt @@ -1,6 +1,11 @@ set(LLVM_NO_RTTI 1) -add_lldb_library(lldbCore +macro(add_core_subdirectory group) + list(APPEND CORE_SOURCES ${ARGN}) + source_group(${group} FILES ${ARGN}) +endmacro() + +list(APPEND CORE_SOURCES Address.cpp AddressRange.cpp AddressResolver.cpp @@ -11,7 +16,6 @@ Broadcaster.cpp Communication.cpp Connection.cpp - ConnectionFileDescriptor.cpp ConnectionMachPort.cpp ConnectionSharedMemory.cpp ConstString.cpp @@ -74,3 +78,8 @@ VMRange.cpp ) +add_core_subdirectory(posix + posix/ConnectionFileDescriptorPosix.cpp + ) + +add_lldb_library(lldbCore ${CORE_SOURCES}) \ No newline at end of file Index: source/Core/ConnectionFileDescriptor.cpp =================================================================== --- source/Core/ConnectionFileDescriptor.cpp +++ /dev/null @@ -1,812 +0,0 @@ -//===-- ConnectionFileDescriptor.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 - -#include "lldb/Core/ConnectionFileDescriptor.h" -#include "lldb/Host/Config.h" -#include "lldb/Host/IOObject.h" -#include "lldb/Host/SocketAddress.h" -#include "lldb/Host/Socket.h" - -// C Includes -#include -#include -#include -#include -#include - -#ifndef LLDB_DISABLE_POSIX -#include -#endif - -// C++ Includes -// Other libraries and framework includes -#include "llvm/Support/ErrorHandling.h" -#if defined(__APPLE__) -#include "llvm/ADT/SmallVector.h" -#endif -// Project includes -#include "lldb/lldb-private-log.h" -#include "lldb/Core/Communication.h" -#include "lldb/Core/Log.h" -#include "lldb/Core/Timer.h" -#include "lldb/Host/Host.h" -#include "lldb/Host/Socket.h" -#include "lldb/Interpreter/Args.h" - - -using namespace lldb; -using namespace lldb_private; - -ConnectionFileDescriptor::ConnectionFileDescriptor () : - Connection(), - m_pipe (), - m_mutex (Mutex::eMutexTypeRecursive), - m_shutting_down (false), - m_waiting_for_accept (false) -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", - static_cast(this)); -} - -ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : - Connection(), - m_pipe (), - m_mutex (Mutex::eMutexTypeRecursive), - m_shutting_down (false), - m_waiting_for_accept (false) -{ - m_write_sp.reset(new File(fd, owns_fd)); - m_read_sp.reset(new File(fd, false)); - - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", - static_cast(this), fd, owns_fd); - OpenCommandPipe (); -} - - -ConnectionFileDescriptor::~ConnectionFileDescriptor () -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", - static_cast(this)); - Disconnect (NULL); - CloseCommandPipe (); -} - -void -ConnectionFileDescriptor::OpenCommandPipe () -{ - CloseCommandPipe(); - - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - // Make the command file descriptor here: - if (!m_pipe.Open()) - { - if (log) - log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", - static_cast(this), strerror(errno)); - } - else - { - if (log) - log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", - static_cast(this), - m_pipe.GetReadFileDescriptor(), - m_pipe.GetWriteFileDescriptor()); - } -} - -void -ConnectionFileDescriptor::CloseCommandPipe () -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::CloseCommandPipe()", - static_cast(this)); - - m_pipe.Close(); -} - -bool -ConnectionFileDescriptor::IsConnected () const -{ - return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); -} - -ConnectionStatus -ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) -{ - Mutex::Locker locker (m_mutex); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::Connect (url = '%s')", - static_cast(this), s); - - OpenCommandPipe(); - - if (s && s[0]) - { - if (strstr(s, "listen://") == s) - { - // listen://HOST:PORT - return SocketListen (s + strlen("listen://"), error_ptr); - } - else if (strstr(s, "accept://") == s) - { - // unix://SOCKNAME - return NamedSocketAccept (s + strlen("accept://"), error_ptr); - } - else if (strstr(s, "unix-accept://") == s) - { - // unix://SOCKNAME - return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); - } - else if (strstr(s, "connect://") == s) - { - return ConnectTCP (s + strlen("connect://"), error_ptr); - } - else if (strstr(s, "tcp-connect://") == s) - { - return ConnectTCP (s + strlen("tcp-connect://"), error_ptr); - } - else if (strstr(s, "udp://") == s) - { - return ConnectUDP (s + strlen("udp://"), error_ptr); - } -#ifndef LLDB_DISABLE_POSIX - else if (strstr(s, "fd://") == s) - { - // Just passing a native file descriptor within this current process - // that is already opened (possibly from a service or other source). - s += strlen ("fd://"); - bool success = false; - int fd = Args::StringToSInt32 (s, -1, 0, &success); - - if (success) - { - // We have what looks to be a valid file descriptor, but we - // should make sure it is. We currently are doing this by trying to - // get the flags from the file descriptor and making sure it - // isn't a bad fd. - errno = 0; - int flags = ::fcntl (fd, F_GETFL, 0); - if (flags == -1 || errno == EBADF) - { - if (error_ptr) - error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); - m_read_sp.reset(); - m_write_sp.reset(); - return eConnectionStatusError; - } - else - { - // Don't take ownership of a file descriptor that gets passed - // to us since someone else opened the file descriptor and - // handed it to us. - // TODO: Since are using a URL to open connection we should - // eventually parse options using the web standard where we - // have "fd://123?opt1=value;opt2=value" and we can have an - // option be "owns=1" or "owns=0" or something like this to - // allow us to specify this. For now, we assume we must - // assume we don't own it. - - std::unique_ptr tcp_socket; - tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, false)); - // Try and get a socket option from this file descriptor to - // see if this is a socket and set m_is_socket accordingly. - int resuse; - bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); - if (is_socket) - { - m_read_sp = std::move(tcp_socket); - m_write_sp = m_read_sp; - } - else - { - m_read_sp.reset(new File(fd, false)); - m_write_sp.reset(new File(fd, false)); - } - return eConnectionStatusSuccess; - } - } - - if (error_ptr) - error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); - m_read_sp.reset(); - m_write_sp.reset(); - return eConnectionStatusError; - } - else if (strstr(s, "file://") == s) - { - // file:///PATH - const char *path = s + strlen("file://"); - int fd = -1; - do - { - fd = ::open (path, O_RDWR); - } while (fd == -1 && errno == EINTR); - - if (fd == -1) - { - if (error_ptr) - error_ptr->SetErrorToErrno(); - return eConnectionStatusError; - } - - if (::isatty(fd)) - { - // Set up serial terminal emulation - struct termios options; - ::tcgetattr (fd, &options); - - // Set port speed to maximum - ::cfsetospeed (&options, B115200); - ::cfsetispeed (&options, B115200); - - // Raw input, disable echo and signals - options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); - - // Make sure only one character is needed to return from a read - options.c_cc[VMIN] = 1; - options.c_cc[VTIME] = 0; - - ::tcsetattr (fd, TCSANOW, &options); - } - - int flags = ::fcntl (fd, F_GETFL, 0); - if (flags >= 0) - { - if ((flags & O_NONBLOCK) == 0) - { - flags |= O_NONBLOCK; - ::fcntl (fd, F_SETFL, flags); - } - } - m_read_sp.reset(new File(fd, true)); - m_write_sp.reset(new File(fd, false)); - return eConnectionStatusSuccess; - } -#endif - if (error_ptr) - error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); - return eConnectionStatusError; - } - if (error_ptr) - error_ptr->SetErrorString("invalid connect arguments"); - return eConnectionStatusError; -} - -bool -ConnectionFileDescriptor::InterruptRead() -{ - return m_pipe.Write("i", 1) == 1; -} - -ConnectionStatus -ConnectionFileDescriptor::Disconnect (Error *error_ptr) -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", - static_cast(this)); - - ConnectionStatus status = eConnectionStatusSuccess; - - if (!IsConnected()) - { - if (log) - log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", - static_cast(this)); - return eConnectionStatusSuccess; - } - - if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) - static_cast(*m_read_sp).PreDisconnect(); - - // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely - // because somebody is doing a blocking read on our file descriptor. If that's the case, - // then send the "q" char to the command file channel so the read will wake up and the connection - // will then know to shut down. - - m_shutting_down = true; - - Mutex::Locker locker; - bool got_lock = locker.TryLock (m_mutex); - - if (!got_lock) - { - if (m_pipe.WriteDescriptorIsValid()) - { - int result; - result = m_pipe.Write("q", 1) == 1; - 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); - } - else if (log) - { - log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", - static_cast(this)); - } - locker.Lock (m_mutex); - } - - Error error = m_read_sp->Close(); - Error error2 = m_write_sp->Close(); - if (error.Fail() || error2.Fail()) - status = eConnectionStatusError; - if (error_ptr) - *error_ptr = error.Fail() ? error : error2; - - m_shutting_down = false; - return status; -} - -size_t -ConnectionFileDescriptor::Read (void *dst, - size_t dst_len, - uint32_t timeout_usec, - ConnectionStatus &status, - Error *error_ptr) -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - - Mutex::Locker locker; - bool got_lock = locker.TryLock (m_mutex); - if (!got_lock) - { - if (log) - log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", - static_cast(this)); - if (error_ptr) - error_ptr->SetErrorString ("failed to get the connection lock for read."); - - status = eConnectionStatusTimedOut; - return 0; - } - else if (m_shutting_down) - return eConnectionStatusError; - - status = BytesAvailable (timeout_usec, error_ptr); - if (status != eConnectionStatusSuccess) - return 0; - - Error error; - size_t bytes_read = dst_len; - error = m_read_sp->Read(dst, bytes_read); - - if (log) - { - log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", - static_cast(this), - static_cast(m_read_sp->GetWaitableHandle()), - static_cast(dst), - static_cast(dst_len), - static_cast(bytes_read), - error.AsCString()); - } - - if (bytes_read == 0) - { - error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. - status = eConnectionStatusEndOfFile; - } - - if (error_ptr) - *error_ptr = error; - - if (error.Fail()) - { - uint32_t error_value = error.GetError(); - switch (error_value) - { - case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. - if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) - status = eConnectionStatusTimedOut; - else - status = eConnectionStatusSuccess; - return 0; - - case EFAULT: // Buf points outside the allocated address space. - case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. - case EINVAL: // The pointer associated with fildes was negative. - case EIO: // An I/O error occurred while reading from the file system. - // The process group is orphaned. - // The file is a regular file, nbyte is greater than 0, - // the starting position is before the end-of-file, and - // the starting position is greater than or equal to the - // offset maximum established for the open file - // descriptor associated with fildes. - case EISDIR: // An attempt is made to read a directory. - case ENOBUFS: // An attempt to allocate a memory buffer fails. - case ENOMEM: // Insufficient memory is available. - status = eConnectionStatusError; - break; // Break to close.... - - case ENOENT: // no such file or directory - case EBADF: // fildes is not a valid file or socket descriptor open for reading. - case ENXIO: // An action is requested of a device that does not exist.. - // A requested action cannot be performed by the device. - case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. - case ENOTCONN: // A read is attempted on an unconnected socket. - status = eConnectionStatusLostConnection; - break; // Break to close.... - - case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. - status = eConnectionStatusTimedOut; - return 0; - - default: - if (log) - log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", - static_cast(this), strerror(error_value)); - status = eConnectionStatusError; - break; // Break to close.... - - } - - return 0; - } - return bytes_read; -} - -size_t -ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) -{ - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); - if (log) - log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", - static_cast(this), static_cast(src), - static_cast(src_len)); - - if (!IsConnected ()) - { - if (error_ptr) - error_ptr->SetErrorString("not connected"); - status = eConnectionStatusNoConnection; - return 0; - } - - - Error error; - - size_t bytes_sent = src_len; - error = m_write_sp->Write(src, bytes_sent); - - if (log) - { - log->Printf ("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", - static_cast(this), - static_cast(m_write_sp->GetWaitableHandle()), - static_cast(src), - static_cast(src_len), - static_cast(bytes_sent), - error.AsCString()); - } - - if (error_ptr) - *error_ptr = error; - - if (error.Fail()) - { - switch (error.GetError()) - { - case EAGAIN: - case EINTR: - status = eConnectionStatusSuccess; - return 0; - - case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. - case ENOTCONN: // A read is attempted on an unconnected socket. - status = eConnectionStatusLostConnection; - break; // Break to close.... - - default: - status = eConnectionStatusError; - break; // Break to close.... - } - - return 0; - } - - status = eConnectionStatusSuccess; - return bytes_sent; -} - - - -// This ConnectionFileDescriptor::BytesAvailable() uses select(). -// -// PROS: -// - select is consistent across most unix platforms -// - The Apple specific version allows for unlimited fds in the fd_sets by -// setting the _DARWIN_UNLIMITED_SELECT define prior to including the -// required header files. -// CONS: -// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. -// This implementation will assert if it runs into that hard limit to let -// users know that another ConnectionFileDescriptor::BytesAvailable() should -// 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) -{ - // Don't need to take the mutex here separately since we are only called from Read. If we - // ever get used more generally we will need to lock here as well. - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_CONNECTION)); - if (log) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", - static_cast(this), timeout_usec); - - struct timeval *tv_ptr; - struct timeval tv; - if (timeout_usec == UINT32_MAX) - { - // Inifinite 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 - // and cause problems in the loop below where like in FS_SET() - const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); - const int pipe_fd = m_pipe.GetReadFileDescriptor(); - - if (handle != IOObject::kInvalidHandleValue) - { -#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 - // this will allow ::select() to not return an error. - 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 - while (handle == m_read_sp->GetWaitableHandle()) - { - const int nfds = std::max(handle, pipe_fd) + 1; -#if defined(__APPLE__) - llvm::SmallVector read_fds; - read_fds.resize((nfds/FD_SETSIZE) + 1); - for (size_t i=0; iPrintf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", - static_cast(this), nfds, handle, pipe_fd, - static_cast(tv_ptr)); - else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", - static_cast(this), nfds, handle, - static_cast(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(this), nfds, handle, - pipe_fd, static_cast(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(this), nfds, handle, - static_cast(tv_ptr), num_set_fds, - error.AsCString()); - } - - if (error_ptr) - *error_ptr = error; - - if (error.Fail()) - { - switch (error.GetError()) - { - case EBADF: // One of the descriptor sets specified an invalid descriptor. - return eConnectionStatusLostConnection; - - case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. - default: // Other unknown error - return eConnectionStatusError; - - case EAGAIN: // The kernel was (perhaps temporarily) unable to - // allocate the requested number of file descriptors, - // or we have non-blocking IO - case EINTR: // A signal was delivered before the time limit - // expired and before any of the selected events - // occurred. - break; // Lets keep reading to until we timeout - } - } - else if (num_set_fds == 0) - { - return eConnectionStatusTimedOut; - } - else if (num_set_fds > 0) - { - if (FD_ISSET(handle, FD_SET_DATA(read_fds))) - return eConnectionStatusSuccess; - if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) - { - // We got a command to exit. Read the data from that pipe: - char buffer[16]; - ssize_t bytes_read; - - do - { - bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); - } while (bytes_read < 0 && errno == EINTR); - - switch (buffer[0]) - { - case 'q': - if (log) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", - static_cast(this), - static_cast(bytes_read), buffer); - return eConnectionStatusEndOfFile; - case 'i': - // Interrupt the current read - return eConnectionStatusInterrupted; - } - } - } - } - } - - if (error_ptr) - error_ptr->SetErrorString("not connected"); - return eConnectionStatusLostConnection; -} - -ConnectionStatus -ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) -{ - Socket* socket = nullptr; - Error error = Socket::UnixDomainAccept(socket_name, socket); - if (error_ptr) - *error_ptr = error; - m_write_sp.reset(socket); - m_read_sp = m_write_sp; - return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; -} - -ConnectionStatus -ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) -{ - Socket* socket = nullptr; - Error error = Socket::UnixDomainConnect(socket_name, socket); - if (error_ptr) - *error_ptr = error; - m_write_sp.reset(socket); - m_read_sp = m_write_sp; - return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; -} - -ConnectionStatus -ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr) -{ - m_port_predicate.SetValue(0, eBroadcastNever); - - Socket* socket = nullptr; - m_waiting_for_accept = true; - Error error = Socket::TcpListen(s, socket, &m_port_predicate); - if (error_ptr) - *error_ptr = error; - if (error.Fail()) - return eConnectionStatusError; - - std::unique_ptr listening_socket_up; - - listening_socket_up.reset(socket); - socket = nullptr; - error = listening_socket_up->BlockingAccept(s, socket); - listening_socket_up.reset(); - if (error_ptr) - *error_ptr = error; - if (error.Fail()) - return eConnectionStatusError; - - m_write_sp.reset(socket); - m_read_sp = m_write_sp; - return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; -} - -ConnectionStatus -ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr) -{ - Socket* socket = nullptr; - Error error = Socket::TcpConnect(s, socket); - if (error_ptr) - *error_ptr = error; - m_write_sp.reset(socket); - m_read_sp = m_write_sp; - return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; -} - -ConnectionStatus -ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr) -{ - Socket* send_socket = nullptr; - Socket* recv_socket = nullptr; - Error error = Socket::UdpConnect(s, send_socket, recv_socket); - if (error_ptr) - *error_ptr = error; - m_write_sp.reset(send_socket); - m_read_sp.reset(recv_socket); - return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; -} - - -uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) -{ - uint16_t bound_port = 0; - if (timeout_sec == UINT32_MAX) - m_port_predicate.WaitForValueNotEqualTo (0, bound_port); - else - { - TimeValue timeout = TimeValue::Now(); - timeout.OffsetWithSeconds(timeout_sec); - m_port_predicate.WaitForValueNotEqualTo (0, bound_port, &timeout); - } - return bound_port; -} Index: source/Core/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- /dev/null +++ source/Core/posix/ConnectionFileDescriptorPosix.cpp @@ -0,0 +1,780 @@ +//===-- ConnectionFileDescriptorPosix.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 + +#include "lldb/Core/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/IOObject.h" +#include "lldb/Host/SocketAddress.h" +#include "lldb/Host/Socket.h" + +// C Includes +#include +#include +#include +#include +#include + +#ifndef LLDB_DISABLE_POSIX +#include +#endif + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/ErrorHandling.h" +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Socket.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +ConnectionFileDescriptor::ConnectionFileDescriptor() + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast(this)); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) +{ + m_write_sp.reset(new File(fd, owns_fd)); + m_read_sp.reset(new File(fd, false)); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", static_cast(this), fd, + owns_fd); + OpenCommandPipe(); +} + +ConnectionFileDescriptor::~ConnectionFileDescriptor() +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast(this)); + Disconnect(NULL); + CloseCommandPipe(); +} + +void +ConnectionFileDescriptor::OpenCommandPipe() +{ + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + if (!m_pipe.Open()) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", static_cast(this), + strerror(errno)); + } + else + { + if (log) + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", static_cast(this), + m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor()); + } +} + +void +ConnectionFileDescriptor::CloseCommandPipe() +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast(this)); + + m_pipe.Close(); +} + +bool +ConnectionFileDescriptor::IsConnected() const +{ + return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); +} + +ConnectionStatus +ConnectionFileDescriptor::Connect(const char *s, Error *error_ptr) +{ + Mutex::Locker locker(m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast(this), s); + + OpenCommandPipe(); + + if (s && s[0]) + { + if (strstr(s, "listen://") == s) + { + // listen://HOST:PORT + return SocketListen(s + strlen("listen://"), error_ptr); + } + else if (strstr(s, "accept://") == s) + { + // unix://SOCKNAME + return NamedSocketAccept(s + strlen("accept://"), error_ptr); + } + else if (strstr(s, "unix-accept://") == s) + { + // unix://SOCKNAME + return NamedSocketAccept(s + strlen("unix-accept://"), error_ptr); + } + else if (strstr(s, "connect://") == s) + { + return ConnectTCP(s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "tcp-connect://") == s) + { + return ConnectTCP(s + strlen("tcp-connect://"), error_ptr); + } + else if (strstr(s, "udp://") == s) + { + return ConnectUDP(s + strlen("udp://"), error_ptr); + } +#ifndef LLDB_DISABLE_POSIX + else if (strstr(s, "fd://") == s) + { + // Just passing a native file descriptor within this current process + // that is already opened (possibly from a service or other source). + s += strlen("fd://"); + bool success = false; + int fd = Args::StringToSInt32(s, -1, 0, &success); + + if (success) + { + // We have what looks to be a valid file descriptor, but we + // should make sure it is. We currently are doing this by trying to + // get the flags from the file descriptor and making sure it + // isn't a bad fd. + errno = 0; + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags == -1 || errno == EBADF) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", s); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } + else + { + // Don't take ownership of a file descriptor that gets passed + // to us since someone else opened the file descriptor and + // handed it to us. + // TODO: Since are using a URL to open connection we should + // eventually parse options using the web standard where we + // have "fd://123?opt1=value;opt2=value" and we can have an + // option be "owns=1" or "owns=0" or something like this to + // allow us to specify this. For now, we assume we must + // assume we don't own it. + + std::unique_ptr tcp_socket; + tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, false)); + // Try and get a socket option from this file descriptor to + // see if this is a socket and set m_is_socket accordingly. + int resuse; + bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); + if (is_socket) + { + m_read_sp = std::move(tcp_socket); + m_write_sp = m_read_sp; + } + else + { + m_read_sp.reset(new File(fd, false)); + m_write_sp.reset(new File(fd, false)); + } + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"fd://%s\"", s); + m_read_sp.reset(); + m_write_sp.reset(); + return eConnectionStatusError; + } + else if (strstr(s, "file://") == s) + { + // file:///PATH + const char *path = s + strlen("file://"); + int fd = -1; + do + { + fd = ::open(path, O_RDWR); + } while (fd == -1 && errno == EINTR); + + if (fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(fd)) + { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr(fd, &options); + + // Set port speed to maximum + ::cfsetospeed(&options, B115200); + ::cfsetispeed(&options, B115200); + + // Raw input, disable echo and signals + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Make sure only one character is needed to return from a read + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + + ::tcsetattr(fd, TCSANOW, &options); + } + + int flags = ::fcntl(fd, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl(fd, F_SETFL, flags); + } + } + m_read_sp.reset(new File(fd, true)); + m_write_sp.reset(new File(fd, false)); + return eConnectionStatusSuccess; + } +#endif + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +bool +ConnectionFileDescriptor::InterruptRead() +{ + return m_pipe.Write("i", 1) == 1; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect(Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast(this)); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (!IsConnected()) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast(this)); + return eConnectionStatusSuccess; + } + + if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + static_cast(*m_read_sp).PreDisconnect(); + + // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely + // because somebody is doing a blocking read on our file descriptor. If that's the case, + // then send the "q" char to the command file channel so the read will wake up and the connection + // will then know to shut down. + + m_shutting_down = true; + + Mutex::Locker locker; + bool got_lock = locker.TryLock(m_mutex); + + if (!got_lock) + { + if (m_pipe.WriteDescriptorIsValid()) + { + int result; + result = m_pipe.Write("q", 1) == 1; + 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); + } + else if (log) + { + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", + static_cast(this)); + } + locker.Lock(m_mutex); + } + + Error error = m_read_sp->Close(); + Error error2 = m_write_sp->Close(); + if (error.Fail() || error2.Fail()) + status = eConnectionStatusError; + if (error_ptr) + *error_ptr = error.Fail() ? error : error2; + + m_shutting_down = false; + return status; +} + +size_t +ConnectionFileDescriptor::Read(void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + + Mutex::Locker locker; + bool got_lock = locker.TryLock(m_mutex); + if (!got_lock) + { + if (log) + log->Printf("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", static_cast(this)); + if (error_ptr) + error_ptr->SetErrorString("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + else if (m_shutting_down) + return eConnectionStatusError; + + status = BytesAvailable(timeout_usec, error_ptr); + if (status != eConnectionStatusSuccess) + return 0; + + Error error; + size_t bytes_read = dst_len; + error = m_read_sp->Read(dst, bytes_read); + + if (log) + { + log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", + static_cast(this), static_cast(m_read_sp->GetWaitableHandle()), static_cast(dst), + static_cast(dst_len), static_cast(bytes_read), error.AsCString()); + } + + if (bytes_read == 0) + { + error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + uint32_t error_value = error.GetError(); + switch (error_value) + { + case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. + if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, + // the starting position is before the end-of-file, and + // the starting position is greater than or equal to the + // offset maximum established for the open file + // descriptor associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. + status = eConnectionStatusTimedOut; + return 0; + + default: + if (log) + log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", static_cast(this), + strerror(error_value)); + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + return bytes_read; +} + +size_t +ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast(this), + static_cast(src), static_cast(src_len)); + + if (!IsConnected()) + { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + Error error; + + size_t bytes_sent = src_len; + error = m_write_sp->Write(src, bytes_sent); + + if (log) + { + log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", + static_cast(this), static_cast(m_write_sp->GetWaitableHandle()), static_cast(src), + static_cast(src_len), static_cast(bytes_sent), error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// - The Apple specific version allows for unlimited fds in the fd_sets by +// setting the _DARWIN_UNLIMITED_SELECT define prior to including the +// required header files. +// CONS: +// - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. +// This implementation will assert if it runs into that hard limit to let +// users know that another ConnectionFileDescriptor::BytesAvailable() should +// 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) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast(this), timeout_usec); + + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Inifinite 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 + // and cause problems in the loop below where like in FS_SET() + const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); + const int pipe_fd = m_pipe.GetReadFileDescriptor(); + + if (handle != IOObject::kInvalidHandleValue) + { +#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 + // this will allow ::select() to not return an error. + 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 + while (handle == m_read_sp->GetWaitableHandle()) + { + const int nfds = std::max(handle, pipe_fd) + 1; +#if defined(__APPLE__) + llvm::SmallVector 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(this), nfds, handle, pipe_fd, static_cast(tv_ptr)); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + static_cast(this), nfds, handle, static_cast(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(this), nfds, handle, pipe_fd, static_cast(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(this), nfds, handle, static_cast(tv_ptr), num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (FD_ISSET(handle, FD_SET_DATA(read_fds))) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read(pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + + switch (buffer[0]) + { + case 'q': + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + static_cast(this), static_cast(bytes_read), buffer); + return eConnectionStatusEndOfFile; + case 'i': + // Interrupt the current read + return eConnectionStatusInterrupted; + } + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept(const char *socket_name, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::UnixDomainAccept(socket_name, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect(const char *socket_name, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::UnixDomainConnect(socket_name, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr) +{ + m_port_predicate.SetValue(0, eBroadcastNever); + + Socket *socket = nullptr; + m_waiting_for_accept = true; + Error error = Socket::TcpListen(s, socket, &m_port_predicate); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + std::unique_ptr listening_socket_up; + + listening_socket_up.reset(socket); + socket = nullptr; + error = listening_socket_up->BlockingAccept(s, socket); + listening_socket_up.reset(); + if (error_ptr) + *error_ptr = error; + if (error.Fail()) + return eConnectionStatusError; + + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr) +{ + Socket *socket = nullptr; + Error error = Socket::TcpConnect(s, socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(socket); + m_read_sp = m_write_sp; + return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr) +{ + Socket *send_socket = nullptr; + Socket *recv_socket = nullptr; + Error error = Socket::UdpConnect(s, send_socket, recv_socket); + if (error_ptr) + *error_ptr = error; + m_write_sp.reset(send_socket); + m_read_sp.reset(recv_socket); + return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; +} + +uint16_t +ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) +{ + uint16_t bound_port = 0; + if (timeout_sec == UINT32_MAX) + m_port_predicate.WaitForValueNotEqualTo(0, bound_port); + else + { + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithSeconds(timeout_sec); + m_port_predicate.WaitForValueNotEqualTo(0, bound_port, &timeout); + } + return bound_port; +}