Home | History | Annotate | Download | only in base
      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(&sections_);
     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(&section)) {
    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