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