summaryrefslogtreecommitdiffstats
path: root/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
diff options
context:
space:
mode:
authoremaste <emaste@FreeBSD.org>2015-07-03 16:57:06 +0000
committeremaste <emaste@FreeBSD.org>2015-07-03 16:57:06 +0000
commit8037fa4ee916fa20b3c63cbf531f4ee7e1c76138 (patch)
tree3c2e41c3be19b7fc7666ed45a5f91ec3b6e35f2a /source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
parentd61b076ede88b56f3372a55e7d1eac6a9d717120 (diff)
downloadFreeBSD-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.cpp376
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;
+}
OpenPOWER on IntegriCloud