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