100e305d2STamas Berghammer //===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===//
200e305d2STamas Berghammer //
3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
5*2946cd70SChandler 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();
3905a55de3SOleksiy Vyalov   if (log)
40f9da9483SOleksiy Vyalov     log->Printf("Connected to Android device \"%s\"", device_id.c_str());
4105a55de3SOleksiy Vyalov 
42b9c1b51eSKate Stone   if (remote_port != 0) {
439fe526c2SOleksiy Vyalov     if (log)
44b9c1b51eSKate Stone       log->Printf("Forwarding remote TCP port %d to local TCP port %d",
45b9c1b51eSKate Stone                   remote_port, local_port);
46e7eabbb5SOleksiy Vyalov     return adb.SetPortForwarding(local_port, remote_port);
4700e305d2STamas Berghammer   }
4800e305d2STamas Berghammer 
499fe526c2SOleksiy Vyalov   if (log)
50b9c1b51eSKate Stone     log->Printf("Forwarding remote socket \"%s\" to local TCP port %d",
51245f7fdcSZachary Turner                 remote_socket_name.str().c_str(), local_port);
52e7df5f5dSOleksiy Vyalov 
53e7df5f5dSOleksiy Vyalov   if (!socket_namespace)
5497206d57SZachary Turner     return Status("Invalid socket namespace");
55e7df5f5dSOleksiy Vyalov 
56b9c1b51eSKate Stone   return adb.SetPortForwarding(local_port, remote_socket_name,
57b9c1b51eSKate Stone                                *socket_namespace);
589fe526c2SOleksiy Vyalov }
599fe526c2SOleksiy Vyalov 
6097206d57SZachary Turner static Status DeleteForwardPortWithAdb(uint16_t local_port,
61b9c1b51eSKate Stone                                        const std::string &device_id) {
6205a55de3SOleksiy Vyalov   AdbClient adb(device_id);
63e7eabbb5SOleksiy Vyalov   return adb.DeletePortForwarding(local_port);
64e7eabbb5SOleksiy Vyalov }
65e7eabbb5SOleksiy Vyalov 
6697206d57SZachary Turner static Status FindUnusedPort(uint16_t &port) {
6797206d57SZachary Turner   Status error;
6811827799SChris Bieneman   std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false));
69e98628ceSOleksiy Vyalov   if (error.Fail())
70e98628ceSOleksiy Vyalov     return error;
71e98628ceSOleksiy Vyalov 
72e98628ceSOleksiy Vyalov   error = tcp_socket->Listen("127.0.0.1:0", 1);
73e7eabbb5SOleksiy Vyalov   if (error.Success())
74e98628ceSOleksiy Vyalov     port = tcp_socket->GetLocalPortNumber();
75e98628ceSOleksiy Vyalov 
76e7eabbb5SOleksiy Vyalov   return error;
7700e305d2STamas Berghammer }
7800e305d2STamas Berghammer 
79b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() {}
8000e305d2STamas Berghammer 
81b9c1b51eSKate Stone PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() {
8200e305d2STamas Berghammer   for (const auto &it : m_port_forwards)
833ea689b3SChaoren Lin     DeleteForwardPortWithAdb(it.second, m_device_id);
8400e305d2STamas Berghammer }
8500e305d2STamas Berghammer 
86b9c1b51eSKate Stone bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
87b9c1b51eSKate Stone                                                      std::string &connect_url) {
889fe526c2SOleksiy Vyalov   uint16_t remote_port = 0;
899fe526c2SOleksiy Vyalov   std::string socket_name;
909fe526c2SOleksiy Vyalov   if (!m_gdb_client.LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name))
919fe526c2SOleksiy Vyalov     return false;
9200e305d2STamas Berghammer 
939fe526c2SOleksiy Vyalov   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_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)
989fe526c2SOleksiy Vyalov     log->Printf("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) {
1041c1d76b3SOleksiy Vyalov   DeleteForwardPort(pid);
10500e305d2STamas Berghammer   return m_gdb_client.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 
115e7eabbb5SOleksiy Vyalov   int remote_port;
116245f7fdcSZachary Turner   llvm::StringRef scheme, host, path;
11700e305d2STamas Berghammer   const char *url = args.GetArgumentAtIndex(0);
1183ea689b3SChaoren Lin   if (!url)
11997206d57SZachary Turner     return Status("URL is null.");
120e7eabbb5SOleksiy Vyalov   if (!UriParser::Parse(url, scheme, host, remote_port, path))
12197206d57SZachary Turner     return Status("Invalid URL: %s", url);
122a29d6475SOleksiy Vyalov   if (host != "localhost")
1233ea689b3SChaoren Lin     m_device_id = host;
12400e305d2STamas Berghammer 
125e7df5f5dSOleksiy Vyalov   m_socket_namespace.reset();
126e7df5f5dSOleksiy Vyalov   if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
127e7df5f5dSOleksiy Vyalov     m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
128e7df5f5dSOleksiy Vyalov   else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
129e7df5f5dSOleksiy Vyalov     m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
130e7df5f5dSOleksiy Vyalov 
1319fe526c2SOleksiy Vyalov   std::string connect_url;
132b9c1b51eSKate Stone   auto error =
133b9c1b51eSKate Stone       MakeConnectURL(g_remote_platform_pid, (remote_port < 0) ? 0 : remote_port,
134245f7fdcSZachary Turner                      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 
141e7eabbb5SOleksiy Vyalov   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
142e7eabbb5SOleksiy Vyalov   if (log)
1439fe526c2SOleksiy Vyalov     log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
14400e305d2STamas Berghammer 
1451c1d76b3SOleksiy Vyalov   error = PlatformRemoteGDBServer::ConnectRemote(args);
1461c1d76b3SOleksiy Vyalov   if (error.Fail())
1471c1d76b3SOleksiy Vyalov     DeleteForwardPort(g_remote_platform_pid);
1481c1d76b3SOleksiy Vyalov 
1491c1d76b3SOleksiy Vyalov   return error;
15000e305d2STamas Berghammer }
15100e305d2STamas Berghammer 
15297206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::DisconnectRemote() {
1531c1d76b3SOleksiy Vyalov   DeleteForwardPort(g_remote_platform_pid);
1541c1d76b3SOleksiy Vyalov   return PlatformRemoteGDBServer::DisconnectRemote();
15500e305d2STamas Berghammer }
15600e305d2STamas Berghammer 
157b9c1b51eSKate Stone void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) {
1581c1d76b3SOleksiy Vyalov   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1591c1d76b3SOleksiy Vyalov 
1601c1d76b3SOleksiy Vyalov   auto it = m_port_forwards.find(pid);
1611c1d76b3SOleksiy Vyalov   if (it == m_port_forwards.end())
1621c1d76b3SOleksiy Vyalov     return;
1631c1d76b3SOleksiy Vyalov 
1643ea689b3SChaoren Lin   const auto port = it->second;
1653ea689b3SChaoren Lin   const auto error = DeleteForwardPortWithAdb(port, m_device_id);
1661c1d76b3SOleksiy Vyalov   if (error.Fail()) {
1671c1d76b3SOleksiy Vyalov     if (log)
168b9c1b51eSKate Stone       log->Printf("Failed to delete port forwarding (pid=%" PRIu64
169b9c1b51eSKate Stone                   ", port=%d, device=%s): %s",
1703ea689b3SChaoren Lin                   pid, port, m_device_id.c_str(), error.AsCString());
1711c1d76b3SOleksiy Vyalov   }
1721c1d76b3SOleksiy Vyalov   m_port_forwards.erase(it);
17300e305d2STamas Berghammer }
17454971856SOleksiy Vyalov 
17597206d57SZachary Turner Status PlatformAndroidRemoteGDBServer::MakeConnectURL(
176b9c1b51eSKate Stone     const lldb::pid_t pid, const uint16_t remote_port,
177245f7fdcSZachary Turner     llvm::StringRef remote_socket_name, std::string &connect_url) {
178e7eabbb5SOleksiy Vyalov   static const int kAttempsNum = 5;
179e7eabbb5SOleksiy Vyalov 
18097206d57SZachary Turner   Status error;
18105097246SAdrian Prantl   // There is a race possibility that somebody will occupy a port while we're
18205097246SAdrian Prantl   // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to
18305097246SAdrian Prantl   // mitigate such problem.
184b9c1b51eSKate Stone   for (auto i = 0; i < kAttempsNum; ++i) {
1859fe526c2SOleksiy Vyalov     uint16_t local_port = 0;
186e7eabbb5SOleksiy Vyalov     error = FindUnusedPort(local_port);
187e7eabbb5SOleksiy Vyalov     if (error.Fail())
188e7eabbb5SOleksiy Vyalov       return error;
189e7eabbb5SOleksiy Vyalov 
190b9c1b51eSKate Stone     error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name,
191b9c1b51eSKate Stone                                m_socket_namespace, m_device_id);
192b9c1b51eSKate Stone     if (error.Success()) {
193e7eabbb5SOleksiy Vyalov       m_port_forwards[pid] = local_port;
1949fe526c2SOleksiy Vyalov       std::ostringstream url_str;
1959fe526c2SOleksiy Vyalov       url_str << "connect://localhost:" << local_port;
1969fe526c2SOleksiy Vyalov       connect_url = url_str.str();
197e7eabbb5SOleksiy Vyalov       break;
198e7eabbb5SOleksiy Vyalov     }
199e7eabbb5SOleksiy Vyalov   }
200e7eabbb5SOleksiy Vyalov 
201e7eabbb5SOleksiy Vyalov   return error;
202e7eabbb5SOleksiy Vyalov }
203ccd6cffbSTamas Berghammer 
204b9c1b51eSKate Stone lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess(
2053165945aSZachary Turner     llvm::StringRef connect_url, llvm::StringRef plugin_name,
206b9c1b51eSKate Stone     lldb_private::Debugger &debugger, lldb_private::Target *target,
20797206d57SZachary Turner     lldb_private::Status &error) {
208b9c1b51eSKate Stone   // We don't have the pid of the remote gdbserver when it isn't started by us
20905097246SAdrian Prantl   // but we still want to store the list of port forwards we set up in our port
21005097246SAdrian Prantl   // forward map. Generate a fake pid for these cases what won't collide with
21105097246SAdrian Prantl   // any other valid pid on android.
212ccd6cffbSTamas Berghammer   static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
213ccd6cffbSTamas Berghammer 
214ccd6cffbSTamas Berghammer   int remote_port;
215245f7fdcSZachary Turner   llvm::StringRef scheme, host, path;
216b9c1b51eSKate Stone   if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) {
2173165945aSZachary Turner     error.SetErrorStringWithFormat("Invalid URL: %s",
2183165945aSZachary Turner                                    connect_url.str().c_str());
219ccd6cffbSTamas Berghammer     return nullptr;
220ccd6cffbSTamas Berghammer   }
221ccd6cffbSTamas Berghammer 
222ccd6cffbSTamas Berghammer   std::string new_connect_url;
223ccd6cffbSTamas Berghammer   error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
224245f7fdcSZachary Turner                          (remote_port < 0) ? 0 : remote_port, path,
225ccd6cffbSTamas Berghammer                          new_connect_url);
226ccd6cffbSTamas Berghammer   if (error.Fail())
227ccd6cffbSTamas Berghammer     return nullptr;
228ccd6cffbSTamas Berghammer 
2293165945aSZachary Turner   return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name,
2303165945aSZachary Turner                                                  debugger, target, error);
231ccd6cffbSTamas Berghammer }
232