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"
13e98628ceSOleksiy Vyalov #include "lldb/Host/common/TCPSocket.h"
1400e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h"
1500e305d2STamas Berghammer #include "Utility/UriParser.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 
2300e305d2STamas Berghammer static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
2400e305d2STamas Berghammer 
2500e305d2STamas Berghammer static Error
269fe526c2SOleksiy Vyalov ForwardPortWithAdb (const uint16_t local_port,
279fe526c2SOleksiy Vyalov                     const uint16_t remote_port,
289fe526c2SOleksiy Vyalov                     const char* remote_socket_name,
29e7df5f5dSOleksiy Vyalov                     const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace,
309fe526c2SOleksiy Vyalov                     std::string& device_id)
3100e305d2STamas Berghammer {
32db264a6dSTamas Berghammer     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_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();
4005a55de3SOleksiy Vyalov     if (log)
41f9da9483SOleksiy Vyalov         log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
4205a55de3SOleksiy Vyalov 
439fe526c2SOleksiy Vyalov     if (remote_port != 0)
449fe526c2SOleksiy Vyalov     {
459fe526c2SOleksiy Vyalov         if (log)
469fe526c2SOleksiy Vyalov             log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port);
47e7eabbb5SOleksiy Vyalov         return adb.SetPortForwarding(local_port, remote_port);
4800e305d2STamas Berghammer     }
4900e305d2STamas Berghammer 
509fe526c2SOleksiy Vyalov     if (log)
519fe526c2SOleksiy Vyalov         log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port);
52e7df5f5dSOleksiy Vyalov 
53e7df5f5dSOleksiy Vyalov     if (!socket_namespace)
54e7df5f5dSOleksiy Vyalov         return Error("Invalid socket namespace");
55e7df5f5dSOleksiy Vyalov 
56e7df5f5dSOleksiy Vyalov     return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace);
579fe526c2SOleksiy Vyalov }
589fe526c2SOleksiy Vyalov 
5900e305d2STamas Berghammer static Error
60e7eabbb5SOleksiy Vyalov DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
6100e305d2STamas Berghammer {
6205a55de3SOleksiy Vyalov     AdbClient adb (device_id);
63e7eabbb5SOleksiy Vyalov     return adb.DeletePortForwarding (local_port);
64e7eabbb5SOleksiy Vyalov }
65e7eabbb5SOleksiy Vyalov 
66e7eabbb5SOleksiy Vyalov static Error
67e7eabbb5SOleksiy Vyalov FindUnusedPort (uint16_t& port)
68e7eabbb5SOleksiy Vyalov {
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 
8100e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer ()
8200e305d2STamas Berghammer {
8300e305d2STamas Berghammer }
8400e305d2STamas Berghammer 
8500e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
8600e305d2STamas Berghammer {
8700e305d2STamas Berghammer     for (const auto& it : m_port_forwards)
883ea689b3SChaoren Lin         DeleteForwardPortWithAdb(it.second, m_device_id);
8900e305d2STamas Berghammer }
9000e305d2STamas Berghammer 
919fe526c2SOleksiy Vyalov bool
929fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
9300e305d2STamas Berghammer {
949fe526c2SOleksiy Vyalov     uint16_t remote_port = 0;
959fe526c2SOleksiy Vyalov     std::string socket_name;
969fe526c2SOleksiy Vyalov     if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name))
979fe526c2SOleksiy Vyalov         return false;
9800e305d2STamas Berghammer 
999fe526c2SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1009fe526c2SOleksiy Vyalov 
1019fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (pid,
1029fe526c2SOleksiy Vyalov                                  remote_port,
1039fe526c2SOleksiy Vyalov                                  socket_name.c_str (),
1049fe526c2SOleksiy Vyalov                                  connect_url);
1059fe526c2SOleksiy Vyalov     if (error.Success() && log)
1069fe526c2SOleksiy Vyalov         log->Printf("gdbserver connect URL: %s", connect_url.c_str());
1079fe526c2SOleksiy Vyalov 
1089fe526c2SOleksiy Vyalov     return error.Success();
10900e305d2STamas Berghammer }
11000e305d2STamas Berghammer 
11100e305d2STamas Berghammer bool
11200e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
11300e305d2STamas Berghammer {
1141c1d76b3SOleksiy Vyalov     DeleteForwardPort (pid);
11500e305d2STamas Berghammer     return m_gdb_client.KillSpawnedProcess (pid);
11600e305d2STamas Berghammer }
11700e305d2STamas Berghammer 
11800e305d2STamas Berghammer Error
11900e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
12000e305d2STamas Berghammer {
1213ea689b3SChaoren Lin     m_device_id.clear();
1223ea689b3SChaoren Lin 
12300e305d2STamas Berghammer     if (args.GetArgumentCount() != 1)
12400e305d2STamas Berghammer         return Error("\"platform connect\" takes a single argument: <connect-url>");
12500e305d2STamas Berghammer 
126e7eabbb5SOleksiy Vyalov     int remote_port;
12700e305d2STamas Berghammer     std::string scheme, host, path;
12800e305d2STamas Berghammer     const char *url = args.GetArgumentAtIndex (0);
1293ea689b3SChaoren Lin     if (!url)
1303ea689b3SChaoren Lin         return Error("URL is null.");
131e7eabbb5SOleksiy Vyalov     if (!UriParser::Parse (url, scheme, host, remote_port, path))
1323ea689b3SChaoren Lin         return Error("Invalid URL: %s", url);
133a29d6475SOleksiy Vyalov     if (host != "localhost")
1343ea689b3SChaoren Lin         m_device_id = host;
13500e305d2STamas Berghammer 
136e7df5f5dSOleksiy Vyalov     m_socket_namespace.reset();
137e7df5f5dSOleksiy Vyalov     if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
138e7df5f5dSOleksiy Vyalov         m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
139e7df5f5dSOleksiy Vyalov     else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
140e7df5f5dSOleksiy Vyalov         m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
141e7df5f5dSOleksiy Vyalov 
1429fe526c2SOleksiy Vyalov     std::string connect_url;
1439fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (g_remote_platform_pid,
1449fe526c2SOleksiy Vyalov                                  (remote_port < 0) ? 0 : remote_port,
1459fe526c2SOleksiy Vyalov                                  path.c_str (),
1469fe526c2SOleksiy Vyalov                                  connect_url);
1479fe526c2SOleksiy Vyalov 
14800e305d2STamas Berghammer     if (error.Fail ())
14900e305d2STamas Berghammer         return error;
15000e305d2STamas Berghammer 
1519fe526c2SOleksiy Vyalov     args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
152e7eabbb5SOleksiy Vyalov 
153e7eabbb5SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
154e7eabbb5SOleksiy Vyalov     if (log)
1559fe526c2SOleksiy Vyalov         log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
15600e305d2STamas Berghammer 
1571c1d76b3SOleksiy Vyalov     error = PlatformRemoteGDBServer::ConnectRemote(args);
1581c1d76b3SOleksiy Vyalov     if (error.Fail ())
1591c1d76b3SOleksiy Vyalov         DeleteForwardPort (g_remote_platform_pid);
1601c1d76b3SOleksiy Vyalov 
1611c1d76b3SOleksiy Vyalov     return error;
16200e305d2STamas Berghammer }
16300e305d2STamas Berghammer 
16400e305d2STamas Berghammer Error
16500e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::DisconnectRemote ()
16600e305d2STamas Berghammer {
1671c1d76b3SOleksiy Vyalov     DeleteForwardPort (g_remote_platform_pid);
1681c1d76b3SOleksiy Vyalov     return PlatformRemoteGDBServer::DisconnectRemote ();
16900e305d2STamas Berghammer }
17000e305d2STamas Berghammer 
1711c1d76b3SOleksiy Vyalov void
1721c1d76b3SOleksiy Vyalov PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
1731c1d76b3SOleksiy Vyalov {
1741c1d76b3SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1751c1d76b3SOleksiy Vyalov 
1761c1d76b3SOleksiy Vyalov     auto it = m_port_forwards.find(pid);
1771c1d76b3SOleksiy Vyalov     if (it == m_port_forwards.end())
1781c1d76b3SOleksiy Vyalov         return;
1791c1d76b3SOleksiy Vyalov 
1803ea689b3SChaoren Lin     const auto port = it->second;
1813ea689b3SChaoren Lin     const auto error = DeleteForwardPortWithAdb(port, m_device_id);
1821c1d76b3SOleksiy Vyalov     if (error.Fail()) {
1831c1d76b3SOleksiy Vyalov         if (log)
1841c1d76b3SOleksiy Vyalov             log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
1853ea689b3SChaoren Lin                          pid, port, m_device_id.c_str(), error.AsCString());
1861c1d76b3SOleksiy Vyalov     }
1871c1d76b3SOleksiy Vyalov     m_port_forwards.erase(it);
18800e305d2STamas Berghammer }
18954971856SOleksiy Vyalov 
190e7eabbb5SOleksiy Vyalov Error
1919fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
192e7eabbb5SOleksiy Vyalov                                                const uint16_t remote_port,
1939fe526c2SOleksiy Vyalov                                                const char* remote_socket_name,
1949fe526c2SOleksiy Vyalov                                                std::string& connect_url)
195e7eabbb5SOleksiy Vyalov {
196e7eabbb5SOleksiy Vyalov     static const int kAttempsNum = 5;
197e7eabbb5SOleksiy Vyalov 
198e7eabbb5SOleksiy Vyalov     Error error;
199e7eabbb5SOleksiy Vyalov     // There is a race possibility that somebody will occupy
200e7eabbb5SOleksiy Vyalov     // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
201e7eabbb5SOleksiy Vyalov     // adding the loop to mitigate such problem.
202e7eabbb5SOleksiy Vyalov     for (auto i = 0; i < kAttempsNum; ++i)
203e7eabbb5SOleksiy Vyalov     {
2049fe526c2SOleksiy Vyalov         uint16_t local_port = 0;
205e7eabbb5SOleksiy Vyalov         error = FindUnusedPort(local_port);
206e7eabbb5SOleksiy Vyalov         if (error.Fail())
207e7eabbb5SOleksiy Vyalov             return error;
208e7eabbb5SOleksiy Vyalov 
209e7df5f5dSOleksiy Vyalov         error = ForwardPortWithAdb(local_port,
210e7df5f5dSOleksiy Vyalov                                    remote_port,
211e7df5f5dSOleksiy Vyalov                                    remote_socket_name,
212e7df5f5dSOleksiy Vyalov                                    m_socket_namespace,
213e7df5f5dSOleksiy Vyalov                                    m_device_id);
214e7eabbb5SOleksiy Vyalov         if (error.Success())
215e7eabbb5SOleksiy Vyalov         {
216e7eabbb5SOleksiy Vyalov             m_port_forwards[pid] = local_port;
2179fe526c2SOleksiy Vyalov             std::ostringstream url_str;
2189fe526c2SOleksiy Vyalov             url_str << "connect://localhost:" << local_port;
2199fe526c2SOleksiy Vyalov             connect_url = url_str.str();
220e7eabbb5SOleksiy Vyalov             break;
221e7eabbb5SOleksiy Vyalov         }
222e7eabbb5SOleksiy Vyalov     }
223e7eabbb5SOleksiy Vyalov 
224e7eabbb5SOleksiy Vyalov     return error;
225e7eabbb5SOleksiy Vyalov }
226*ccd6cffbSTamas Berghammer 
227*ccd6cffbSTamas Berghammer lldb::ProcessSP
228*ccd6cffbSTamas Berghammer PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url,
229*ccd6cffbSTamas Berghammer                                                const char* plugin_name,
230*ccd6cffbSTamas Berghammer                                                lldb_private::Debugger &debugger,
231*ccd6cffbSTamas Berghammer                                                lldb_private::Target *target,
232*ccd6cffbSTamas Berghammer                                                lldb_private::Error &error)
233*ccd6cffbSTamas Berghammer {
234*ccd6cffbSTamas Berghammer     // We don't have the pid of the remote gdbserver when it isn't started by us but we still want
235*ccd6cffbSTamas Berghammer     // to store the list of port forwards we set up in our port forward map. Generate a fake pid for
236*ccd6cffbSTamas Berghammer     // these cases what won't collide with any other valid pid on android.
237*ccd6cffbSTamas Berghammer     static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
238*ccd6cffbSTamas Berghammer 
239*ccd6cffbSTamas Berghammer     int remote_port;
240*ccd6cffbSTamas Berghammer     std::string scheme, host, path;
241*ccd6cffbSTamas Berghammer     if (!UriParser::Parse(connect_url, scheme, host, remote_port, path))
242*ccd6cffbSTamas Berghammer     {
243*ccd6cffbSTamas Berghammer         error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
244*ccd6cffbSTamas Berghammer         return nullptr;
245*ccd6cffbSTamas Berghammer     }
246*ccd6cffbSTamas Berghammer 
247*ccd6cffbSTamas Berghammer     std::string new_connect_url;
248*ccd6cffbSTamas Berghammer     error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
249*ccd6cffbSTamas Berghammer                            (remote_port < 0) ? 0 : remote_port,
250*ccd6cffbSTamas Berghammer                            path.c_str(),
251*ccd6cffbSTamas Berghammer                            new_connect_url);
252*ccd6cffbSTamas Berghammer     if (error.Fail())
253*ccd6cffbSTamas Berghammer         return nullptr;
254*ccd6cffbSTamas Berghammer 
255*ccd6cffbSTamas Berghammer     return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(),
256*ccd6cffbSTamas Berghammer                                                    plugin_name,
257*ccd6cffbSTamas Berghammer                                                    debugger,
258*ccd6cffbSTamas Berghammer                                                    target,
259*ccd6cffbSTamas Berghammer                                                    error);
260*ccd6cffbSTamas Berghammer }
261*ccd6cffbSTamas Berghammer 
262*ccd6cffbSTamas Berghammer size_t
263*ccd6cffbSTamas Berghammer PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error)
264*ccd6cffbSTamas Berghammer {
265*ccd6cffbSTamas Berghammer     std::vector<std::string> connection_urls;
266*ccd6cffbSTamas Berghammer     GetPendingGdbServerList(connection_urls);
267*ccd6cffbSTamas Berghammer 
268*ccd6cffbSTamas Berghammer     for (size_t i = 0; i < connection_urls.size(); ++i)
269*ccd6cffbSTamas Berghammer     {
270*ccd6cffbSTamas Berghammer         ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error);
271*ccd6cffbSTamas Berghammer         if (error.Fail())
272*ccd6cffbSTamas Berghammer             return i; // We already connected to i process succsessfully
273*ccd6cffbSTamas Berghammer     }
274*ccd6cffbSTamas Berghammer     return connection_urls.size();
275*ccd6cffbSTamas Berghammer }
276