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"
1405a55de3SOleksiy Vyalov #include "AdbClient.h"
1500e305d2STamas Berghammer #include "PlatformAndroidRemoteGDBServer.h"
1600e305d2STamas Berghammer #include "Utility/UriParser.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 
2400e305d2STamas Berghammer static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
2500e305d2STamas Berghammer 
2600e305d2STamas Berghammer static Error
279fe526c2SOleksiy Vyalov ForwardPortWithAdb (const uint16_t local_port,
289fe526c2SOleksiy Vyalov                     const uint16_t remote_port,
299fe526c2SOleksiy Vyalov                     const char* remote_socket_name,
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);
529fe526c2SOleksiy Vyalov     return adb.SetPortForwarding(local_port, remote_socket_name);
539fe526c2SOleksiy Vyalov }
549fe526c2SOleksiy Vyalov 
5500e305d2STamas Berghammer static Error
56e7eabbb5SOleksiy Vyalov DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
5700e305d2STamas Berghammer {
5805a55de3SOleksiy Vyalov     AdbClient adb (device_id);
59e7eabbb5SOleksiy Vyalov     return adb.DeletePortForwarding (local_port);
60e7eabbb5SOleksiy Vyalov }
61e7eabbb5SOleksiy Vyalov 
62e7eabbb5SOleksiy Vyalov static Error
63e7eabbb5SOleksiy Vyalov FindUnusedPort (uint16_t& port)
64e7eabbb5SOleksiy Vyalov {
65e98628ceSOleksiy Vyalov     Error error;
66e98628ceSOleksiy Vyalov     std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error));
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 
7700e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer ()
7800e305d2STamas Berghammer {
7900e305d2STamas Berghammer }
8000e305d2STamas Berghammer 
8100e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
8200e305d2STamas Berghammer {
8300e305d2STamas Berghammer     for (const auto& it : m_port_forwards)
843ea689b3SChaoren Lin         DeleteForwardPortWithAdb(it.second, m_device_id);
8500e305d2STamas Berghammer }
8600e305d2STamas Berghammer 
879fe526c2SOleksiy Vyalov bool
889fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
8900e305d2STamas Berghammer {
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 
979fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (pid,
989fe526c2SOleksiy Vyalov                                  remote_port,
999fe526c2SOleksiy Vyalov                                  socket_name.c_str (),
1009fe526c2SOleksiy Vyalov                                  connect_url);
1019fe526c2SOleksiy Vyalov     if (error.Success() && log)
1029fe526c2SOleksiy Vyalov         log->Printf("gdbserver connect URL: %s", connect_url.c_str());
1039fe526c2SOleksiy Vyalov 
1049fe526c2SOleksiy Vyalov     return error.Success();
10500e305d2STamas Berghammer }
10600e305d2STamas Berghammer 
10700e305d2STamas Berghammer bool
10800e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
10900e305d2STamas Berghammer {
1101c1d76b3SOleksiy Vyalov     DeleteForwardPort (pid);
11100e305d2STamas Berghammer     return m_gdb_client.KillSpawnedProcess (pid);
11200e305d2STamas Berghammer }
11300e305d2STamas Berghammer 
11400e305d2STamas Berghammer Error
11500e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
11600e305d2STamas Berghammer {
1173ea689b3SChaoren Lin     m_device_id.clear();
1183ea689b3SChaoren Lin 
11900e305d2STamas Berghammer     if (args.GetArgumentCount() != 1)
12000e305d2STamas Berghammer         return Error("\"platform connect\" takes a single argument: <connect-url>");
12100e305d2STamas Berghammer 
122e7eabbb5SOleksiy Vyalov     int remote_port;
12300e305d2STamas Berghammer     std::string scheme, host, path;
12400e305d2STamas Berghammer     const char *url = args.GetArgumentAtIndex (0);
1253ea689b3SChaoren Lin     if (!url)
1263ea689b3SChaoren Lin         return Error("URL is null.");
127e7eabbb5SOleksiy Vyalov     if (!UriParser::Parse (url, scheme, host, remote_port, path))
1283ea689b3SChaoren Lin         return Error("Invalid URL: %s", url);
129*a29d6475SOleksiy Vyalov     if (host != "localhost")
1303ea689b3SChaoren Lin         m_device_id = host;
13100e305d2STamas Berghammer 
1329fe526c2SOleksiy Vyalov     std::string connect_url;
1339fe526c2SOleksiy Vyalov     auto error = MakeConnectURL (g_remote_platform_pid,
1349fe526c2SOleksiy Vyalov                                  (remote_port < 0) ? 0 : remote_port,
1359fe526c2SOleksiy Vyalov                                  path.c_str (),
1369fe526c2SOleksiy Vyalov                                  connect_url);
1379fe526c2SOleksiy Vyalov 
13800e305d2STamas Berghammer     if (error.Fail ())
13900e305d2STamas Berghammer         return error;
14000e305d2STamas Berghammer 
1419fe526c2SOleksiy Vyalov     args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
142e7eabbb5SOleksiy Vyalov 
143e7eabbb5SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
144e7eabbb5SOleksiy Vyalov     if (log)
1459fe526c2SOleksiy Vyalov         log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
14600e305d2STamas Berghammer 
1471c1d76b3SOleksiy Vyalov     error = PlatformRemoteGDBServer::ConnectRemote(args);
1481c1d76b3SOleksiy Vyalov     if (error.Fail ())
1491c1d76b3SOleksiy Vyalov         DeleteForwardPort (g_remote_platform_pid);
1501c1d76b3SOleksiy Vyalov 
1511c1d76b3SOleksiy Vyalov     return error;
15200e305d2STamas Berghammer }
15300e305d2STamas Berghammer 
15400e305d2STamas Berghammer Error
15500e305d2STamas Berghammer PlatformAndroidRemoteGDBServer::DisconnectRemote ()
15600e305d2STamas Berghammer {
1571c1d76b3SOleksiy Vyalov     DeleteForwardPort (g_remote_platform_pid);
1581c1d76b3SOleksiy Vyalov     return PlatformRemoteGDBServer::DisconnectRemote ();
15900e305d2STamas Berghammer }
16000e305d2STamas Berghammer 
1611c1d76b3SOleksiy Vyalov void
1621c1d76b3SOleksiy Vyalov PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
1631c1d76b3SOleksiy Vyalov {
1641c1d76b3SOleksiy Vyalov     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
1651c1d76b3SOleksiy Vyalov 
1661c1d76b3SOleksiy Vyalov     auto it = m_port_forwards.find(pid);
1671c1d76b3SOleksiy Vyalov     if (it == m_port_forwards.end())
1681c1d76b3SOleksiy Vyalov         return;
1691c1d76b3SOleksiy Vyalov 
1703ea689b3SChaoren Lin     const auto port = it->second;
1713ea689b3SChaoren Lin     const auto error = DeleteForwardPortWithAdb(port, m_device_id);
1721c1d76b3SOleksiy Vyalov     if (error.Fail()) {
1731c1d76b3SOleksiy Vyalov         if (log)
1741c1d76b3SOleksiy Vyalov             log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
1753ea689b3SChaoren Lin                          pid, port, m_device_id.c_str(), error.AsCString());
1761c1d76b3SOleksiy Vyalov     }
1771c1d76b3SOleksiy Vyalov     m_port_forwards.erase(it);
17800e305d2STamas Berghammer }
17954971856SOleksiy Vyalov 
180e7eabbb5SOleksiy Vyalov Error
1819fe526c2SOleksiy Vyalov PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
182e7eabbb5SOleksiy Vyalov                                                const uint16_t remote_port,
1839fe526c2SOleksiy Vyalov                                                const char* remote_socket_name,
1849fe526c2SOleksiy Vyalov                                                std::string& connect_url)
185e7eabbb5SOleksiy Vyalov {
186e7eabbb5SOleksiy Vyalov     static const int kAttempsNum = 5;
187e7eabbb5SOleksiy Vyalov 
188e7eabbb5SOleksiy Vyalov     Error error;
189e7eabbb5SOleksiy Vyalov     // There is a race possibility that somebody will occupy
190e7eabbb5SOleksiy Vyalov     // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
191e7eabbb5SOleksiy Vyalov     // adding the loop to mitigate such problem.
192e7eabbb5SOleksiy Vyalov     for (auto i = 0; i < kAttempsNum; ++i)
193e7eabbb5SOleksiy Vyalov     {
1949fe526c2SOleksiy Vyalov         uint16_t local_port = 0;
195e7eabbb5SOleksiy Vyalov         error = FindUnusedPort(local_port);
196e7eabbb5SOleksiy Vyalov         if (error.Fail())
197e7eabbb5SOleksiy Vyalov             return error;
198e7eabbb5SOleksiy Vyalov 
1999fe526c2SOleksiy Vyalov         error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, m_device_id);
200e7eabbb5SOleksiy Vyalov         if (error.Success())
201e7eabbb5SOleksiy Vyalov         {
202e7eabbb5SOleksiy Vyalov             m_port_forwards[pid] = local_port;
2039fe526c2SOleksiy Vyalov             std::ostringstream url_str;
2049fe526c2SOleksiy Vyalov             url_str << "connect://localhost:" << local_port;
2059fe526c2SOleksiy Vyalov             connect_url = url_str.str();
206e7eabbb5SOleksiy Vyalov             break;
207e7eabbb5SOleksiy Vyalov         }
208e7eabbb5SOleksiy Vyalov     }
209e7eabbb5SOleksiy Vyalov 
210e7eabbb5SOleksiy Vyalov     return error;
211e7eabbb5SOleksiy Vyalov }
212