1/* 2 * Copyright 2008 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#if defined(WEBRTC_LINUX) 12#include "webrtc/base/linux.h" 13 14#include <ctype.h> 15 16#include <errno.h> 17#include <sys/utsname.h> 18#include <sys/wait.h> 19 20#include <cstdio> 21#include <set> 22 23#include "webrtc/base/stringencode.h" 24 25namespace rtc { 26 27static const char kCpuInfoFile[] = "/proc/cpuinfo"; 28static const char kCpuMaxFreqFile[] = 29 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; 30 31ProcCpuInfo::ProcCpuInfo() { 32} 33 34ProcCpuInfo::~ProcCpuInfo() { 35} 36 37bool ProcCpuInfo::LoadFromSystem() { 38 ConfigParser procfs; 39 if (!procfs.Open(kCpuInfoFile)) { 40 return false; 41 } 42 return procfs.Parse(§ions_); 43}; 44 45bool ProcCpuInfo::GetSectionCount(size_t* count) { 46 if (sections_.empty()) { 47 return false; 48 } 49 if (count) { 50 *count = sections_.size(); 51 } 52 return true; 53} 54 55bool ProcCpuInfo::GetNumCpus(int* num) { 56 if (sections_.empty()) { 57 return false; 58 } 59 int total_cpus = 0; 60#if defined(__arm__) 61 // Count the number of blocks that have a "processor" key defined. On ARM, 62 // there may be extra blocks of information that aren't per-processor. 63 size_t section_count = sections_.size(); 64 for (size_t i = 0; i < section_count; ++i) { 65 int processor_id; 66 if (GetSectionIntValue(i, "processor", &processor_id)) { 67 ++total_cpus; 68 } 69 } 70 // Single core ARM systems don't include "processor" keys at all, so return 71 // that we have a single core if we didn't find any explicitly above. 72 if (total_cpus == 0) { 73 total_cpus = 1; 74 } 75#else 76 // On X86, there is exactly one info section per processor. 77 total_cpus = static_cast<int>(sections_.size()); 78#endif 79 if (num) { 80 *num = total_cpus; 81 } 82 return true; 83} 84 85bool ProcCpuInfo::GetNumPhysicalCpus(int* num) { 86 if (sections_.empty()) { 87 return false; 88 } 89 // TODO: /proc/cpuinfo only reports cores that are currently 90 // _online_, so this may underreport the number of physical cores. 91#if defined(__arm__) 92 // ARM (currently) has no hyperthreading, so just return the same value 93 // as GetNumCpus. 94 return GetNumCpus(num); 95#else 96 int total_cores = 0; 97 std::set<int> physical_ids; 98 size_t section_count = sections_.size(); 99 for (size_t i = 0; i < section_count; ++i) { 100 int physical_id; 101 int cores; 102 // Count the cores for the physical id only if we have not counted the id. 103 if (GetSectionIntValue(i, "physical id", &physical_id) && 104 GetSectionIntValue(i, "cpu cores", &cores) && 105 physical_ids.find(physical_id) == physical_ids.end()) { 106 physical_ids.insert(physical_id); 107 total_cores += cores; 108 } 109 } 110 111 if (num) { 112 *num = total_cores; 113 } 114 return true; 115#endif 116} 117 118bool ProcCpuInfo::GetCpuFamily(int* id) { 119 int cpu_family = 0; 120 121#if defined(__arm__) 122 // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But 123 // there is 'CPU Architecture' which can be used as 'cpu family'. 124 // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of 125 // ARM cpu families, architectures, and their mappings. 126 // There may be multiple sessions that aren't per-processor. We need to scan 127 // through each session until we find the first 'CPU architecture'. 128 size_t section_count = sections_.size(); 129 for (size_t i = 0; i < section_count; ++i) { 130 if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) { 131 // We returns the first one (if there are multiple entries). 132 break; 133 }; 134 } 135#else 136 GetSectionIntValue(0, "cpu family", &cpu_family); 137#endif 138 if (id) { 139 *id = cpu_family; 140 } 141 return true; 142} 143 144bool ProcCpuInfo::GetSectionStringValue(size_t section_num, 145 const std::string& key, 146 std::string* result) { 147 if (section_num >= sections_.size()) { 148 return false; 149 } 150 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); 151 if (iter == sections_[section_num].end()) { 152 return false; 153 } 154 *result = iter->second; 155 return true; 156} 157 158bool ProcCpuInfo::GetSectionIntValue(size_t section_num, 159 const std::string& key, 160 int* result) { 161 if (section_num >= sections_.size()) { 162 return false; 163 } 164 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); 165 if (iter == sections_[section_num].end()) { 166 return false; 167 } 168 return FromString(iter->second, result); 169} 170 171ConfigParser::ConfigParser() {} 172 173ConfigParser::~ConfigParser() {} 174 175bool ConfigParser::Open(const std::string& filename) { 176 FileStream* fs = new FileStream(); 177 if (!fs->Open(filename, "r", NULL)) { 178 return false; 179 } 180 instream_.reset(fs); 181 return true; 182} 183 184void ConfigParser::Attach(StreamInterface* stream) { 185 instream_.reset(stream); 186} 187 188bool ConfigParser::Parse(MapVector* key_val_pairs) { 189 // Parses the file and places the found key-value pairs into key_val_pairs. 190 SimpleMap section; 191 while (ParseSection(§ion)) { 192 key_val_pairs->push_back(section); 193 section.clear(); 194 } 195 return (!key_val_pairs->empty()); 196} 197 198bool ConfigParser::ParseSection(SimpleMap* key_val_pair) { 199 // Parses the next section in the filestream and places the found key-value 200 // pairs into key_val_pair. 201 std::string key, value; 202 while (ParseLine(&key, &value)) { 203 (*key_val_pair)[key] = value; 204 } 205 return (!key_val_pair->empty()); 206} 207 208bool ConfigParser::ParseLine(std::string* key, std::string* value) { 209 // Parses the next line in the filestream and places the found key-value 210 // pair into key and val. 211 std::string line; 212 if ((instream_->ReadLine(&line)) == SR_EOS) { 213 return false; 214 } 215 std::vector<std::string> tokens; 216 if (2 != split(line, ':', &tokens)) { 217 return false; 218 } 219 // Removes whitespace at the end of Key name 220 size_t pos = tokens[0].length() - 1; 221 while ((pos > 0) && isspace(tokens[0][pos])) { 222 pos--; 223 } 224 tokens[0].erase(pos + 1); 225 // Removes whitespace at the start of value 226 pos = 0; 227 while (pos < tokens[1].length() && isspace(tokens[1][pos])) { 228 pos++; 229 } 230 tokens[1].erase(0, pos); 231 *key = tokens[0]; 232 *value = tokens[1]; 233 return true; 234} 235 236#if !defined(WEBRTC_CHROMIUM_BUILDs) 237static bool ExpectLineFromStream(FileStream* stream, 238 std::string* out) { 239 StreamResult res = stream->ReadLine(out); 240 if (res != SR_SUCCESS) { 241 if (res != SR_EOS) { 242 LOG(LS_ERROR) << "Error when reading from stream"; 243 } else { 244 LOG(LS_ERROR) << "Incorrect number of lines in stream"; 245 } 246 return false; 247 } 248 return true; 249} 250 251static void ExpectEofFromStream(FileStream* stream) { 252 std::string unused; 253 StreamResult res = stream->ReadLine(&unused); 254 if (res == SR_SUCCESS) { 255 LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream"; 256 } else if (res != SR_EOS) { 257 LOG(LS_WARNING) << "Error when checking for extra lines from stream"; 258 } 259} 260 261// For caching the lsb_release output (reading it invokes a sub-process and 262// hence is somewhat expensive). 263static std::string lsb_release_string; 264static CriticalSection lsb_release_string_critsec; 265 266std::string ReadLinuxLsbRelease() { 267 CritScope cs(&lsb_release_string_critsec); 268 if (!lsb_release_string.empty()) { 269 // Have cached result from previous call. 270 return lsb_release_string; 271 } 272 // No cached result. Run lsb_release and parse output. 273 POpenStream lsb_release_output; 274 if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) { 275 LOG_ERR(LS_ERROR) << "Can't run lsb_release"; 276 return lsb_release_string; // empty 277 } 278 // Read in the command's output and build the string. 279 std::ostringstream sstr; 280 std::string line; 281 int wait_status; 282 283 if (!ExpectLineFromStream(&lsb_release_output, &line)) { 284 return lsb_release_string; // empty 285 } 286 sstr << "DISTRIB_ID=" << line; 287 288 if (!ExpectLineFromStream(&lsb_release_output, &line)) { 289 return lsb_release_string; // empty 290 } 291 sstr << " DISTRIB_DESCRIPTION=\"" << line << '"'; 292 293 if (!ExpectLineFromStream(&lsb_release_output, &line)) { 294 return lsb_release_string; // empty 295 } 296 sstr << " DISTRIB_RELEASE=" << line; 297 298 if (!ExpectLineFromStream(&lsb_release_output, &line)) { 299 return lsb_release_string; // empty 300 } 301 sstr << " DISTRIB_CODENAME=" << line; 302 303 // Should not be anything left. 304 ExpectEofFromStream(&lsb_release_output); 305 306 lsb_release_output.Close(); 307 wait_status = lsb_release_output.GetWaitStatus(); 308 if (wait_status == -1 || 309 !WIFEXITED(wait_status) || 310 WEXITSTATUS(wait_status) != 0) { 311 LOG(LS_WARNING) << "Unexpected exit status from lsb_release"; 312 } 313 314 lsb_release_string = sstr.str(); 315 316 return lsb_release_string; 317} 318#endif 319 320std::string ReadLinuxUname() { 321 struct utsname buf; 322 if (uname(&buf) < 0) { 323 LOG_ERR(LS_ERROR) << "Can't call uname()"; 324 return std::string(); 325 } 326 std::ostringstream sstr; 327 sstr << buf.sysname << " " 328 << buf.release << " " 329 << buf.version << " " 330 << buf.machine; 331 return sstr.str(); 332} 333 334int ReadCpuMaxFreq() { 335 FileStream fs; 336 std::string str; 337 int freq = -1; 338 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) || 339 SR_SUCCESS != fs.ReadLine(&str) || 340 !FromString(str, &freq)) { 341 return -1; 342 } 343 return freq; 344} 345 346} // namespace rtc 347 348#endif // defined(WEBRTC_LINUX) 349