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 76fd2433e1SJonas Devlieghere PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() = default; 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) { 85*8ccfcab3SPavel Labath assert(IsConnected()); 869fe526c2SOleksiy Vyalov uint16_t remote_port = 0; 879fe526c2SOleksiy Vyalov std::string socket_name; 88*8ccfcab3SPavel Labath if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port, 89*8ccfcab3SPavel Labath socket_name)) 909fe526c2SOleksiy Vyalov return false; 9100e305d2STamas Berghammer 929fe526c2SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 939fe526c2SOleksiy Vyalov 94b9c1b51eSKate Stone auto error = 95b9c1b51eSKate Stone MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url); 969fe526c2SOleksiy Vyalov if (error.Success() && log) 9763e5fb76SJonas Devlieghere LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str()); 989fe526c2SOleksiy Vyalov 999fe526c2SOleksiy Vyalov return error.Success(); 10000e305d2STamas Berghammer } 10100e305d2STamas Berghammer 102b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { 103*8ccfcab3SPavel Labath assert(IsConnected()); 1041c1d76b3SOleksiy Vyalov DeleteForwardPort(pid); 105*8ccfcab3SPavel Labath return m_gdb_client_up->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 11500e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0); 1163ea689b3SChaoren Lin if (!url) 11797206d57SZachary Turner return Status("URL is null."); 1180e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(url); 1190e5a4147SMichał Górny if (!parsed_url) 12097206d57SZachary Turner return Status("Invalid URL: %s", url); 1210e5a4147SMichał Górny if (parsed_url->hostname != "localhost") 1220e5a4147SMichał Górny m_device_id = parsed_url->hostname.str(); 12300e305d2STamas Berghammer 124e7df5f5dSOleksiy Vyalov m_socket_namespace.reset(); 1250e5a4147SMichał Górny if (parsed_url->scheme == "unix-connect") 126e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; 1270e5a4147SMichał Górny else if (parsed_url->scheme == "unix-abstract-connect") 128e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; 129e7df5f5dSOleksiy Vyalov 1309fe526c2SOleksiy Vyalov std::string connect_url; 1310e5a4147SMichał Górny auto error = 1320e5a4147SMichał Górny MakeConnectURL(g_remote_platform_pid, parsed_url->port.getValueOr(0), 1330e5a4147SMichał Górny parsed_url->path, connect_url); 1349fe526c2SOleksiy Vyalov 13500e305d2STamas Berghammer if (error.Fail()) 13600e305d2STamas Berghammer return error; 13700e305d2STamas Berghammer 138ecbb0bb1SZachary Turner args.ReplaceArgumentAtIndex(0, connect_url); 139e7eabbb5SOleksiy Vyalov 140e7eabbb5SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 14163e5fb76SJonas Devlieghere LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str()); 14200e305d2STamas Berghammer 1431c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args); 1441c1d76b3SOleksiy Vyalov if (error.Fail()) 1451c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1461c1d76b3SOleksiy Vyalov 1471c1d76b3SOleksiy Vyalov return error; 14800e305d2STamas Berghammer } 14900e305d2STamas Berghammer 15097206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() { 1511c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1521c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote(); 15300e305d2STamas Berghammer } 15400e305d2STamas Berghammer 155b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { 1561c1d76b3SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 1571c1d76b3SOleksiy Vyalov 1581c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid); 1591c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end()) 1601c1d76b3SOleksiy Vyalov return; 1611c1d76b3SOleksiy Vyalov 1623ea689b3SChaoren Lin const auto port = it->second; 1633ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id); 1641c1d76b3SOleksiy Vyalov if (error.Fail()) { 16563e5fb76SJonas Devlieghere LLDB_LOGF(log, 16663e5fb76SJonas Devlieghere "Failed to delete port forwarding (pid=%" PRIu64 167b9c1b51eSKate Stone ", port=%d, device=%s): %s", 1683ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString()); 1691c1d76b3SOleksiy Vyalov } 1701c1d76b3SOleksiy Vyalov m_port_forwards.erase(it); 17100e305d2STamas Berghammer } 17254971856SOleksiy Vyalov 17397206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL( 174b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port, 175245f7fdcSZachary Turner llvm::StringRef remote_socket_name, std::string &connect_url) { 176e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5; 177e7eabbb5SOleksiy Vyalov 17897206d57SZachary Turner Status error; 17905097246SAdrian Prantl // There is a race possibility that somebody will occupy a port while we're 18005097246SAdrian Prantl // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to 18105097246SAdrian Prantl // mitigate such problem. 182b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) { 1839fe526c2SOleksiy Vyalov uint16_t local_port = 0; 184e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port); 185e7eabbb5SOleksiy Vyalov if (error.Fail()) 186e7eabbb5SOleksiy Vyalov return error; 187e7eabbb5SOleksiy Vyalov 188b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, 189b9c1b51eSKate Stone m_socket_namespace, m_device_id); 190b9c1b51eSKate Stone if (error.Success()) { 191e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port; 1929fe526c2SOleksiy Vyalov std::ostringstream url_str; 193a9d7b458SEmre Kultursay url_str << "connect://127.0.0.1:" << local_port; 1949fe526c2SOleksiy Vyalov connect_url = url_str.str(); 195e7eabbb5SOleksiy Vyalov break; 196e7eabbb5SOleksiy Vyalov } 197e7eabbb5SOleksiy Vyalov } 198e7eabbb5SOleksiy Vyalov 199e7eabbb5SOleksiy Vyalov return error; 200e7eabbb5SOleksiy Vyalov } 201ccd6cffbSTamas Berghammer 202b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( 2033165945aSZachary Turner llvm::StringRef connect_url, llvm::StringRef plugin_name, 204b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target, 20597206d57SZachary Turner lldb_private::Status &error) { 206b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us 20705097246SAdrian Prantl // but we still want to store the list of port forwards we set up in our port 20805097246SAdrian Prantl // forward map. Generate a fake pid for these cases what won't collide with 20905097246SAdrian Prantl // any other valid pid on android. 210ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; 211ccd6cffbSTamas Berghammer 2120e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(connect_url); 2130e5a4147SMichał Górny if (!parsed_url) { 2143165945aSZachary Turner error.SetErrorStringWithFormat("Invalid URL: %s", 2153165945aSZachary Turner connect_url.str().c_str()); 216ccd6cffbSTamas Berghammer return nullptr; 217ccd6cffbSTamas Berghammer } 218ccd6cffbSTamas Berghammer 219ccd6cffbSTamas Berghammer std::string new_connect_url; 220ccd6cffbSTamas Berghammer error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 2210e5a4147SMichał Górny parsed_url->port.getValueOr(0), parsed_url->path, 2220e5a4147SMichał Górny new_connect_url); 223ccd6cffbSTamas Berghammer if (error.Fail()) 224ccd6cffbSTamas Berghammer return nullptr; 225ccd6cffbSTamas Berghammer 2263165945aSZachary Turner return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, 2273165945aSZachary Turner debugger, target, error); 228ccd6cffbSTamas Berghammer } 229