From 8037fa4ee916fa20b3c63cbf531f4ee7e1c76138 Mon Sep 17 00:00:00 2001
From: emaste <emaste@FreeBSD.org>
Date: Fri, 3 Jul 2015 16:57:06 +0000
Subject: Import LLDB as of upstream SVN 241361 (git 612c075f)

---
 .../GDBRemoteCommunicationServerPlatform.cpp       | 376 +++++++++++++++++++++
 1 file changed, 376 insertions(+)
 create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp

(limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp')

diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
new file mode 100644
index 0000000..f5e5d76
--- /dev/null
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -0,0 +1,376 @@
+//===-- GDBRemoteCommunicationServerPlatform.cpp ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationServerPlatform.h"
+
+#include <errno.h>
+
+// C Includes
+// C++ Includes
+#include <cstring>
+#include <chrono>
+
+// Other libraries and framework includes
+#include "lldb/Core/Log.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/ConnectionFileDescriptor.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/StringConvert.h"
+#include "lldb/Target/FileAction.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+
+// Project includes
+#include "Utility/StringExtractorGDBRemote.h"
+#include "Utility/UriParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+//----------------------------------------------------------------------
+// GDBRemoteCommunicationServerPlatform constructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform() :
+    GDBRemoteCommunicationServerCommon ("gdb-remote.server", "gdb-remote.server.rx_packet"),
+    m_platform_sp (Platform::GetHostPlatform ()),
+    m_port_map (),
+    m_port_offset(0)
+{
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC,
+                                  &GDBRemoteCommunicationServerPlatform::Handle_qC);
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir,
+                                  &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,
+                                  &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
+                                  &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo);
+    RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir,
+                                  &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir);
+
+    RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt,
+                          [this](StringExtractorGDBRemote packet,
+                                 Error &error,
+                                 bool &interrupt,
+                                 bool &quit)
+                          {
+                              error.SetErrorString("interrupt received");
+                              interrupt = true;
+                              return PacketResult::Success;
+                          });
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform()
+{
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet)
+{
+#ifdef _WIN32
+    return SendErrorResponse(9);
+#else
+    // Spawn a local debugserver as a platform so we can then attach or launch
+    // a process...
+
+    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
+    if (log)
+        log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__);
+
+    // Sleep and wait a bit for debugserver to start to listen...
+    ConnectionFileDescriptor file_conn;
+    std::string hostname;
+    // TODO: /tmp/ should not be hardcoded. User might want to override /tmp
+    // with the TMPDIR environment variable
+    packet.SetFilePos(::strlen ("qLaunchGDBServer;"));
+    std::string name;
+    std::string value;
+    uint16_t port = UINT16_MAX;
+    while (packet.GetNameColonValue(name, value))
+    {
+        if (name.compare ("host") == 0)
+            hostname.swap(value);
+        else if (name.compare ("port") == 0)
+            port = StringConvert::ToUInt32(value.c_str(), 0, 0);
+    }
+    if (port == UINT16_MAX)
+        port = GetNextAvailablePort();
+
+    // Spawn a new thread to accept the port that gets bound after
+    // binding to port 0 (zero).
+
+    // ignore the hostname send from the remote end, just use the ip address
+    // that we're currently communicating with as the hostname
+
+    // Spawn a debugserver and try to get the port it listens to.
+    ProcessLaunchInfo debugserver_launch_info;
+    if (hostname.empty())
+        hostname = "127.0.0.1";
+    if (log)
+        log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port);
+
+    // Do not run in a new session so that it can not linger after the
+    // platform closes.
+    debugserver_launch_info.SetLaunchInSeparateProcessGroup(false);
+    debugserver_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false);
+
+    std::string platform_scheme;
+    std::string platform_ip;
+    int platform_port;
+    std::string platform_path;
+    bool ok = UriParser::Parse(GetConnection()->GetURI().c_str(), platform_scheme, platform_ip, platform_port, platform_path);
+    assert(ok);
+    Error error = StartDebugserverProcess (
+                                     platform_ip.c_str(),
+                                     port,
+                                     debugserver_launch_info,
+                                     port);
+
+    lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID();
+
+
+    if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+    {
+        Mutex::Locker locker (m_spawned_pids_mutex);
+        m_spawned_pids.insert(debugserver_pid);
+        if (port > 0)
+            AssociatePortWithProcess(port, debugserver_pid);
+    }
+    else
+    {
+        if (port > 0)
+            FreePort (port);
+    }
+
+    if (error.Success())
+    {
+        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);
+
+        if (packet_result != PacketResult::Success)
+        {
+            if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+                ::kill (debugserver_pid, SIGINT);
+        }
+        return packet_result;
+    }
+    else
+    {
+        if (log)
+            log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ());
+    }
+    return SendErrorResponse (9);
+#endif
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo (StringExtractorGDBRemote &packet)
+{
+    lldb::pid_t pid = m_process_launch_info.GetProcessID ();
+    m_process_launch_info.Clear ();
+
+    if (pid == LLDB_INVALID_PROCESS_ID)
+        return SendErrorResponse (1);
+
+    ProcessInstanceInfo proc_info;
+    if (!Host::GetProcessInfo (pid, proc_info))
+        return SendErrorResponse (1);
+
+    StreamString response;
+    CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+    return SendPacketNoLock (response.GetData (), response.GetSize ());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir (StringExtractorGDBRemote &packet)
+{
+    // If this packet is sent to a platform, then change the current working directory
+
+    char cwd[PATH_MAX];
+    if (getcwd(cwd, sizeof(cwd)) == NULL)
+        return SendErrorResponse(errno);
+
+    StreamString response;
+    response.PutBytesAsRawHex8(cwd, strlen(cwd));
+    return SendPacketNoLock(response.GetData(), response.GetSize());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet)
+{
+    packet.SetFilePos (::strlen ("QSetWorkingDir:"));
+    std::string path;
+    packet.GetHexByteString (path);
+
+    // If this packet is sent to a platform, then change the current working directory
+    if (::chdir(path.c_str()) != 0)
+        return SendErrorResponse (errno);
+    return SendOKResponse ();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qC (StringExtractorGDBRemote &packet)
+{
+    // NOTE: lldb should now be using qProcessInfo for process IDs.  This path here
+    // should not be used.  It is reporting process id instead of thread id.  The
+    // correct answer doesn't seem to make much sense for lldb-platform.
+    // CONSIDER: flip to "unsupported".
+    lldb::pid_t pid = m_process_launch_info.GetProcessID();
+
+    StreamString response;
+    response.Printf("QC%" PRIx64, pid);
+
+    // If we launch a process and this GDB server is acting as a platform,
+    // then we need to clear the process launch state so we can start
+    // launching another process. In order to launch a process a bunch or
+    // packets need to be sent: environment packets, working directory,
+    // disable ASLR, and many more settings. When we launch a process we
+    // then need to know when to clear this information. Currently we are
+    // selecting the 'qC' packet as that packet which seems to make the most
+    // sense.
+    if (pid != LLDB_INVALID_PROCESS_ID)
+    {
+        m_process_launch_info.Clear();
+    }
+
+    return SendPacketNoLock (response.GetData(), response.GetSize());
+}
+
+bool
+GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped (lldb::pid_t pid)
+{
+    Mutex::Locker locker (m_spawned_pids_mutex);
+    FreePortForProcess(pid);
+    return m_spawned_pids.erase(pid) > 0;
+}
+
+bool
+GDBRemoteCommunicationServerPlatform::ReapDebugserverProcess (void *callback_baton,
+                                                   lldb::pid_t pid,
+                                                   bool exited,
+                                                   int signal,    // Zero for no signal
+                                                   int status)    // Exit value of process if signal is zero
+{
+    GDBRemoteCommunicationServerPlatform *server = (GDBRemoteCommunicationServerPlatform *)callback_baton;
+    server->DebugserverProcessReaped (pid);
+    return true;
+}
+
+Error
+GDBRemoteCommunicationServerPlatform::LaunchProcess ()
+{
+    if (!m_process_launch_info.GetArguments ().GetArgumentCount ())
+        return Error ("%s: no process command line specified to launch", __FUNCTION__);
+
+    // specify the process monitor if not already set.  This should
+    // generally be what happens since we need to reap started
+    // processes.
+    if (!m_process_launch_info.GetMonitorProcessCallback ())
+        m_process_launch_info.SetMonitorProcessCallback(ReapDebugserverProcess, this, false);
+
+    Error error = m_platform_sp->LaunchProcess (m_process_launch_info);
+    if (!error.Success ())
+    {
+        fprintf (stderr, "%s: failed to launch executable %s", __FUNCTION__, m_process_launch_info.GetArguments ().GetArgumentAtIndex (0));
+        return error;
+    }
+
+    printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID());
+
+    // add to list of spawned processes.  On an lldb-gdbserver, we
+    // would expect there to be only one.
+    const auto pid = m_process_launch_info.GetProcessID();
+    if (pid != LLDB_INVALID_PROCESS_ID)
+    {
+        // add to spawned pids
+        Mutex::Locker locker (m_spawned_pids_mutex);
+        m_spawned_pids.insert(pid);
+    }
+
+    return error;
+}
+
+void
+GDBRemoteCommunicationServerPlatform::SetPortMap (PortMap &&port_map)
+{
+    m_port_map = port_map;
+}
+
+uint16_t
+GDBRemoteCommunicationServerPlatform::GetNextAvailablePort ()
+{
+    if (m_port_map.empty())
+        return 0; // Bind to port zero and get a port, we didn't have any limitations
+
+    for (auto &pair : m_port_map)
+    {
+        if (pair.second == LLDB_INVALID_PROCESS_ID)
+        {
+            pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID;
+            return pair.first;
+        }
+    }
+    return UINT16_MAX;
+}
+
+bool
+GDBRemoteCommunicationServerPlatform::AssociatePortWithProcess (uint16_t port, lldb::pid_t pid)
+{
+    PortMap::iterator pos = m_port_map.find(port);
+    if (pos != m_port_map.end())
+    {
+        pos->second = pid;
+        return true;
+    }
+    return false;
+}
+
+bool
+GDBRemoteCommunicationServerPlatform::FreePort (uint16_t port)
+{
+    PortMap::iterator pos = m_port_map.find(port);
+    if (pos != m_port_map.end())
+    {
+        pos->second = LLDB_INVALID_PROCESS_ID;
+        return true;
+    }
+    return false;
+}
+
+bool
+GDBRemoteCommunicationServerPlatform::FreePortForProcess (lldb::pid_t pid)
+{
+    if (!m_port_map.empty())
+    {
+        for (auto &pair : m_port_map)
+        {
+            if (pair.second == pid)
+            {
+                pair.second = LLDB_INVALID_PROCESS_ID;
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void
+GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset)
+{
+    m_port_offset = port_offset;
+}
-- 
cgit v1.1