1 //===-- HostInfoLinux.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/Core/Log.h" 11 #include "lldb/Host/linux/HostInfoLinux.h" 12 13 #include <limits.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <sys/utsname.h> 17 18 #include <algorithm> 19 20 using namespace lldb_private; 21 22 namespace 23 { 24 struct HostInfoLinuxFields 25 { 26 HostInfoLinuxFields() 27 : m_os_major(0) 28 , m_os_minor(0) 29 , m_os_update(0) 30 { 31 } 32 33 std::string m_distribution_id; 34 uint32_t m_os_major; 35 uint32_t m_os_minor; 36 uint32_t m_os_update; 37 }; 38 39 HostInfoLinuxFields *g_fields = nullptr; 40 } 41 42 void 43 HostInfoLinux::Initialize() 44 { 45 HostInfoPosix::Initialize(); 46 47 g_fields = new HostInfoLinuxFields(); 48 } 49 50 uint32_t 51 HostInfoLinux::GetMaxThreadNameLength() 52 { 53 return 16; 54 } 55 56 bool 57 HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) 58 { 59 static bool is_initialized = false; 60 static bool success = false; 61 62 if (!is_initialized) 63 { 64 is_initialized = true; 65 struct utsname un; 66 67 if (uname(&un)) 68 goto finished; 69 70 int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor, &g_fields->m_os_update); 71 if (status == 3) 72 { 73 success = true; 74 goto finished; 75 } 76 77 // Some kernels omit the update version, so try looking for just "X.Y" and 78 // set update to 0. 79 g_fields->m_os_update = 0; 80 status = sscanf(un.release, "%u.%u", &g_fields->m_os_major, &g_fields->m_os_minor); 81 success = !!(status == 2); 82 } 83 84 finished: 85 major = g_fields->m_os_major; 86 minor = g_fields->m_os_minor; 87 update = g_fields->m_os_update; 88 return success; 89 } 90 91 llvm::StringRef 92 HostInfoLinux::GetDistributionId() 93 { 94 static bool is_initialized = false; 95 // Try to run 'lbs_release -i', and use that response 96 // for the distribution id. 97 98 if (!is_initialized) 99 { 100 is_initialized = true; 101 102 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); 103 if (log) 104 log->Printf("attempting to determine Linux distribution..."); 105 106 // check if the lsb_release command exists at one of the 107 // following paths 108 const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; 109 110 for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) 111 { 112 const char *const get_distribution_info_exe = exe_paths[exe_index]; 113 if (access(get_distribution_info_exe, F_OK)) 114 { 115 // this exe doesn't exist, move on to next exe 116 if (log) 117 log->Printf("executable doesn't exist: %s", get_distribution_info_exe); 118 continue; 119 } 120 121 // execute the distribution-retrieval command, read output 122 std::string get_distribution_id_command(get_distribution_info_exe); 123 get_distribution_id_command += " -i"; 124 125 FILE *file = popen(get_distribution_id_command.c_str(), "r"); 126 if (!file) 127 { 128 if (log) 129 log->Printf("failed to run command: \"%s\", cannot retrieve " 130 "platform information", 131 get_distribution_id_command.c_str()); 132 break; 133 } 134 135 // retrieve the distribution id string. 136 char distribution_id[256] = {'\0'}; 137 if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) 138 { 139 if (log) 140 log->Printf("distribution id command returned \"%s\"", distribution_id); 141 142 const char *const distributor_id_key = "Distributor ID:\t"; 143 if (strstr(distribution_id, distributor_id_key)) 144 { 145 // strip newlines 146 std::string id_string(distribution_id + strlen(distributor_id_key)); 147 id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); 148 149 // lower case it and convert whitespace to underscores 150 std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) 151 { 152 return tolower(isspace(ch) ? '_' : ch); 153 }); 154 155 g_fields->m_distribution_id = id_string; 156 if (log) 157 log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); 158 } 159 else 160 { 161 if (log) 162 log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); 163 } 164 } 165 else 166 { 167 if (log) 168 log->Printf("failed to retrieve distribution id, \"%s\" returned no" 169 " lines", 170 get_distribution_id_command.c_str()); 171 } 172 173 // clean up the file 174 pclose(file); 175 } 176 } 177 178 return g_fields->m_distribution_id.c_str(); 179 } 180 181 FileSpec 182 HostInfoLinux::GetProgramFileSpec() 183 { 184 static FileSpec g_program_filespec; 185 186 if (!g_program_filespec) 187 { 188 char exe_path[PATH_MAX]; 189 ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); 190 if (len > 0) 191 { 192 exe_path[len] = 0; 193 g_program_filespec.SetFile(exe_path, false); 194 } 195 } 196 197 return g_program_filespec; 198 } 199 200 bool 201 HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) 202 { 203 FileSpec temp_file("/usr/lib/lldb", true); 204 file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); 205 return true; 206 } 207 208 bool 209 HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) 210 { 211 // XDG Base Directory Specification 212 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 213 // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. 214 const char *xdg_data_home = getenv("XDG_DATA_HOME"); 215 if (xdg_data_home && xdg_data_home[0]) 216 { 217 std::string user_plugin_dir(xdg_data_home); 218 user_plugin_dir += "/lldb"; 219 file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); 220 } 221 else 222 file_spec.GetDirectory().SetCString("~/.local/share/lldb"); 223 return true; 224 } 225 226 void 227 HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) 228 { 229 HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); 230 231 const char *distribution_id = GetDistributionId().data(); 232 233 // On Linux, "unknown" in the vendor slot isn't what we want for the default 234 // triple. It's probably an artifact of config.guess. 235 if (arch_32.IsValid()) 236 { 237 arch_32.SetDistributionId(distribution_id); 238 if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 239 arch_32.GetTriple().setVendorName(""); 240 } 241 if (arch_64.IsValid()) 242 { 243 arch_64.SetDistributionId(distribution_id); 244 if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 245 arch_64.GetTriple().setVendorName(""); 246 } 247 } 248