1 //===-- HostInfoLinux.cpp -------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/Host/linux/HostInfoLinux.h" 10 #include "lldb/Host/Config.h" 11 #include "lldb/Host/FileSystem.h" 12 #include "lldb/Utility/LLDBLog.h" 13 #include "lldb/Utility/Log.h" 14 15 #include "llvm/Support/Threading.h" 16 17 #include <climits> 18 #include <cstdio> 19 #include <cstring> 20 #include <sys/utsname.h> 21 #include <unistd.h> 22 23 #include <algorithm> 24 #include <mutex> 25 #include <optional> 26 27 using namespace lldb_private; 28 29 namespace { 30 struct HostInfoLinuxFields { 31 llvm::once_flag m_distribution_once_flag; 32 std::string m_distribution_id; 33 }; 34 } // namespace 35 36 static HostInfoLinuxFields *g_fields = nullptr; 37 38 void HostInfoLinux::Initialize(SharedLibraryDirectoryHelper *helper) { 39 HostInfoPosix::Initialize(helper); 40 41 g_fields = new HostInfoLinuxFields(); 42 } 43 44 void HostInfoLinux::Terminate() { 45 assert(g_fields && "Missing call to Initialize?"); 46 delete g_fields; 47 g_fields = nullptr; 48 HostInfoBase::Terminate(); 49 } 50 51 llvm::StringRef HostInfoLinux::GetDistributionId() { 52 assert(g_fields && "Missing call to Initialize?"); 53 // Try to run 'lbs_release -i', and use that response for the distribution 54 // id. 55 llvm::call_once(g_fields->m_distribution_once_flag, []() { 56 Log *log = GetLog(LLDBLog::Host); 57 LLDB_LOGF(log, "attempting to determine Linux distribution..."); 58 59 // check if the lsb_release command exists at one of the following paths 60 const char *const exe_paths[] = {"/bin/lsb_release", 61 "/usr/bin/lsb_release"}; 62 63 for (size_t exe_index = 0; 64 exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) { 65 const char *const get_distribution_info_exe = exe_paths[exe_index]; 66 if (access(get_distribution_info_exe, F_OK)) { 67 // this exe doesn't exist, move on to next exe 68 LLDB_LOGF(log, "executable doesn't exist: %s", 69 get_distribution_info_exe); 70 continue; 71 } 72 73 // execute the distribution-retrieval command, read output 74 std::string get_distribution_id_command(get_distribution_info_exe); 75 get_distribution_id_command += " -i"; 76 77 FILE *file = popen(get_distribution_id_command.c_str(), "r"); 78 if (!file) { 79 LLDB_LOGF(log, 80 "failed to run command: \"%s\", cannot retrieve " 81 "platform information", 82 get_distribution_id_command.c_str()); 83 break; 84 } 85 86 // retrieve the distribution id string. 87 char distribution_id[256] = {'\0'}; 88 if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != 89 nullptr) { 90 LLDB_LOGF(log, "distribution id command returned \"%s\"", 91 distribution_id); 92 93 const char *const distributor_id_key = "Distributor ID:\t"; 94 if (strstr(distribution_id, distributor_id_key)) { 95 // strip newlines 96 std::string id_string(distribution_id + strlen(distributor_id_key)); 97 llvm::erase(id_string, '\n'); 98 99 // lower case it and convert whitespace to underscores 100 std::transform( 101 id_string.begin(), id_string.end(), id_string.begin(), 102 [](char ch) { return tolower(isspace(ch) ? '_' : ch); }); 103 104 g_fields->m_distribution_id = id_string; 105 LLDB_LOGF(log, "distribution id set to \"%s\"", 106 g_fields->m_distribution_id.c_str()); 107 } else { 108 LLDB_LOGF(log, "failed to find \"%s\" field in \"%s\"", 109 distributor_id_key, distribution_id); 110 } 111 } else { 112 LLDB_LOGF(log, 113 "failed to retrieve distribution id, \"%s\" returned no" 114 " lines", 115 get_distribution_id_command.c_str()); 116 } 117 118 // clean up the file 119 pclose(file); 120 } 121 }); 122 123 return g_fields->m_distribution_id; 124 } 125 126 FileSpec HostInfoLinux::GetProgramFileSpec() { 127 static FileSpec g_program_filespec; 128 129 if (!g_program_filespec) { 130 char exe_path[PATH_MAX]; 131 ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); 132 if (len > 0) { 133 exe_path[len] = 0; 134 g_program_filespec.SetFile(exe_path, FileSpec::Style::native); 135 } 136 } 137 138 return g_program_filespec; 139 } 140 141 void HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, 142 ArchSpec &arch_64) { 143 HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); 144 145 // On Linux, "unknown" in the vendor slot isn't what we want for the default 146 // triple. It's probably an artifact of config.guess. 147 if (arch_32.IsValid()) { 148 if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 149 arch_32.GetTriple().setVendorName(llvm::StringRef()); 150 } 151 if (arch_64.IsValid()) { 152 if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 153 arch_64.GetTriple().setVendorName(llvm::StringRef()); 154 } 155 } 156