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 25 namespace rtc { 26 27 static const char kCpuInfoFile[] = "/proc/cpuinfo"; 28 static const char kCpuMaxFreqFile[] = 29 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; 30 31 ProcCpuInfo::ProcCpuInfo() { 32 } 33 34 ProcCpuInfo::~ProcCpuInfo() { 35 } 36 37 bool ProcCpuInfo::LoadFromSystem() { 38 ConfigParser procfs; 39 if (!procfs.Open(kCpuInfoFile)) { 40 return false; 41 } 42 return procfs.Parse(§ions_); 43 }; 44 45 bool 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 55 bool 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 85 bool 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 118 bool 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 144 bool 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 158 bool 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 171 ConfigParser::ConfigParser() {} 172 173 ConfigParser::~ConfigParser() {} 174 175 bool 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 184 void ConfigParser::Attach(StreamInterface* stream) { 185 instream_.reset(stream); 186 } 187 188 bool 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 198 bool 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 208 bool 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 std::string ReadLinuxUname() { 237 struct utsname buf; 238 if (uname(&buf) < 0) { 239 LOG_ERR(LS_ERROR) << "Can't call uname()"; 240 return std::string(); 241 } 242 std::ostringstream sstr; 243 sstr << buf.sysname << " " 244 << buf.release << " " 245 << buf.version << " " 246 << buf.machine; 247 return sstr.str(); 248 } 249 250 int ReadCpuMaxFreq() { 251 FileStream fs; 252 std::string str; 253 int freq = -1; 254 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) || 255 SR_SUCCESS != fs.ReadLine(&str) || 256 !FromString(str, &freq)) { 257 return -1; 258 } 259 return freq; 260 } 261 262 } // namespace rtc 263 264 #endif // defined(WEBRTC_LINUX) 265