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