Files
clang-p2996/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
Pavel Labath 8ccfcab34f [lldb/platform-gdb] Clear cached protocol state upon disconnection
Previously we would persist the flags indicating whether the remote side
supports a particular feature across reconnects, which is obviously not
a good idea.

I implement the clearing by nuking (its the only way to be sure :) the
entire GDBRemoteCommunication object in the disconnect operation and
creating a new one upon connection. This allows us to maintain a nice
invariant that the GDBRemoteCommunication object (which is now a
pointer) exists only if it is connected. The downside to that is that a
lot of functions now needs to check the validity of the pointer instead
of blindly accessing the object.

The process communication does not suffer from the same issue because we
always destroy the entire Process object for a relaunch.

Differential Revision: https://reviews.llvm.org/D116539
2022-01-10 16:27:30 +01:00

229 lines
7.5 KiB
C++

//===-- PlatformAndroidRemoteGDBServer.cpp --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.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 Status 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();
LLDB_LOGF(log, "Connected to Android device \"%s\"", device_id.c_str());
if (remote_port != 0) {
LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d",
remote_port, local_port);
return adb.SetPortForwarding(local_port, remote_port);
}
LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d",
remote_socket_name.str().c_str(), local_port);
if (!socket_namespace)
return Status("Invalid socket namespace");
return adb.SetPortForwarding(local_port, remote_socket_name,
*socket_namespace);
}
static Status DeleteForwardPortWithAdb(uint16_t local_port,
const std::string &device_id) {
AdbClient adb(device_id);
return adb.DeletePortForwarding(local_port);
}
static Status FindUnusedPort(uint16_t &port) {
Status 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() = default;
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) {
assert(IsConnected());
uint16_t remote_port = 0;
std::string socket_name;
if (!m_gdb_client_up->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)
LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str());
return error.Success();
}
bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
assert(IsConnected());
DeleteForwardPort(pid);
return m_gdb_client_up->KillSpawnedProcess(pid);
}
Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) {
m_device_id.clear();
if (args.GetArgumentCount() != 1)
return Status(
"\"platform connect\" takes a single argument: <connect-url>");
const char *url = args.GetArgumentAtIndex(0);
if (!url)
return Status("URL is null.");
llvm::Optional<URI> parsed_url = URI::Parse(url);
if (!parsed_url)
return Status("Invalid URL: %s", url);
if (parsed_url->hostname != "localhost")
m_device_id = parsed_url->hostname.str();
m_socket_namespace.reset();
if (parsed_url->scheme == "unix-connect")
m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
else if (parsed_url->scheme == "unix-abstract-connect")
m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
std::string connect_url;
auto error =
MakeConnectURL(g_remote_platform_pid, parsed_url->port.getValueOr(0),
parsed_url->path, connect_url);
if (error.Fail())
return error;
args.ReplaceArgumentAtIndex(0, connect_url);
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str());
error = PlatformRemoteGDBServer::ConnectRemote(args);
if (error.Fail())
DeleteForwardPort(g_remote_platform_pid);
return error;
}
Status 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()) {
LLDB_LOGF(log,
"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);
}
Status 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;
Status 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://127.0.0.1:" << 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::Status &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;
llvm::Optional<URI> parsed_url = URI::Parse(connect_url);
if (!parsed_url) {
error.SetErrorStringWithFormat("Invalid URL: %s",
connect_url.str().c_str());
return nullptr;
}
std::string new_connect_url;
error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
parsed_url->port.getValueOr(0), parsed_url->path,
new_connect_url);
if (error.Fail())
return nullptr;
return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name,
debugger, target, error);
}