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 bool 92 HostInfoLinux::GetOSBuildString(std::string &s) 93 { 94 struct utsname un; 95 ::memset(&un, 0, sizeof(utsname)); 96 s.clear(); 97 98 if (uname(&un) < 0) 99 return false; 100 101 s.assign(un.release); 102 return true; 103 } 104 105 bool 106 HostInfoLinux::GetOSKernelDescription(std::string &s) 107 { 108 struct utsname un; 109 110 ::memset(&un, 0, sizeof(utsname)); 111 s.clear(); 112 113 if (uname(&un) < 0) 114 return false; 115 116 s.assign(un.version); 117 return true; 118 } 119 120 llvm::StringRef 121 HostInfoLinux::GetDistributionId() 122 { 123 static bool is_initialized = false; 124 // Try to run 'lbs_release -i', and use that response 125 // for the distribution id. 126 127 if (!is_initialized) 128 { 129 is_initialized = true; 130 131 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST)); 132 if (log) 133 log->Printf("attempting to determine Linux distribution..."); 134 135 // check if the lsb_release command exists at one of the 136 // following paths 137 const char *const exe_paths[] = {"/bin/lsb_release", "/usr/bin/lsb_release"}; 138 139 for (size_t exe_index = 0; exe_index < sizeof(exe_paths) / sizeof(exe_paths[0]); ++exe_index) 140 { 141 const char *const get_distribution_info_exe = exe_paths[exe_index]; 142 if (access(get_distribution_info_exe, F_OK)) 143 { 144 // this exe doesn't exist, move on to next exe 145 if (log) 146 log->Printf("executable doesn't exist: %s", get_distribution_info_exe); 147 continue; 148 } 149 150 // execute the distribution-retrieval command, read output 151 std::string get_distribution_id_command(get_distribution_info_exe); 152 get_distribution_id_command += " -i"; 153 154 FILE *file = popen(get_distribution_id_command.c_str(), "r"); 155 if (!file) 156 { 157 if (log) 158 log->Printf("failed to run command: \"%s\", cannot retrieve " 159 "platform information", 160 get_distribution_id_command.c_str()); 161 break; 162 } 163 164 // retrieve the distribution id string. 165 char distribution_id[256] = {'\0'}; 166 if (fgets(distribution_id, sizeof(distribution_id) - 1, file) != NULL) 167 { 168 if (log) 169 log->Printf("distribution id command returned \"%s\"", distribution_id); 170 171 const char *const distributor_id_key = "Distributor ID:\t"; 172 if (strstr(distribution_id, distributor_id_key)) 173 { 174 // strip newlines 175 std::string id_string(distribution_id + strlen(distributor_id_key)); 176 id_string.erase(std::remove(id_string.begin(), id_string.end(), '\n'), id_string.end()); 177 178 // lower case it and convert whitespace to underscores 179 std::transform(id_string.begin(), id_string.end(), id_string.begin(), [](char ch) 180 { 181 return tolower(isspace(ch) ? '_' : ch); 182 }); 183 184 g_fields->m_distribution_id = id_string; 185 if (log) 186 log->Printf("distribution id set to \"%s\"", g_fields->m_distribution_id.c_str()); 187 } 188 else 189 { 190 if (log) 191 log->Printf("failed to find \"%s\" field in \"%s\"", distributor_id_key, distribution_id); 192 } 193 } 194 else 195 { 196 if (log) 197 log->Printf("failed to retrieve distribution id, \"%s\" returned no" 198 " lines", 199 get_distribution_id_command.c_str()); 200 } 201 202 // clean up the file 203 pclose(file); 204 } 205 } 206 207 return g_fields->m_distribution_id.c_str(); 208 } 209 210 FileSpec 211 HostInfoLinux::GetProgramFileSpec() 212 { 213 static FileSpec g_program_filespec; 214 215 if (!g_program_filespec) 216 { 217 char exe_path[PATH_MAX]; 218 ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); 219 if (len > 0) 220 { 221 exe_path[len] = 0; 222 g_program_filespec.SetFile(exe_path, false); 223 } 224 } 225 226 return g_program_filespec; 227 } 228 229 bool 230 HostInfoLinux::ComputeSystemPluginsDirectory(FileSpec &file_spec) 231 { 232 FileSpec temp_file("/usr/lib/lldb", true); 233 file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); 234 return true; 235 } 236 237 bool 238 HostInfoLinux::ComputeUserPluginsDirectory(FileSpec &file_spec) 239 { 240 // XDG Base Directory Specification 241 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 242 // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. 243 const char *xdg_data_home = getenv("XDG_DATA_HOME"); 244 if (xdg_data_home && xdg_data_home[0]) 245 { 246 std::string user_plugin_dir(xdg_data_home); 247 user_plugin_dir += "/lldb"; 248 file_spec.GetDirectory().SetCString(user_plugin_dir.c_str()); 249 } 250 else 251 file_spec.GetDirectory().SetCString("~/.local/share/lldb"); 252 return true; 253 } 254 255 void 256 HostInfoLinux::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) 257 { 258 HostInfoPosix::ComputeHostArchitectureSupport(arch_32, arch_64); 259 260 const char *distribution_id = GetDistributionId().data(); 261 262 // On Linux, "unknown" in the vendor slot isn't what we want for the default 263 // triple. It's probably an artifact of config.guess. 264 if (arch_32.IsValid()) 265 { 266 arch_32.SetDistributionId(distribution_id); 267 if (arch_32.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 268 arch_32.GetTriple().setVendorName(""); 269 } 270 if (arch_64.IsValid()) 271 { 272 arch_64.SetDistributionId(distribution_id); 273 if (arch_64.GetTriple().getVendor() == llvm::Triple::UnknownVendor) 274 arch_64.GetTriple().setVendorName(""); 275 } 276 } 277