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 static
37 // variables correctly in a thread safe way. Really each of the variables in
38 // HostInfoBaseFields should live in the functions in which they are used and
39 // each one should be static, but the work around is in place to avoid this
40 // 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
48       // cleaned 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   assert(type != lldb::ePathTypeClangDir);
118 
119 #if defined(LLDB_DISABLE_PYTHON)
120   if (type == lldb::ePathTypePythonDir)
121     return false;
122 #endif
123 
124   FileSpec *result = nullptr;
125   switch (type) {
126   case lldb::ePathTypeLLDBShlibDir: {
127     static llvm::once_flag g_once_flag;
128     static bool success = false;
129     llvm::call_once(g_once_flag, []() {
130       success =
131           HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir);
132       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
133       if (log)
134         log->Printf("HostInfoBase::GetLLDBPath(ePathTypeLLDBShlibDir) => '%s'",
135                     g_fields->m_lldb_so_dir.GetPath().c_str());
136     });
137     if (success)
138       result = &g_fields->m_lldb_so_dir;
139   } break;
140   case lldb::ePathTypeSupportExecutableDir: {
141     static llvm::once_flag g_once_flag;
142     static bool success = false;
143     llvm::call_once(g_once_flag, []() {
144       success = HostInfo::ComputeSupportExeDirectory(
145           g_fields->m_lldb_support_exe_dir);
146       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
147       if (log)
148         log->Printf(
149             "HostInfoBase::GetLLDBPath(ePathTypeSupportExecutableDir) => '%s'",
150             g_fields->m_lldb_support_exe_dir.GetPath().c_str());
151     });
152     if (success)
153       result = &g_fields->m_lldb_support_exe_dir;
154   } break;
155   case lldb::ePathTypeHeaderDir: {
156     static llvm::once_flag g_once_flag;
157     static bool success = false;
158     llvm::call_once(g_once_flag, []() {
159       success = HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir);
160       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
161       if (log)
162         log->Printf("HostInfoBase::GetLLDBPath(ePathTypeHeaderDir) => '%s'",
163                     g_fields->m_lldb_headers_dir.GetPath().c_str());
164     });
165     if (success)
166       result = &g_fields->m_lldb_headers_dir;
167   } break;
168   case lldb::ePathTypePythonDir: {
169     static llvm::once_flag g_once_flag;
170     static bool success = false;
171     llvm::call_once(g_once_flag, []() {
172       success = HostInfo::ComputePythonDirectory(g_fields->m_lldb_python_dir);
173       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
174       if (log)
175         log->Printf("HostInfoBase::GetLLDBPath(ePathTypePythonDir) => '%s'",
176                     g_fields->m_lldb_python_dir.GetPath().c_str());
177     });
178     if (success)
179       result = &g_fields->m_lldb_python_dir;
180   } break;
181   case lldb::ePathTypeLLDBSystemPlugins: {
182     static llvm::once_flag g_once_flag;
183     static bool success = false;
184     llvm::call_once(g_once_flag, []() {
185       success = HostInfo::ComputeSystemPluginsDirectory(
186           g_fields->m_lldb_system_plugin_dir);
187       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
188       if (log)
189         log->Printf(
190             "HostInfoBase::GetLLDBPath(ePathTypeLLDBSystemPlugins) => '%s'",
191             g_fields->m_lldb_system_plugin_dir.GetPath().c_str());
192     });
193     if (success)
194       result = &g_fields->m_lldb_system_plugin_dir;
195   } break;
196   case lldb::ePathTypeLLDBUserPlugins: {
197     static llvm::once_flag g_once_flag;
198     static bool success = false;
199     llvm::call_once(g_once_flag, []() {
200       success = HostInfo::ComputeUserPluginsDirectory(
201           g_fields->m_lldb_user_plugin_dir);
202       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
203       if (log)
204         log->Printf(
205             "HostInfoBase::GetLLDBPath(ePathTypeLLDBUserPlugins) => '%s'",
206             g_fields->m_lldb_user_plugin_dir.GetPath().c_str());
207     });
208     if (success)
209       result = &g_fields->m_lldb_user_plugin_dir;
210   } break;
211   case lldb::ePathTypeLLDBTempSystemDir: {
212     static llvm::once_flag g_once_flag;
213     static bool success = false;
214     llvm::call_once(g_once_flag, []() {
215       success = HostInfo::ComputeProcessTempFileDirectory(
216           g_fields->m_lldb_process_tmp_dir);
217       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
218       if (log)
219         log->Printf(
220             "HostInfoBase::GetLLDBPath(ePathTypeLLDBTempSystemDir) => '%s'",
221             g_fields->m_lldb_process_tmp_dir.GetPath().c_str());
222     });
223     if (success)
224       result = &g_fields->m_lldb_process_tmp_dir;
225   } break;
226   case lldb::ePathTypeGlobalLLDBTempSystemDir: {
227     static llvm::once_flag g_once_flag;
228     static bool success = false;
229     llvm::call_once(g_once_flag, []() {
230       success = HostInfo::ComputeGlobalTempFileDirectory(
231           g_fields->m_lldb_global_tmp_dir);
232       Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
233       if (log)
234         log->Printf("HostInfoBase::GetLLDBPath("
235                     "ePathTypeGlobalLLDBTempSystemDir) => '%s'",
236                     g_fields->m_lldb_global_tmp_dir.GetPath().c_str());
237     });
238     if (success)
239       result = &g_fields->m_lldb_global_tmp_dir;
240   } break;
241   default:
242     llvm_unreachable("Unreachable!");
243   }
244 
245   if (!result)
246     return false;
247   file_spec = *result;
248   return true;
249 }
250 
251 ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
252   if (triple.empty())
253     return ArchSpec();
254   llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
255   if (!ArchSpec::ContainsOnlyArch(normalized_triple))
256     return ArchSpec(triple);
257 
258   if (auto kind = HostInfo::ParseArchitectureKind(triple))
259     return HostInfo::GetArchitecture(*kind);
260 
261   llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
262 
263   if (normalized_triple.getVendorName().empty())
264     normalized_triple.setVendor(host_triple.getVendor());
265   if (normalized_triple.getOSName().empty())
266     normalized_triple.setOS(host_triple.getOS());
267   if (normalized_triple.getEnvironmentName().empty())
268     normalized_triple.setEnvironment(host_triple.getEnvironment());
269   return ArchSpec(normalized_triple);
270 }
271 
272 bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
273   // To get paths related to LLDB we get the path to the executable that
274   // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB".
275   // On other posix systems, we will get .../lib(64|32)?/liblldb.so.
276 
277   FileSpec lldb_file_spec(
278       Host::GetModuleFileSpecForHostAddress(reinterpret_cast<void *>(
279           reinterpret_cast<intptr_t>(HostInfoBase::GetLLDBPath))));
280 
281   // This is necessary because when running the testsuite the shlib might be a
282   // symbolic link inside the Python resource dir.
283   FileSystem::ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
284 
285   // Remove the filename so that this FileSpec only represents the directory.
286   file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
287 
288   return (bool)file_spec.GetDirectory();
289 }
290 
291 bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
292   return GetLLDBPath(lldb::ePathTypeLLDBShlibDir, file_spec);
293 }
294 
295 bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
296   FileSpec temp_file_spec;
297   if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
298     return false;
299 
300   std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
301   temp_file_spec.AppendPathComponent(pid_str);
302   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
303     return false;
304 
305   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
306   return true;
307 }
308 
309 bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
310   llvm::SmallVector<char, 16> tmpdir;
311   llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
312   file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()), true);
313   return true;
314 }
315 
316 bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
317   file_spec.Clear();
318 
319   FileSpec temp_file_spec;
320   if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
321     return false;
322 
323   temp_file_spec.AppendPathComponent("lldb");
324   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
325     return false;
326 
327   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
328   return true;
329 }
330 
331 bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
332   // TODO(zturner): Figure out how to compute the header directory for all
333   // platforms.
334   return false;
335 }
336 
337 bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
338   // TODO(zturner): Figure out how to compute the system plugins directory for
339   // all platforms.
340   return false;
341 }
342 
343 bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
344   // TODO(zturner): Figure out how to compute the user plugins directory for
345   // all platforms.
346   return false;
347 }
348 
349 void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
350                                                   ArchSpec &arch_64) {
351   llvm::Triple triple(llvm::sys::getProcessTriple());
352 
353   arch_32.Clear();
354   arch_64.Clear();
355 
356   switch (triple.getArch()) {
357   default:
358     arch_32.SetTriple(triple);
359     break;
360 
361   case llvm::Triple::aarch64:
362   case llvm::Triple::ppc64:
363   case llvm::Triple::ppc64le:
364   case llvm::Triple::x86_64:
365     arch_64.SetTriple(triple);
366     arch_32.SetTriple(triple.get32BitArchVariant());
367     break;
368 
369   case llvm::Triple::mips64:
370   case llvm::Triple::mips64el:
371   case llvm::Triple::sparcv9:
372   case llvm::Triple::systemz:
373     arch_64.SetTriple(triple);
374     break;
375   }
376 }
377