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 (FileSystem::Instance().Exists(m_lldb_process_tmp_dir)) {
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_clang_resource_dir;
62   FileSpec m_lldb_system_plugin_dir;
63   FileSpec m_lldb_user_plugin_dir;
64   FileSpec m_lldb_process_tmp_dir;
65   FileSpec m_lldb_global_tmp_dir;
66 };
67 
68 HostInfoBaseFields *g_fields = nullptr;
69 }
70 
71 void HostInfoBase::Initialize() { g_fields = new HostInfoBaseFields(); }
72 
73 void HostInfoBase::Terminate() {
74   delete g_fields;
75   g_fields = nullptr;
76 }
77 
78 llvm::StringRef HostInfoBase::GetTargetTriple() {
79   static llvm::once_flag g_once_flag;
80   llvm::call_once(g_once_flag, []() {
81     g_fields->m_host_triple =
82         HostInfo::GetArchitecture().GetTriple().getTriple();
83   });
84   return g_fields->m_host_triple;
85 }
86 
87 const ArchSpec &HostInfoBase::GetArchitecture(ArchitectureKind arch_kind) {
88   static llvm::once_flag g_once_flag;
89   llvm::call_once(g_once_flag, []() {
90     HostInfo::ComputeHostArchitectureSupport(g_fields->m_host_arch_32,
91                                              g_fields->m_host_arch_64);
92   });
93 
94   // If an explicit 32 or 64-bit architecture was requested, return that.
95   if (arch_kind == eArchKind32)
96     return g_fields->m_host_arch_32;
97   if (arch_kind == eArchKind64)
98     return g_fields->m_host_arch_64;
99 
100   // Otherwise prefer the 64-bit architecture if it is valid.
101   return (g_fields->m_host_arch_64.IsValid()) ? g_fields->m_host_arch_64
102                                               : g_fields->m_host_arch_32;
103 }
104 
105 llvm::Optional<HostInfoBase::ArchitectureKind> HostInfoBase::ParseArchitectureKind(llvm::StringRef kind) {
106   return llvm::StringSwitch<llvm::Optional<ArchitectureKind>>(kind)
107       .Case(LLDB_ARCH_DEFAULT, eArchKindDefault)
108       .Case(LLDB_ARCH_DEFAULT_32BIT, eArchKind32)
109       .Case(LLDB_ARCH_DEFAULT_64BIT, eArchKind64)
110       .Default(llvm::None);
111 }
112 
113 FileSpec HostInfoBase::GetShlibDir() {
114   static llvm::once_flag g_once_flag;
115   static bool success = false;
116   llvm::call_once(g_once_flag, []() {
117     success = HostInfo::ComputeSharedLibraryDirectory(g_fields->m_lldb_so_dir);
118     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
119     LLDB_LOG(log, "shlib dir -> `{0}`", g_fields->m_lldb_so_dir);
120   });
121   return success ? g_fields->m_lldb_so_dir : FileSpec();
122 }
123 
124 FileSpec HostInfoBase::GetSupportExeDir() {
125   static llvm::once_flag g_once_flag;
126   static bool success = false;
127   llvm::call_once(g_once_flag, []() {
128     success =
129         HostInfo::ComputeSupportExeDirectory(g_fields->m_lldb_support_exe_dir);
130     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
131     LLDB_LOG(log, "support exe dir -> `{0}`", g_fields->m_lldb_support_exe_dir);
132   });
133   return success ? g_fields->m_lldb_support_exe_dir : FileSpec();
134 }
135 
136 FileSpec HostInfoBase::GetHeaderDir() {
137   static llvm::once_flag g_once_flag;
138   static bool success = false;
139   llvm::call_once(g_once_flag, []() {
140     success = HostInfo::ComputeHeaderDirectory(g_fields->m_lldb_headers_dir);
141     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
142     LLDB_LOG(log, "header dir -> `{0}`", g_fields->m_lldb_headers_dir);
143   });
144   return success ? g_fields->m_lldb_headers_dir : FileSpec();
145 }
146 
147 FileSpec HostInfoBase::GetSystemPluginDir() {
148   static llvm::once_flag g_once_flag;
149   static bool success = false;
150   llvm::call_once(g_once_flag, []() {
151     success = HostInfo::ComputeSystemPluginsDirectory(
152         g_fields->m_lldb_system_plugin_dir);
153     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
154     LLDB_LOG(log, "system plugin dir -> `{0}`",
155              g_fields->m_lldb_system_plugin_dir);
156   });
157   return success ? g_fields->m_lldb_system_plugin_dir : FileSpec();
158 }
159 
160 FileSpec HostInfoBase::GetUserPluginDir() {
161   static llvm::once_flag g_once_flag;
162   static bool success = false;
163   llvm::call_once(g_once_flag, []() {
164     success =
165         HostInfo::ComputeUserPluginsDirectory(g_fields->m_lldb_user_plugin_dir);
166     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
167     LLDB_LOG(log, "user plugin dir -> `{0}`", g_fields->m_lldb_user_plugin_dir);
168   });
169   return success ? g_fields->m_lldb_user_plugin_dir : FileSpec();
170 }
171 
172 FileSpec HostInfoBase::GetProcessTempDir() {
173   static llvm::once_flag g_once_flag;
174   static bool success = false;
175   llvm::call_once(g_once_flag, []() {
176     success = HostInfo::ComputeProcessTempFileDirectory(
177         g_fields->m_lldb_process_tmp_dir);
178     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
179     LLDB_LOG(log, "process temp dir -> `{0}`",
180              g_fields->m_lldb_process_tmp_dir);
181   });
182   return success ? g_fields->m_lldb_process_tmp_dir : FileSpec();
183 }
184 
185 FileSpec HostInfoBase::GetGlobalTempDir() {
186   static llvm::once_flag g_once_flag;
187   static bool success = false;
188   llvm::call_once(g_once_flag, []() {
189     success = HostInfo::ComputeGlobalTempFileDirectory(
190         g_fields->m_lldb_global_tmp_dir);
191     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
192     LLDB_LOG(log, "global temp dir -> `{0}`", g_fields->m_lldb_global_tmp_dir);
193   });
194   return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
195 }
196 
197 FileSpec HostInfoBase::GetReproducerTempDir() {
198   static llvm::once_flag g_once_flag;
199   static bool success = false;
200   llvm::call_once(g_once_flag, []() {
201     success = HostInfo::ComputeReproducerTempFileDirectory(
202         g_fields->m_lldb_global_tmp_dir);
203     Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
204     LLDB_LOG(log, "reproducer temp dir -> `{0}`",
205              g_fields->m_lldb_global_tmp_dir);
206   });
207   return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
208 }
209 
210 ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
211   if (triple.empty())
212     return ArchSpec();
213   llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
214   if (!ArchSpec::ContainsOnlyArch(normalized_triple))
215     return ArchSpec(triple);
216 
217   if (auto kind = HostInfo::ParseArchitectureKind(triple))
218     return HostInfo::GetArchitecture(*kind);
219 
220   llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple());
221 
222   if (normalized_triple.getVendorName().empty())
223     normalized_triple.setVendor(host_triple.getVendor());
224   if (normalized_triple.getOSName().empty())
225     normalized_triple.setOS(host_triple.getOS());
226   if (normalized_triple.getEnvironmentName().empty())
227     normalized_triple.setEnvironment(host_triple.getEnvironment());
228   return ArchSpec(normalized_triple);
229 }
230 
231 bool HostInfoBase::ComputeSharedLibraryDirectory(FileSpec &file_spec) {
232   // To get paths related to LLDB we get the path to the executable that
233   // contains this function. On MacOSX this will be "LLDB.framework/.../LLDB".
234   // On other posix systems, we will get .../lib(64|32)?/liblldb.so.
235 
236   FileSpec lldb_file_spec(Host::GetModuleFileSpecForHostAddress(
237       reinterpret_cast<void *>(reinterpret_cast<intptr_t>(
238           HostInfoBase::ComputeSharedLibraryDirectory))));
239 
240   // This is necessary because when running the testsuite the shlib might be a
241   // symbolic link inside the Python resource dir.
242   FileSystem::Instance().ResolveSymbolicLink(lldb_file_spec, lldb_file_spec);
243 
244   // Remove the filename so that this FileSpec only represents the directory.
245   file_spec.GetDirectory() = lldb_file_spec.GetDirectory();
246 
247   return (bool)file_spec.GetDirectory();
248 }
249 
250 bool HostInfoBase::ComputeSupportExeDirectory(FileSpec &file_spec) {
251   file_spec = GetShlibDir();
252   return bool(file_spec);
253 }
254 
255 bool HostInfoBase::ComputeProcessTempFileDirectory(FileSpec &file_spec) {
256   FileSpec temp_file_spec;
257   if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
258     return false;
259 
260   std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
261   temp_file_spec.AppendPathComponent(pid_str);
262   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
263     return false;
264 
265   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
266   return true;
267 }
268 
269 bool HostInfoBase::ComputeTempFileBaseDirectory(FileSpec &file_spec) {
270   llvm::SmallVector<char, 16> tmpdir;
271   llvm::sys::path::system_temp_directory(/*ErasedOnReboot*/ true, tmpdir);
272   file_spec = FileSpec(std::string(tmpdir.data(), tmpdir.size()));
273   FileSystem::Instance().Resolve(file_spec);
274   return true;
275 }
276 
277 bool HostInfoBase::ComputeGlobalTempFileDirectory(FileSpec &file_spec) {
278   file_spec.Clear();
279 
280   FileSpec temp_file_spec;
281   if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
282     return false;
283 
284   temp_file_spec.AppendPathComponent("lldb");
285   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
286     return false;
287 
288   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
289   return true;
290 }
291 
292 bool HostInfoBase::ComputeReproducerTempFileDirectory(FileSpec &file_spec) {
293   file_spec.Clear();
294 
295   FileSpec temp_file_spec;
296   if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
297     return false;
298 
299   temp_file_spec.AppendPathComponent("reproducer");
300   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
301     return false;
302 
303   std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
304   temp_file_spec.AppendPathComponent(pid_str);
305   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
306     return false;
307 
308   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
309   return true;
310 }
311 
312 bool HostInfoBase::ComputeHeaderDirectory(FileSpec &file_spec) {
313   // TODO(zturner): Figure out how to compute the header directory for all
314   // platforms.
315   return false;
316 }
317 
318 bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
319   // TODO(zturner): Figure out how to compute the system plugins directory for
320   // all platforms.
321   return false;
322 }
323 
324 bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
325   // TODO(zturner): Figure out how to compute the user plugins directory for
326   // all platforms.
327   return false;
328 }
329 
330 void HostInfoBase::ComputeHostArchitectureSupport(ArchSpec &arch_32,
331                                                   ArchSpec &arch_64) {
332   llvm::Triple triple(llvm::sys::getProcessTriple());
333 
334   arch_32.Clear();
335   arch_64.Clear();
336 
337   switch (triple.getArch()) {
338   default:
339     arch_32.SetTriple(triple);
340     break;
341 
342   case llvm::Triple::aarch64:
343   case llvm::Triple::ppc64:
344   case llvm::Triple::ppc64le:
345   case llvm::Triple::x86_64:
346     arch_64.SetTriple(triple);
347     arch_32.SetTriple(triple.get32BitArchVariant());
348     break;
349 
350   case llvm::Triple::mips64:
351   case llvm::Triple::mips64el:
352   case llvm::Triple::sparcv9:
353   case llvm::Triple::systemz:
354     arch_64.SetTriple(triple);
355     break;
356   }
357 }
358