diff options
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp')
| -rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp | 376 | 
1 files changed, 376 insertions, 0 deletions
| diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp new file mode 100644 index 0000000000000..f5e5d76f2e6fa --- /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; +} | 
