Files
clang-p2996/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
Tamas Berghammer ccd6cffba3 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
2015-12-08 14:08:19 +00:00

276 lines
8.9 KiB
C++

//===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Other libraries and framework includes
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Host/common/TCPSocket.h"
#include "PlatformAndroidRemoteGDBServer.h"
#include "Utility/UriParser.h"
#include <sstream>
using namespace lldb;
using namespace lldb_private;
using namespace platform_android;
static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
static Error
ForwardPortWithAdb (const uint16_t local_port,
const uint16_t remote_port,
const char* remote_socket_name,
const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace,
std::string& device_id)
{
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
AdbClient adb;
auto error = AdbClient::CreateByDeviceID(device_id, adb);
if (error.Fail ())
return error;
device_id = adb.GetDeviceID();
if (log)
log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
if (remote_port != 0)
{
if (log)
log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port);
return adb.SetPortForwarding(local_port, remote_port);
}
if (log)
log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port);
if (!socket_namespace)
return Error("Invalid socket namespace");
return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace);
}
static Error
DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
{
AdbClient adb (device_id);
return adb.DeletePortForwarding (local_port);
}
static Error
FindUnusedPort (uint16_t& port)
{
Error error;
std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error));
if (error.Fail())
return error;
error = tcp_socket->Listen("127.0.0.1:0", 1);
if (error.Success())
port = tcp_socket->GetLocalPortNumber();
return error;
}
PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer ()
{
}
PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
{
for (const auto& it : m_port_forwards)
DeleteForwardPortWithAdb(it.second, m_device_id);
}
bool
PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
{
uint16_t remote_port = 0;
std::string socket_name;
if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name))
return false;
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
auto error = MakeConnectURL (pid,
remote_port,
socket_name.c_str (),
connect_url);
if (error.Success() && log)
log->Printf("gdbserver connect URL: %s", connect_url.c_str());
return error.Success();
}
bool
PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
{
DeleteForwardPort (pid);
return m_gdb_client.KillSpawnedProcess (pid);
}
Error
PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
{
m_device_id.clear();
if (args.GetArgumentCount() != 1)
return Error("\"platform connect\" takes a single argument: <connect-url>");
int remote_port;
std::string scheme, host, path;
const char *url = args.GetArgumentAtIndex (0);
if (!url)
return Error("URL is null.");
if (!UriParser::Parse (url, scheme, host, remote_port, path))
return Error("Invalid URL: %s", url);
if (host != "localhost")
m_device_id = host;
m_socket_namespace.reset();
if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
std::string connect_url;
auto error = MakeConnectURL (g_remote_platform_pid,
(remote_port < 0) ? 0 : remote_port,
path.c_str (),
connect_url);
if (error.Fail ())
return error;
args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
error = PlatformRemoteGDBServer::ConnectRemote(args);
if (error.Fail ())
DeleteForwardPort (g_remote_platform_pid);
return error;
}
Error
PlatformAndroidRemoteGDBServer::DisconnectRemote ()
{
DeleteForwardPort (g_remote_platform_pid);
return PlatformRemoteGDBServer::DisconnectRemote ();
}
void
PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
{
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
auto it = m_port_forwards.find(pid);
if (it == m_port_forwards.end())
return;
const auto port = it->second;
const auto error = DeleteForwardPortWithAdb(port, m_device_id);
if (error.Fail()) {
if (log)
log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
pid, port, m_device_id.c_str(), error.AsCString());
}
m_port_forwards.erase(it);
}
Error
PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
const uint16_t remote_port,
const char* remote_socket_name,
std::string& connect_url)
{
static const int kAttempsNum = 5;
Error error;
// There is a race possibility that somebody will occupy
// a port while we're in between FindUnusedPort and ForwardPortWithAdb -
// adding the loop to mitigate such problem.
for (auto i = 0; i < kAttempsNum; ++i)
{
uint16_t local_port = 0;
error = FindUnusedPort(local_port);
if (error.Fail())
return error;
error = ForwardPortWithAdb(local_port,
remote_port,
remote_socket_name,
m_socket_namespace,
m_device_id);
if (error.Success())
{
m_port_forwards[pid] = local_port;
std::ostringstream url_str;
url_str << "connect://localhost:" << local_port;
connect_url = url_str.str();
break;
}
}
return error;
}
lldb::ProcessSP
PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url,
const char* plugin_name,
lldb_private::Debugger &debugger,
lldb_private::Target *target,
lldb_private::Error &error)
{
// We don't have the pid of the remote gdbserver when it isn't started by us but we still want
// to store the list of port forwards we set up in our port forward map. Generate a fake pid for
// these cases what won't collide with any other valid pid on android.
static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
int remote_port;
std::string scheme, host, path;
if (!UriParser::Parse(connect_url, scheme, host, remote_port, path))
{
error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
return nullptr;
}
std::string new_connect_url;
error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
(remote_port < 0) ? 0 : remote_port,
path.c_str(),
new_connect_url);
if (error.Fail())
return nullptr;
return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(),
plugin_name,
debugger,
target,
error);
}
size_t
PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error)
{
std::vector<std::string> connection_urls;
GetPendingGdbServerList(connection_urls);
for (size_t i = 0; i < connection_urls.size(); ++i)
{
ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error);
if (error.Fail())
return i; // We already connected to i process succsessfully
}
return connection_urls.size();
}