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/common/TCPSocket.h"
14 #include "PlatformAndroidRemoteGDBServer.h"
15 #include "Utility/UriParser.h"
16 
17 #include <sstream>
18 
19 using namespace lldb;
20 using namespace lldb_private;
21 using namespace platform_android;
22 
23 static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
24 
25 static Error
26 ForwardPortWithAdb (const uint16_t local_port,
27                     const uint16_t remote_port,
28                     const char* remote_socket_name,
29                     const llvm::Optional<AdbClient::UnixSocketNamespace>& socket_namespace,
30                     std::string& device_id)
31 {
32     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
33 
34     AdbClient adb;
35     auto error = AdbClient::CreateByDeviceID(device_id, adb);
36     if (error.Fail ())
37         return error;
38 
39     device_id = adb.GetDeviceID();
40     if (log)
41         log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
42 
43     if (remote_port != 0)
44     {
45         if (log)
46             log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port);
47         return adb.SetPortForwarding(local_port, remote_port);
48     }
49 
50     if (log)
51         log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port);
52 
53     if (!socket_namespace)
54         return Error("Invalid socket namespace");
55 
56     return adb.SetPortForwarding(local_port, remote_socket_name, *socket_namespace);
57 }
58 
59 static Error
60 DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
61 {
62     AdbClient adb (device_id);
63     return adb.DeletePortForwarding (local_port);
64 }
65 
66 static Error
67 FindUnusedPort (uint16_t& port)
68 {
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 }
84 
85 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
86 {
87     for (const auto& it : m_port_forwards)
88         DeleteForwardPortWithAdb(it.second, m_device_id);
89 }
90 
91 bool
92 PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
93 {
94     uint16_t remote_port = 0;
95     std::string socket_name;
96     if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name))
97         return false;
98 
99     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
100 
101     auto error = MakeConnectURL (pid,
102                                  remote_port,
103                                  socket_name.c_str (),
104                                  connect_url);
105     if (error.Success() && log)
106         log->Printf("gdbserver connect URL: %s", connect_url.c_str());
107 
108     return error.Success();
109 }
110 
111 bool
112 PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
113 {
114     DeleteForwardPort (pid);
115     return m_gdb_client.KillSpawnedProcess (pid);
116 }
117 
118 Error
119 PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
120 {
121     m_device_id.clear();
122 
123     if (args.GetArgumentCount() != 1)
124         return Error("\"platform connect\" takes a single argument: <connect-url>");
125 
126     int remote_port;
127     std::string scheme, host, path;
128     const char *url = args.GetArgumentAtIndex (0);
129     if (!url)
130         return Error("URL is null.");
131     if (!UriParser::Parse (url, scheme, host, remote_port, path))
132         return Error("Invalid URL: %s", url);
133     if (host != "localhost")
134         m_device_id = host;
135 
136     m_socket_namespace.reset();
137     if (scheme == ConnectionFileDescriptor::UNIX_CONNECT_SCHEME)
138         m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem;
139     else if (scheme == ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME)
140         m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract;
141 
142     std::string connect_url;
143     auto error = MakeConnectURL (g_remote_platform_pid,
144                                  (remote_port < 0) ? 0 : remote_port,
145                                  path.c_str (),
146                                  connect_url);
147 
148     if (error.Fail ())
149         return error;
150 
151     args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
152 
153     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
154     if (log)
155         log->Printf("Rewritten platform connect URL: %s", connect_url.c_str());
156 
157     error = PlatformRemoteGDBServer::ConnectRemote(args);
158     if (error.Fail ())
159         DeleteForwardPort (g_remote_platform_pid);
160 
161     return error;
162 }
163 
164 Error
165 PlatformAndroidRemoteGDBServer::DisconnectRemote ()
166 {
167     DeleteForwardPort (g_remote_platform_pid);
168     return PlatformRemoteGDBServer::DisconnectRemote ();
169 }
170 
171 void
172 PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
173 {
174     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
175 
176     auto it = m_port_forwards.find(pid);
177     if (it == m_port_forwards.end())
178         return;
179 
180     const auto port = it->second;
181     const auto error = DeleteForwardPortWithAdb(port, m_device_id);
182     if (error.Fail()) {
183         if (log)
184             log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
185                          pid, port, m_device_id.c_str(), error.AsCString());
186     }
187     m_port_forwards.erase(it);
188 }
189 
190 Error
191 PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
192                                                const uint16_t remote_port,
193                                                const char* remote_socket_name,
194                                                std::string& connect_url)
195 {
196     static const int kAttempsNum = 5;
197 
198     Error error;
199     // There is a race possibility that somebody will occupy
200     // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
201     // adding the loop to mitigate such problem.
202     for (auto i = 0; i < kAttempsNum; ++i)
203     {
204         uint16_t local_port = 0;
205         error = FindUnusedPort(local_port);
206         if (error.Fail())
207             return error;
208 
209         error = ForwardPortWithAdb(local_port,
210                                    remote_port,
211                                    remote_socket_name,
212                                    m_socket_namespace,
213                                    m_device_id);
214         if (error.Success())
215         {
216             m_port_forwards[pid] = local_port;
217             std::ostringstream url_str;
218             url_str << "connect://localhost:" << local_port;
219             connect_url = url_str.str();
220             break;
221         }
222     }
223 
224     return error;
225 }
226 
227 lldb::ProcessSP
228 PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url,
229                                                const char* plugin_name,
230                                                lldb_private::Debugger &debugger,
231                                                lldb_private::Target *target,
232                                                lldb_private::Error &error)
233 {
234     // We don't have the pid of the remote gdbserver when it isn't started by us but we still want
235     // to store the list of port forwards we set up in our port forward map. Generate a fake pid for
236     // these cases what won't collide with any other valid pid on android.
237     static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
238 
239     int remote_port;
240     std::string scheme, host, path;
241     if (!UriParser::Parse(connect_url, scheme, host, remote_port, path))
242     {
243         error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
244         return nullptr;
245     }
246 
247     std::string new_connect_url;
248     error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
249                            (remote_port < 0) ? 0 : remote_port,
250                            path.c_str(),
251                            new_connect_url);
252     if (error.Fail())
253         return nullptr;
254 
255     return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(),
256                                                    plugin_name,
257                                                    debugger,
258                                                    target,
259                                                    error);
260 }
261 
262 size_t
263 PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error)
264 {
265     std::vector<std::string> connection_urls;
266     GetPendingGdbServerList(connection_urls);
267 
268     for (size_t i = 0; i < connection_urls.size(); ++i)
269     {
270         ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error);
271         if (error.Fail())
272             return i; // We already connected to i process succsessfully
273     }
274     return connection_urls.size();
275 }
276