1 //===-- PlatformAndroid.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 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "lldb/Core/Log.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/PluginManager.h"
16 #include "lldb/Core/Section.h"
17 #include "lldb/Host/HostInfo.h"
18 #include "lldb/Host/StringConvert.h"
19 #include "Utility/UriParser.h"
20 
21 // Project includes
22 #include "AdbClient.h"
23 #include "PlatformAndroid.h"
24 #include "PlatformAndroidRemoteGDBServer.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 using namespace lldb_private::platform_android;
29 
30 static uint32_t g_initialize_count = 0;
31 
32 void
33 PlatformAndroid::Initialize ()
34 {
35     PlatformLinux::Initialize ();
36 
37     if (g_initialize_count++ == 0)
38     {
39 #if defined(__ANDROID__)
40         PlatformSP default_platform_sp (new PlatformAndroid(true));
41         default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
42         Platform::SetHostPlatform (default_platform_sp);
43 #endif
44         PluginManager::RegisterPlugin (PlatformAndroid::GetPluginNameStatic(false),
45                                        PlatformAndroid::GetPluginDescriptionStatic(false),
46                                        PlatformAndroid::CreateInstance);
47     }
48 }
49 
50 void
51 PlatformAndroid::Terminate ()
52 {
53     if (g_initialize_count > 0)
54     {
55         if (--g_initialize_count == 0)
56         {
57             PluginManager::UnregisterPlugin (PlatformAndroid::CreateInstance);
58         }
59     }
60 
61     PlatformLinux::Terminate ();
62 }
63 
64 PlatformSP
65 PlatformAndroid::CreateInstance (bool force, const ArchSpec *arch)
66 {
67     Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
68     if (log)
69     {
70         const char *arch_name;
71         if (arch && arch->GetArchitectureName ())
72             arch_name = arch->GetArchitectureName ();
73         else
74             arch_name = "<null>";
75 
76         const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : "<null>";
77 
78         log->Printf ("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr);
79     }
80 
81     bool create = force;
82     if (create == false && arch && arch->IsValid())
83     {
84         const llvm::Triple &triple = arch->GetTriple();
85         switch (triple.getVendor())
86         {
87             case llvm::Triple::PC:
88                 create = true;
89                 break;
90 
91 #if defined(__ANDROID__)
92             // Only accept "unknown" for the vendor if the host is android and
93             // it "unknown" wasn't specified (it was just returned because it
94             // was NOT specified_
95             case llvm::Triple::VendorType::UnknownVendor:
96                 create = !arch->TripleVendorWasSpecified();
97                 break;
98 #endif
99             default:
100                 break;
101         }
102 
103         if (create)
104         {
105             switch (triple.getOS())
106             {
107                 case llvm::Triple::Android:
108                     break;
109 
110 #if defined(__ANDROID__)
111                 // Only accept "unknown" for the OS if the host is android and
112                 // it "unknown" wasn't specified (it was just returned because it
113                 // was NOT specified)
114                 case llvm::Triple::OSType::UnknownOS:
115                     create = !arch->TripleOSWasSpecified();
116                     break;
117 #endif
118                 default:
119                     create = false;
120                     break;
121             }
122         }
123     }
124 
125     if (create)
126     {
127         if (log)
128             log->Printf ("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__);
129         return PlatformSP(new PlatformAndroid(false));
130     }
131 
132     if (log)
133         log->Printf ("PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__);
134 
135     return PlatformSP();
136 }
137 
138 PlatformAndroid::PlatformAndroid (bool is_host) :
139     PlatformLinux(is_host),
140     m_sdk_version(0)
141 {
142 }
143 
144 PlatformAndroid::~PlatformAndroid()
145 {
146 }
147 
148 ConstString
149 PlatformAndroid::GetPluginNameStatic (bool is_host)
150 {
151     if (is_host)
152     {
153         static ConstString g_host_name(Platform::GetHostPlatformName ());
154         return g_host_name;
155     }
156     else
157     {
158         static ConstString g_remote_name("remote-android");
159         return g_remote_name;
160     }
161 }
162 
163 const char *
164 PlatformAndroid::GetPluginDescriptionStatic (bool is_host)
165 {
166     if (is_host)
167         return "Local Android user platform plug-in.";
168     else
169         return "Remote Android user platform plug-in.";
170 }
171 
172 ConstString
173 PlatformAndroid::GetPluginName()
174 {
175     return GetPluginNameStatic(IsHost());
176 }
177 
178 Error
179 PlatformAndroid::ConnectRemote(Args& args)
180 {
181     m_device_id.clear();
182 
183     if (IsHost())
184     {
185         return Error ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
186     }
187 
188     if (!m_remote_platform_sp)
189         m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
190 
191     int port;
192     std::string scheme, host, path;
193     const char *url = args.GetArgumentAtIndex(0);
194     if (!url)
195         return Error("URL is null.");
196     if (!UriParser::Parse(url, scheme, host, port, path))
197         return Error("Invalid URL: %s", url);
198     if (scheme == "adb")
199         m_device_id = host;
200 
201     auto error = PlatformLinux::ConnectRemote(args);
202     if (error.Success())
203     {
204         AdbClient adb;
205         error = AdbClient::CreateByDeviceID(m_device_id, adb);
206         if (error.Fail())
207             return error;
208 
209         m_device_id = adb.GetDeviceID();
210     }
211     return error;
212 }
213 
214 Error
215 PlatformAndroid::GetFile (const FileSpec& source,
216                           const FileSpec& destination)
217 {
218     if (IsHost() || !m_remote_platform_sp)
219         return PlatformLinux::GetFile(source, destination);
220 
221     FileSpec source_spec (source.GetPath (false), false, FileSpec::ePathSyntaxPosix);
222     if (source_spec.IsRelative())
223         source_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (source_spec.GetCString (false));
224 
225     AdbClient adb (m_device_id);
226     return adb.PullFile (source_spec, destination);
227 }
228 
229 Error
230 PlatformAndroid::PutFile (const FileSpec& source,
231                           const FileSpec& destination,
232                           uint32_t uid,
233                           uint32_t gid)
234 {
235     if (IsHost() || !m_remote_platform_sp)
236         return PlatformLinux::PutFile (source, destination, uid, gid);
237 
238     FileSpec destination_spec (destination.GetPath (false), false, FileSpec::ePathSyntaxPosix);
239     if (destination_spec.IsRelative())
240         destination_spec = GetRemoteWorkingDirectory ().CopyByAppendingPathComponent (destination_spec.GetCString (false));
241 
242     AdbClient adb (m_device_id);
243     // TODO: Set correct uid and gid on remote file.
244     return adb.PushFile(source, destination_spec);
245 }
246 
247 const char *
248 PlatformAndroid::GetCacheHostname ()
249 {
250     return m_device_id.c_str ();
251 }
252 
253 Error
254 PlatformAndroid::DownloadModuleSlice (const FileSpec &src_file_spec,
255                                       const uint64_t src_offset,
256                                       const uint64_t src_size,
257                                       const FileSpec &dst_file_spec)
258 {
259     if (src_offset != 0)
260         return Error ("Invalid offset - %" PRIu64, src_offset);
261 
262     return GetFile (src_file_spec, dst_file_spec);
263 }
264 
265 Error
266 PlatformAndroid::DisconnectRemote()
267 {
268     Error error = PlatformLinux::DisconnectRemote();
269     if (error.Success())
270     {
271         m_device_id.clear();
272         m_sdk_version = 0;
273     }
274     return error;
275 }
276 
277 uint32_t
278 PlatformAndroid::GetSdkVersion()
279 {
280     if (!IsConnected())
281         return 0;
282 
283     if (m_sdk_version != 0)
284         return m_sdk_version;
285 
286     int status = 0;
287     std::string version_string;
288     Error error = RunShellCommand("getprop ro.build.version.sdk",
289                                   GetWorkingDirectory(),
290                                   &status,
291                                   nullptr,
292                                   &version_string,
293                                   1);
294     if (error.Fail() || status != 0 || version_string.empty())
295     {
296         Log* log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM);
297         if (log)
298             log->Printf("Get SDK version failed. (status: %d, error: %s, output: %s)",
299                         status, error.AsCString(), version_string.c_str());
300         return 0;
301     }
302     version_string.erase(version_string.size() - 1); // Remove trailing new line
303 
304     m_sdk_version = StringConvert::ToUInt32(version_string.c_str());
305     return m_sdk_version;
306 }
307 
308 Error
309 PlatformAndroid::DownloadSymbolFile (const lldb::ModuleSP& module_sp,
310                                      const FileSpec& dst_file_spec)
311 {
312     // For oat file we can try to fetch additional debug info from the device
313     if (module_sp->GetFileSpec().GetFileNameExtension() != ConstString("oat"))
314         return Error("Symbol file downloading only supported for oat files");
315 
316     // If we have no information about the platform file we can't execute oatdump
317     if (!module_sp->GetPlatformFileSpec())
318         return Error("No platform file specified");
319 
320     // Symbolizer isn't available before SDK version 23
321     if (GetSdkVersion() < 23)
322         return Error("Symbol file generation only supported on SDK 23+");
323 
324     // If we already have symtab then we don't have to try and generate one
325     if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr)
326         return Error("Symtab already available in the module");
327 
328     AdbClient adb(m_device_id);
329 
330     std::string tmpdir;
331     Error error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", 5000 /* ms */, &tmpdir);
332     if (error.Fail() || tmpdir.empty())
333         return Error("Failed to generate temporary directory on the device (%s)", error.AsCString());
334     tmpdir.erase(tmpdir.size() - 1); // Remove trailing new line
335 
336     // Create file remover for the temporary directory created on the device
337     std::unique_ptr<std::string, std::function<void(std::string*)>> tmpdir_remover(
338         &tmpdir,
339         [this, &adb](std::string* s) {
340             StreamString command;
341             command.Printf("rm -rf %s", s->c_str());
342             Error error = adb.Shell(command.GetData(), 5000 /* ms */, nullptr);
343 
344             Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
345             if (error.Fail())
346                 log->Printf("Failed to remove temp directory: %s", error.AsCString());
347         }
348     );
349 
350     FileSpec symfile_platform_filespec(tmpdir.c_str(), false);
351     symfile_platform_filespec.AppendPathComponent("symbolized.oat");
352 
353     // Execute oatdump on the remote device to generate a file with symtab
354     StreamString command;
355     command.Printf("oatdump --symbolize=%s --output=%s",
356                    module_sp->GetPlatformFileSpec().GetCString(false),
357                    symfile_platform_filespec.GetCString(false));
358     error = adb.Shell(command.GetData(), 60000 /* ms */, nullptr);
359     if (error.Fail())
360         return Error("Oatdump failed: %s", error.AsCString());
361 
362     // Download the symbolfile from the remote device
363     return GetFile(symfile_platform_filespec, dst_file_spec);
364 }
365