Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2011, 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 #ifdef LINUX
     29 #include "talk/base/linux.h"
     30 
     31 #include <errno.h>
     32 #include <sys/utsname.h>
     33 
     34 #include <cstdio>
     35 
     36 #include "talk/base/stringencode.h"
     37 
     38 namespace talk_base {
     39 
     40 static const char kCpuInfoFile[] = "/proc/cpuinfo";
     41 static const char kCpuMaxFreqFile[] =
     42     "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
     43 
     44 ProcCpuInfo::ProcCpuInfo() {
     45 }
     46 
     47 ProcCpuInfo::~ProcCpuInfo() {
     48 }
     49 
     50 bool ProcCpuInfo::LoadFromSystem() {
     51   ConfigParser procfs;
     52   if (!procfs.Open(kCpuInfoFile)) {
     53     return false;
     54   }
     55   return procfs.Parse(&cpu_info_);
     56 };
     57 
     58 bool ProcCpuInfo::GetNumCpus(int *num) {
     59   if (cpu_info_.size() == 0) {
     60     return false;
     61   }
     62   *num = cpu_info_.size();
     63   return true;
     64 }
     65 
     66 bool ProcCpuInfo::GetNumPhysicalCpus(int *num) {
     67   if (cpu_info_.size() == 0) {
     68     return false;
     69   }
     70   int total_cores = 0;
     71   int physical_id_prev = -1;
     72   int cpus = static_cast<int>(cpu_info_.size());
     73   for (int i = 0; i < cpus; ++i) {
     74     int physical_id;
     75     if (GetCpuIntValue(i, "physical id", &physical_id)) {
     76       if (physical_id != physical_id_prev) {
     77         physical_id_prev = physical_id;
     78         int cores;
     79         if (GetCpuIntValue(i, "cpu cores", &cores)) {
     80           total_cores += cores;
     81         }
     82       }
     83     }
     84   }
     85   return total_cores;
     86 }
     87 
     88 bool ProcCpuInfo::GetCpuStringValue(int cpu_id, const std::string& key,
     89                                     std::string *result) {
     90   if (cpu_id >= static_cast<int>(cpu_info_.size()))
     91     return false;
     92   ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
     93   if (iter == cpu_info_[cpu_id].end()) {
     94     return false;
     95   }
     96   *result = iter->second;
     97   return true;
     98 }
     99 
    100 bool ProcCpuInfo::GetCpuIntValue(int cpu_id, const std::string& key,
    101                                  int *result) {
    102   if (cpu_id >= static_cast<int>(cpu_info_.size())) {
    103     return false;
    104   }
    105   ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
    106   if (iter == cpu_info_[cpu_id].end()) {
    107     return false;
    108   }
    109   *result = atoi((iter->second).c_str());
    110   return true;
    111 }
    112 
    113 ConfigParser::ConfigParser() {}
    114 
    115 ConfigParser::~ConfigParser() {}
    116 
    117 bool ConfigParser::Open(const std::string& filename) {
    118   FileStream *fs = new FileStream();
    119   if (!fs->Open(filename, "r")) {
    120     return false;
    121   }
    122   instream_.reset(fs);
    123   return true;
    124 }
    125 
    126 void ConfigParser::Attach(StreamInterface* stream) {
    127   instream_.reset(stream);
    128 }
    129 
    130 bool ConfigParser::Parse(MapVector *key_val_pairs) {
    131   // Parses the file and places the found key-value pairs into key_val_pairs.
    132   SimpleMap section;
    133   while (ParseSection(&section)) {
    134     key_val_pairs->push_back(section);
    135     section.clear();
    136   }
    137   return (!key_val_pairs->empty());
    138 }
    139 
    140 bool ConfigParser::ParseSection(SimpleMap *key_val_pair) {
    141   // Parses the next section in the filestream and places the found key-value
    142   // pairs into key_val_pair.
    143   std::string key, value;
    144   while (ParseLine(&key, &value)) {
    145     (*key_val_pair)[key] = value;
    146   }
    147   return (!key_val_pair->empty());
    148 }
    149 
    150 bool ConfigParser::ParseLine(std::string *key, std::string *value) {
    151   // Parses the next line in the filestream and places the found key-value
    152   // pair into key and val.
    153   std::string line;
    154   if ((instream_->ReadLine(&line)) == EOF) {
    155     return false;
    156   }
    157   std::vector<std::string> tokens;
    158   if (2 != split(line, ':', &tokens)) {
    159     return false;
    160   }
    161   // Removes whitespace at the end of Key name
    162   size_t pos = tokens[0].length() - 1;
    163   while ((pos > 0) && isspace(tokens[0][pos])) {
    164     pos--;
    165   }
    166   tokens[0].erase(pos + 1);
    167   // Removes whitespace at the start of value
    168   pos = 0;
    169   while (pos < tokens[1].length() && isspace(tokens[1][pos])) {
    170     pos++;
    171   }
    172   tokens[1].erase(0, pos);
    173   *key = tokens[0];
    174   *value = tokens[1];
    175   return true;
    176 }
    177 
    178 static bool ExpectLineFromStream(FileStream *stream,
    179                                  std::string *out) {
    180   StreamResult res = stream->ReadLine(out);
    181   if (res != SR_SUCCESS) {
    182     if (res != SR_EOS) {
    183       LOG(LS_ERROR) << "Error when reading from stream";
    184     } else {
    185       LOG(LS_ERROR) << "Incorrect number of lines in stream";
    186     }
    187     return false;
    188   }
    189   return true;
    190 }
    191 
    192 static void ExpectEofFromStream(FileStream *stream) {
    193   std::string unused;
    194   StreamResult res = stream->ReadLine(&unused);
    195   if (res == SR_SUCCESS) {
    196     LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
    197   } else if (res != SR_EOS) {
    198     LOG(LS_WARNING) << "Error when checking for extra lines from stream";
    199   }
    200 }
    201 
    202 // For caching the lsb_release output (reading it invokes a sub-process and
    203 // hence is somewhat expensive).
    204 static std::string lsb_release_string;
    205 static CriticalSection lsb_release_string_critsec;
    206 
    207 std::string ReadLinuxLsbRelease() {
    208   CritScope cs(&lsb_release_string_critsec);
    209   if (!lsb_release_string.empty()) {
    210     // Have cached result from previous call.
    211     return lsb_release_string;
    212   }
    213   // No cached result. Run lsb_release and parse output.
    214   POpenStream lsb_release_output;
    215   if (!lsb_release_output.Open("lsb_release -idrcs", "r")) {
    216     LOG_ERR(LS_ERROR) << "Can't run lsb_release";
    217     return lsb_release_string;  // empty
    218   }
    219   // Read in the command's output and build the string.
    220   std::ostringstream sstr;
    221   std::string line;
    222   int wait_status;
    223 
    224   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
    225     return lsb_release_string;  // empty
    226   }
    227   sstr << "DISTRIB_ID=" << line;
    228 
    229   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
    230     return lsb_release_string;  // empty
    231   }
    232   sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
    233 
    234   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
    235     return lsb_release_string;  // empty
    236   }
    237   sstr << " DISTRIB_RELEASE=" << line;
    238 
    239   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
    240     return lsb_release_string;  // empty
    241   }
    242   sstr << " DISTRIB_CODENAME=" << line;
    243 
    244   // Should not be anything left.
    245   ExpectEofFromStream(&lsb_release_output);
    246 
    247   lsb_release_output.Close();
    248   wait_status = lsb_release_output.GetWaitStatus();
    249   if (wait_status == -1 ||
    250       !WIFEXITED(wait_status) ||
    251       WEXITSTATUS(wait_status) != 0) {
    252     LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
    253   }
    254 
    255   lsb_release_string = sstr.str();
    256 
    257   return lsb_release_string;
    258 }
    259 
    260 std::string ReadLinuxUname() {
    261   struct utsname buf;
    262   if (uname(&buf) < 0) {
    263     LOG_ERR(LS_ERROR) << "Can't call uname()";
    264     return std::string();
    265   }
    266   std::ostringstream sstr;
    267   sstr << buf.sysname << " "
    268        << buf.release << " "
    269        << buf.version << " "
    270        << buf.machine;
    271   return sstr.str();
    272 }
    273 
    274 int ReadCpuMaxFreq() {
    275   FileStream fs;
    276   std::string str;
    277   if (!fs.Open(kCpuMaxFreqFile, "r") || SR_SUCCESS != fs.ReadLine(&str)) {
    278     return -1;
    279   }
    280   return atoi(str.c_str());
    281 }
    282 
    283 }  // namespace talk_base
    284 
    285 #endif  // LINUX
    286