100e305d2STamas Berghammer //===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===// 200e305d2STamas Berghammer // 3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 600e305d2STamas Berghammer // 700e305d2STamas Berghammer //===----------------------------------------------------------------------===// 800e305d2STamas Berghammer 96e181cf3SOleksiy Vyalov #include "lldb/Host/ConnectionFileDescriptor.h" 10b9c1b51eSKate Stone #include "lldb/Host/common/TCPSocket.h" 116f9e6901SZachary Turner #include "lldb/Utility/Log.h" 1297206d57SZachary Turner #include "lldb/Utility/Status.h" 135f7e583bSPavel Labath #include "lldb/Utility/UriParser.h" 146e181cf3SOleksiy Vyalov 1500e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h" 1600e305d2STamas Berghammer 1754971856SOleksiy Vyalov #include <sstream> 1854971856SOleksiy Vyalov 1900e305d2STamas Berghammer using namespace lldb; 2000e305d2STamas Berghammer using namespace lldb_private; 21db264a6dSTamas Berghammer using namespace platform_android; 2200e305d2STamas Berghammer 23b9c1b51eSKate Stone static const lldb::pid_t g_remote_platform_pid = 24b9c1b51eSKate Stone 0; // Alias for the process id of lldb-platform 2500e305d2STamas Berghammer 2697206d57SZachary Turner static Status ForwardPortWithAdb( 27b9c1b51eSKate Stone const uint16_t local_port, const uint16_t remote_port, 28245f7fdcSZachary Turner llvm::StringRef remote_socket_name, 29e7df5f5dSOleksiy Vyalov const llvm::Optional<AdbClient::UnixSocketNamespace> &socket_namespace, 30b9c1b51eSKate Stone std::string &device_id) { 31db264a6dSTamas Berghammer Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 3200e305d2STamas Berghammer 3305a55de3SOleksiy Vyalov AdbClient adb; 343ea689b3SChaoren Lin auto error = AdbClient::CreateByDeviceID(device_id, adb); 3500e305d2STamas Berghammer if (error.Fail()) 3600e305d2STamas Berghammer return error; 3700e305d2STamas Berghammer 38f9da9483SOleksiy Vyalov device_id = adb.GetDeviceID(); 3905a55de3SOleksiy Vyalov if (log) 40f9da9483SOleksiy Vyalov log->Printf("Connected to Android device \"%s\"", device_id.c_str()); 4105a55de3SOleksiy Vyalov 42b9c1b51eSKate Stone if (remote_port != 0) { 439fe526c2SOleksiy Vyalov if (log) 44b9c1b51eSKate Stone log->Printf("Forwarding remote TCP port %d to local TCP port %d", 45b9c1b51eSKate Stone remote_port, local_port); 46e7eabbb5SOleksiy Vyalov return adb.SetPortForwarding(local_port, remote_port); 4700e305d2STamas Berghammer } 4800e305d2STamas Berghammer 499fe526c2SOleksiy Vyalov if (log) 50b9c1b51eSKate Stone log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", 51245f7fdcSZachary Turner remote_socket_name.str().c_str(), local_port); 52e7df5f5dSOleksiy Vyalov 53e7df5f5dSOleksiy Vyalov if (!socket_namespace) 5497206d57SZachary Turner return Status("Invalid socket namespace"); 55e7df5f5dSOleksiy Vyalov 56b9c1b51eSKate Stone return adb.SetPortForwarding(local_port, remote_socket_name, 57b9c1b51eSKate Stone *socket_namespace); 589fe526c2SOleksiy Vyalov } 599fe526c2SOleksiy Vyalov 6097206d57SZachary Turner static Status DeleteForwardPortWithAdb(uint16_t local_port, 61b9c1b51eSKate Stone const std::string &device_id) { 6205a55de3SOleksiy Vyalov AdbClient adb(device_id); 63e7eabbb5SOleksiy Vyalov return adb.DeletePortForwarding(local_port); 64e7eabbb5SOleksiy Vyalov } 65e7eabbb5SOleksiy Vyalov 6697206d57SZachary Turner static Status FindUnusedPort(uint16_t &port) { 6797206d57SZachary Turner Status error; 6811827799SChris Bieneman std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false)); 69e98628ceSOleksiy Vyalov if (error.Fail()) 70e98628ceSOleksiy Vyalov return error; 71e98628ceSOleksiy Vyalov 72e98628ceSOleksiy Vyalov error = tcp_socket->Listen("127.0.0.1:0", 1); 73e7eabbb5SOleksiy Vyalov if (error.Success()) 74e98628ceSOleksiy Vyalov port = tcp_socket->GetLocalPortNumber(); 75e98628ceSOleksiy Vyalov 76e7eabbb5SOleksiy Vyalov return error; 7700e305d2STamas Berghammer } 7800e305d2STamas Berghammer 79b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() {} 8000e305d2STamas Berghammer 81b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() { 8200e305d2STamas Berghammer for (const auto &it : m_port_forwards) 833ea689b3SChaoren Lin DeleteForwardPortWithAdb(it.second, m_device_id); 8400e305d2STamas Berghammer } 8500e305d2STamas Berghammer 86b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, 87b9c1b51eSKate Stone std::string &connect_url) { 889fe526c2SOleksiy Vyalov uint16_t remote_port = 0; 899fe526c2SOleksiy Vyalov std::string socket_name; 909fe526c2SOleksiy Vyalov if (!m_gdb_client.LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name)) 919fe526c2SOleksiy Vyalov return false; 9200e305d2STamas Berghammer 939fe526c2SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 949fe526c2SOleksiy Vyalov 95b9c1b51eSKate Stone auto error = 96b9c1b51eSKate Stone MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url); 979fe526c2SOleksiy Vyalov if (error.Success() && log) 989fe526c2SOleksiy Vyalov log->Printf("gdbserver connect URL: %s", connect_url.c_str()); 999fe526c2SOleksiy Vyalov 1009fe526c2SOleksiy Vyalov return error.Success(); 10100e305d2STamas Berghammer } 10200e305d2STamas Berghammer 103b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { 1041c1d76b3SOleksiy Vyalov DeleteForwardPort(pid); 10500e305d2STamas Berghammer return m_gdb_client.KillSpawnedProcess(pid); 10600e305d2STamas Berghammer } 10700e305d2STamas Berghammer 10897206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) { 1093ea689b3SChaoren Lin m_device_id.clear(); 1103ea689b3SChaoren Lin 11100e305d2STamas Berghammer if (args.GetArgumentCount() != 1) 11297206d57SZachary Turner return Status( 11397206d57SZachary Turner "\"platform connect\" takes a single argument: <connect-url>"); 11400e305d2STamas Berghammer 115e7eabbb5SOleksiy Vyalov int remote_port; 116245f7fdcSZachary Turner llvm::StringRef scheme, host, path; 11700e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0); 1183ea689b3SChaoren Lin if (!url) 11997206d57SZachary Turner return Status("URL is null."); 120e7eabbb5SOleksiy Vyalov if (!UriParser::Parse(url, scheme, host, remote_port, path)) 12197206d57SZachary Turner return Status("Invalid URL: %s", url); 122a29d6475SOleksiy Vyalov if (host != "localhost") 1233ea689b3SChaoren Lin m_device_id = host; 12400e305d2STamas Berghammer 125e7df5f5dSOleksiy Vyalov m_socket_namespace.reset(); 126e7df5f5dSOleksiy Vyalov if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) 127e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; 128e7df5f5dSOleksiy Vyalov else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) 129e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; 130e7df5f5dSOleksiy Vyalov 1319fe526c2SOleksiy Vyalov std::string connect_url; 132b9c1b51eSKate Stone auto error = 133b9c1b51eSKate Stone MakeConnectURL(g_remote_platform_pid, (remote_port < 0) ? 0 : remote_port, 134245f7fdcSZachary Turner path, connect_url); 1359fe526c2SOleksiy Vyalov 13600e305d2STamas Berghammer if (error.Fail()) 13700e305d2STamas Berghammer return error; 13800e305d2STamas Berghammer 139ecbb0bb1SZachary Turner args.ReplaceArgumentAtIndex(0, connect_url); 140e7eabbb5SOleksiy Vyalov 141e7eabbb5SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 142e7eabbb5SOleksiy Vyalov if (log) 1439fe526c2SOleksiy Vyalov log->Printf("Rewritten platform connect URL: %s", connect_url.c_str()); 14400e305d2STamas Berghammer 1451c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args); 1461c1d76b3SOleksiy Vyalov if (error.Fail()) 1471c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1481c1d76b3SOleksiy Vyalov 1491c1d76b3SOleksiy Vyalov return error; 15000e305d2STamas Berghammer } 15100e305d2STamas Berghammer 15297206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() { 1531c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1541c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote(); 15500e305d2STamas Berghammer } 15600e305d2STamas Berghammer 157b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { 1581c1d76b3SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 1591c1d76b3SOleksiy Vyalov 1601c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid); 1611c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end()) 1621c1d76b3SOleksiy Vyalov return; 1631c1d76b3SOleksiy Vyalov 1643ea689b3SChaoren Lin const auto port = it->second; 1653ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id); 1661c1d76b3SOleksiy Vyalov if (error.Fail()) { 1671c1d76b3SOleksiy Vyalov if (log) 168b9c1b51eSKate Stone log->Printf("Failed to delete port forwarding (pid=%" PRIu64 169b9c1b51eSKate Stone ", port=%d, device=%s): %s", 1703ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString()); 1711c1d76b3SOleksiy Vyalov } 1721c1d76b3SOleksiy Vyalov m_port_forwards.erase(it); 17300e305d2STamas Berghammer } 17454971856SOleksiy Vyalov 17597206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL( 176b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port, 177245f7fdcSZachary Turner llvm::StringRef remote_socket_name, std::string &connect_url) { 178e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5; 179e7eabbb5SOleksiy Vyalov 18097206d57SZachary Turner Status error; 18105097246SAdrian Prantl // There is a race possibility that somebody will occupy a port while we're 18205097246SAdrian Prantl // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to 18305097246SAdrian Prantl // mitigate such problem. 184b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) { 1859fe526c2SOleksiy Vyalov uint16_t local_port = 0; 186e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port); 187e7eabbb5SOleksiy Vyalov if (error.Fail()) 188e7eabbb5SOleksiy Vyalov return error; 189e7eabbb5SOleksiy Vyalov 190b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, 191b9c1b51eSKate Stone m_socket_namespace, m_device_id); 192b9c1b51eSKate Stone if (error.Success()) { 193e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port; 1949fe526c2SOleksiy Vyalov std::ostringstream url_str; 1959fe526c2SOleksiy Vyalov url_str << "connect://localhost:" << local_port; 1969fe526c2SOleksiy Vyalov connect_url = url_str.str(); 197e7eabbb5SOleksiy Vyalov break; 198e7eabbb5SOleksiy Vyalov } 199e7eabbb5SOleksiy Vyalov } 200e7eabbb5SOleksiy Vyalov 201e7eabbb5SOleksiy Vyalov return error; 202e7eabbb5SOleksiy Vyalov } 203ccd6cffbSTamas Berghammer 204b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( 2053165945aSZachary Turner llvm::StringRef connect_url, llvm::StringRef plugin_name, 206b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target, 20797206d57SZachary Turner lldb_private::Status &error) { 208b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us 20905097246SAdrian Prantl // but we still want to store the list of port forwards we set up in our port 21005097246SAdrian Prantl // forward map. Generate a fake pid for these cases what won't collide with 21105097246SAdrian Prantl // any other valid pid on android. 212ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; 213ccd6cffbSTamas Berghammer 214ccd6cffbSTamas Berghammer int remote_port; 215245f7fdcSZachary Turner llvm::StringRef scheme, host, path; 216b9c1b51eSKate Stone if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) { 2173165945aSZachary Turner error.SetErrorStringWithFormat("Invalid URL: %s", 2183165945aSZachary Turner connect_url.str().c_str()); 219ccd6cffbSTamas Berghammer return nullptr; 220ccd6cffbSTamas Berghammer } 221ccd6cffbSTamas Berghammer 222ccd6cffbSTamas Berghammer std::string new_connect_url; 223ccd6cffbSTamas Berghammer error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 224245f7fdcSZachary Turner (remote_port < 0) ? 0 : remote_port, path, 225ccd6cffbSTamas Berghammer new_connect_url); 226ccd6cffbSTamas Berghammer if (error.Fail()) 227ccd6cffbSTamas Berghammer return nullptr; 228ccd6cffbSTamas Berghammer 2293165945aSZachary Turner return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, 2303165945aSZachary Turner debugger, target, error); 231ccd6cffbSTamas Berghammer } 232