1 /* 2 * libjingle 3 * Copyright 2008 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #if defined(LINUX) || defined(ANDROID) 29 #include "talk/base/linux.h" 30 31 #include <ctype.h> 32 33 #include <errno.h> 34 #include <sys/utsname.h> 35 #include <sys/wait.h> 36 37 #include <cstdio> 38 #include <set> 39 40 #include "talk/base/stringencode.h" 41 42 namespace talk_base { 43 44 static const char kCpuInfoFile[] = "/proc/cpuinfo"; 45 static const char kCpuMaxFreqFile[] = 46 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; 47 48 ProcCpuInfo::ProcCpuInfo() { 49 } 50 51 ProcCpuInfo::~ProcCpuInfo() { 52 } 53 54 bool ProcCpuInfo::LoadFromSystem() { 55 ConfigParser procfs; 56 if (!procfs.Open(kCpuInfoFile)) { 57 return false; 58 } 59 return procfs.Parse(§ions_); 60 }; 61 62 bool ProcCpuInfo::GetSectionCount(size_t* count) { 63 if (sections_.empty()) { 64 return false; 65 } 66 if (count) { 67 *count = sections_.size(); 68 } 69 return true; 70 } 71 72 bool ProcCpuInfo::GetNumCpus(int* num) { 73 if (sections_.empty()) { 74 return false; 75 } 76 int total_cpus = 0; 77 #if defined(__arm__) 78 // Count the number of blocks that have a "processor" key defined. On ARM, 79 // there may be extra blocks of information that aren't per-processor. 80 size_t section_count = sections_.size(); 81 for (size_t i = 0; i < section_count; ++i) { 82 int processor_id; 83 if (GetSectionIntValue(i, "processor", &processor_id)) { 84 ++total_cpus; 85 } 86 } 87 // Single core ARM systems don't include "processor" keys at all, so return 88 // that we have a single core if we didn't find any explicitly above. 89 if (total_cpus == 0) { 90 total_cpus = 1; 91 } 92 #else 93 // On X86, there is exactly one info section per processor. 94 total_cpus = static_cast<int>(sections_.size()); 95 #endif 96 if (num) { 97 *num = total_cpus; 98 } 99 return true; 100 } 101 102 bool ProcCpuInfo::GetNumPhysicalCpus(int* num) { 103 if (sections_.empty()) { 104 return false; 105 } 106 // TODO: /proc/cpuinfo only reports cores that are currently 107 // _online_, so this may underreport the number of physical cores. 108 #if defined(__arm__) 109 // ARM (currently) has no hyperthreading, so just return the same value 110 // as GetNumCpus. 111 return GetNumCpus(num); 112 #else 113 int total_cores = 0; 114 std::set<int> physical_ids; 115 size_t section_count = sections_.size(); 116 for (size_t i = 0; i < section_count; ++i) { 117 int physical_id; 118 int cores; 119 // Count the cores for the physical id only if we have not counted the id. 120 if (GetSectionIntValue(i, "physical id", &physical_id) && 121 GetSectionIntValue(i, "cpu cores", &cores) && 122 physical_ids.find(physical_id) == physical_ids.end()) { 123 physical_ids.insert(physical_id); 124 total_cores += cores; 125 } 126 } 127 128 if (num) { 129 *num = total_cores; 130 } 131 return true; 132 #endif 133 } 134 135 bool ProcCpuInfo::GetCpuFamily(int* id) { 136 int cpu_family = 0; 137 138 #if defined(__arm__) 139 // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But 140 // there is 'CPU Architecture' which can be used as 'cpu family'. 141 // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of 142 // ARM cpu families, architectures, and their mappings. 143 // There may be multiple sessions that aren't per-processor. We need to scan 144 // through each session until we find the first 'CPU architecture'. 145 size_t section_count = sections_.size(); 146 for (size_t i = 0; i < section_count; ++i) { 147 if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) { 148 // We returns the first one (if there are multiple entries). 149 break; 150 }; 151 } 152 #else 153 GetSectionIntValue(0, "cpu family", &cpu_family); 154 #endif 155 if (id) { 156 *id = cpu_family; 157 } 158 return true; 159 } 160 161 bool ProcCpuInfo::GetSectionStringValue(size_t section_num, 162 const std::string& key, 163 std::string* result) { 164 if (section_num >= sections_.size()) { 165 return false; 166 } 167 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); 168 if (iter == sections_[section_num].end()) { 169 return false; 170 } 171 *result = iter->second; 172 return true; 173 } 174 175 bool ProcCpuInfo::GetSectionIntValue(size_t section_num, 176 const std::string& key, 177 int* result) { 178 if (section_num >= sections_.size()) { 179 return false; 180 } 181 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); 182 if (iter == sections_[section_num].end()) { 183 return false; 184 } 185 return FromString(iter->second, result); 186 } 187 188 ConfigParser::ConfigParser() {} 189 190 ConfigParser::~ConfigParser() {} 191 192 bool ConfigParser::Open(const std::string& filename) { 193 FileStream* fs = new FileStream(); 194 if (!fs->Open(filename, "r", NULL)) { 195 return false; 196 } 197 instream_.reset(fs); 198 return true; 199 } 200 201 void ConfigParser::Attach(StreamInterface* stream) { 202 instream_.reset(stream); 203 } 204 205 bool ConfigParser::Parse(MapVector* key_val_pairs) { 206 // Parses the file and places the found key-value pairs into key_val_pairs. 207 SimpleMap section; 208 while (ParseSection(§ion)) { 209 key_val_pairs->push_back(section); 210 section.clear(); 211 } 212 return (!key_val_pairs->empty()); 213 } 214 215 bool ConfigParser::ParseSection(SimpleMap* key_val_pair) { 216 // Parses the next section in the filestream and places the found key-value 217 // pairs into key_val_pair. 218 std::string key, value; 219 while (ParseLine(&key, &value)) { 220 (*key_val_pair)[key] = value; 221 } 222 return (!key_val_pair->empty()); 223 } 224 225 bool ConfigParser::ParseLine(std::string* key, std::string* value) { 226 // Parses the next line in the filestream and places the found key-value 227 // pair into key and val. 228 std::string line; 229 if ((instream_->ReadLine(&line)) == SR_EOS) { 230 return false; 231 } 232 std::vector<std::string> tokens; 233 if (2 != split(line, ':', &tokens)) { 234 return false; 235 } 236 // Removes whitespace at the end of Key name 237 size_t pos = tokens[0].length() - 1; 238 while ((pos > 0) && isspace(tokens[0][pos])) { 239 pos--; 240 } 241 tokens[0].erase(pos + 1); 242 // Removes whitespace at the start of value 243 pos = 0; 244 while (pos < tokens[1].length() && isspace(tokens[1][pos])) { 245 pos++; 246 } 247 tokens[1].erase(0, pos); 248 *key = tokens[0]; 249 *value = tokens[1]; 250 return true; 251 } 252 253 254 std::string ReadLinuxUname() { 255 struct utsname buf; 256 if (uname(&buf) < 0) { 257 LOG_ERR(LS_ERROR) << "Can't call uname()"; 258 return std::string(); 259 } 260 std::ostringstream sstr; 261 sstr << buf.sysname << " " 262 << buf.release << " " 263 << buf.version << " " 264 << buf.machine; 265 return sstr.str(); 266 } 267 268 int ReadCpuMaxFreq() { 269 FileStream fs; 270 std::string str; 271 int freq = -1; 272 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) || 273 SR_SUCCESS != fs.ReadLine(&str) || 274 !FromString(str, &freq)) { 275 return -1; 276 } 277 return freq; 278 } 279 280 } // namespace talk_base 281 282 #endif // defined(LINUX) || defined(ANDROID) 283