diff --git a/lldb/include/lldb/Core/ConnectionFileDescriptor.h b/lldb/include/lldb/Core/ConnectionFileDescriptor.h index 3a2f0dd1ed0b..01cc40a37de0 100644 --- a/lldb/include/lldb/Core/ConnectionFileDescriptor.h +++ b/lldb/include/lldb/Core/ConnectionFileDescriptor.h @@ -76,6 +76,9 @@ public: in_port_t GetWritePort () const; + in_port_t + GetBoundPort (uint32_t timeout_sec); + protected: typedef enum @@ -95,7 +98,7 @@ protected: BytesAvailable (uint32_t timeout_usec, Error *error_ptr); lldb::ConnectionStatus - SocketListen (uint16_t listen_port_num, Error *error_ptr); + SocketListen (const char *host_and_port, Error *error_ptr); lldb::ConnectionStatus ConnectTCP (const char *host_and_port, Error *error_ptr); @@ -117,11 +120,12 @@ protected: FDType m_fd_send_type; FDType m_fd_recv_type; std::unique_ptr m_udp_send_sockaddr; - bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. uint32_t m_socket_timeout_usec; int m_pipe_read; // A pipe that we select on the reading end of along with int m_pipe_write; // m_fd_recv so we can force ourselves out of the select. - Mutex m_mutex; + Mutex m_mutex; + 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 + bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. bool 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. diff --git a/lldb/include/lldb/Host/SocketAddress.h b/lldb/include/lldb/Host/SocketAddress.h index 5e79e94fa9ec..02d24a33e9c1 100644 --- a/lldb/include/lldb/Host/SocketAddress.h +++ b/lldb/include/lldb/Host/SocketAddress.h @@ -121,10 +121,12 @@ public: // address. //------------------------------------------------------------------ bool - SetAddress (const struct addrinfo *hints_ptr, // Optional hints where the family, protocol and other things can be specified. - const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") - const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") - struct addrinfo *addr_info_ptr); // If non-NULL, this will get filled in with the match + getaddrinfo (const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") + const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") + int ai_family = PF_UNSPEC, + int ai_socktype = 0, + int ai_protocol = 0, + int ai_flags = 0); //------------------------------------------------------------------ // Quick way to set the SocketAddress to localhost given the family. @@ -135,6 +137,10 @@ public: SetToLocalhost (sa_family_t family, in_port_t port); + bool + SetToAnyAddress (sa_family_t family, + in_port_t port); + //------------------------------------------------------------------ // Returns true if there is a valid socket address in this object. //------------------------------------------------------------------ diff --git a/lldb/source/Core/ConnectionFileDescriptor.cpp b/lldb/source/Core/ConnectionFileDescriptor.cpp index 5764a212ab43..d711ef8e185a 100644 --- a/lldb/source/Core/ConnectionFileDescriptor.cpp +++ b/lldb/source/Core/ConnectionFileDescriptor.cpp @@ -97,11 +97,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor () : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (false), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (false), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -116,11 +116,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (owns_fd), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (owns_fd), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -218,12 +218,15 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) if (s && s[0]) { - char *end = NULL; if (strstr(s, "listen://")) { // listen://HOST:PORT - unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); - return SocketListen (listen_port, error_ptr); + return SocketListen (s + strlen("listen://"), error_ptr); + } + else if (strstr(s, "accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("accept://"), error_ptr); } else if (strstr(s, "unix-accept://")) { @@ -363,6 +366,9 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) if (log) log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + // Reset the port predicate when disconnecting and don't broadcast + m_port_predicate.SetValue(0, eBroadcastNever); + ConnectionStatus status = eConnectionStatusSuccess; if (m_fd_send < 0 && m_fd_recv < 0) @@ -1281,16 +1287,31 @@ ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *er } ConnectionStatus -ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +ConnectionFileDescriptor::SocketListen (const char *host_and_port, Error *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + log->Printf ("%p ConnectionFileDescriptor::SocketListen (%s)", this, host_and_port); Disconnect (NULL); m_fd_send_type = m_fd_recv_type = eFDTypeSocket; - int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listen_port == -1) + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + { + // Might be just a port number + port = Args::StringToSInt32(host_and_port, -1); + if (port == -1) + return eConnectionStatusError; + else + host_str.clear(); + } + const sa_family_t family = AF_INET; + const int socktype = SOCK_STREAM; + const int protocol = IPPROTO_TCP; + int listen_fd = ::socket (family, socktype, protocol); + if (listen_fd == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); @@ -1298,41 +1319,114 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p } // enable local address reuse - SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); - SocketAddress localhost; - if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + SocketAddress listen_addr; + if (host_str.empty()) + listen_addr.SetToLocalhost(family, port); + else if (host_str.compare("*") == 0) + listen_addr.SetToAnyAddress(family, port); + else { - int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (!listen_addr.getaddrinfo(host_str.c_str(), port_str.c_str(), family, socktype, protocol)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unable to resolve hostname '%s'", host_str.c_str()); + Close (listen_fd, eFDTypeSocket, NULL); + return eConnectionStatusError; + } + } + + SocketAddress anyaddr; + if (anyaddr.SetToAnyAddress (family, port)) + { + int err = ::bind (listen_fd, anyaddr, anyaddr.GetLength()); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - err = ::listen (listen_port, 1); + err = ::listen (listen_fd, 1); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + // We were asked to listen on port zero which means we + // must now read the actual port that was given to us + // as port zero is a special code for "find an open port + // for me". + if (port == 0) + port = GetSocketPort(listen_fd); + + // Set the port predicate since when doing a listen://: + // it often needs to accept the incoming connection which is a blocking + // system call. Allowing access to the bound port using a predicate allows + // us to wait for the port predicate to be set to a non-zero value from + // another thread in an efficient manor. + m_port_predicate.SetValue(port, eBroadcastAlways); + + + bool accept_connection = false; + + // Loop until we are happy with our connection + while (!accept_connection) + { + struct sockaddr_in accept_addr; + ::memset (&accept_addr, 0, sizeof accept_addr); + accept_addr.sin_len = sizeof accept_addr; + socklen_t accept_addr_len = sizeof accept_addr; + + int fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len); + + if (fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + break; + } + + if (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + if (accept_addr_len == listen_addr.sockaddr_in().sin_len && + accept_addr.sin_addr.s_addr == listen_addr.sockaddr_in().sin_addr.s_addr) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + ::close (fd); + m_fd_send = m_fd_recv = -1; + const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; + const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sockaddr_in().sin_addr.s_addr; + ::fprintf (stderr, "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + } + } + } + if (m_fd_send == -1) { - if (error_ptr) - error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } } // We are done with the listen port - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); m_should_close_fd = true; @@ -1446,7 +1540,7 @@ ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_pt { // Socket was created, now lets bind to the requested port SocketAddress addr; - addr.SetToLocalhost (AF_INET, 0); + addr.SetToAnyAddress (AF_INET, 0); if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) { @@ -1585,11 +1679,13 @@ in_port_t ConnectionFileDescriptor::GetSocketPort (int fd) { // We bound to port zero, so we need to figure out which port we actually bound to - SocketAddress sock_addr; - socklen_t sock_addr_len = sock_addr.GetMaxLength (); - if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) - return sock_addr.GetPort (); - + if (fd >= 0) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + } return 0; } @@ -1609,4 +1705,17 @@ ConnectionFileDescriptor::GetWritePort () const return ConnectionFileDescriptor::GetSocketPort (m_fd_send); } - +in_port_t +ConnectionFileDescriptor::GetBoundPort (uint32_t timeout_sec) +{ + in_port_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; +} diff --git a/lldb/source/Host/common/SocketAddress.cpp b/lldb/source/Host/common/SocketAddress.cpp index 1fa7531af9dc..f5a58a6d208e 100644 --- a/lldb/source/Host/common/SocketAddress.cpp +++ b/lldb/source/Host/common/SocketAddress.cpp @@ -128,8 +128,8 @@ SocketAddress::GetPort () const { switch (GetFamily()) { - case AF_INET: return m_socket_addr.sa_ipv4.sin_port; - case AF_INET6: return m_socket_addr.sa_ipv6.sin6_port; + case AF_INET: return ntohs(m_socket_addr.sa_ipv4.sin_port); + case AF_INET6: return ntohs(m_socket_addr.sa_ipv6.sin6_port); } return 0; } @@ -206,31 +206,29 @@ SocketAddress::operator=(const struct sockaddr_storage &s) } bool -SocketAddress::SetAddress (const struct addrinfo *hints_ptr, - const char *host, - const char *service, - struct addrinfo *addr_info_ptr) +SocketAddress::getaddrinfo (const char *host, + const char *service, + int ai_family, + int ai_socktype, + int ai_protocol, + int ai_flags) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai_family; + hints.ai_socktype = ai_socktype; + hints.ai_protocol = ai_protocol; + hints.ai_flags = ai_flags; + struct addrinfo *service_info_list = NULL; - int err = ::getaddrinfo (host, service, hints_ptr, &service_info_list); + int err = ::getaddrinfo (host, service, &hints, &service_info_list); if (err == 0 && service_info_list) - { - if (addr_info_ptr) - *addr_info_ptr = *service_info_list; *this = service_info_list; - } else Clear(); :: freeaddrinfo (service_info_list); - - const bool is_valid = IsValid(); - if (!is_valid) - { - if (addr_info_ptr) - ::memset (addr_info_ptr, 0, sizeof(struct addrinfo)); - } - return is_valid; + return IsValid(); } @@ -243,7 +241,7 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port) SetFamily (AF_INET); if (SetPort (port)) { - m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY); + m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); return true; } break; @@ -252,7 +250,7 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port) SetFamily (AF_INET6); if (SetPort (port)) { - m_socket_addr.sa_ipv6.sin6_addr = in6addr_any; + m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback; return true; } break; @@ -261,3 +259,31 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port) Clear(); return false; } + +bool +SocketAddress::SetToAnyAddress (sa_family_t family, in_port_t port) +{ + switch (family) + { + case AF_INET: + SetFamily (AF_INET); + if (SetPort (port)) + { + m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY); + return true; + } + break; + + case AF_INET6: + SetFamily (AF_INET6); + if (SetPort (port)) + { + m_socket_addr.sa_ipv6.sin6_addr = in6addr_any; + return true; + } + break; + + } + Clear(); + return false; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d53347a2e383..04de58bcd4c3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -17,6 +17,7 @@ // C++ Includes // Other libraries and framework includes +#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" @@ -144,7 +145,9 @@ GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, m_private_is_running (false), m_history (512), m_send_acks (true), - m_is_platform (is_platform) + m_is_platform (is_platform), + m_listen_thread (LLDB_INVALID_HOST_THREAD), + m_listen_url () { } @@ -539,7 +542,56 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri } Error -GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, +GDBRemoteCommunication::StartListenThread (const char *hostname, in_port_t port) +{ + Error error; + if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread)) + { + error.SetErrorString("listen thread already running"); + } + else + { + char listen_url[512]; + if (hostname && hostname[0]) + snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname ? hostname : "localhost", port); + else + snprintf(listen_url, sizeof(listen_url), "listen://%i", port); + m_listen_url = listen_url; + SetConnection(new ConnectionFileDescriptor()); + m_listen_thread = Host::ThreadCreate (listen_url, GDBRemoteCommunication::ListenThread, this, &error); + } + return error; +} + +bool +GDBRemoteCommunication::JoinListenThread () +{ + if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread)) + { + Host::ThreadJoin(m_listen_thread, NULL, NULL); + m_listen_thread = LLDB_INVALID_HOST_THREAD; + } + return true; +} + +lldb::thread_result_t +GDBRemoteCommunication::ListenThread (lldb::thread_arg_t arg) +{ + GDBRemoteCommunication *comm = (GDBRemoteCommunication *)arg; + Error error; + ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)comm->GetConnection (); + + if (connection) + { + // Do the listen on another thread so we can continue on... + if (connection->Connect(comm->m_listen_url.c_str(), &error) != eConnectionStatusSuccess) + comm->SetConnection(NULL); + } + return NULL; +} + +Error +GDBRemoteCommunication::StartDebugserverProcess (const char *host_and_port, lldb_private::ProcessLaunchInfo &launch_info, uint16_t &port) { @@ -594,42 +646,69 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, // Start args with "debugserver /file/path -r --" debugserver_args.AppendArgument(debugserver_path); - debugserver_args.AppendArgument(debugserver_url); + + // If a host and port is supplied then use it + if (host_and_port) + debugserver_args.AppendArgument(host_and_port); // use native registers, not the GDB registers debugserver_args.AppendArgument("--native-regs"); // make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver debugserver_args.AppendArgument("--setsid"); - - char named_pipe_path[PATH_MAX]; - - // Create a temporary file to get the stdout/stderr and redirect the - // output of the command into this file. We will later read this file - // if all goes well and fill the data into "command_output_ptr" - FileSpec tmpdir_file_spec; - if (Host::GetLLDBPath (ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) - { - tmpdir_file_spec.GetFilename().SetCString("debugserver-named-pipe.XXXXXX"); - strncpy(named_pipe_path, tmpdir_file_spec.GetPath().c_str(), sizeof(named_pipe_path)); - } - else - { - strncpy(named_pipe_path, "/tmp/debugserver-named-pipe.XXXXXX", sizeof(named_pipe_path)); - } - if (::mktemp (named_pipe_path)) + char named_pipe_path[PATH_MAX]; + + if (host_and_port) { - if (::mkfifo(named_pipe_path, 0600) == 0) + // Create a temporary file to get the stdout/stderr and redirect the + // output of the command into this file. We will later read this file + // if all goes well and fill the data into "command_output_ptr" + FileSpec tmpdir_file_spec; + if (Host::GetLLDBPath (ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { - debugserver_args.AppendArgument("--named-pipe"); - debugserver_args.AppendArgument(named_pipe_path); + tmpdir_file_spec.GetFilename().SetCString("debugserver-named-pipe.XXXXXX"); + strncpy(named_pipe_path, tmpdir_file_spec.GetPath().c_str(), sizeof(named_pipe_path)); + } + else + { + strncpy(named_pipe_path, "/tmp/debugserver-named-pipe.XXXXXX", sizeof(named_pipe_path)); + } + + if (::mktemp (named_pipe_path)) + { + if (::mkfifo(named_pipe_path, 0600) == 0) + { + debugserver_args.AppendArgument("--named-pipe"); + debugserver_args.AppendArgument(named_pipe_path); + } + else + named_pipe_path[0] = '\0'; } else named_pipe_path[0] = '\0'; } else + { named_pipe_path[0] = '\0'; + + // No host and port given, so lets listen on our end and make the debugserver + // connect to us.. + error = StartListenThread ("localhost", 0); + if (error.Fail()) + return error; + ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection (); + port = connection->GetBoundPort(3); + assert (port != 0); + char port_cstr[32]; + snprintf(port_cstr, sizeof(port_cstr), "localhost:%i", port); + // Send the host and port down that debugserver and specify an option + // so that it connects back to the port we are listening to in this process + debugserver_args.AppendArgument("--reverse-connect"); + debugserver_args.AppendArgument(port_cstr); + } + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); if (env_debugserver_log_file) { @@ -669,7 +748,11 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, } Host::Unlink(named_pipe_path); } - + else + { + // Make sure we actually connect with the debugserver... + JoinListenThread(); + } } else { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index 98e29e4a3ece..a21679e51dc7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -108,8 +108,8 @@ public: // Start a debugserver instance on the current host using the // supplied connection URL. //------------------------------------------------------------------ - static lldb_private::Error - StartDebugserverProcess (const char *connect_url, + lldb_private::Error + StartDebugserverProcess (const char *host_and_port, lldb_private::ProcessLaunchInfo &launch_info, uint16_t &port); @@ -256,9 +256,22 @@ protected: // a single process + lldb_private::Error + StartListenThread (const char *hostname = "localhost", + in_port_t port = 0); + bool + JoinListenThread (); + + static lldb::thread_result_t + ListenThread (lldb::thread_arg_t arg); private: + + lldb::thread_t m_listen_thread; + std::string m_listen_url; + + //------------------------------------------------------------------ // For GDBRemoteCommunication only //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 56414e213b1a..50cdd4060082 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -812,9 +812,9 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false); - error = GDBRemoteCommunication::StartDebugserverProcess (host_and_port_cstr, - debugserver_launch_info, - port); + error = StartDebugserverProcess (host_and_port_cstr, + debugserver_launch_info, + port); lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); @@ -1130,7 +1130,6 @@ GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packe mode_t mode = packet.GetHexMaxU32(false, 0600); Error error; int fd = ::open (path.c_str(), flags, mode); - printf ("open('%s', flags=0x%x, mode=%o) fd = %i (%s)\n", path.c_str(), flags, mode, fd, fd == -1 ? strerror(errno) : ""); const int save_errno = fd == -1 ? errno : 0; StreamString response; response.PutChar('F'); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 45278972ec1c..c6dc6cff9825 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -845,31 +845,35 @@ Error ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) { Error error; - // Sleep and wait a bit for debugserver to start to listen... - std::unique_ptr conn_ap(new ConnectionFileDescriptor()); - if (conn_ap.get()) + // Only connect if we have a valid connect URL + + if (connect_url && connect_url[0]) { - const uint32_t max_retry_count = 50; - uint32_t retry_count = 0; - while (!m_gdb_comm.IsConnected()) + std::unique_ptr conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) { - if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) + const uint32_t max_retry_count = 50; + uint32_t retry_count = 0; + while (!m_gdb_comm.IsConnected()) { - m_gdb_comm.SetConnection (conn_ap.release()); - break; - } - else if (error.WasInterrupted()) - { - // If we were interrupted, don't keep retrying. - break; - } - - retry_count++; - - if (retry_count >= max_retry_count) - break; + if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) + { + m_gdb_comm.SetConnection (conn_ap.release()); + break; + } + else if (error.WasInterrupted()) + { + // If we were interrupted, don't keep retrying. + break; + } + + retry_count++; + + if (retry_count >= max_retry_count) + break; - usleep (100000); + usleep (100000); + } } } @@ -2501,9 +2505,9 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); debugserver_launch_info.SetUserID(process_info.GetUserID()); - error = GDBRemoteCommunication::StartDebugserverProcess ("localhost:0", - debugserver_launch_info, - port); + error = m_gdb_comm.StartDebugserverProcess (NULL, + debugserver_launch_info, + port); if (error.Success ()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); @@ -2522,10 +2526,17 @@ ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info return error; } - char connect_url[128]; - snprintf (connect_url, sizeof(connect_url), "connect://localhost:%u", port); - - error = ConnectToDebugserver (connect_url); + if (m_gdb_comm.IsConnected()) + { + // Finish the connection process by doing the handshake without connecting (send NULL URL) + ConnectToDebugserver (NULL); + } + else + { + char connect_url[128]; + snprintf (connect_url, sizeof(connect_url), "connect://localhost:%u", port); + error = ConnectToDebugserver (connect_url); + } } return error; diff --git a/lldb/tools/debugserver/source/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp index b0b694c5504a..42469ce89e31 100644 --- a/lldb/tools/debugserver/source/debugserver.cpp +++ b/lldb/tools/debugserver/source/debugserver.cpp @@ -644,21 +644,34 @@ PortWasBoundCallbackNamedPipe (const void *baton, in_port_t port) } static int -StartListening (RNBRemote *remote, const char *listen_host, int listen_port, const char *named_pipe_path) +ConnectRemote (RNBRemote *remote, const char *host, int port, bool reverse_connect, const char *named_pipe_path) { if (!remote->Comm().IsConnected()) { - if (listen_port != 0) - RNBLogSTDOUT ("Listening to port %i for a connection from %s...\n", listen_port, listen_host ? listen_host : "localhost"); - if (remote->Comm().Listen(listen_host, listen_port, PortWasBoundCallbackNamedPipe, named_pipe_path) != rnb_success) + if (reverse_connect) { - RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); - return 0; + if (port == 0) + { + DNBLogThreaded("error: invalid port supplied for reverse connection: %i.\n", port); + return 0; + } + if (remote->Comm().Connect(host, port) != rnb_success) + { + DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port); + return 0; + } } else { - remote->StartReadRemoteDataThread(); + if (port != 0) + RNBLogSTDOUT ("Listening to port %i for a connection from %s...\n", port, host ? host : "localhost"); + if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe, named_pipe_path) != rnb_success) + { + RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); + return 0; + } } + remote->StartReadRemoteDataThread(); } return 1; } @@ -746,6 +759,7 @@ static struct option g_long_options[] = { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process) { "platform", required_argument, NULL, 'p' }, // Put this executable into a remote platform mode { "named-pipe", required_argument, NULL, 'P' }, + { "reverse-connect", no_argument, NULL, 'R' }, { NULL, 0, NULL, 0 } }; @@ -801,6 +815,7 @@ main (int argc, char *argv[]) useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. bool no_stdio = false; + bool reverse_connect = false; // Set to true by an option to indicate we should reverse connect to the host:port supplied as the first debugserver argument #if !defined (DNBLOG_ENABLED) compile_options += "(no-logging) "; @@ -997,6 +1012,9 @@ main (int argc, char *argv[]) // Do nothing, native regs is the default these days break; + case 'R': + reverse_connect = true; + break; case 'v': DNBLogSetVerbose(1); break; @@ -1117,8 +1135,8 @@ main (int argc, char *argv[]) compile_options.c_str(), RNB_ARCH); - std::string listen_host; - int listen_port = INT32_MAX; + std::string host; + int port = INT32_MAX; char str[PATH_MAX]; str[0] = '\0'; @@ -1131,24 +1149,24 @@ main (int argc, char *argv[]) } // accept 'localhost:' prefix on port number - int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port); + int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &port); if (items_scanned == 2) { - listen_host = str; - DNBLogDebug("host = '%s' port = %i", listen_host.c_str(), listen_port); + host = str; + DNBLogDebug("host = '%s' port = %i", host.c_str(), port); } else { // No hostname means "localhost" - int items_scanned = ::sscanf (argv[0], "%i", &listen_port); + int items_scanned = ::sscanf (argv[0], "%i", &port); if (items_scanned == 1) { - listen_host = "localhost"; - DNBLogDebug("host = '%s' port = %i", listen_host.c_str(), listen_port); + host = "localhost"; + DNBLogDebug("host = '%s' port = %i", host.c_str(), port); } else if (argv[0][0] == '/') { - listen_port = INT32_MAX; + port = INT32_MAX; strncpy(str, argv[0], sizeof(str)); } else @@ -1263,9 +1281,9 @@ main (int argc, char *argv[]) } else #endif - if (listen_port != INT32_MAX) + if (port != INT32_MAX) { - if (!StartListening (remote, listen_host.c_str(), listen_port, named_pipe_path.c_str())) + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str())) mode = eRNBRunLoopModeExit; } else if (str[0] == '/') @@ -1376,9 +1394,9 @@ main (int argc, char *argv[]) if (mode != eRNBRunLoopModeExit) { - if (listen_port != INT32_MAX) + if (port != INT32_MAX) { - if (!StartListening (remote, listen_host.c_str(), listen_port, named_pipe_path.c_str())) + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str())) mode = eRNBRunLoopModeExit; } else if (str[0] == '/') @@ -1401,9 +1419,9 @@ main (int argc, char *argv[]) if (mode == eRNBRunLoopModeInferiorExecuting) { - if (listen_port != INT32_MAX) + if (port != INT32_MAX) { - if (!StartListening (remote, listen_host.c_str(), listen_port, named_pipe_path.c_str())) + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str())) mode = eRNBRunLoopModeExit; } else if (str[0] == '/') @@ -1428,9 +1446,9 @@ main (int argc, char *argv[]) break; case eRNBRunLoopModePlatformMode: - if (listen_port != INT32_MAX) + if (port != INT32_MAX) { - if (!StartListening (remote, listen_host.c_str(), listen_port, named_pipe_path.c_str())) + if (!ConnectRemote (remote, host.c_str(), port, reverse_connect, named_pipe_path.c_str())) mode = eRNBRunLoopModeExit; } else if (str[0] == '/') diff --git a/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp b/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp index b675dadceb76..4d9f02d07c99 100644 --- a/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-gdbserver/lldb-gdbserver.cpp @@ -221,18 +221,14 @@ main (int argc, char *argv[]) std::unique_ptr conn_ap(new ConnectionFileDescriptor()); if (conn_ap.get()) { - std::auto_ptr conn_ap(new ConnectionFileDescriptor()); - if (conn_ap.get()) - { - std::string connect_url ("listen://"); - connect_url.append(host_and_port); + std::string connect_url ("listen://"); + connect_url.append(host_and_port); - printf ("Listening for a connection on %s...\n", host_and_port); - if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) - { - printf ("Connection established.\n"); - gdb_server.SetConnection (conn_ap.release()); - } + printf ("Listening for a connection on %s...\n", host_and_port); + if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + { + printf ("Connection established.\n"); + gdb_server.SetConnection (conn_ap.release()); } } diff --git a/lldb/tools/lldb-platform/lldb-platform.cpp b/lldb/tools/lldb-platform/lldb-platform.cpp index bc9d62974771..d58e8042818d 100644 --- a/lldb/tools/lldb-platform/lldb-platform.cpp +++ b/lldb/tools/lldb-platform/lldb-platform.cpp @@ -262,24 +262,18 @@ main (int argc, char *argv[]) std::unique_ptr conn_ap(new ConnectionFileDescriptor()); if (conn_ap.get()) { - for (int j = 0; j < listen_host_port.size(); j++) - { - char c = listen_host_port[j]; - if (c > '9' || c < '0') - printf("WARNING: passing anything but a number as argument to --listen will most probably make connecting impossible.\n"); - } - std::auto_ptr conn_ap(new ConnectionFileDescriptor()); - if (conn_ap.get()) - { - std::string connect_url ("listen://"); - connect_url.append(listen_host_port.c_str()); + std::string connect_url ("listen://"); + connect_url.append(listen_host_port.c_str()); - printf ("Listening for a connection on %s...\n", listen_host_port.c_str()); - if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) - { - printf ("Connection established.\n"); - gdb_server.SetConnection (conn_ap.release()); - } + printf ("Listening for a connection from %s...\n", listen_host_port.c_str()); + if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + { + printf ("Connection established.\n"); + gdb_server.SetConnection (conn_ap.release()); + } + else + { + printf ("error: %s\n", error.AsCString()); } }