1 //===-- PlatformAndroidRemoteGDBServer.cpp --------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Host/ConnectionFileDescriptor.h"
10 #include "lldb/Host/common/TCPSocket.h"
11 #include "lldb/Utility/LLDBLog.h"
12 #include "lldb/Utility/Log.h"
13 #include "lldb/Utility/Status.h"
14 #include "lldb/Utility/UriParser.h"
16 #include "PlatformAndroidRemoteGDBServer.h"
22 using namespace lldb_private
;
23 using namespace platform_android
;
25 static const lldb::pid_t g_remote_platform_pid
=
26 0; // Alias for the process id of lldb-platform
28 static Status
ForwardPortWithAdb(
29 const uint16_t local_port
, const uint16_t remote_port
,
30 llvm::StringRef remote_socket_name
,
31 const std::optional
<AdbClient::UnixSocketNamespace
> &socket_namespace
,
32 std::string
&device_id
) {
33 Log
*log
= GetLog(LLDBLog::Platform
);
36 auto error
= AdbClient::CreateByDeviceID(device_id
, adb
);
40 device_id
= adb
.GetDeviceID();
41 LLDB_LOGF(log
, "Connected to Android device \"%s\"", device_id
.c_str());
43 if (remote_port
!= 0) {
44 LLDB_LOGF(log
, "Forwarding remote TCP port %d to local TCP port %d",
45 remote_port
, local_port
);
46 return adb
.SetPortForwarding(local_port
, remote_port
);
49 LLDB_LOGF(log
, "Forwarding remote socket \"%s\" to local TCP port %d",
50 remote_socket_name
.str().c_str(), local_port
);
52 if (!socket_namespace
)
53 return Status("Invalid socket namespace");
55 return adb
.SetPortForwarding(local_port
, remote_socket_name
,
59 static Status
DeleteForwardPortWithAdb(uint16_t local_port
,
60 const std::string
&device_id
) {
61 AdbClient
adb(device_id
);
62 return adb
.DeletePortForwarding(local_port
);
65 static Status
FindUnusedPort(uint16_t &port
) {
67 std::unique_ptr
<TCPSocket
> tcp_socket(new TCPSocket(true, false));
71 error
= tcp_socket
->Listen("127.0.0.1:0", 1);
73 port
= tcp_socket
->GetLocalPortNumber();
78 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() {
79 for (const auto &it
: m_port_forwards
)
80 DeleteForwardPortWithAdb(it
.second
, m_device_id
);
83 bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t
&pid
,
84 std::string
&connect_url
) {
85 assert(IsConnected());
86 uint16_t remote_port
= 0;
87 std::string socket_name
;
88 if (!m_gdb_client_up
->LaunchGDBServer("127.0.0.1", pid
, remote_port
,
92 Log
*log
= GetLog(LLDBLog::Platform
);
94 uint16_t local_port
= 0;
95 const char *gdbstub_port
= std::getenv("ANDROID_PLATFORM_LOCAL_GDB_PORT");
97 local_port
= std::stoi(gdbstub_port
);
99 auto error
= MakeConnectURL(pid
, local_port
, remote_port
, socket_name
.c_str(),
101 if (error
.Success() && log
)
102 LLDB_LOGF(log
, "gdbserver connect URL: %s", connect_url
.c_str());
104 return error
.Success();
107 bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid
) {
108 assert(IsConnected());
109 DeleteForwardPort(pid
);
110 return m_gdb_client_up
->KillSpawnedProcess(pid
);
113 Status
PlatformAndroidRemoteGDBServer::ConnectRemote(Args
&args
) {
116 if (args
.GetArgumentCount() != 1)
118 "\"platform connect\" takes a single argument: <connect-url>");
120 const char *url
= args
.GetArgumentAtIndex(0);
122 return Status("URL is null.");
123 std::optional
<URI
> parsed_url
= URI::Parse(url
);
125 return Status("Invalid URL: %s", url
);
126 if (parsed_url
->hostname
!= "localhost")
127 m_device_id
= parsed_url
->hostname
.str();
129 m_socket_namespace
.reset();
130 if (parsed_url
->scheme
== "unix-connect")
131 m_socket_namespace
= AdbClient::UnixSocketNamespaceFileSystem
;
132 else if (parsed_url
->scheme
== "unix-abstract-connect")
133 m_socket_namespace
= AdbClient::UnixSocketNamespaceAbstract
;
135 uint16_t local_port
= 0;
136 const char *platform_local_port
= std::getenv("ANDROID_PLATFORM_LOCAL_PORT");
137 if (platform_local_port
)
138 local_port
= std::stoi(platform_local_port
);
140 std::string connect_url
;
141 auto error
= MakeConnectURL(g_remote_platform_pid
, local_port
,
142 parsed_url
->port
.value_or(0), parsed_url
->path
,
148 args
.ReplaceArgumentAtIndex(0, connect_url
);
150 Log
*log
= GetLog(LLDBLog::Platform
);
151 LLDB_LOGF(log
, "Rewritten platform connect URL: %s", connect_url
.c_str());
153 error
= PlatformRemoteGDBServer::ConnectRemote(args
);
155 DeleteForwardPort(g_remote_platform_pid
);
160 Status
PlatformAndroidRemoteGDBServer::DisconnectRemote() {
161 DeleteForwardPort(g_remote_platform_pid
);
162 return PlatformRemoteGDBServer::DisconnectRemote();
165 void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid
) {
166 Log
*log
= GetLog(LLDBLog::Platform
);
168 auto it
= m_port_forwards
.find(pid
);
169 if (it
== m_port_forwards
.end())
172 const auto port
= it
->second
;
173 const auto error
= DeleteForwardPortWithAdb(port
, m_device_id
);
176 "Failed to delete port forwarding (pid=%" PRIu64
177 ", port=%d, device=%s): %s",
178 pid
, port
, m_device_id
.c_str(), error
.AsCString());
180 m_port_forwards
.erase(it
);
183 Status
PlatformAndroidRemoteGDBServer::MakeConnectURL(
184 const lldb::pid_t pid
, const uint16_t local_port
,
185 const uint16_t remote_port
, llvm::StringRef remote_socket_name
,
186 std::string
&connect_url
) {
187 static const int kAttempsNum
= 5;
191 auto forward
= [&](const uint16_t local
, const uint16_t remote
) {
192 error
= ForwardPortWithAdb(local
, remote
, remote_socket_name
,
193 m_socket_namespace
, m_device_id
);
194 if (error
.Success()) {
195 m_port_forwards
[pid
] = local
;
196 std::ostringstream url_str
;
197 url_str
<< "connect://127.0.0.1:" << local
;
198 connect_url
= url_str
.str();
204 return forward(local_port
, remote_port
);
206 // There is a race possibility that somebody will occupy a port while we're
207 // in between FindUnusedPort and ForwardPortWithAdb - adding the loop to
208 // mitigate such problem.
209 for (auto i
= 0; i
< kAttempsNum
; ++i
) {
210 uint16_t local_port
= 0;
211 error
= FindUnusedPort(local_port
);
215 if (forward(local_port
, remote_port
).Success())
222 lldb::ProcessSP
PlatformAndroidRemoteGDBServer::ConnectProcess(
223 llvm::StringRef connect_url
, llvm::StringRef plugin_name
,
224 lldb_private::Debugger
&debugger
, lldb_private::Target
*target
,
225 lldb_private::Status
&error
) {
226 // We don't have the pid of the remote gdbserver when it isn't started by us
227 // but we still want to store the list of port forwards we set up in our port
228 // forward map. Generate a fake pid for these cases what won't collide with
229 // any other valid pid on android.
230 static lldb::pid_t s_remote_gdbserver_fake_pid
= 0xffffffffffffffffULL
;
232 std::optional
<URI
> parsed_url
= URI::Parse(connect_url
);
234 error
.SetErrorStringWithFormat("Invalid URL: %s",
235 connect_url
.str().c_str());
239 std::string new_connect_url
;
240 error
= MakeConnectURL(s_remote_gdbserver_fake_pid
--, 0,
241 parsed_url
->port
.value_or(0), parsed_url
->path
,
246 return PlatformRemoteGDBServer::ConnectProcess(new_connect_url
, plugin_name
,
247 debugger
, target
, error
);