Summary: This patch adds IPv6 support to LLDB/Host's TCP socket implementation. Supporting IPv6 involved a few significant changes to the implementation of the socket layers, and I have performed some significant code cleanup along the way. This patch changes the Socket constructors for all types of sockets to not create sockets until first use. This is required for IPv6 support because the socket type will vary based on the address you are connecting to. This also has the benefit of removing code that could have errors from the Socket subclass constructors (which seems like a win to me). The patch also slightly changes the API and behaviors of the Listen/Accept pattern. Previously both Listen and Accept calls took an address specified as a string. Now only listen does. This change was made because the Listen call can result in opening more than one socket. In order to support listening for both IPv4 and IPv6 connections we need to open one AF_INET socket and one AF_INET6 socket. During the listen call we construct a map of file descriptors to addrin structures which represent the allowable incoming connection address. This map removes the need for taking an address into the Accept call. This does have a change in functionality. Previously you could Listen for connections based on one address, and Accept connections from a different address. This is no longer supported. I could not find anywhere in LLDB where we actually used the APIs in that way. The new API does still support AnyAddr for allowing incoming connections from any address. The Listen implementation is implemented using kqueue on FreeBSD and Darwin, WSAPoll on Windows and poll(2) everywhere else. Reviewers: zturner, clayborg Subscribers: jasonmolenda, labath, lldb-commits, emaste Differential Revision: https://reviews.llvm.org/D31823 llvm-svn: 300579
234 lines
7.5 KiB
C++
234 lines
7.5 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/Host/ConnectionFileDescriptor.h"
|
|
#include "lldb/Host/common/TCPSocket.h"
|
|
#include "lldb/Utility/Error.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/UriParser.h"
|
|
|
|
#include "PlatformAndroidRemoteGDBServer.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,
|
|
llvm::StringRef 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.str().c_str(), 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(true, false));
|
|
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;
|
|
llvm::StringRef 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, connect_url);
|
|
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
args.ReplaceArgumentAtIndex(0, connect_url);
|
|
|
|
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,
|
|
llvm::StringRef 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(
|
|
llvm::StringRef connect_url, llvm::StringRef 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;
|
|
llvm::StringRef scheme, host, path;
|
|
if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) {
|
|
error.SetErrorStringWithFormat("Invalid URL: %s",
|
|
connect_url.str().c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
std::string new_connect_url;
|
|
error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
|
|
(remote_port < 0) ? 0 : remote_port, path,
|
|
new_connect_url);
|
|
if (error.Fail())
|
|
return nullptr;
|
|
|
|
return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name,
|
|
debugger, target, error);
|
|
}
|