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