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"
14*6e181cf3SOleksiy Vyalov #include "lldb/Host/ConnectionFileDescriptor.h"
15*6e181cf3SOleksiy 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 
2500e305d2STamas Berghammer static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
2600e305d2STamas Berghammer 
2700e305d2STamas Berghammer static Error
289fe526c2SOleksiy Vyalov ForwardPortWithAdb (const uint16_t local_port,
299fe526c2SOleksiy Vyalov                     const uint16_t remote_port,
309fe526c2SOleksiy Vyalov                     const char* remote_socket_name,
31e7df5f5dSOleksiy Vyalov                     const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace,
329fe526c2SOleksiy Vyalov                     std::string& device_id)
3300e305d2STamas Berghammer {
34db264a6dSTamas Berghammer     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
3500e305d2STamas Berghammer 
3605a55de3SOleksiy Vyalov     AdbClient adb;
373ea689b3SChaoren Lin     auto error = AdbClient::CreateByDeviceID(device_id, adb);
3800e305d2STamas Berghammer     if (error.Fail ())
3900e305d2STamas Berghammer         return error;
4000e305d2STamas Berghammer 
41f9da9483SOleksiy Vyalov     device_id = adb.GetDeviceID();
4205a55de3SOleksiy Vyalov     if (log)
43f9da9483SOleksiy Vyalov         log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
4405a55de3SOleksiy Vyalov 
459fe526c2SOleksiy Vyalov     if (remote_port != 0)
469fe526c2SOleksiy Vyalov     {
479fe526c2SOleksiy Vyalov         if (log)
489fe526c2SOleksiy Vyalov             log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port);
49e7eabbb5SOleksiy Vyalov         return adb.SetPortForwarding(local_port, remote_port);
5000e305d2STamas Berghammer     }
5100e305d2STamas Berghammer 
529fe526c2SOleksiy Vyalov     if (log)
539fe526c2SOleksiy Vyalov         log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port);
54e7df5f5dSOleksiy Vyalov 
55e7df5f5dSOleksiy Vyalov     if (!socket_namespace)
56e7df5f5dSOleksiy Vyalov         return Error("Invalid socket namespace");
57e7df5f5dSOleksiy Vyalov 
58e7df5f5dSOleksiy Vyalov     return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace);
599fe526c2SOleksiy Vyalov }
609fe526c2SOleksiy Vyalov 
6100e305d2STamas Berghammer static Error
62e7eabbb5SOleksiy Vyalov DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
6300e305d2STamas Berghammer {
6405a55de3SOleksiy Vyalov     AdbClient adb (device_id);
65e7eabbb5SOleksiy Vyalov     return adb.DeletePortForwarding (local_port);
66e7eabbb5SOleksiy Vyalov }
67e7eabbb5SOleksiy Vyalov 
68e7eabbb5SOleksiy Vyalov static Error
69e7eabbb5SOleksiy Vyalov FindUnusedPort (uint16_t& port)
70e7eabbb5SOleksiy Vyalov {
71e98628ceSOleksiy Vyalov     Error error;
72e98628ceSOleksiy Vyalov     std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error));
73e98628ceSOleksiy Vyalov     if (error.Fail())
74e98628ceSOleksiy Vyalov         return error;
75e98628ceSOleksiy Vyalov 
76e98628ceSOleksiy Vyalov     error = tcp_socket->Listen("127.0.0.1:0", 1);
77e7eabbb5SOleksiy Vyalov     if (error.Success())
78e98628ceSOleksiy Vyalov         port = tcp_socket->GetLocalPortNumber();
79e98628ceSOleksiy Vyalov 
80e7eabbb5SOleksiy Vyalov     return error;
8100e305d2STamas Berghammer }
8200e305d2STamas Berghammer 
8300e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer ()
8400e305d2STamas Berghammer {
8500e305d2STamas Berghammer }
8600e305d2STamas Berghammer 
8700e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
8800e305d2STamas Berghammer {
8900e305d2STamas Berghammer     for (const auto& it : m_port_forwards)
903ea689b3SChaoren Lin         DeleteForwardPortWithAdb(it.second, m_device_id);
9100e305d2STamas Berghammer }
9200e305d2STamas Berghammer 
939fe526c2SOleksiy Vyalov bool
949fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
9500e305d2STamas Berghammer {
969fe526c2SOleksiy Vyalov     uint16_t remote_port = 0;
979fe526c2SOleksiy Vyalov     std::string socket_name;
989fe526c2SOleksiy Vyalov     if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name))
999fe526c2SOleksiy Vyalov         return false;
10000e305d2STamas Berghammer 
1019fe526c2SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1029fe526c2SOleksiy Vyalov 
1039fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (pid,
1049fe526c2SOleksiy Vyalov                                  remote_port,
1059fe526c2SOleksiy Vyalov                                  socket_name.c_str (),
1069fe526c2SOleksiy Vyalov                                  connect_url);
1079fe526c2SOleksiy Vyalov     if (error.Success() && log)
1089fe526c2SOleksiy Vyalov         log->Printf("gdbserver connect URL: %s", connect_url.c_str());
1099fe526c2SOleksiy Vyalov 
1109fe526c2SOleksiy Vyalov     return error.Success();
11100e305d2STamas Berghammer }
11200e305d2STamas Berghammer 
11300e305d2STamas Berghammer bool
11400e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
11500e305d2STamas Berghammer {
1161c1d76b3SOleksiy Vyalov     DeleteForwardPort (pid);
11700e305d2STamas Berghammer     return m_gdb_client.KillSpawnedProcess (pid);
11800e305d2STamas Berghammer }
11900e305d2STamas Berghammer 
12000e305d2STamas Berghammer Error
12100e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
12200e305d2STamas Berghammer {
1233ea689b3SChaoren Lin     m_device_id.clear();
1243ea689b3SChaoren Lin 
12500e305d2STamas Berghammer     if (args.GetArgumentCount() != 1)
12600e305d2STamas Berghammer         return Error("\"platform connect\" takes a single argument: <connect-url>");
12700e305d2STamas Berghammer 
128e7eabbb5SOleksiy Vyalov     int remote_port;
12900e305d2STamas Berghammer     std::string scheme, host, path;
13000e305d2STamas Berghammer     const char *url = args.GetArgumentAtIndex (0);
1313ea689b3SChaoren Lin     if (!url)
1323ea689b3SChaoren Lin         return Error("URL is null.");
133e7eabbb5SOleksiy Vyalov     if (!UriParser::Parse (url, scheme, host, remote_port, path))
1343ea689b3SChaoren Lin         return Error("Invalid URL: %s", url);
135a29d6475SOleksiy Vyalov     if (host != "localhost")
1363ea689b3SChaoren Lin         m_device_id = host;
13700e305d2STamas Berghammer 
138e7df5f5dSOleksiy Vyalov     m_socket_namespace.reset();
139e7df5f5dSOleksiy Vyalov     if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
140e7df5f5dSOleksiy Vyalov         m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
141e7df5f5dSOleksiy Vyalov     else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
142e7df5f5dSOleksiy Vyalov         m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
143e7df5f5dSOleksiy Vyalov 
1449fe526c2SOleksiy Vyalov     std::string connect_url;
1459fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (g_remote_platform_pid,
1469fe526c2SOleksiy Vyalov                                  (remote_port < 0) ? 0 : remote_port,
1479fe526c2SOleksiy Vyalov                                  path.c_str (),
1489fe526c2SOleksiy Vyalov                                  connect_url);
1499fe526c2SOleksiy Vyalov 
15000e305d2STamas Berghammer     if (error.Fail ())
15100e305d2STamas Berghammer         return error;
15200e305d2STamas Berghammer 
1539fe526c2SOleksiy Vyalov     args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
154e7eabbb5SOleksiy Vyalov 
155e7eabbb5SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
156e7eabbb5SOleksiy Vyalov     if (log)
1579fe526c2SOleksiy Vyalov         log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
15800e305d2STamas Berghammer 
1591c1d76b3SOleksiy Vyalov     error = PlatformRemoteGDBServer::ConnectRemote(args);
1601c1d76b3SOleksiy Vyalov     if (error.Fail ())
1611c1d76b3SOleksiy Vyalov         DeleteForwardPort (g_remote_platform_pid);
1621c1d76b3SOleksiy Vyalov 
1631c1d76b3SOleksiy Vyalov     return error;
16400e305d2STamas Berghammer }
16500e305d2STamas Berghammer 
16600e305d2STamas Berghammer Error
16700e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::DisconnectRemote ()
16800e305d2STamas Berghammer {
1691c1d76b3SOleksiy Vyalov     DeleteForwardPort (g_remote_platform_pid);
1701c1d76b3SOleksiy Vyalov     return PlatformRemoteGDBServer::DisconnectRemote ();
17100e305d2STamas Berghammer }
17200e305d2STamas Berghammer 
1731c1d76b3SOleksiy Vyalov void
1741c1d76b3SOleksiy Vyalov PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
1751c1d76b3SOleksiy Vyalov {
1761c1d76b3SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1771c1d76b3SOleksiy Vyalov 
1781c1d76b3SOleksiy Vyalov     auto it = m_port_forwards.find(pid);
1791c1d76b3SOleksiy Vyalov     if (it == m_port_forwards.end())
1801c1d76b3SOleksiy Vyalov         return;
1811c1d76b3SOleksiy Vyalov 
1823ea689b3SChaoren Lin     const auto port = it->second;
1833ea689b3SChaoren Lin     const auto error = DeleteForwardPortWithAdb(port, m_device_id);
1841c1d76b3SOleksiy Vyalov     if (error.Fail()) {
1851c1d76b3SOleksiy Vyalov         if (log)
1861c1d76b3SOleksiy Vyalov             log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
1873ea689b3SChaoren Lin                          pid, port, m_device_id.c_str(), error.AsCString());
1881c1d76b3SOleksiy Vyalov     }
1891c1d76b3SOleksiy Vyalov     m_port_forwards.erase(it);
19000e305d2STamas Berghammer }
19154971856SOleksiy Vyalov 
192e7eabbb5SOleksiy Vyalov Error
1939fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
194e7eabbb5SOleksiy Vyalov                                                const uint16_t remote_port,
1959fe526c2SOleksiy Vyalov                                                const char* remote_socket_name,
1969fe526c2SOleksiy Vyalov                                                std::string& connect_url)
197e7eabbb5SOleksiy Vyalov {
198e7eabbb5SOleksiy Vyalov     static const int kAttempsNum = 5;
199e7eabbb5SOleksiy Vyalov 
200e7eabbb5SOleksiy Vyalov     Error error;
201e7eabbb5SOleksiy Vyalov     // There is a race possibility that somebody will occupy
202e7eabbb5SOleksiy Vyalov     // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
203e7eabbb5SOleksiy Vyalov     // adding the loop to mitigate such problem.
204e7eabbb5SOleksiy Vyalov     for (auto i = 0; i < kAttempsNum; ++i)
205e7eabbb5SOleksiy Vyalov     {
2069fe526c2SOleksiy Vyalov         uint16_t local_port = 0;
207e7eabbb5SOleksiy Vyalov         error = FindUnusedPort(local_port);
208e7eabbb5SOleksiy Vyalov         if (error.Fail())
209e7eabbb5SOleksiy Vyalov             return error;
210e7eabbb5SOleksiy Vyalov 
211e7df5f5dSOleksiy Vyalov         error = ForwardPortWithAdb(local_port,
212e7df5f5dSOleksiy Vyalov                                    remote_port,
213e7df5f5dSOleksiy Vyalov                                    remote_socket_name,
214e7df5f5dSOleksiy Vyalov                                    m_socket_namespace,
215e7df5f5dSOleksiy Vyalov                                    m_device_id);
216e7eabbb5SOleksiy Vyalov         if (error.Success())
217e7eabbb5SOleksiy Vyalov         {
218e7eabbb5SOleksiy Vyalov             m_port_forwards[pid] = local_port;
2199fe526c2SOleksiy Vyalov             std::ostringstream url_str;
2209fe526c2SOleksiy Vyalov             url_str << "connect://localhost:" << local_port;
2219fe526c2SOleksiy Vyalov             connect_url = url_str.str();
222e7eabbb5SOleksiy Vyalov             break;
223e7eabbb5SOleksiy Vyalov         }
224e7eabbb5SOleksiy Vyalov     }
225e7eabbb5SOleksiy Vyalov 
226e7eabbb5SOleksiy Vyalov     return error;
227e7eabbb5SOleksiy Vyalov }
228ccd6cffbSTamas Berghammer 
229ccd6cffbSTamas Berghammer lldb::ProcessSP
230ccd6cffbSTamas Berghammer PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url,
231ccd6cffbSTamas Berghammer                                                const char* plugin_name,
232ccd6cffbSTamas Berghammer                                                lldb_private::Debugger &debugger,
233ccd6cffbSTamas Berghammer                                                lldb_private::Target *target,
234ccd6cffbSTamas Berghammer                                                lldb_private::Error &error)
235ccd6cffbSTamas Berghammer {
236ccd6cffbSTamas Berghammer     // We don't have the pid of the remote gdbserver when it isn't started by us but we still want
237ccd6cffbSTamas Berghammer     // to store the list of port forwards we set up in our port forward map. Generate a fake pid for
238ccd6cffbSTamas Berghammer     // these cases what won't collide with any other valid pid on android.
239ccd6cffbSTamas Berghammer     static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
240ccd6cffbSTamas Berghammer 
241ccd6cffbSTamas Berghammer     int remote_port;
242ccd6cffbSTamas Berghammer     std::string scheme, host, path;
243ccd6cffbSTamas Berghammer     if (!UriParser::Parse(connect_url, scheme, host, remote_port, path))
244ccd6cffbSTamas Berghammer     {
245ccd6cffbSTamas Berghammer         error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
246ccd6cffbSTamas Berghammer         return nullptr;
247ccd6cffbSTamas Berghammer     }
248ccd6cffbSTamas Berghammer 
249ccd6cffbSTamas Berghammer     std::string new_connect_url;
250ccd6cffbSTamas Berghammer     error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
251ccd6cffbSTamas Berghammer                            (remote_port < 0) ? 0 : remote_port,
252ccd6cffbSTamas Berghammer                            path.c_str(),
253ccd6cffbSTamas Berghammer                            new_connect_url);
254ccd6cffbSTamas Berghammer     if (error.Fail())
255ccd6cffbSTamas Berghammer         return nullptr;
256ccd6cffbSTamas Berghammer 
257ccd6cffbSTamas Berghammer     return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(),
258ccd6cffbSTamas Berghammer                                                    plugin_name,
259ccd6cffbSTamas Berghammer                                                    debugger,
260ccd6cffbSTamas Berghammer                                                    target,
261ccd6cffbSTamas Berghammer                                                    error);
262ccd6cffbSTamas Berghammer }
263