1 //===-- PlatformAndroidRemoteGDBServer.cpp ----------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // Other libraries and framework includes
11 #include "lldb/Core/Error.h"
12 #include "lldb/Core/Log.h"
13 #include "lldb/Host/ConnectionFileDescriptor.h"
14 #include "lldb/Host/common/TCPSocket.h"
15 
16 #include "PlatformAndroidRemoteGDBServer.h"
17 #include "Utility/UriParser.h"
18 
19 #include <sstream>
20 
21 using namespace lldb;
22 using namespace lldb_private;
23 using namespace platform_android;
24 
25 static const lldb::pid_t g_remote_platform_pid =
26     0; // Alias for the process id of lldb-platform
27 
28 static Error ForwardPortWithAdb(
29     const uint16_t local_port, const uint16_t remote_port,
30     const char *remote_socket_name,
31     const llvm::Optional<AdbClient::UnixSocketNamespace> &socket_namespace,
32     std::string &device_id) {
33   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
34 
35   AdbClient adb;
36   auto error = AdbClient::CreateByDeviceID(device_id, adb);
37   if (error.Fail())
38     return error;
39 
40   device_id = adb.GetDeviceID();
41   if (log)
42     log->Printf("Connected to Android device \"%s\"", device_id.c_str());
43 
44   if (remote_port != 0) {
45     if (log)
46       log->Printf("Forwarding remote TCP port %d to local TCP port %d",
47                   remote_port, local_port);
48     return adb.SetPortForwarding(local_port, remote_port);
49   }
50 
51   if (log)
52     log->Printf("Forwarding remote socket \"%s\" to local TCP port %d",
53                 remote_socket_name, local_port);
54 
55   if (!socket_namespace)
56     return Error("Invalid socket namespace");
57 
58   return adb.SetPortForwarding(local_port, remote_socket_name,
59                                *socket_namespace);
60 }
61 
62 static Error DeleteForwardPortWithAdb(uint16_t local_port,
63                                       const std::string &device_id) {
64   AdbClient adb(device_id);
65   return adb.DeletePortForwarding(local_port);
66 }
67 
68 static Error FindUnusedPort(uint16_t &port) {
69   Error error;
70   std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error));
71   if (error.Fail())
72     return error;
73 
74   error = tcp_socket->Listen("127.0.0.1:0", 1);
75   if (error.Success())
76     port = tcp_socket->GetLocalPortNumber();
77 
78   return error;
79 }
80 
81 PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer() {}
82 
83 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() {
84   for (const auto &it : m_port_forwards)
85     DeleteForwardPortWithAdb(it.second, m_device_id);
86 }
87 
88 bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid,
89                                                      std::string &connect_url) {
90   uint16_t remote_port = 0;
91   std::string socket_name;
92   if (!m_gdb_client.LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name))
93     return false;
94 
95   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
96 
97   auto error =
98       MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url);
99   if (error.Success() && log)
100     log->Printf("gdbserver connect URL: %s", connect_url.c_str());
101 
102   return error.Success();
103 }
104 
105 bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) {
106   DeleteForwardPort(pid);
107   return m_gdb_client.KillSpawnedProcess(pid);
108 }
109 
110 Error PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) {
111   m_device_id.clear();
112 
113   if (args.GetArgumentCount() != 1)
114     return Error("\"platform connect\" takes a single argument: <connect-url>");
115 
116   int remote_port;
117   std::string scheme, host, path;
118   const char *url = args.GetArgumentAtIndex(0);
119   if (!url)
120     return Error("URL is null.");
121   if (!UriParser::Parse(url, scheme, host, remote_port, path))
122     return Error("Invalid URL: %s", url);
123   if (host != "localhost")
124     m_device_id = host;
125 
126   m_socket_namespace.reset();
127   if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
128     m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
129   else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
130     m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
131 
132   std::string connect_url;
133   auto error =
134       MakeConnectURL(g_remote_platform_pid, (remote_port < 0) ? 0 : remote_port,
135                      path.c_str(), connect_url);
136 
137   if (error.Fail())
138     return error;
139 
140   args.ReplaceArgumentAtIndex(0, connect_url.c_str());
141 
142   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
143   if (log)
144     log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
145 
146   error = PlatformRemoteGDBServer::ConnectRemote(args);
147   if (error.Fail())
148     DeleteForwardPort(g_remote_platform_pid);
149 
150   return error;
151 }
152 
153 Error PlatformAndroidRemoteGDBServer::DisconnectRemote() {
154   DeleteForwardPort(g_remote_platform_pid);
155   return PlatformRemoteGDBServer::DisconnectRemote();
156 }
157 
158 void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) {
159   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
160 
161   auto it = m_port_forwards.find(pid);
162   if (it == m_port_forwards.end())
163     return;
164 
165   const auto port = it->second;
166   const auto error = DeleteForwardPortWithAdb(port, m_device_id);
167   if (error.Fail()) {
168     if (log)
169       log->Printf("Failed to delete port forwarding (pid=%" PRIu64
170                   ", port=%d, device=%s): %s",
171                   pid, port, m_device_id.c_str(), error.AsCString());
172   }
173   m_port_forwards.erase(it);
174 }
175 
176 Error PlatformAndroidRemoteGDBServer::MakeConnectURL(
177     const lldb::pid_t pid, const uint16_t remote_port,
178     const char *remote_socket_name, std::string &connect_url) {
179   static const int kAttempsNum = 5;
180 
181   Error error;
182   // There is a race possibility that somebody will occupy
183   // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
184   // adding the loop to mitigate such problem.
185   for (auto i = 0; i < kAttempsNum; ++i) {
186     uint16_t local_port = 0;
187     error = FindUnusedPort(local_port);
188     if (error.Fail())
189       return error;
190 
191     error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name,
192                                m_socket_namespace, m_device_id);
193     if (error.Success()) {
194       m_port_forwards[pid] = local_port;
195       std::ostringstream url_str;
196       url_str << "connect://localhost:" << local_port;
197       connect_url = url_str.str();
198       break;
199     }
200   }
201 
202   return error;
203 }
204 
205 lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess(
206     const char *connect_url, const char *plugin_name,
207     lldb_private::Debugger &debugger, lldb_private::Target *target,
208     lldb_private::Error &error) {
209   // We don't have the pid of the remote gdbserver when it isn't started by us
210   // but we still want
211   // to store the list of port forwards we set up in our port forward map.
212   // Generate a fake pid for
213   // these cases what won't collide with any other valid pid on android.
214   static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
215 
216   int remote_port;
217   std::string scheme, host, path;
218   if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) {
219     error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
220     return nullptr;
221   }
222 
223   std::string new_connect_url;
224   error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
225                          (remote_port < 0) ? 0 : remote_port, path.c_str(),
226                          new_connect_url);
227   if (error.Fail())
228     return nullptr;
229 
230   return PlatformRemoteGDBServer::ConnectProcess(
231       new_connect_url.c_str(), plugin_name, debugger, target, error);
232 }
233