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