156 lines · cpp
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-exception6//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 27using namespace lldb_private;28 29namespace {30struct HostInfoLinuxFields {31 llvm::once_flag m_distribution_once_flag;32 std::string m_distribution_id;33};34} // namespace35 36static HostInfoLinuxFields *g_fields = nullptr;37 38void HostInfoLinux::Initialize(SharedLibraryDirectoryHelper *helper) {39 HostInfoPosix::Initialize(helper);40 41 g_fields = new HostInfoLinuxFields();42}43 44void HostInfoLinux::Terminate() {45 assert(g_fields && "Missing call to Initialize?");46 delete g_fields;47 g_fields = nullptr;48 HostInfoBase::Terminate();49}50 51llvm::StringRef HostInfoLinux::GetDistributionId() {52 assert(g_fields && "Missing call to Initialize?");53 // Try to run 'lbs_release -i', and use that response for the distribution54 // 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 paths60 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 exe68 LLDB_LOGF(log, "executable doesn't exist: %s",69 get_distribution_info_exe);70 continue;71 }72 73 // execute the distribution-retrieval command, read output74 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 newlines96 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 underscores100 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 file119 pclose(file);120 }121 });122 123 return g_fields->m_distribution_id;124}125 126FileSpec 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 141void 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 default146 // 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