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