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" 11*c34698a8SPavel Labath #include "lldb/Utility/LLDBLog.h" 126f9e6901SZachary Turner #include "lldb/Utility/Log.h" 1397206d57SZachary Turner #include "lldb/Utility/Status.h" 145f7e583bSPavel Labath #include "lldb/Utility/UriParser.h" 156e181cf3SOleksiy Vyalov 1600e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h" 1700e305d2STamas Berghammer 1854971856SOleksiy Vyalov #include <sstream> 1954971856SOleksiy Vyalov 2000e305d2STamas Berghammer using namespace lldb; 2100e305d2STamas Berghammer using namespace lldb_private; 22db264a6dSTamas Berghammer using namespace platform_android; 2300e305d2STamas Berghammer 24b9c1b51eSKate Stone static const lldb::pid_t g_remote_platform_pid = 25b9c1b51eSKate Stone 0; // Alias for the process id of lldb-platform 2600e305d2STamas Berghammer 2797206d57SZachary Turner static Status ForwardPortWithAdb( 28b9c1b51eSKate Stone const uint16_t local_port, const uint16_t remote_port, 29245f7fdcSZachary Turner llvm::StringRef remote_socket_name, 30e7df5f5dSOleksiy Vyalov const llvm::Optional<AdbClient::UnixSocketNamespace> &socket_namespace, 31b9c1b51eSKate Stone std::string &device_id) { 32a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform); 3300e305d2STamas Berghammer 3405a55de3SOleksiy Vyalov AdbClient adb; 353ea689b3SChaoren Lin auto error = AdbClient::CreateByDeviceID(device_id, adb); 3600e305d2STamas Berghammer if (error.Fail()) 3700e305d2STamas Berghammer return error; 3800e305d2STamas Berghammer 39f9da9483SOleksiy Vyalov device_id = adb.GetDeviceID(); 4063e5fb76SJonas Devlieghere LLDB_LOGF(log, "Connected to Android device \"%s\"", device_id.c_str()); 4105a55de3SOleksiy Vyalov 42b9c1b51eSKate Stone if (remote_port != 0) { 4363e5fb76SJonas Devlieghere LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d", 44b9c1b51eSKate Stone remote_port, local_port); 45e7eabbb5SOleksiy Vyalov return adb.SetPortForwarding(local_port, remote_port); 4600e305d2STamas Berghammer } 4700e305d2STamas Berghammer 4863e5fb76SJonas Devlieghere LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d", 49245f7fdcSZachary Turner remote_socket_name.str().c_str(), local_port); 50e7df5f5dSOleksiy Vyalov 51e7df5f5dSOleksiy Vyalov if (!socket_namespace) 5297206d57SZachary Turner return Status("Invalid socket namespace"); 53e7df5f5dSOleksiy Vyalov 54b9c1b51eSKate Stone return adb.SetPortForwarding(local_port, remote_socket_name, 55b9c1b51eSKate Stone *socket_namespace); 569fe526c2SOleksiy Vyalov } 579fe526c2SOleksiy Vyalov 5897206d57SZachary Turner static Status DeleteForwardPortWithAdb(uint16_t local_port, 59b9c1b51eSKate Stone const std::string &device_id) { 6005a55de3SOleksiy Vyalov AdbClient adb(device_id); 61e7eabbb5SOleksiy Vyalov return adb.DeletePortForwarding(local_port); 62e7eabbb5SOleksiy Vyalov } 63e7eabbb5SOleksiy Vyalov 6497206d57SZachary Turner static Status FindUnusedPort(uint16_t &port) { 6597206d57SZachary Turner Status error; 6611827799SChris Bieneman std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false)); 67e98628ceSOleksiy Vyalov if (error.Fail()) 68e98628ceSOleksiy Vyalov return error; 69e98628ceSOleksiy Vyalov 70e98628ceSOleksiy Vyalov error = tcp_socket->Listen("127.0.0.1:0", 1); 71e7eabbb5SOleksiy Vyalov if (error.Success()) 72e98628ceSOleksiy Vyalov port = tcp_socket->GetLocalPortNumber(); 73e98628ceSOleksiy Vyalov 74e7eabbb5SOleksiy Vyalov return error; 7500e305d2STamas Berghammer } 7600e305d2STamas Berghammer 77fd2433e1SJonas Devlieghere PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() = default; 7800e305d2STamas Berghammer 79b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() { 8000e305d2STamas Berghammer for (const auto &it : m_port_forwards) 813ea689b3SChaoren Lin DeleteForwardPortWithAdb(it.second, m_device_id); 8200e305d2STamas Berghammer } 8300e305d2STamas Berghammer 84b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, 85b9c1b51eSKate Stone std::string &connect_url) { 868ccfcab3SPavel Labath assert(IsConnected()); 879fe526c2SOleksiy Vyalov uint16_t remote_port = 0; 889fe526c2SOleksiy Vyalov std::string socket_name; 898ccfcab3SPavel Labath if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port, 908ccfcab3SPavel Labath socket_name)) 919fe526c2SOleksiy Vyalov return false; 9200e305d2STamas Berghammer 93a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::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) 9863e5fb76SJonas Devlieghere LLDB_LOGF(log, "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) { 1048ccfcab3SPavel Labath assert(IsConnected()); 1051c1d76b3SOleksiy Vyalov DeleteForwardPort(pid); 1068ccfcab3SPavel Labath return m_gdb_client_up->KillSpawnedProcess(pid); 10700e305d2STamas Berghammer } 10800e305d2STamas Berghammer 10997206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) { 1103ea689b3SChaoren Lin m_device_id.clear(); 1113ea689b3SChaoren Lin 11200e305d2STamas Berghammer if (args.GetArgumentCount() != 1) 11397206d57SZachary Turner return Status( 11497206d57SZachary Turner "\"platform connect\" takes a single argument: <connect-url>"); 11500e305d2STamas Berghammer 11600e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0); 1173ea689b3SChaoren Lin if (!url) 11897206d57SZachary Turner return Status("URL is null."); 1190e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(url); 1200e5a4147SMichał Górny if (!parsed_url) 12197206d57SZachary Turner return Status("Invalid URL: %s", url); 1220e5a4147SMichał Górny if (parsed_url->hostname != "localhost") 1230e5a4147SMichał Górny m_device_id = parsed_url->hostname.str(); 12400e305d2STamas Berghammer 125e7df5f5dSOleksiy Vyalov m_socket_namespace.reset(); 1260e5a4147SMichał Górny if (parsed_url->scheme == "unix-connect") 127e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; 1280e5a4147SMichał Górny else if (parsed_url->scheme == "unix-abstract-connect") 129e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; 130e7df5f5dSOleksiy Vyalov 1319fe526c2SOleksiy Vyalov std::string connect_url; 1320e5a4147SMichał Górny auto error = 1330e5a4147SMichał Górny MakeConnectURL(g_remote_platform_pid, parsed_url->port.getValueOr(0), 1340e5a4147SMichał Górny parsed_url->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 141a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform); 14263e5fb76SJonas Devlieghere LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str()); 14300e305d2STamas Berghammer 1441c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args); 1451c1d76b3SOleksiy Vyalov if (error.Fail()) 1461c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1471c1d76b3SOleksiy Vyalov 1481c1d76b3SOleksiy Vyalov return error; 14900e305d2STamas Berghammer } 15000e305d2STamas Berghammer 15197206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() { 1521c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1531c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote(); 15400e305d2STamas Berghammer } 15500e305d2STamas Berghammer 156b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { 157a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform); 1581c1d76b3SOleksiy Vyalov 1591c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid); 1601c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end()) 1611c1d76b3SOleksiy Vyalov return; 1621c1d76b3SOleksiy Vyalov 1633ea689b3SChaoren Lin const auto port = it->second; 1643ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id); 1651c1d76b3SOleksiy Vyalov if (error.Fail()) { 16663e5fb76SJonas Devlieghere LLDB_LOGF(log, 16763e5fb76SJonas Devlieghere "Failed to delete port forwarding (pid=%" PRIu64 168b9c1b51eSKate Stone ", port=%d, device=%s): %s", 1693ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString()); 1701c1d76b3SOleksiy Vyalov } 1711c1d76b3SOleksiy Vyalov m_port_forwards.erase(it); 17200e305d2STamas Berghammer } 17354971856SOleksiy Vyalov 17497206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL( 175b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port, 176245f7fdcSZachary Turner llvm::StringRef remote_socket_name, std::string &connect_url) { 177e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5; 178e7eabbb5SOleksiy Vyalov 17997206d57SZachary Turner Status error; 18005097246SAdrian Prantl // There is a race possibility that somebody will occupy a port while we're 18105097246SAdrian Prantl // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to 18205097246SAdrian Prantl // mitigate such problem. 183b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) { 1849fe526c2SOleksiy Vyalov uint16_t local_port = 0; 185e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port); 186e7eabbb5SOleksiy Vyalov if (error.Fail()) 187e7eabbb5SOleksiy Vyalov return error; 188e7eabbb5SOleksiy Vyalov 189b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, 190b9c1b51eSKate Stone m_socket_namespace, m_device_id); 191b9c1b51eSKate Stone if (error.Success()) { 192e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port; 1939fe526c2SOleksiy Vyalov std::ostringstream url_str; 194a9d7b458SEmre Kultursay url_str << "connect://127.0.0.1:" << local_port; 1959fe526c2SOleksiy Vyalov connect_url = url_str.str(); 196e7eabbb5SOleksiy Vyalov break; 197e7eabbb5SOleksiy Vyalov } 198e7eabbb5SOleksiy Vyalov } 199e7eabbb5SOleksiy Vyalov 200e7eabbb5SOleksiy Vyalov return error; 201e7eabbb5SOleksiy Vyalov } 202ccd6cffbSTamas Berghammer 203b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( 2043165945aSZachary Turner llvm::StringRef connect_url, llvm::StringRef plugin_name, 205b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target, 20697206d57SZachary Turner lldb_private::Status &error) { 207b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us 20805097246SAdrian Prantl // but we still want to store the list of port forwards we set up in our port 20905097246SAdrian Prantl // forward map. Generate a fake pid for these cases what won't collide with 21005097246SAdrian Prantl // any other valid pid on android. 211ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; 212ccd6cffbSTamas Berghammer 2130e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(connect_url); 2140e5a4147SMichał Górny if (!parsed_url) { 2153165945aSZachary Turner error.SetErrorStringWithFormat("Invalid URL: %s", 2163165945aSZachary Turner connect_url.str().c_str()); 217ccd6cffbSTamas Berghammer return nullptr; 218ccd6cffbSTamas Berghammer } 219ccd6cffbSTamas Berghammer 220ccd6cffbSTamas Berghammer std::string new_connect_url; 221ccd6cffbSTamas Berghammer error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 2220e5a4147SMichał Górny parsed_url->port.getValueOr(0), parsed_url->path, 2230e5a4147SMichał Górny new_connect_url); 224ccd6cffbSTamas Berghammer if (error.Fail()) 225ccd6cffbSTamas Berghammer return nullptr; 226ccd6cffbSTamas Berghammer 2273165945aSZachary Turner return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, 2283165945aSZachary Turner debugger, target, error); 229ccd6cffbSTamas Berghammer } 230