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