The break the linux bots (and probably any other machine which would run the test suite in a massively parallel way). The problem is that it can happen that we only successfully create an IPv6 listening socket (because the relevant IPv4 port is used by another process) and then the connecting side attempts to connect to the IPv4 port and fails. It's not very obvious how to fix this problem, so I am reverting this until we come up with a solution. llvm-svn: 300669
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(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;
|
|
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);
|
|
}
|