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/Core/Error.h"
12 #include "lldb/Core/Log.h"
13 #include "lldb/Host/common/TCPSocket.h"
14 #include "AdbClient.h"
15 #include "PlatformAndroidRemoteGDBServer.h"
16 #include "Utility/UriParser.h"
17 
18 #include <sstream>
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace platform_android;
23 
24 static const lldb::pid_t g_remote_platform_pid = 0; // Alias for the process id of lldb-platform
25 
26 static Error
27 ForwardPortWithAdb (const uint16_t local_port,
28                     const uint16_t remote_port,
29                     const char* remote_socket_name,
30                     std::string& device_id)
31 {
32     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
33 
34     AdbClient adb;
35     auto error = AdbClient::CreateByDeviceID(device_id, adb);
36     if (error.Fail ())
37         return error;
38 
39     device_id = adb.GetDeviceID();
40     if (log)
41         log->Printf("Connected to Android device \"%s\"", device_id.c_str ());
42 
43     if (remote_port != 0)
44     {
45         if (log)
46             log->Printf("Forwarding remote TCP port %d to local TCP port %d", remote_port, local_port);
47         return adb.SetPortForwarding(local_port, remote_port);
48     }
49 
50     if (log)
51         log->Printf("Forwarding remote socket \"%s\" to local TCP port %d", remote_socket_name, local_port);
52     return adb.SetPortForwarding(local_port, remote_socket_name);
53 }
54 
55 static Error
56 DeleteForwardPortWithAdb (uint16_t local_port, const std::string& device_id)
57 {
58     AdbClient adb (device_id);
59     return adb.DeletePortForwarding (local_port);
60 }
61 
62 static Error
63 FindUnusedPort (uint16_t& port)
64 {
65     Error error;
66     std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error));
67     if (error.Fail())
68         return error;
69 
70     error = tcp_socket->Listen("127.0.0.1:0", 1);
71     if (error.Success())
72         port = tcp_socket->GetLocalPortNumber();
73 
74     return error;
75 }
76 
77 PlatformAndroidRemoteGDBServer::PlatformAndroidRemoteGDBServer ()
78 {
79 }
80 
81 PlatformAndroidRemoteGDBServer::~PlatformAndroidRemoteGDBServer ()
82 {
83     for (const auto& it : m_port_forwards)
84         DeleteForwardPortWithAdb(it.second, m_device_id);
85 }
86 
87 bool
88 PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url)
89 {
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 = MakeConnectURL (pid,
98                                  remote_port,
99                                  socket_name.c_str (),
100                                  connect_url);
101     if (error.Success() && log)
102         log->Printf("gdbserver connect URL: %s", connect_url.c_str());
103 
104     return error.Success();
105 }
106 
107 bool
108 PlatformAndroidRemoteGDBServer::KillSpawnedProcess (lldb::pid_t pid)
109 {
110     DeleteForwardPort (pid);
111     return m_gdb_client.KillSpawnedProcess (pid);
112 }
113 
114 Error
115 PlatformAndroidRemoteGDBServer::ConnectRemote (Args& args)
116 {
117     m_device_id.clear();
118 
119     if (args.GetArgumentCount() != 1)
120         return Error("\"platform connect\" takes a single argument: <connect-url>");
121 
122     int remote_port;
123     std::string scheme, host, path;
124     const char *url = args.GetArgumentAtIndex (0);
125     if (!url)
126         return Error("URL is null.");
127     if (!UriParser::Parse (url, scheme, host, remote_port, path))
128         return Error("Invalid URL: %s", url);
129     if (host != "localhost")
130         m_device_id = host;
131 
132     std::string connect_url;
133     auto error = MakeConnectURL (g_remote_platform_pid,
134                                  (remote_port < 0) ? 0 : remote_port,
135                                  path.c_str (),
136                                  connect_url);
137 
138     if (error.Fail ())
139         return error;
140 
141     args.ReplaceArgumentAtIndex (0, connect_url.c_str ());
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 Error
155 PlatformAndroidRemoteGDBServer::DisconnectRemote ()
156 {
157     DeleteForwardPort (g_remote_platform_pid);
158     return PlatformRemoteGDBServer::DisconnectRemote ();
159 }
160 
161 void
162 PlatformAndroidRemoteGDBServer::DeleteForwardPort (lldb::pid_t pid)
163 {
164     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
165 
166     auto it = m_port_forwards.find(pid);
167     if (it == m_port_forwards.end())
168         return;
169 
170     const auto port = it->second;
171     const auto error = DeleteForwardPortWithAdb(port, m_device_id);
172     if (error.Fail()) {
173         if (log)
174             log->Printf("Failed to delete port forwarding (pid=%" PRIu64 ", port=%d, device=%s): %s",
175                          pid, port, m_device_id.c_str(), error.AsCString());
176     }
177     m_port_forwards.erase(it);
178 }
179 
180 Error
181 PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
182                                                const uint16_t remote_port,
183                                                const char* remote_socket_name,
184                                                std::string& connect_url)
185 {
186     static const int kAttempsNum = 5;
187 
188     Error error;
189     // There is a race possibility that somebody will occupy
190     // a port while we're in between FindUnusedPort and ForwardPortWithAdb -
191     // adding the loop to mitigate such problem.
192     for (auto i = 0; i < kAttempsNum; ++i)
193     {
194         uint16_t local_port = 0;
195         error = FindUnusedPort(local_port);
196         if (error.Fail())
197             return error;
198 
199         error = ForwardPortWithAdb(local_port, remote_port, remote_socket_name, m_device_id);
200         if (error.Success())
201         {
202             m_port_forwards[pid] = local_port;
203             std::ostringstream url_str;
204             url_str << "connect://localhost:" << local_port;
205             connect_url = url_str.str();
206             break;
207         }
208     }
209 
210     return error;
211 }
212