180814287SRaphael Isemann //===-- PlatformAndroidRemoteGDBServer.cpp --------------------------------===// 200e305d2STamas Berghammer // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler 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(); 3963e5fb76SJonas Devlieghere LLDB_LOGF(log, "Connected to Android device \"%s\"", device_id.c_str()); 4005a55de3SOleksiy Vyalov 41b9c1b51eSKate Stone if (remote_port != 0) { 4263e5fb76SJonas Devlieghere LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d", 43b9c1b51eSKate Stone remote_port, local_port); 44e7eabbb5SOleksiy Vyalov return adb.SetPortForwarding(local_port, remote_port); 4500e305d2STamas Berghammer } 4600e305d2STamas Berghammer 4763e5fb76SJonas Devlieghere LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d", 48245f7fdcSZachary Turner remote_socket_name.str().c_str(), local_port); 49e7df5f5dSOleksiy Vyalov 50e7df5f5dSOleksiy Vyalov if (!socket_namespace) 5197206d57SZachary Turner return Status("Invalid socket namespace"); 52e7df5f5dSOleksiy Vyalov 53b9c1b51eSKate Stone return adb.SetPortForwarding(local_port, remote_socket_name, 54b9c1b51eSKate Stone *socket_namespace); 559fe526c2SOleksiy Vyalov } 569fe526c2SOleksiy Vyalov 5797206d57SZachary Turner static Status DeleteForwardPortWithAdb(uint16_t local_port, 58b9c1b51eSKate Stone const std::string &device_id) { 5905a55de3SOleksiy Vyalov AdbClient adb(device_id); 60e7eabbb5SOleksiy Vyalov return adb.DeletePortForwarding(local_port); 61e7eabbb5SOleksiy Vyalov } 62e7eabbb5SOleksiy Vyalov 6397206d57SZachary Turner static Status FindUnusedPort(uint16_t &port) { 6497206d57SZachary Turner Status error; 6511827799SChris Bieneman std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false)); 66e98628ceSOleksiy Vyalov if (error.Fail()) 67e98628ceSOleksiy Vyalov return error; 68e98628ceSOleksiy Vyalov 69e98628ceSOleksiy Vyalov error = tcp_socket->Listen("127.0.0.1:0", 1); 70e7eabbb5SOleksiy Vyalov if (error.Success()) 71e98628ceSOleksiy Vyalov port = tcp_socket->GetLocalPortNumber(); 72e98628ceSOleksiy Vyalov 73e7eabbb5SOleksiy Vyalov return error; 7400e305d2STamas Berghammer } 7500e305d2STamas Berghammer 76b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() {} 7700e305d2STamas Berghammer 78b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() { 7900e305d2STamas Berghammer for (const auto &it : m_port_forwards) 803ea689b3SChaoren Lin DeleteForwardPortWithAdb(it.second, m_device_id); 8100e305d2STamas Berghammer } 8200e305d2STamas Berghammer 83b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, 84b9c1b51eSKate Stone std::string &connect_url) { 859fe526c2SOleksiy Vyalov uint16_t remote_port = 0; 869fe526c2SOleksiy Vyalov std::string socket_name; 879fe526c2SOleksiy Vyalov if (!m_gdb_client.LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name)) 889fe526c2SOleksiy Vyalov return false; 8900e305d2STamas Berghammer 909fe526c2SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 919fe526c2SOleksiy Vyalov 92b9c1b51eSKate Stone auto error = 93b9c1b51eSKate Stone MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url); 949fe526c2SOleksiy Vyalov if (error.Success() && log) 9563e5fb76SJonas Devlieghere LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str()); 969fe526c2SOleksiy Vyalov 979fe526c2SOleksiy Vyalov return error.Success(); 9800e305d2STamas Berghammer } 9900e305d2STamas Berghammer 100b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { 1011c1d76b3SOleksiy Vyalov DeleteForwardPort(pid); 10200e305d2STamas Berghammer return m_gdb_client.KillSpawnedProcess(pid); 10300e305d2STamas Berghammer } 10400e305d2STamas Berghammer 10597206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) { 1063ea689b3SChaoren Lin m_device_id.clear(); 1073ea689b3SChaoren Lin 10800e305d2STamas Berghammer if (args.GetArgumentCount() != 1) 10997206d57SZachary Turner return Status( 11097206d57SZachary Turner "\"platform connect\" takes a single argument: <connect-url>"); 11100e305d2STamas Berghammer 112e7eabbb5SOleksiy Vyalov int remote_port; 113245f7fdcSZachary Turner llvm::StringRef scheme, host, path; 11400e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0); 1153ea689b3SChaoren Lin if (!url) 11697206d57SZachary Turner return Status("URL is null."); 117e7eabbb5SOleksiy Vyalov if (!UriParser::Parse(url, scheme, host, remote_port, path)) 11897206d57SZachary Turner return Status("Invalid URL: %s", url); 119a29d6475SOleksiy Vyalov if (host != "localhost") 120*adcd0268SBenjamin Kramer m_device_id = std::string(host); 12100e305d2STamas Berghammer 122e7df5f5dSOleksiy Vyalov m_socket_namespace.reset(); 123e7df5f5dSOleksiy Vyalov if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) 124e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; 125e7df5f5dSOleksiy Vyalov else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) 126e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; 127e7df5f5dSOleksiy Vyalov 1289fe526c2SOleksiy Vyalov std::string connect_url; 129b9c1b51eSKate Stone auto error = 130b9c1b51eSKate Stone MakeConnectURL(g_remote_platform_pid, (remote_port < 0) ? 0 : remote_port, 131245f7fdcSZachary Turner path, connect_url); 1329fe526c2SOleksiy Vyalov 13300e305d2STamas Berghammer if (error.Fail()) 13400e305d2STamas Berghammer return error; 13500e305d2STamas Berghammer 136ecbb0bb1SZachary Turner args.ReplaceArgumentAtIndex(0, connect_url); 137e7eabbb5SOleksiy Vyalov 138e7eabbb5SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 13963e5fb76SJonas Devlieghere LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str()); 14000e305d2STamas Berghammer 1411c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args); 1421c1d76b3SOleksiy Vyalov if (error.Fail()) 1431c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1441c1d76b3SOleksiy Vyalov 1451c1d76b3SOleksiy Vyalov return error; 14600e305d2STamas Berghammer } 14700e305d2STamas Berghammer 14897206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() { 1491c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1501c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote(); 15100e305d2STamas Berghammer } 15200e305d2STamas Berghammer 153b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { 1541c1d76b3SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 1551c1d76b3SOleksiy Vyalov 1561c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid); 1571c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end()) 1581c1d76b3SOleksiy Vyalov return; 1591c1d76b3SOleksiy Vyalov 1603ea689b3SChaoren Lin const auto port = it->second; 1613ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id); 1621c1d76b3SOleksiy Vyalov if (error.Fail()) { 16363e5fb76SJonas Devlieghere LLDB_LOGF(log, 16463e5fb76SJonas Devlieghere "Failed to delete port forwarding (pid=%" PRIu64 165b9c1b51eSKate Stone ", port=%d, device=%s): %s", 1663ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString()); 1671c1d76b3SOleksiy Vyalov } 1681c1d76b3SOleksiy Vyalov m_port_forwards.erase(it); 16900e305d2STamas Berghammer } 17054971856SOleksiy Vyalov 17197206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL( 172b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port, 173245f7fdcSZachary Turner llvm::StringRef remote_socket_name, std::string &connect_url) { 174e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5; 175e7eabbb5SOleksiy Vyalov 17697206d57SZachary Turner Status error; 17705097246SAdrian Prantl // There is a race possibility that somebody will occupy a port while we're 17805097246SAdrian Prantl // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to 17905097246SAdrian Prantl // mitigate such problem. 180b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) { 1819fe526c2SOleksiy Vyalov uint16_t local_port = 0; 182e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port); 183e7eabbb5SOleksiy Vyalov if (error.Fail()) 184e7eabbb5SOleksiy Vyalov return error; 185e7eabbb5SOleksiy Vyalov 186b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, 187b9c1b51eSKate Stone m_socket_namespace, m_device_id); 188b9c1b51eSKate Stone if (error.Success()) { 189e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port; 1909fe526c2SOleksiy Vyalov std::ostringstream url_str; 1919fe526c2SOleksiy Vyalov url_str << "connect://localhost:" << local_port; 1929fe526c2SOleksiy Vyalov connect_url = url_str.str(); 193e7eabbb5SOleksiy Vyalov break; 194e7eabbb5SOleksiy Vyalov } 195e7eabbb5SOleksiy Vyalov } 196e7eabbb5SOleksiy Vyalov 197e7eabbb5SOleksiy Vyalov return error; 198e7eabbb5SOleksiy Vyalov } 199ccd6cffbSTamas Berghammer 200b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( 2013165945aSZachary Turner llvm::StringRef connect_url, llvm::StringRef plugin_name, 202b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target, 20397206d57SZachary Turner lldb_private::Status &error) { 204b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us 20505097246SAdrian Prantl // but we still want to store the list of port forwards we set up in our port 20605097246SAdrian Prantl // forward map. Generate a fake pid for these cases what won't collide with 20705097246SAdrian Prantl // any other valid pid on android. 208ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; 209ccd6cffbSTamas Berghammer 210ccd6cffbSTamas Berghammer int remote_port; 211245f7fdcSZachary Turner llvm::StringRef scheme, host, path; 212b9c1b51eSKate Stone if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) { 2133165945aSZachary Turner error.SetErrorStringWithFormat("Invalid URL: %s", 2143165945aSZachary Turner connect_url.str().c_str()); 215ccd6cffbSTamas Berghammer return nullptr; 216ccd6cffbSTamas Berghammer } 217ccd6cffbSTamas Berghammer 218ccd6cffbSTamas Berghammer std::string new_connect_url; 219ccd6cffbSTamas Berghammer error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 220245f7fdcSZachary Turner (remote_port < 0) ? 0 : remote_port, path, 221ccd6cffbSTamas Berghammer new_connect_url); 222ccd6cffbSTamas Berghammer if (error.Fail()) 223ccd6cffbSTamas Berghammer return nullptr; 224ccd6cffbSTamas Berghammer 2253165945aSZachary Turner return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, 2263165945aSZachary Turner debugger, target, error); 227ccd6cffbSTamas Berghammer } 228