/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp
Line | Count | Source (jump to first uncovered line) |
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/LLDBLog.h" |
12 | | #include "lldb/Utility/Log.h" |
13 | | #include "lldb/Utility/Status.h" |
14 | | #include "lldb/Utility/UriParser.h" |
15 | | |
16 | | #include "PlatformAndroidRemoteGDBServer.h" |
17 | | |
18 | | #include <optional> |
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 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 | 0 | std::string &device_id) { |
33 | 0 | Log *log = GetLog(LLDBLog::Platform); |
34 | |
|
35 | 0 | AdbClient adb; |
36 | 0 | auto error = AdbClient::CreateByDeviceID(device_id, adb); |
37 | 0 | if (error.Fail()) |
38 | 0 | return error; |
39 | | |
40 | 0 | device_id = adb.GetDeviceID(); |
41 | 0 | LLDB_LOGF(log, "Connected to Android device \"%s\"", device_id.c_str()); |
42 | |
|
43 | 0 | if (remote_port != 0) { |
44 | 0 | LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d", |
45 | 0 | remote_port, local_port); |
46 | 0 | return adb.SetPortForwarding(local_port, remote_port); |
47 | 0 | } |
48 | | |
49 | 0 | LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d", |
50 | 0 | remote_socket_name.str().c_str(), local_port); |
51 | |
|
52 | 0 | if (!socket_namespace) |
53 | 0 | return Status("Invalid socket namespace"); |
54 | | |
55 | 0 | return adb.SetPortForwarding(local_port, remote_socket_name, |
56 | 0 | *socket_namespace); |
57 | 0 | } |
58 | | |
59 | | static Status DeleteForwardPortWithAdb(uint16_t local_port, |
60 | 0 | const std::string &device_id) { |
61 | 0 | AdbClient adb(device_id); |
62 | 0 | return adb.DeletePortForwarding(local_port); |
63 | 0 | } |
64 | | |
65 | 0 | static Status FindUnusedPort(uint16_t &port) { |
66 | 0 | Status error; |
67 | 0 | std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false)); |
68 | 0 | if (error.Fail()) |
69 | 0 | return error; |
70 | | |
71 | 0 | error = tcp_socket->Listen("127.0.0.1:0", 1); |
72 | 0 | if (error.Success()) |
73 | 0 | port = tcp_socket->GetLocalPortNumber(); |
74 | |
|
75 | 0 | return error; |
76 | 0 | } |
77 | | |
78 | 7 | PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer() { |
79 | 7 | for (const auto &it : m_port_forwards) |
80 | 0 | DeleteForwardPortWithAdb(it.second, m_device_id); |
81 | 7 | } |
82 | | |
83 | | bool PlatformAndroidRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, |
84 | 0 | std::string &connect_url) { |
85 | 0 | assert(IsConnected()); |
86 | 0 | uint16_t remote_port = 0; |
87 | 0 | std::string socket_name; |
88 | 0 | if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port, |
89 | 0 | socket_name)) |
90 | 0 | return false; |
91 | | |
92 | 0 | Log *log = GetLog(LLDBLog::Platform); |
93 | |
|
94 | 0 | uint16_t local_port = 0; |
95 | 0 | const char *gdbstub_port = std::getenv("ANDROID_PLATFORM_LOCAL_GDB_PORT"); |
96 | 0 | if (gdbstub_port) |
97 | 0 | local_port = std::stoi(gdbstub_port); |
98 | |
|
99 | 0 | auto error = MakeConnectURL(pid, local_port, remote_port, socket_name.c_str(), |
100 | 0 | connect_url); |
101 | 0 | if (error.Success() && log) |
102 | 0 | LLDB_LOGF(log, "gdbserver connect URL: %s", connect_url.c_str()); |
103 | |
|
104 | 0 | return error.Success(); |
105 | 0 | } |
106 | | |
107 | 0 | bool PlatformAndroidRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { |
108 | 0 | assert(IsConnected()); |
109 | 0 | DeleteForwardPort(pid); |
110 | 0 | return m_gdb_client_up->KillSpawnedProcess(pid); |
111 | 0 | } |
112 | | |
113 | 0 | Status PlatformAndroidRemoteGDBServer::ConnectRemote(Args &args) { |
114 | 0 | m_device_id.clear(); |
115 | |
|
116 | 0 | if (args.GetArgumentCount() != 1) |
117 | 0 | return Status( |
118 | 0 | "\"platform connect\" takes a single argument: <connect-url>"); |
119 | | |
120 | 0 | const char *url = args.GetArgumentAtIndex(0); |
121 | 0 | if (!url) |
122 | 0 | return Status("URL is null."); |
123 | 0 | std::optional<URI> parsed_url = URI::Parse(url); |
124 | 0 | if (!parsed_url) |
125 | 0 | return Status("Invalid URL: %s", url); |
126 | 0 | if (parsed_url->hostname != "localhost") |
127 | 0 | m_device_id = parsed_url->hostname.str(); |
128 | |
|
129 | 0 | m_socket_namespace.reset(); |
130 | 0 | if (parsed_url->scheme == "unix-connect") |
131 | 0 | m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; |
132 | 0 | else if (parsed_url->scheme == "unix-abstract-connect") |
133 | 0 | m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; |
134 | |
|
135 | 0 | uint16_t local_port = 0; |
136 | 0 | const char *platform_local_port = std::getenv("ANDROID_PLATFORM_LOCAL_PORT"); |
137 | 0 | if (platform_local_port) |
138 | 0 | local_port = std::stoi(platform_local_port); |
139 | |
|
140 | 0 | std::string connect_url; |
141 | 0 | auto error = MakeConnectURL(g_remote_platform_pid, local_port, |
142 | 0 | parsed_url->port.value_or(0), parsed_url->path, |
143 | 0 | connect_url); |
144 | |
|
145 | 0 | if (error.Fail()) |
146 | 0 | return error; |
147 | | |
148 | 0 | args.ReplaceArgumentAtIndex(0, connect_url); |
149 | |
|
150 | 0 | Log *log = GetLog(LLDBLog::Platform); |
151 | 0 | LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str()); |
152 | |
|
153 | 0 | error = PlatformRemoteGDBServer::ConnectRemote(args); |
154 | 0 | if (error.Fail()) |
155 | 0 | DeleteForwardPort(g_remote_platform_pid); |
156 | |
|
157 | 0 | return error; |
158 | 0 | } |
159 | | |
160 | 0 | Status PlatformAndroidRemoteGDBServer::DisconnectRemote() { |
161 | 0 | DeleteForwardPort(g_remote_platform_pid); |
162 | 0 | return PlatformRemoteGDBServer::DisconnectRemote(); |
163 | 0 | } |
164 | | |
165 | 0 | void PlatformAndroidRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { |
166 | 0 | Log *log = GetLog(LLDBLog::Platform); |
167 | |
|
168 | 0 | auto it = m_port_forwards.find(pid); |
169 | 0 | if (it == m_port_forwards.end()) |
170 | 0 | return; |
171 | | |
172 | 0 | const auto port = it->second; |
173 | 0 | const auto error = DeleteForwardPortWithAdb(port, m_device_id); |
174 | 0 | if (error.Fail()) { |
175 | 0 | LLDB_LOGF(log, |
176 | 0 | "Failed to delete port forwarding (pid=%" PRIu64 |
177 | 0 | ", port=%d, device=%s): %s", |
178 | 0 | pid, port, m_device_id.c_str(), error.AsCString()); |
179 | 0 | } |
180 | 0 | m_port_forwards.erase(it); |
181 | 0 | } |
182 | | |
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 | 0 | std::string &connect_url) { |
187 | 0 | static const int kAttempsNum = 5; |
188 | |
|
189 | 0 | Status error; |
190 | |
|
191 | 0 | auto forward = [&](const uint16_t local, const uint16_t remote) { |
192 | 0 | error = ForwardPortWithAdb(local, remote, remote_socket_name, |
193 | 0 | m_socket_namespace, m_device_id); |
194 | 0 | if (error.Success()) { |
195 | 0 | m_port_forwards[pid] = local; |
196 | 0 | std::ostringstream url_str; |
197 | 0 | url_str << "connect://127.0.0.1:" << local; |
198 | 0 | connect_url = url_str.str(); |
199 | 0 | } |
200 | 0 | return error; |
201 | 0 | }; |
202 | |
|
203 | 0 | if (local_port != 0) |
204 | 0 | return forward(local_port, remote_port); |
205 | | |
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 | 0 | for (auto i = 0; i < kAttempsNum; ++i) { |
210 | 0 | uint16_t local_port = 0; |
211 | 0 | error = FindUnusedPort(local_port); |
212 | 0 | if (error.Fail()) |
213 | 0 | return error; |
214 | | |
215 | 0 | if (forward(local_port, remote_port).Success()) |
216 | 0 | break; |
217 | 0 | } |
218 | | |
219 | 0 | return error; |
220 | 0 | } |
221 | | |
222 | | lldb::ProcessSP PlatformAndroidRemoteGDBServer::ConnectProcess( |
223 | | llvm::StringRef connect_url, llvm::StringRef plugin_name, |
224 | | lldb_private::Debugger &debugger, lldb_private::Target *target, |
225 | 0 | 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 | 0 | static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; |
231 | |
|
232 | 0 | std::optional<URI> parsed_url = URI::Parse(connect_url); |
233 | 0 | if (!parsed_url) { |
234 | 0 | error.SetErrorStringWithFormat("Invalid URL: %s", |
235 | 0 | connect_url.str().c_str()); |
236 | 0 | return nullptr; |
237 | 0 | } |
238 | | |
239 | 0 | std::string new_connect_url; |
240 | 0 | error = MakeConnectURL(s_remote_gdbserver_fake_pid--, 0, |
241 | 0 | parsed_url->port.value_or(0), parsed_url->path, |
242 | 0 | new_connect_url); |
243 | 0 | if (error.Fail()) |
244 | 0 | return nullptr; |
245 | | |
246 | 0 | return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, |
247 | 0 | debugger, target, error); |
248 | 0 | } |