Modify "platform connect" to connect to processes as well
The standard remote debugging workflow with gdb is to start the
application on the remote host under gdbserver (e.g.: gdbserver :5039
a.out) and then connect to it with gdb.
The same workflow is supported by debugserver/lldb-gdbserver with a very
similar syntax but to access all features of lldb we need to be
connected also to an lldb-platform instance running on the target.
Before this change this had to be done manually with starting a separate
lldb-platform on the target machine and then connecting to it with lldb
before connecting to the process.
This change modifies the behavior of "platform connect" with
automatically connecting to the process instance if it was started by
the remote platform. With this command replacing gdbserver in a gdb
based worflow is usually as simple as replacing the command to execute
gdbserver with executing lldb-platform.
Differential revision: http://reviews.llvm.org/D14952
llvm-svn: 255016
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
index 19a29b4..022c558 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp
@@ -34,6 +34,7 @@
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/UnixSignals.h"
+#include "lldb/Utility/JSON.h"
// Project includes
#include "Utility/StringExtractorGDBRemote.h"
@@ -54,7 +55,8 @@
m_spawned_pids_mutex (Mutex::eMutexTypeRecursive),
m_platform_sp (Platform::GetHostPlatform ()),
m_port_map (),
- m_port_offset(0)
+ m_port_offset(0),
+ m_pending_gdb_server{ LLDB_INVALID_PROCESS_ID, 0, "" }
{
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC,
&GDBRemoteCommunicationServerPlatform::Handle_qC);
@@ -62,6 +64,8 @@
&GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer,
&GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer);
+ RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer,
+ &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess,
&GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo,
@@ -90,38 +94,16 @@
{
}
-GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet)
+Error
+GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& args,
+ std::string hostname,
+ lldb::pid_t& pid,
+ uint16_t& port,
+ std::string& socket_name)
{
-#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).
@@ -132,6 +114,8 @@
ProcessLaunchInfo debugserver_launch_info;
if (hostname.empty())
hostname = "127.0.0.1";
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port);
@@ -148,11 +132,8 @@
UNUSED_IF_ASSERT_DISABLED(ok);
assert(ok);
- std::string socket_name;
std::ostringstream url;
-
uint16_t* port_ptr = &port;
- url << m_socket_scheme << "://";
if (m_socket_protocol == Socket::ProtocolTcp)
url << platform_ip << ":" << port;
else
@@ -165,56 +146,108 @@
Error error = StartDebugserverProcess (url.str().c_str(),
nullptr,
debugserver_launch_info,
- port_ptr);
+ port_ptr,
+ args);
- lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID();
-
-
- if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ pid = debugserver_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
{
Mutex::Locker locker (m_spawned_pids_mutex);
- m_spawned_pids.insert(debugserver_pid);
+ m_spawned_pids.insert(pid);
if (port > 0)
- AssociatePortWithProcess(port, debugserver_pid);
+ AssociatePortWithProcess(port, pid);
}
else
{
if (port > 0)
- FreePort (port);
+ FreePort(port);
+ }
+ return error;
+}
+
+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__);
+
+ ConnectionFileDescriptor file_conn;
+ std::string hostname;
+ 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 (error.Success())
+ lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ std::string socket_name;
+ Error error = LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name);
+ if (error.Fail())
{
if (log)
- log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid);
-
- 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)
- ::kill (debugserver_pid, SIGINT);
- }
- return packet_result;
+ log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ());
+ return SendErrorResponse(9);
}
- else
+
+ if (log)
+ log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid);
+
+ StreamGDBRemote response;
+ response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset);
+ if (!socket_name.empty())
{
- if (log)
- log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ());
+ response.PutCString("socket_name:");
+ response.PutCStringAsRawHex8(socket_name.c_str());
+ response.PutChar(';');
}
- return SendErrorResponse (9);
+
+ PacketResult packet_result = SendPacketNoLock(response.GetData(), response.GetSize());
+ if (packet_result != PacketResult::Success)
+ {
+ if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ ::kill (debugserver_pid, SIGINT);
+ }
+ return packet_result;
#endif
}
GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer (StringExtractorGDBRemote &packet)
+{
+ if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse(4);
+
+ JSONObject::SP server_sp = std::make_shared<JSONObject>();
+ server_sp->SetObject("port", std::make_shared<JSONNumber>(m_pending_gdb_server.port));
+ if (!m_pending_gdb_server.socket_name.empty())
+ server_sp->SetObject("socket_name",
+ std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str()));
+
+ JSONArray server_list;
+ server_list.AppendObject(server_sp);
+
+ StreamGDBRemote response;
+ server_list.Write(response);
+
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
+ return SendPacketNoLock(escaped_response.GetData(), escaped_response.GetSize());
+}
+
+GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet)
{
packet.SetFilePos(::strlen ("qKillSpawnedProcess:"));
@@ -553,7 +586,17 @@
}
void
-GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset)
+GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset)
{
m_port_offset = port_offset;
}
+
+void
+GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(lldb::pid_t pid,
+ uint16_t port,
+ const std::string& socket_name)
+{
+ m_pending_gdb_server.pid = pid;
+ m_pending_gdb_server.port = port;
+ m_pending_gdb_server.socket_name = socket_name;
+}