1 //===-- HostInfoBase.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 #include "lldb/Host/Config.h"
11 
12 #include "lldb/Host/FileSystem.h"
13 #include "lldb/Host/Host.h"
14 #include "lldb/Host/HostInfo.h"
15 #include "lldb/Host/HostInfoBase.h"
16 #include "lldb/Utility/ArchSpec.h"
17 #include "lldb/Utility/Log.h"
18 #include "lldb/Utility/StreamString.h"
19 
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/Triple.h"
22 #include "llvm/Support/Host.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/Support/ScopedPrinter.h"
25 #include "llvm/Support/Threading.h"
26 #include "llvm/Support/raw_ostream.h"
27 
28 #include <mutex>
29 #include <thread>
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 namespace {
35 //----------------------------------------------------------------------
36 // The HostInfoBaseFields is a work around for windows not supporting
37 // static variables correctly in a thread safe way. Really each of the
38 // variables in HostInfoBaseFields should live in the functions in which
39 // they are used and each one should be static, but the work around is
40 // in place to avoid this restriction. Ick.
41 //----------------------------------------------------------------------
42 
43 struct HostInfoBaseFields {
44   ~HostInfoBaseFields() {
45     if (m_lldb_process_tmp_dir.Exists()) {
46       // Remove the LLDB temporary directory if we have one. Set "recurse" to
47       // true to all files that were created for the LLDB process can be cleaned
48       // up.
49       llvm::sys::fs::remove_directories(m_lldb_process_tmp_dir.GetPath());
50     }
51   }
52 
53   std::string m_host_triple;
54 
55   ArchSpec m_host_arch_32;
56   ArchSpec m_host_arch_64;
57 
58   FileSpec m_lldb_so_dir;
59   FileSpec m_lldb_support_exe_dir;
60   FileSpec m_lldb_headers_dir;
61   FileSpec m_lldb_python_dir;
62   FileSpec m_lldb_clang_resource_dir;
63   FileSpec m_lldb_system_plugin_dir;
64   FileSpec m_lldb_user_plugin_dir;
65   FileSpec m_lldb_process_tmp_dir;
66   FileSpec m_lldb_global_tmp_dir;
67 };
68 
69 HostInfoBaseFields *g_fields = nullptr;
70 }
71 
72 void HostInfoBase::Initialize() { g_fields = new HostInfoBaseFields(); }
73 
74 void HostInfoBase::Terminate() {
75   delete g_fields;
76   g_fields = nullptr;
77 }
78 
79 llvm::StringRef HostInfoBase::GetTargetTriple() {
80   static llvm::once_flag g_once_flag;
81   llvm::call_once(g_once_flag, []() {
82     g_fields->m_host_triple =
83         HostInfo::GetArchitecture().GetTriple().getTriple();
84   });
85   return g_fields->m_host_triple;
86 }
87 
88 const ArchSpec &HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) {
89   static llvm::once_flag g_once_flag;
90   llvm::call_once(g_once_flag, []() {
91     HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32,
92                                              g_fields->m_host_arch_64);
93   });
94 
95   // If an explicit 32 or 64-bit architecture was requested, return that.
96   if (arch_kind == eArchKind32)
97     return g_fields->m_host_arch_32;
98   if (arch_kind == eArchKind64)
99     return g_fields->m_host_arch_64;
100 
101   // Otherwise prefer the 64-bit architecture if it is valid.
102   return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64
103                                               : g_fields->m_host_arch_32;
104 }
105 
106 llvm::Optional<HostInfoBase::ArchitectureKind> HostInfoBase::ParseArchitectureKind(llvm::StringRef kind) {
107   return llvm::StringSwitch<llvm::Optional<ArchitectureKind>>(kind)
108       .Case(LLDB_ARCH_DEFAULT, eArchKindDefault)
109       .Case(LLDB_ARCH_DEFAULT_32BIT, eArchKind32)
110       .Case(LLDB_ARCH_DEFAULT_64BIT, eArchKind64)
111       .Default(llvm::None);
112 }
113 
114 bool HostInfoBase::GetLLDBPath(lldb::PathType type, FileSpec &file_spec) {
115   file_spec.Clear();
116 
117 #if defined(LLDB_DISABLE_PYTHON)
118   if (type == lldb::ePathTypePythonDir)
119     return false;
120 #endif
121 
122   FileSpec *result = nullptr;
123   switch (type) {
124   case lldb::ePathTypeLLDBShlibDir: {
125     static llvm::once_flag g_once_flag;
126     static bool success = false;
127     llvm::call_once(g_once_flag, []() {
128       success =
129           HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir);
130       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
131       if (log)
132         log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBShlibDir) => '%s'",
133                     g_fields->m_lldb_so_dir.GetPath().c_str());
134     });
135     if (success)
136       result = &g_fields->m_lldb_so_dir;
137   } break;
138   case lldb::ePathTypeSupportExecutableDir: {
139     static llvm::once_flag g_once_flag;
140     static bool success = false;
141     llvm::call_once(g_once_flag, []() {
142       success = HostInfo::ComputeSupportExeDirectory(
143           g_fields->m_lldb_support_exe_dir);
144       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
145       if (log)
146         log->Printf(
147             "HostInfoBase::GetLLDBPath(ePathTypeSupportExecutableDir) => '%s'",
148             g_fields->m_lldb_support_exe_dir.GetPath().c_str());
149     });
150     if (success)
151       result = &g_fields->m_lldb_support_exe_dir;
152   } break;
153   case lldb::ePathTypeHeaderDir: {
154     static llvm::once_flag g_once_flag;
155     static bool success = false;
156     llvm::call_once(g_once_flag, []() {
157       success = HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir);
158       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
159       if (log)
160         log->Printf("HostInfoBase::GetLLDBPath(ePathTypeHeaderDir) => '%s'",
161                     g_fields->m_lldb_headers_dir.GetPath().c_str());
162     });
163     if (success)
164       result = &g_fields->m_lldb_headers_dir;
165   } break;
166   case lldb::ePathTypePythonDir: {
167     static llvm::once_flag g_once_flag;
168     static bool success = false;
169     llvm::call_once(g_once_flag, []() {
170       success = HostInfo::ComputePythonDirectory(g_fields->m_lldb_python_dir);
171       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
172       if (log)
173         log->Printf("HostInfoBase::GetLLDBPath(ePathTypePythonDir) => '%s'",
174                     g_fields->m_lldb_python_dir.GetPath().c_str());
175     });
176     if (success)
177       result = &g_fields->m_lldb_python_dir;
178   } break;
179   case lldb::ePathTypeClangDir: {
180     static llvm::once_flag g_once_flag;
181     static bool success = false;
182     llvm::call_once(g_once_flag, []() {
183       success =
184           HostInfo::ComputeClangDirectory(g_fields->m_lldb_clang_resource_dir);
185       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
186       if (log)
187         log->Printf(
188             "HostInfoBase::GetLLDBPath(ePathTypeClangResourceDir) => '%s'",
189             g_fields->m_lldb_clang_resource_dir.GetPath().c_str());
190     });
191     if (success)
192       result = &g_fields->m_lldb_clang_resource_dir;
193   } break;
194   case lldb::ePathTypeLLDBSystemPlugins: {
195     static llvm::once_flag g_once_flag;
196     static bool success = false;
197     llvm::call_once(g_once_flag, []() {
198       success = HostInfo::ComputeSystemPluginsDirectory(
199           g_fields->m_lldb_system_plugin_dir);
200       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
201       if (log)
202         log->Printf(
203             "HostInfoBase::GetLLDBPath(ePathTypeLLDBSystemPlugins) => '%s'",
204             g_fields->m_lldb_system_plugin_dir.GetPath().c_str());
205     });
206     if (success)
207       result = &g_fields->m_lldb_system_plugin_dir;
208   } break;
209   case lldb::ePathTypeLLDBUserPlugins: {
210     static llvm::once_flag g_once_flag;
211     static bool success = false;
212     llvm::call_once(g_once_flag, []() {
213       success = HostInfo::ComputeUserPluginsDirectory(
214           g_fields->m_lldb_user_plugin_dir);
215       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
216       if (log)
217         log->Printf(
218             "HostInfoBase::GetLLDBPath(ePathTypeLLDBUserPlugins) => '%s'",
219             g_fields->m_lldb_user_plugin_dir.GetPath().c_str());
220     });
221     if (success)
222       result = &g_fields->m_lldb_user_plugin_dir;
223   } break;
224   case lldb::ePathTypeLLDBTempSystemDir: {
225     static llvm::once_flag g_once_flag;
226     static bool success = false;
227     llvm::call_once(g_once_flag, []() {
228       success = HostInfo::ComputeProcessTempFileDirectory(
229           g_fields->m_lldb_process_tmp_dir);
230       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
231       if (log)
232         log->Printf(
233             "HostInfoBase::GetLLDBPath(ePathTypeLLDBTempSystemDir) => '%s'",
234             g_fields->m_lldb_process_tmp_dir.GetPath().c_str());
235     });
236     if (success)
237       result = &g_fields->m_lldb_process_tmp_dir;
238   } break;
239   case lldb::ePathTypeGlobalLLDBTempSystemDir: {
240     static llvm::once_flag g_once_flag;
241     static bool success = false;
242     llvm::call_once(g_once_flag, []() {
243       success = HostInfo::ComputeGlobalTempFileDirectory(
244           g_fields->m_lldb_global_tmp_dir);
245       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
246       if (log)
247         log->Printf("HostInfoBase::GetLLDBPath("
248                     "ePathTypeGlobalLLDBTempSystemDir) => '%s'",
249                     g_fields->m_lldb_global_tmp_dir.GetPath().c_str());
250     });
251     if (success)
252       result = &g_fields->m_lldb_global_tmp_dir;
253   } break;
254   }
255 
256   if (!result)
257     return false;
258   file_spec = *result;
259   return true;
260 }
261 
262 ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
263   if (triple.empty())
264     return ArchSpec();
265   llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
266   if (!ArchSpec::ContainsOnlyArch(normalized_triple))
267     return ArchSpec(triple);
268 
269   if (auto kind = HostInfo::ParseArchitectureKind(triple))
270     return HostInfo::GetArchitecture(*kind);
271 
272   llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
273 
274   if (normalized_triple.getVendorName().empty())
275     normalized_triple.setVendor(host_triple.getVendor());
276   if (normalized_triple.getOSName().empty())
277     normalized_triple.setOS(host_triple.getOS());
278   if (normalized_triple.getEnvironmentName().empty())
279     normalized_triple.setEnvironment(host_triple.getEnvironment());
280   return ArchSpec(normalized_triple);
281 }
282 
283 bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
284   // To get paths related to LLDB we get the path to the executable that
285   // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB",
286   // on linux this is assumed to be the "lldb" main executable. If LLDB on
287   // linux is actually in a shared library (liblldb.so) then this function will
288   // need to be modified to "do the right thing".
289 
290   FileSpec lldb_file_spec(
291       Host::GetModuleFileSpecForHostAddress(reinterpret_cast<void *>(
292           reinterpret_cast<intptr_t>(HostInfoBase::GetLLDBPath))));
293 
294   // This is necessary because when running the testsuite the shlib might be a
295   // symbolic link inside the Python resource dir.
296   FileSystem::ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
297 
298   // Remove the filename so that this FileSpec only represents the directory.
299   file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
300 
301   return (bool)file_spec.GetDirectory();
302 }
303 
304 bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
305   return GetLLDBPath(lldb::ePathTypeLLDBShlibDir, file_spec);
306 }
307 
308 bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
309   FileSpec temp_file_spec;
310   if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
311     return false;
312 
313   std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
314   temp_file_spec.AppendPathComponent(pid_str);
315   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
316     return false;
317 
318   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
319   return true;
320 }
321 
322 bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
323   llvm::SmallVector<char, 16> tmpdir;
324   llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
325   file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()), true);
326   return true;
327 }
328 
329 bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
330   file_spec.Clear();
331 
332   FileSpec temp_file_spec;
333   if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
334     return false;
335 
336   temp_file_spec.AppendPathComponent("lldb");
337   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
338     return false;
339 
340   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
341   return true;
342 }
343 
344 bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
345   // TODO(zturner): Figure out how to compute the header directory for all
346   // platforms.
347   return false;
348 }
349 
350 bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
351   // TODO(zturner): Figure out how to compute the system plugins directory for
352   // all platforms.
353   return false;
354 }
355 
356 bool HostInfoBase::ComputeClangDirectory(FileSpec &file_spec) { return false; }
357 
358 bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
359   // TODO(zturner): Figure out how to compute the user plugins directory for all
360   // platforms.
361   return false;
362 }
363 
364 void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
365                                                   ArchSpec &arch_64) {
366   llvm::Triple triple(llvm::sys::getProcessTriple());
367 
368   arch_32.Clear();
369   arch_64.Clear();
370 
371   switch (triple.getArch()) {
372   default:
373     arch_32.SetTriple(triple);
374     break;
375 
376   case llvm::Triple::aarch64:
377   case llvm::Triple::ppc64:
378   case llvm::Triple::x86_64:
379     arch_64.SetTriple(triple);
380     arch_32.SetTriple(triple.get32BitArchVariant());
381     break;
382 
383   case llvm::Triple::mips64:
384   case llvm::Triple::mips64el:
385   case llvm::Triple::sparcv9:
386   case llvm::Triple::systemz:
387     arch_64.SetTriple(triple);
388     break;
389   }
390 }
391