197a14e60SZachary Turner //===-- HostInfoLinux.cpp ---------------------------------------*- C++ -*-===// 297a14e60SZachary Turner // 397a14e60SZachary Turner // The LLVM Compiler Infrastructure 497a14e60SZachary Turner // 597a14e60SZachary Turner // This file is distributed under the University of Illinois Open Source 697a14e60SZachary Turner // License. See LICENSE.TXT for details. 797a14e60SZachary Turner // 897a14e60SZachary Turner //===----------------------------------------------------------------------===// 997a14e60SZachary Turner 1097a14e60SZachary Turner #include "lldb/Core/Log.h" 1197a14e60SZachary Turner #include "lldb/Host/linux/HostInfoLinux.h" 1297a14e60SZachary Turner 13e47ffc3bSZachary Turner #include <limits.h> 1497a14e60SZachary Turner #include <stdio.h> 1597a14e60SZachary Turner #include <string.h> 1697a14e60SZachary Turner #include <sys/utsname.h> 1797a14e60SZachary Turner 1897a14e60SZachary Turner #include <algorithm> 19*ff48e4beSGreg Clayton #include <mutex> // std::once 2097a14e60SZachary Turner 2197a14e60SZachary Turner using namespace lldb_private; 2297a14e60SZachary Turner 23673b6e4fSZachary Turner namespace 24673b6e4fSZachary Turner { 25673b6e4fSZachary Turner struct HostInfoLinuxFields 26673b6e4fSZachary Turner { 27673b6e4fSZachary Turner HostInfoLinuxFields() 28673b6e4fSZachary Turner : m_os_major(0) 29673b6e4fSZachary Turner , m_os_minor(0) 30673b6e4fSZachary Turner , m_os_update(0) 31673b6e4fSZachary Turner { 32673b6e4fSZachary Turner } 33673b6e4fSZachary Turner 34673b6e4fSZachary Turner std::string m_distribution_id; 35673b6e4fSZachary Turner uint32_t m_os_major; 36673b6e4fSZachary Turner uint32_t m_os_minor; 37673b6e4fSZachary Turner uint32_t m_os_update; 38673b6e4fSZachary Turner }; 39673b6e4fSZachary Turner 40673b6e4fSZachary Turner HostInfoLinuxFields *g_fields = nullptr; 41673b6e4fSZachary Turner } 42673b6e4fSZachary Turner 43673b6e4fSZachary Turner void 44673b6e4fSZachary Turner HostInfoLinux::Initialize() 45673b6e4fSZachary Turner { 46673b6e4fSZachary Turner HostInfoPosix::Initialize(); 47673b6e4fSZachary Turner 48673b6e4fSZachary Turner g_fields = new HostInfoLinuxFields(); 49673b6e4fSZachary Turner } 5097a14e60SZachary Turner 5139de3110SZachary Turner uint32_t 5239de3110SZachary Turner HostInfoLinux::GetMaxThreadNameLength() 5339de3110SZachary Turner { 5439de3110SZachary Turner return 16; 5539de3110SZachary Turner } 5639de3110SZachary Turner 5797a14e60SZachary Turner bool 5897a14e60SZachary Turner HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) 5997a14e60SZachary Turner { 6097a14e60SZachary Turner static bool success = false; 61*ff48e4beSGreg Clayton static std::once_flag g_once_flag; 62*ff48e4beSGreg Clayton std::call_once(g_once_flag, []() { 6397a14e60SZachary Turner 6497a14e60SZachary Turner struct utsname un; 65*ff48e4beSGreg Clayton if (uname(&un) == 0) 66*ff48e4beSGreg Clayton { 67673b6e4fSZachary Turner int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); 6897a14e60SZachary Turner if (status == 3) 6997a14e60SZachary Turner success = true; 70*ff48e4beSGreg Clayton else 71*ff48e4beSGreg Clayton { 7297a14e60SZachary Turner // Some kernels omit the update version, so try looking for just "X.Y" and 7397a14e60SZachary Turner // set update to 0. 74673b6e4fSZachary Turner g_fields->m_os_update = 0; 75673b6e4fSZachary Turner status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); 76*ff48e4beSGreg Clayton if (status == 2) 77*ff48e4beSGreg Clayton success = true; 7897a14e60SZachary Turner } 79*ff48e4beSGreg Clayton } 80*ff48e4beSGreg Clayton }); 8197a14e60SZachary Turner 82*ff48e4beSGreg Clayton 83673b6e4fSZachary Turner major = g_fields->m_os_major; 84673b6e4fSZachary Turner minor = g_fields->m_os_minor; 85673b6e4fSZachary Turner update = g_fields->m_os_update; 8697a14e60SZachary Turner return success; 8797a14e60SZachary Turner } 8897a14e60SZachary Turner 8953c038a5SOleksiy Vyalov bool 9053c038a5SOleksiy Vyalov HostInfoLinux::GetOSBuildString(std::string &s) 9153c038a5SOleksiy Vyalov { 9253c038a5SOleksiy Vyalov struct utsname un; 9353c038a5SOleksiy Vyalov ::memset(&un, 0, sizeof(utsname)); 9453c038a5SOleksiy Vyalov s.clear(); 9553c038a5SOleksiy Vyalov 9653c038a5SOleksiy Vyalov if (uname(&un) < 0) 9753c038a5SOleksiy Vyalov return false; 9853c038a5SOleksiy Vyalov 9953c038a5SOleksiy Vyalov s.assign(un.release); 10053c038a5SOleksiy Vyalov return true; 10153c038a5SOleksiy Vyalov } 10253c038a5SOleksiy Vyalov 10353c038a5SOleksiy Vyalov bool 10453c038a5SOleksiy Vyalov HostInfoLinux::GetOSKernelDescription(std::string &s) 10553c038a5SOleksiy Vyalov { 10653c038a5SOleksiy Vyalov struct utsname un; 10753c038a5SOleksiy Vyalov 10853c038a5SOleksiy Vyalov ::memset(&un, 0, sizeof(utsname)); 10953c038a5SOleksiy Vyalov s.clear(); 11053c038a5SOleksiy Vyalov 11153c038a5SOleksiy Vyalov if (uname(&un) < 0) 11253c038a5SOleksiy Vyalov return false; 11353c038a5SOleksiy Vyalov 11453c038a5SOleksiy Vyalov s.assign(un.version); 11553c038a5SOleksiy Vyalov return true; 11653c038a5SOleksiy Vyalov } 11753c038a5SOleksiy Vyalov 11897a14e60SZachary Turner llvm::StringRef 11997a14e60SZachary Turner HostInfoLinux::GetDistributionId() 12097a14e60SZachary Turner { 12197a14e60SZachary Turner // Try to run 'lbs_release -i', and use that response 12297a14e60SZachary Turner // for the distribution id. 123*ff48e4beSGreg Clayton static bool success = false; 124*ff48e4beSGreg Clayton static std::once_flag g_once_flag; 125*ff48e4beSGreg Clayton std::call_once(g_once_flag, []() { 12697a14e60SZachary Turner 12797a14e60SZachary Turner Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); 12897a14e60SZachary Turner if (log) 12997a14e60SZachary Turner log->Printf("attempting to determine Linux distribution..."); 13097a14e60SZachary Turner 13197a14e60SZachary Turner // check if the lsb_release command exists at one of the 13297a14e60SZachary Turner // following paths 13397a14e60SZachary Turner const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; 13497a14e60SZachary Turner 13597a14e60SZachary Turner for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) 13697a14e60SZachary Turner { 13797a14e60SZachary Turner const char *const get_distribution_info_exe = exe_paths[exe_index]; 13897a14e60SZachary Turner if (access(get_distribution_info_exe, F_OK)) 13997a14e60SZachary Turner { 14097a14e60SZachary Turner // this exe doesn't exist, move on to next exe 14197a14e60SZachary Turner if (log) 14297a14e60SZachary Turner log->Printf("executable doesn't exist: %s", get_distribution_info_exe); 14397a14e60SZachary Turner continue; 14497a14e60SZachary Turner } 14597a14e60SZachary Turner 14697a14e60SZachary Turner // execute the distribution-retrieval command, read output 14797a14e60SZachary Turner std::string get_distribution_id_command(get_distribution_info_exe); 14897a14e60SZachary Turner get_distribution_id_command += " -i"; 14997a14e60SZachary Turner 15097a14e60SZachary Turner FILE *file = popen(get_distribution_id_command.c_str(), "r"); 15197a14e60SZachary Turner if (!file) 15297a14e60SZachary Turner { 15397a14e60SZachary Turner if (log) 15497a14e60SZachary Turner log->Printf("failed to run command: \"%s\", cannot retrieve " 15597a14e60SZachary Turner "platform information", 15697a14e60SZachary Turner get_distribution_id_command.c_str()); 15797a14e60SZachary Turner break; 15897a14e60SZachary Turner } 15997a14e60SZachary Turner 16097a14e60SZachary Turner // retrieve the distribution id string. 16197a14e60SZachary Turner char distribution_id[256] = {'\0'}; 16297a14e60SZachary Turner if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) 16397a14e60SZachary Turner { 16497a14e60SZachary Turner if (log) 16597a14e60SZachary Turner log->Printf("distribution id command returned \"%s\"", distribution_id); 16697a14e60SZachary Turner 16797a14e60SZachary Turner const char *const distributor_id_key = "Distributor ID:\t"; 16897a14e60SZachary Turner if (strstr(distribution_id, distributor_id_key)) 16997a14e60SZachary Turner { 17097a14e60SZachary Turner // strip newlines 17197a14e60SZachary Turner std::string id_string(distribution_id + strlen(distributor_id_key)); 17297a14e60SZachary Turner id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); 17397a14e60SZachary Turner 17497a14e60SZachary Turner // lower case it and convert whitespace to underscores 17597a14e60SZachary Turner std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) 17697a14e60SZachary Turner { 17797a14e60SZachary Turner return tolower(isspace(ch) ? '_' : ch); 17897a14e60SZachary Turner }); 17997a14e60SZachary Turner 180673b6e4fSZachary Turner g_fields->m_distribution_id = id_string; 18197a14e60SZachary Turner if (log) 182673b6e4fSZachary Turner log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); 18397a14e60SZachary Turner } 18497a14e60SZachary Turner else 18597a14e60SZachary Turner { 18697a14e60SZachary Turner if (log) 18797a14e60SZachary Turner log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); 18897a14e60SZachary Turner } 18997a14e60SZachary Turner } 19097a14e60SZachary Turner else 19197a14e60SZachary Turner { 19297a14e60SZachary Turner if (log) 19397a14e60SZachary Turner log->Printf("failed to retrieve distribution id, \"%s\" returned no" 19497a14e60SZachary Turner " lines", 19597a14e60SZachary Turner get_distribution_id_command.c_str()); 19697a14e60SZachary Turner } 19797a14e60SZachary Turner 19897a14e60SZachary Turner // clean up the file 19997a14e60SZachary Turner pclose(file); 20097a14e60SZachary Turner } 201*ff48e4beSGreg Clayton }); 20297a14e60SZachary Turner 203673b6e4fSZachary Turner return g_fields->m_distribution_id.c_str(); 20497a14e60SZachary Turner } 20513b18261SZachary Turner 206a21fee0eSZachary Turner FileSpec 207a21fee0eSZachary Turner HostInfoLinux::GetProgramFileSpec() 208a21fee0eSZachary Turner { 209a21fee0eSZachary Turner static FileSpec g_program_filespec; 210a21fee0eSZachary Turner 211a21fee0eSZachary Turner if (!g_program_filespec) 212a21fee0eSZachary Turner { 213a21fee0eSZachary Turner char exe_path[PATH_MAX]; 214a21fee0eSZachary Turner ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); 215a21fee0eSZachary Turner if (len > 0) 216a21fee0eSZachary Turner { 217a21fee0eSZachary Turner exe_path[len] = 0; 218a21fee0eSZachary Turner g_program_filespec.SetFile(exe_path, false); 219a21fee0eSZachary Turner } 220a21fee0eSZachary Turner } 221a21fee0eSZachary Turner 222a21fee0eSZachary Turner return g_program_filespec; 223a21fee0eSZachary Turner } 224a21fee0eSZachary Turner 22542ff0ad8SZachary Turner bool 22642ff0ad8SZachary Turner HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) 22742ff0ad8SZachary Turner { 2280d8400c8SGreg Clayton FileSpec temp_file("/usr/lib/lldb", true); 2290d8400c8SGreg Clayton file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); 23042ff0ad8SZachary Turner return true; 23142ff0ad8SZachary Turner } 23242ff0ad8SZachary Turner 23342ff0ad8SZachary Turner bool 23442ff0ad8SZachary Turner HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) 23542ff0ad8SZachary Turner { 23642ff0ad8SZachary Turner // XDG Base Directory Specification 23742ff0ad8SZachary Turner // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 23842ff0ad8SZachary Turner // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. 23942ff0ad8SZachary Turner const char *xdg_data_home = getenv("XDG_DATA_HOME"); 24042ff0ad8SZachary Turner if (xdg_data_home && xdg_data_home[0]) 24142ff0ad8SZachary Turner { 24242ff0ad8SZachary Turner std::string user_plugin_dir(xdg_data_home); 24342ff0ad8SZachary Turner user_plugin_dir += "/lldb"; 2440d8400c8SGreg Clayton file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); 24542ff0ad8SZachary Turner } 24642ff0ad8SZachary Turner else 2470d8400c8SGreg Clayton file_spec.GetDirectory().SetCString("~/.local/share/lldb"); 24842ff0ad8SZachary Turner return true; 24942ff0ad8SZachary Turner } 25042ff0ad8SZachary Turner 25113b18261SZachary Turner void 25213b18261SZachary Turner HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) 25313b18261SZachary Turner { 25413b18261SZachary Turner HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); 25513b18261SZachary Turner 25613b18261SZachary Turner const char *distribution_id = GetDistributionId().data(); 25713b18261SZachary Turner 25813b18261SZachary Turner // On Linux, "unknown" in the vendor slot isn't what we want for the default 25913b18261SZachary Turner // triple. It's probably an artifact of config.guess. 26013b18261SZachary Turner if (arch_32.IsValid()) 26113b18261SZachary Turner { 26213b18261SZachary Turner arch_32.SetDistributionId(distribution_id); 26313b18261SZachary Turner if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 26413b18261SZachary Turner arch_32.GetTriple().setVendorName(""); 26513b18261SZachary Turner } 26613b18261SZachary Turner if (arch_64.IsValid()) 26713b18261SZachary Turner { 26813b18261SZachary Turner arch_64.SetDistributionId(distribution_id); 26913b18261SZachary Turner if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 27013b18261SZachary Turner arch_64.GetTriple().setVendorName(""); 27113b18261SZachary Turner } 27213b18261SZachary Turner } 273