diff options
author | emaste <emaste@FreeBSD.org> | 2015-07-03 16:57:06 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2015-07-03 16:57:06 +0000 |
commit | 8037fa4ee916fa20b3c63cbf531f4ee7e1c76138 (patch) | |
tree | 3c2e41c3be19b7fc7666ed45a5f91ec3b6e35f2a /source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp | |
parent | d61b076ede88b56f3372a55e7d1eac6a9d717120 (diff) | |
download | FreeBSD-src-8037fa4ee916fa20b3c63cbf531f4ee7e1c76138.zip FreeBSD-src-8037fa4ee916fa20b3c63cbf531f4ee7e1c76138.tar.gz |
Import LLDB as of upstream SVN 241361 (git 612c075f)
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 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; +} |