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"
11c34698a8SPavel 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
ForwardPortWithAdb(const uint16_t local_port,const uint16_t remote_port,llvm::StringRef remote_socket_name,const llvm::Optional<AdbClient::UnixSocketNamespace> & socket_namespace,std::string & device_id)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
DeleteForwardPortWithAdb(uint16_t local_port,const std::string & device_id)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
FindUnusedPort(uint16_t & port)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
~PlatformAndroidRemoteGDBServer()77b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() {
7800e305d2STamas Berghammer for (const auto &it : m_port_forwards)
793ea689b3SChaoren Lin DeleteForwardPortWithAdb(it.second, m_device_id);
8000e305d2STamas Berghammer }
8100e305d2STamas Berghammer
LaunchGDBServer(lldb::pid_t & pid,std::string & connect_url)82b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
83b9c1b51eSKate Stone std::string &connect_url) {
848ccfcab3SPavel Labath assert(IsConnected());
859fe526c2SOleksiy Vyalov uint16_t remote_port = 0;
869fe526c2SOleksiy Vyalov std::string socket_name;
878ccfcab3SPavel Labath if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port,
888ccfcab3SPavel Labath socket_name))
899fe526c2SOleksiy Vyalov return false;
9000e305d2STamas Berghammer
91a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform);
929fe526c2SOleksiy Vyalov
93b9c1b51eSKate Stone auto error =
94b9c1b51eSKate Stone MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url);
959fe526c2SOleksiy Vyalov if (error.Success() && log)
9663e5fb76SJonas Devlieghere LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str());
979fe526c2SOleksiy Vyalov
989fe526c2SOleksiy Vyalov return error.Success();
9900e305d2STamas Berghammer }
10000e305d2STamas Berghammer
KillSpawnedProcess(lldb::pid_t pid)101b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
1028ccfcab3SPavel Labath assert(IsConnected());
1031c1d76b3SOleksiy Vyalov DeleteForwardPort(pid);
1048ccfcab3SPavel Labath return m_gdb_client_up->KillSpawnedProcess(pid);
10500e305d2STamas Berghammer }
10600e305d2STamas Berghammer
ConnectRemote(Args & args)10797206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) {
1083ea689b3SChaoren Lin m_device_id.clear();
1093ea689b3SChaoren Lin
11000e305d2STamas Berghammer if (args.GetArgumentCount() != 1)
11197206d57SZachary Turner return Status(
11297206d57SZachary Turner "\"platform connect\" takes a single argument: <connect-url>");
11300e305d2STamas Berghammer
11400e305d2STamas Berghammer const char *url = args.GetArgumentAtIndex(0);
1153ea689b3SChaoren Lin if (!url)
11697206d57SZachary Turner return Status("URL is null.");
1170e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(url);
1180e5a4147SMichał Górny if (!parsed_url)
11997206d57SZachary Turner return Status("Invalid URL: %s", url);
1200e5a4147SMichał Górny if (parsed_url->hostname != "localhost")
1210e5a4147SMichał Górny m_device_id = parsed_url->hostname.str();
12200e305d2STamas Berghammer
123e7df5f5dSOleksiy Vyalov m_socket_namespace.reset();
1240e5a4147SMichał Górny if (parsed_url->scheme == "unix-connect")
125e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
1260e5a4147SMichał Górny else if (parsed_url->scheme == "unix-abstract-connect")
127e7df5f5dSOleksiy Vyalov m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
128e7df5f5dSOleksiy Vyalov
1299fe526c2SOleksiy Vyalov std::string connect_url;
1300e5a4147SMichał Górny auto error =
131*aa88161bSKazu Hirata MakeConnectURL(g_remote_platform_pid, parsed_url->port.value_or(0),
1320e5a4147SMichał Górny parsed_url->path, connect_url);
1339fe526c2SOleksiy Vyalov
13400e305d2STamas Berghammer if (error.Fail())
13500e305d2STamas Berghammer return error;
13600e305d2STamas Berghammer
137ecbb0bb1SZachary Turner args.ReplaceArgumentAtIndex(0, connect_url);
138e7eabbb5SOleksiy Vyalov
139a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform);
14063e5fb76SJonas Devlieghere LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str());
14100e305d2STamas Berghammer
1421c1d76b3SOleksiy Vyalov error = PlatformRemoteGDBServer::ConnectRemote(args);
1431c1d76b3SOleksiy Vyalov if (error.Fail())
1441c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid);
1451c1d76b3SOleksiy Vyalov
1461c1d76b3SOleksiy Vyalov return error;
14700e305d2STamas Berghammer }
14800e305d2STamas Berghammer
DisconnectRemote()14997206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() {
1501c1d76b3SOleksiy Vyalov DeleteForwardPort(g_remote_platform_pid);
1511c1d76b3SOleksiy Vyalov return PlatformRemoteGDBServer::DisconnectRemote();
15200e305d2STamas Berghammer }
15300e305d2STamas Berghammer
DeleteForwardPort(lldb::pid_t pid)154b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) {
155a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Platform);
1561c1d76b3SOleksiy Vyalov
1571c1d76b3SOleksiy Vyalov auto it = m_port_forwards.find(pid);
1581c1d76b3SOleksiy Vyalov if (it == m_port_forwards.end())
1591c1d76b3SOleksiy Vyalov return;
1601c1d76b3SOleksiy Vyalov
1613ea689b3SChaoren Lin const auto port = it->second;
1623ea689b3SChaoren Lin const auto error = DeleteForwardPortWithAdb(port, m_device_id);
1631c1d76b3SOleksiy Vyalov if (error.Fail()) {
16463e5fb76SJonas Devlieghere LLDB_LOGF(log,
16563e5fb76SJonas Devlieghere "Failed to delete port forwarding (pid=%" PRIu64
166b9c1b51eSKate Stone ", port=%d, device=%s): %s",
1673ea689b3SChaoren Lin pid, port, m_device_id.c_str(), error.AsCString());
1681c1d76b3SOleksiy Vyalov }
1691c1d76b3SOleksiy Vyalov m_port_forwards.erase(it);
17000e305d2STamas Berghammer }
17154971856SOleksiy Vyalov
MakeConnectURL(const lldb::pid_t pid,const uint16_t remote_port,llvm::StringRef remote_socket_name,std::string & connect_url)17297206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL(
173b9c1b51eSKate Stone const lldb::pid_t pid, const uint16_t remote_port,
174245f7fdcSZachary Turner llvm::StringRef remote_socket_name, std::string &connect_url) {
175e7eabbb5SOleksiy Vyalov static const int kAttempsNum = 5;
176e7eabbb5SOleksiy Vyalov
17797206d57SZachary Turner Status error;
17805097246SAdrian Prantl // There is a race possibility that somebody will occupy a port while we're
17905097246SAdrian Prantl // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to
18005097246SAdrian Prantl // mitigate such problem.
181b9c1b51eSKate Stone for (auto i = 0; i < kAttempsNum; ++i) {
1829fe526c2SOleksiy Vyalov uint16_t local_port = 0;
183e7eabbb5SOleksiy Vyalov error = FindUnusedPort(local_port);
184e7eabbb5SOleksiy Vyalov if (error.Fail())
185e7eabbb5SOleksiy Vyalov return error;
186e7eabbb5SOleksiy Vyalov
187b9c1b51eSKate Stone error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name,
188b9c1b51eSKate Stone m_socket_namespace, m_device_id);
189b9c1b51eSKate Stone if (error.Success()) {
190e7eabbb5SOleksiy Vyalov m_port_forwards[pid] = local_port;
1919fe526c2SOleksiy Vyalov std::ostringstream url_str;
192a9d7b458SEmre Kultursay url_str << "connect://127.0.0.1:" << local_port;
1939fe526c2SOleksiy Vyalov connect_url = url_str.str();
194e7eabbb5SOleksiy Vyalov break;
195e7eabbb5SOleksiy Vyalov }
196e7eabbb5SOleksiy Vyalov }
197e7eabbb5SOleksiy Vyalov
198e7eabbb5SOleksiy Vyalov return error;
199e7eabbb5SOleksiy Vyalov }
200ccd6cffbSTamas Berghammer
ConnectProcess(llvm::StringRef connect_url,llvm::StringRef plugin_name,lldb_private::Debugger & debugger,lldb_private::Target * target,lldb_private::Status & error)201b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess(
2023165945aSZachary Turner llvm::StringRef connect_url, llvm::StringRef plugin_name,
203b9c1b51eSKate Stone lldb_private::Debugger &debugger, lldb_private::Target *target,
20497206d57SZachary Turner lldb_private::Status &error) {
205b9c1b51eSKate Stone // We don't have the pid of the remote gdbserver when it isn't started by us
20605097246SAdrian Prantl // but we still want to store the list of port forwards we set up in our port
20705097246SAdrian Prantl // forward map. Generate a fake pid for these cases what won't collide with
20805097246SAdrian Prantl // any other valid pid on android.
209ccd6cffbSTamas Berghammer static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
210ccd6cffbSTamas Berghammer
2110e5a4147SMichał Górny llvm::Optional<URI> parsed_url = URI::Parse(connect_url);
2120e5a4147SMichał Górny if (!parsed_url) {
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--,
220*aa88161bSKazu Hirata parsed_url->port.value_or(0), parsed_url->path,
2210e5a4147SMichał Górny 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