100e305d2STamas Berghammer //===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===// 200e305d2STamas Berghammer // 300e305d2STamas Berghammer // The LLVM Compiler Infrastructure 400e305d2STamas Berghammer // 500e305d2STamas Berghammer // This file is distributed under the University of Illinois Open Source 600e305d2STamas Berghammer // License. See LICENSE.TXT for details. 700e305d2STamas Berghammer // 800e305d2STamas Berghammer //===----------------------------------------------------------------------===// 900e305d2STamas Berghammer 1000e305d2STamas Berghammer // Other libraries and framework includes 1100e305d2STamas Berghammer #include "lldb/Core/Error.h" 1205a55de3SOleksiy Vyalov #include "lldb/Core/Log.h" 136e181cf3SOleksiy Vyalov #include "lldb/Host/ConnectionFileDescriptor.h" 14b9c1b51eSKate Stone #include "lldb/Host/common/TCPSocket.h" 156e181cf3SOleksiy Vyalov 1600e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h" 1700e305d2STamas Berghammer #include "Utility/UriParser.h" 1800e305d2STamas Berghammer 1954971856SOleksiy Vyalov #include <sstream> 2054971856SOleksiy Vyalov 2100e305d2STamas Berghammer using namespace lldb; 2200e305d2STamas Berghammer using namespace lldb_private; 23db264a6dSTamas Berghammer using namespace platform_android; 2400e305d2STamas Berghammer 25b9c1b51eSKate Stone static const lldb::pid_t g_remote_platform_pid = 26b9c1b51eSKate Stone 0; // Alias for the process id of lldb-platform 2700e305d2STamas Berghammer 28b9c1b51eSKate Stone static Error ForwardPortWithAdb( 29b9c1b51eSKate Stone const uint16_t local_port, const uint16_t remote_port, 309fe526c2SOleksiy Vyalov const char *remote_socket_name, 31e7df5f5dSOleksiy Vyalov const llvm::Optional<AdbClient::UnixSocketNamespace> &socket_namespace, 32b9c1b51eSKate Stone std::string &device_id) { 33db264a6dSTamas Berghammer Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 3400e305d2STamas Berghammer 3505a55de3SOleksiy Vyalov AdbClient adb; 363ea689b3SChaoren Lin auto error = AdbClient::CreateByDeviceID(device_id, adb); 3700e305d2STamas Berghammer if (error.Fail()) 3800e305d2STamas Berghammer return error; 3900e305d2STamas Berghammer 40f9da9483SOleksiy Vyalov device_id = adb.GetDeviceID(); 4105a55de3SOleksiy Vyalov if (log) 42f9da9483SOleksiy Vyalov log->Printf("Connected to Android device \"%s\"", device_id.c_str()); 4305a55de3SOleksiy Vyalov 44b9c1b51eSKate Stone if (remote_port != 0) { 459fe526c2SOleksiy Vyalov if (log) 46b9c1b51eSKate Stone log->Printf("Forwarding remote TCP port %d to local TCP port %d", 47b9c1b51eSKate Stone remote_port, local_port); 48e7eabbb5SOleksiy Vyalov return adb.SetPortForwarding(local_port, remote_port); 4900e305d2STamas Berghammer } 5000e305d2STamas Berghammer 519fe526c2SOleksiy Vyalov if (log) 52b9c1b51eSKate Stone log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", 53b9c1b51eSKate Stone remote_socket_name, local_port); 54e7df5f5dSOleksiy Vyalov 55e7df5f5dSOleksiy Vyalov if (!socket_namespace) 56e7df5f5dSOleksiy Vyalov return Error("Invalid socket namespace"); 57e7df5f5dSOleksiy Vyalov 58b9c1b51eSKate Stone return adb.SetPortForwarding(local_port, remote_socket_name, 59b9c1b51eSKate Stone *socket_namespace); 609fe526c2SOleksiy Vyalov } 619fe526c2SOleksiy Vyalov 62b9c1b51eSKate Stone static Error DeleteForwardPortWithAdb(uint16_t local_port, 63b9c1b51eSKate Stone const std::string &device_id) { 6405a55de3SOleksiy Vyalov AdbClient adb(device_id); 65e7eabbb5SOleksiy Vyalov return adb.DeletePortForwarding(local_port); 66e7eabbb5SOleksiy Vyalov } 67e7eabbb5SOleksiy Vyalov 68b9c1b51eSKate Stone static Error FindUnusedPort(uint16_t &port) { 69e98628ceSOleksiy Vyalov Error error; 70e98628ceSOleksiy Vyalov std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error)); 71e98628ceSOleksiy Vyalov if (error.Fail()) 72e98628ceSOleksiy Vyalov return error; 73e98628ceSOleksiy Vyalov 74e98628ceSOleksiy Vyalov error = tcp_socket->Listen("127.0.0.1:0", 1); 75e7eabbb5SOleksiy Vyalov if (error.Success()) 76e98628ceSOleksiy Vyalov port = tcp_socket->GetLocalPortNumber(); 77e98628ceSOleksiy Vyalov 78e7eabbb5SOleksiy Vyalov return error; 7900e305d2STamas Berghammer } 8000e305d2STamas Berghammer 81b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() {} 8200e305d2STamas Berghammer 83b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() { 8400e305d2STamas Berghammer for (const auto &it : m_port_forwards) 853ea689b3SChaoren Lin DeleteForwardPortWithAdb(it.second, m_device_id); 8600e305d2STamas Berghammer } 8700e305d2STamas Berghammer 88b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, 89b9c1b51eSKate Stone std::string &connect_url) { 909fe526c2SOleksiy Vyalov uint16_t remote_port = 0; 919fe526c2SOleksiy Vyalov std::string socket_name; 929fe526c2SOleksiy Vyalov if (!m_gdb_client.LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name)) 939fe526c2SOleksiy Vyalov return false; 9400e305d2STamas Berghammer 959fe526c2SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 969fe526c2SOleksiy Vyalov 97b9c1b51eSKate Stone auto error = 98b9c1b51eSKate Stone MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url); 999fe526c2SOleksiy Vyalov if (error.Success() && log) 1009fe526c2SOleksiy Vyalov log->Printf("gdbserver connect URL: %s", connect_url.c_str()); 1019fe526c2SOleksiy Vyalov 1029fe526c2SOleksiy Vyalov return error.Success(); 10300e305d2STamas Berghammer } 10400e305d2STamas Berghammer 105b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { 1061c1d76b3SOleksiy Vyalov DeleteForwardPort(pid); 10700e305d2STamas Berghammer return m_gdb_client.KillSpawnedProcess(pid); 10800e305d2STamas Berghammer } 10900e305d2STamas Berghammer 110b9c1b51eSKate Stone Error PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) { 1113ea689b3SChaoren Lin m_device_id.clear(); 1123ea689b3SChaoren Lin 11300e305d2STamas Berghammer if (args.GetArgumentCount() != 1) 11400e305d2STamas Berghammer return Error("\"platform connect\" takes a single argument: <connect-url>"); 11500e305d2STamas Berghammer 116e7eabbb5SOleksiy Vyalov int remote_port; 11700e305d2STamas Berghammer std::string scheme, host, path; 11800e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0); 1193ea689b3SChaoren Lin if (!url) 1203ea689b3SChaoren Lin return Error("URL is null."); 121e7eabbb5SOleksiy Vyalov if (!UriParser::Parse(url, scheme, host, remote_port, path)) 1223ea689b3SChaoren Lin return Error("Invalid URL: %s", url); 123a29d6475SOleksiy Vyalov if (host != "localhost") 1243ea689b3SChaoren Lin m_device_id = host; 12500e305d2STamas Berghammer 126e7df5f5dSOleksiy Vyalov m_socket_namespace.reset(); 127e7df5f5dSOleksiy Vyalov if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME) 128e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; 129e7df5f5dSOleksiy Vyalov else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME) 130e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; 131e7df5f5dSOleksiy Vyalov 1329fe526c2SOleksiy Vyalov std::string connect_url; 133b9c1b51eSKate Stone auto error = 134b9c1b51eSKate Stone MakeConnectURL(g_remote_platform_pid, (remote_port < 0) ? 0 : remote_port, 135b9c1b51eSKate Stone path.c_str(), connect_url); 1369fe526c2SOleksiy Vyalov 13700e305d2STamas Berghammer if (error.Fail()) 13800e305d2STamas Berghammer return error; 13900e305d2STamas Berghammer 140*ecbb0bb1SZachary Turner args.ReplaceArgumentAtIndex(0, connect_url); 141e7eabbb5SOleksiy Vyalov 142e7eabbb5SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 143e7eabbb5SOleksiy Vyalov if (log) 1449fe526c2SOleksiy Vyalov log->Printf("Rewritten platform connect URL: %s", connect_url.c_str()); 14500e305d2STamas Berghammer 1461c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args); 1471c1d76b3SOleksiy Vyalov if (error.Fail()) 1481c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1491c1d76b3SOleksiy Vyalov 1501c1d76b3SOleksiy Vyalov return error; 15100e305d2STamas Berghammer } 15200e305d2STamas Berghammer 153b9c1b51eSKate Stone Error PlatformAndroidRemoteGDBServer::DisconnectRemote() { 1541c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid); 1551c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote(); 15600e305d2STamas Berghammer } 15700e305d2STamas Berghammer 158b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { 1591c1d76b3SOleksiy Vyalov Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); 1601c1d76b3SOleksiy Vyalov 1611c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid); 1621c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end()) 1631c1d76b3SOleksiy Vyalov return; 1641c1d76b3SOleksiy Vyalov 1653ea689b3SChaoren Lin const auto port = it->second; 1663ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id); 1671c1d76b3SOleksiy Vyalov if (error.Fail()) { 1681c1d76b3SOleksiy Vyalov if (log) 169b9c1b51eSKate Stone log->Printf("Failed to delete port forwarding (pid=%" PRIu64 170b9c1b51eSKate Stone ", port=%d, device=%s): %s", 1713ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString()); 1721c1d76b3SOleksiy Vyalov } 1731c1d76b3SOleksiy Vyalov m_port_forwards.erase(it); 17400e305d2STamas Berghammer } 17554971856SOleksiy Vyalov 176b9c1b51eSKate Stone Error PlatformAndroidRemoteGDBServer::MakeConnectURL( 177b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port, 178b9c1b51eSKate Stone const char *remote_socket_name, std::string &connect_url) { 179e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5; 180e7eabbb5SOleksiy Vyalov 181e7eabbb5SOleksiy Vyalov Error error; 182e7eabbb5SOleksiy Vyalov // There is a race possibility that somebody will occupy 183e7eabbb5SOleksiy Vyalov // a port while we're in between FindUnusedPort and ForwardPortWithAdb - 184e7eabbb5SOleksiy Vyalov // adding the loop to mitigate such problem. 185b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) { 1869fe526c2SOleksiy Vyalov uint16_t local_port = 0; 187e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port); 188e7eabbb5SOleksiy Vyalov if (error.Fail()) 189e7eabbb5SOleksiy Vyalov return error; 190e7eabbb5SOleksiy Vyalov 191b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, 192b9c1b51eSKate Stone m_socket_namespace, m_device_id); 193b9c1b51eSKate Stone if (error.Success()) { 194e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port; 1959fe526c2SOleksiy Vyalov std::ostringstream url_str; 1969fe526c2SOleksiy Vyalov url_str << "connect://localhost:" << local_port; 1979fe526c2SOleksiy Vyalov connect_url = url_str.str(); 198e7eabbb5SOleksiy Vyalov break; 199e7eabbb5SOleksiy Vyalov } 200e7eabbb5SOleksiy Vyalov } 201e7eabbb5SOleksiy Vyalov 202e7eabbb5SOleksiy Vyalov return error; 203e7eabbb5SOleksiy Vyalov } 204ccd6cffbSTamas Berghammer 205b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( 206b9c1b51eSKate Stone const char *connect_url, const char *plugin_name, 207b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target, 208b9c1b51eSKate Stone lldb_private::Error &error) { 209b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us 210b9c1b51eSKate Stone // but we still want 211b9c1b51eSKate Stone // to store the list of port forwards we set up in our port forward map. 212b9c1b51eSKate Stone // Generate a fake pid for 213ccd6cffbSTamas Berghammer // these cases what won't collide with any other valid pid on android. 214ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; 215ccd6cffbSTamas Berghammer 216ccd6cffbSTamas Berghammer int remote_port; 217ccd6cffbSTamas Berghammer std::string scheme, host, path; 218b9c1b51eSKate Stone if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) { 219ccd6cffbSTamas Berghammer error.SetErrorStringWithFormat("Invalid URL: %s", connect_url); 220ccd6cffbSTamas Berghammer return nullptr; 221ccd6cffbSTamas Berghammer } 222ccd6cffbSTamas Berghammer 223ccd6cffbSTamas Berghammer std::string new_connect_url; 224ccd6cffbSTamas Berghammer error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 225b9c1b51eSKate Stone (remote_port < 0) ? 0 : remote_port, path.c_str(), 226ccd6cffbSTamas Berghammer new_connect_url); 227ccd6cffbSTamas Berghammer if (error.Fail()) 228ccd6cffbSTamas Berghammer return nullptr; 229ccd6cffbSTamas Berghammer 230b9c1b51eSKate Stone return PlatformRemoteGDBServer::ConnectProcess( 231b9c1b51eSKate Stone new_connect_url.c_str(), plugin_name, debugger, target, error); 232ccd6cffbSTamas Berghammer } 233