Add domain socket support to gdb-remote protocol and lldb-server.

http://reviews.llvm.org/D13881

llvm-svn: 250933
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index 0bbee85..ea95298 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -1112,17 +1112,15 @@
 }
 
 Error
-GDBRemoteCommunication::StartDebugserverProcess (const char *hostname,
-                                                 uint16_t in_port,
+GDBRemoteCommunication::StartDebugserverProcess (const char *url,
                                                  Platform *platform,
                                                  ProcessLaunchInfo &launch_info,
-                                                 uint16_t &out_port)
+                                                 uint16_t *port)
 {
     Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
     if (log)
-        log->Printf ("GDBRemoteCommunication::%s(hostname=%s, in_port=%" PRIu16 ", out_port=%" PRIu16, __FUNCTION__, hostname ? hostname : "<empty>", in_port, out_port);
+        log->Printf ("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16, __FUNCTION__, url ? url : "<empty>", port ? *port : uint16_t(0));
 
-    out_port = in_port;
     Error error;
     // If we locate debugserver, keep that located version around
     static FileSpec g_debugserver_file_spec;
@@ -1193,17 +1191,9 @@
         debugserver_args.AppendArgument("gdbserver");
 #endif
 
-        // If a host and port is supplied then use it
-        char host_and_port[128];
-        if (hostname)
-        {
-            snprintf (host_and_port, sizeof(host_and_port), "%s:%u", hostname, in_port);
-            debugserver_args.AppendArgument(host_and_port);
-        }
-        else
-        {
-            host_and_port[0] = '\0';
-        }
+        // If a url is supplied then use it
+        if (url)
+            debugserver_args.AppendArgument(url);
 
         // use native registers, not the GDB registers
         debugserver_args.AppendArgument("--native-regs");
@@ -1214,11 +1204,18 @@
         }
 
         llvm::SmallString<PATH_MAX> named_pipe_path;
-        Pipe port_pipe;
+        // socket_pipe is used by debug server to communicate back either
+        // TCP port or domain socket name which it listens on.
+        // The second purpose of the pipe to serve as a synchronization point -
+        // once data is written to the pipe, debug server is up and running.
+        Pipe socket_pipe;
 
-        if (in_port == 0)
+        // port is null when debug server should listen on domain socket -
+        // we're not interested in port value but rather waiting for debug server
+        // to become available.
+        if ((port != nullptr && *port == 0) || port == nullptr)
         {
-            if (host_and_port[0])
+            if (url)
             {
                 // 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
@@ -1227,7 +1224,7 @@
 #if defined(__APPLE__)
                 // Binding to port zero, we need to figure out what port it ends up
                 // using using a named pipe...
-                error = port_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path);
+                error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe", false, named_pipe_path);
                 if (error.Fail())
                 {
                     if (log)
@@ -1241,7 +1238,7 @@
 #else
                 // Binding to port zero, we need to figure out what port it ends up
                 // using using an unnamed pipe...
-                error = port_pipe.CreateNew(true);
+                error = socket_pipe.CreateNew(true);
                 if (error.Fail())
                 {
                     if (log)
@@ -1250,10 +1247,10 @@
                                 __FUNCTION__, error.AsCString());
                     return error;
                 }
-                int write_fd = port_pipe.GetWriteFileDescriptor();
+                int write_fd = socket_pipe.GetWriteFileDescriptor();
                 debugserver_args.AppendArgument("--pipe");
                 debugserver_args.AppendArgument(std::to_string(write_fd).c_str());
-                launch_info.AppendCloseFileAction(port_pipe.GetReadFileDescriptor());
+                launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor());
 #endif
             }
             else
@@ -1270,11 +1267,11 @@
     
                 ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection ();
                 // Wait for 10 seconds to resolve the bound port
-                out_port = connection->GetListeningPort(10);
-                if (out_port > 0)
+                *port = connection->GetListeningPort(10);
+                if (*port > 0)
                 {
                     char port_cstr[32];
-                    snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", out_port);
+                    snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%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");
@@ -1343,11 +1340,12 @@
         
         error = Host::LaunchProcess(launch_info);
         
-        if (error.Success() && launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+        if (error.Success() &&
+            launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
         {
             if (named_pipe_path.size() > 0)
             {
-                error = port_pipe.OpenAsReader(named_pipe_path, false);
+                error = socket_pipe.OpenAsReader(named_pipe_path, false);
                 if (error.Fail())
                     if (log)
                         log->Printf("GDBRemoteCommunication::%s() "
@@ -1355,24 +1353,24 @@
                                 __FUNCTION__, named_pipe_path.c_str(), error.AsCString());
             }
 
-            if (port_pipe.CanWrite())
-                port_pipe.CloseWriteFileDescriptor();
-            if (port_pipe.CanRead())
+            if (socket_pipe.CanWrite())
+                socket_pipe.CloseWriteFileDescriptor();
+            if (socket_pipe.CanRead())
             {
-                char port_cstr[256];
+                char port_cstr[PATH_MAX] = {0};
                 port_cstr[0] = '\0';
                 size_t num_bytes = sizeof(port_cstr);
                 // Read port from pipe with 10 second timeout.
-                error = port_pipe.ReadWithTimeout(port_cstr, num_bytes,
+                error = socket_pipe.ReadWithTimeout(port_cstr, num_bytes,
                         std::chrono::seconds{10}, num_bytes);
-                if (error.Success())
+                if (error.Success() && (port != nullptr))
                 {
                     assert(num_bytes > 0 && port_cstr[num_bytes-1] == '\0');
-                    out_port = StringConvert::ToUInt32(port_cstr, 0);
+                    *port = StringConvert::ToUInt32(port_cstr, 0);
                     if (log)
                         log->Printf("GDBRemoteCommunication::%s() "
-                                "debugserver listens %u port",
-                                __FUNCTION__, out_port);
+                                    "debugserver listens %u port",
+                                    __FUNCTION__, *port);
                 }
                 else
                 {
@@ -1382,12 +1380,12 @@
                                 __FUNCTION__, named_pipe_path.c_str(), error.AsCString());
 
                 }
-                port_pipe.Close();
+                socket_pipe.Close();
             }
 
             if (named_pipe_path.size() > 0)
             {
-                const auto err = port_pipe.Delete(named_pipe_path);
+                const auto err = socket_pipe.Delete(named_pipe_path);
                 if (err.Fail())
                 {
                     if (log)
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
index 94ffa51..dee3528 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -167,11 +167,10 @@
     // supplied connection URL.
     //------------------------------------------------------------------
     Error
-    StartDebugserverProcess (const char *hostname,
-                             uint16_t in_port, // If set to zero, then out_port will contain the bound port on exit
+    StartDebugserverProcess (const char *url,
                              Platform *platform, // If non NULL, then check with the platform for the GDB server binary if it can't be located
                              ProcessLaunchInfo &launch_info,
-                             uint16_t &out_port);
+                             uint16_t *port);
 
     void
     DumpHistory(Stream &strm);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 42eb0a1..a6fcd14 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3315,10 +3315,16 @@
     return SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)  == PacketResult::Success;
 }
 
-uint16_t
-GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid, const char *remote_accept_hostname)
+bool
+GDBRemoteCommunicationClient::LaunchGDBServer (const char *remote_accept_hostname,
+                                               lldb::pid_t &pid,
+                                               uint16_t &port,
+                                               std::string &socket_name)
 {
     pid = LLDB_INVALID_PROCESS_ID;
+    port = 0;
+    socket_name.clear();
+
     StringExtractorGDBRemote response;
     StreamString stream;
     stream.PutCString("qLaunchGDBServer;");
@@ -3343,22 +3349,30 @@
 
     // give the process a few seconds to startup
     GDBRemoteCommunication::ScopedTimeout timeout (*this, 10);
-    
+
     if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success)
     {
         std::string name;
         std::string value;
-        uint16_t port = 0;
+        StringExtractor extractor;
         while (response.GetNameColonValue(name, value))
         {
             if (name.compare("port") == 0)
                 port = StringConvert::ToUInt32(value.c_str(), 0, 0);
             else if (name.compare("pid") == 0)
                 pid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_PROCESS_ID, 0);
+            else if (name.compare("socket_name") == 0)
+            {
+                extractor.GetStringRef().swap(value);
+                extractor.SetFilePos(0);
+                extractor.GetHexByteString(value);
+
+                socket_name = value;
+            }
         }
-        return port;
+        return true;
     }
-    return 0;
+    return false;
 }
 
 bool
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index b08ff06..4847aad 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -114,8 +114,11 @@
     bool
     GetLaunchSuccess (std::string &error_str);
 
-    uint16_t
-    LaunchGDBserverAndGetPort (lldb::pid_t &pid, const char *remote_accept_hostname);
+    bool
+    LaunchGDBServer (const char *remote_accept_hostname,
+                     lldb::pid_t &pid,
+                     uint16_t &port,
+                     std::string &socket_name);
     
     bool
     KillSpawnedProcess (lldb::pid_t pid);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
index 592d7f7..54aad10 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -15,14 +15,20 @@
 // C++ Includes
 #include <cstring>
 #include <chrono>
+#include <mutex>
+#include <sstream>
 
 // Other libraries and framework includes
+#include "llvm/Support/FileSystem.h"
+
 #include "lldb/Core/Log.h"
+#include "lldb/Core/StreamGDBRemote.h"
 #include "lldb/Core/StreamString.h"
 #include "lldb/Core/StructuredData.h"
 #include "lldb/Host/Config.h"
 #include "lldb/Host/ConnectionFileDescriptor.h"
 #include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
 #include "lldb/Host/StringConvert.h"
 #include "lldb/Target/FileAction.h"
 #include "lldb/Target/Platform.h"
@@ -40,8 +46,9 @@
 //----------------------------------------------------------------------
 // GDBRemoteCommunicationServerPlatform constructor
 //----------------------------------------------------------------------
-GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform() :
+GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform(const Socket::SocketProtocol socket_protocol) :
     GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"),
+    m_socket_protocol(socket_protocol),
     m_spawned_pids_mutex (Mutex::eMutexTypeRecursive),
     m_platform_sp (Platform::GetHostPlatform ()),
     m_port_map (),
@@ -138,11 +145,24 @@
     bool ok = UriParser::Parse(GetConnection()->GetURI().c_str(), platform_scheme, platform_ip, platform_port, platform_path);
     UNUSED_IF_ASSERT_DISABLED(ok);
     assert(ok);
-    Error error = StartDebugserverProcess (platform_ip.c_str(),
-                                           port,
+
+    std::string socket_name;
+    std::ostringstream url;
+
+    uint16_t* port_ptr = &port;
+    if (m_socket_protocol == Socket::ProtocolTcp)
+        url << platform_ip << ":" << port;
+    else
+    {
+        socket_name = GetDomainSocketPath("gdbserver").GetPath();
+        url << socket_name;
+        port_ptr = nullptr;
+    }
+
+    Error error = StartDebugserverProcess (url.str().c_str(),
                                            nullptr,
                                            debugserver_launch_info,
-                                           port);
+                                           port_ptr);
 
     lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID();
 
@@ -165,11 +185,16 @@
         if (log)
             log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid);
 
-        char response[256];
-        const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset);
-        assert (response_len < (int)sizeof(response));
-        PacketResult packet_result = SendPacketNoLock (response, response_len);
+        StreamGDBRemote response;
+        response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset);
+        if (!socket_name.empty())
+        {
+            response.PutCString("socket_name:");
+            response.PutCStringAsRawHex8(socket_name.c_str());
+            response.PutChar(';');
+        }
 
+        PacketResult packet_result = SendPacketNoLock(response.GetData(), response.GetSize());
         if (packet_result != PacketResult::Success)
         {
             if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
@@ -494,6 +519,36 @@
     return false;
 }
 
+const FileSpec&
+GDBRemoteCommunicationServerPlatform::GetDomainSocketDir()
+{
+    static FileSpec g_domainsocket_dir;
+    static std::once_flag g_once_flag;
+
+    std::call_once(g_once_flag, []() {
+        const char* domainsocket_dir_env = ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR");
+        if (domainsocket_dir_env != nullptr)
+            g_domainsocket_dir = FileSpec(domainsocket_dir_env, false);
+        else
+            HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, g_domainsocket_dir);
+    });
+
+    return g_domainsocket_dir;
+}
+
+FileSpec
+GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char* prefix)
+{
+    llvm::SmallString<PATH_MAX> socket_path;
+    llvm::SmallString<PATH_MAX> socket_name((llvm::StringRef(prefix) + ".%%%%%%").str());
+
+    FileSpec socket_path_spec(GetDomainSocketDir());
+    socket_path_spec.AppendPathComponent(socket_name.c_str());
+
+    llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path);
+    return FileSpec(socket_path.c_str(), false);
+}
+
 void
 GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset)
 {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
index f507a08..e34189f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h
@@ -12,6 +12,8 @@
 
 #include "GDBRemoteCommunicationServerCommon.h"
 
+#include "lldb/Host/Socket.h"
+
 #include <set>
 
 namespace lldb_private {
@@ -23,7 +25,7 @@
 public:
     typedef std::map<uint16_t, lldb::pid_t> PortMap;
 
-    GDBRemoteCommunicationServerPlatform();
+    GDBRemoteCommunicationServerPlatform(const Socket::SocketProtocol socket_protocol);
 
     virtual
     ~GDBRemoteCommunicationServerPlatform();
@@ -61,6 +63,7 @@
     SetPortOffset (uint16_t port_offset);
 
 protected:
+    const Socket::SocketProtocol m_socket_protocol;
     Mutex m_spawned_pids_mutex;
     std::set<lldb::pid_t> m_spawned_pids;
     lldb::PlatformSP m_platform_sp;
@@ -103,6 +106,12 @@
                             int signal,
                             int status);
 
+    static const FileSpec&
+    GetDomainSocketDir();
+
+    static FileSpec
+    GetDomainSocketPath(const char* prefix);
+
     //------------------------------------------------------------------
     // For GDBRemoteCommunicationServerPlatform only
     //------------------------------------------------------------------
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 5b6f979..8ed7e45 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3562,15 +3562,22 @@
         // Set hostname being NULL to do the reverse connect where debugserver
         // will bind to port zero and it will communicate back to us the port
         // that we will connect to
-        const char *hostname = NULL;
+        const char *hostname = nullptr;
         uint16_t port = 0;
 #endif
 
-        error = m_gdb_comm.StartDebugserverProcess (hostname,
-                                                    port,
+        StreamString url_str;
+        const char* url = nullptr;
+        if (hostname != nullptr)
+        {
+            url_str.Printf("%s:%u", hostname, port);
+            url = url_str.GetData();
+        }
+
+        error = m_gdb_comm.StartDebugserverProcess (url,
                                                     GetTarget().GetPlatform().get(),
                                                     debugserver_launch_info,
-                                                    port);
+                                                    &port);
 
         if (error.Success ())
             m_debugserver_pid = debugserver_launch_info.GetProcessID();