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