| /* |
| * libjingle |
| * Copyright 2008 Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #if defined(LINUX) || defined(ANDROID) |
| #include "talk/base/linux.h" |
| |
| #include <ctype.h> |
| |
| #include <errno.h> |
| #include <sys/utsname.h> |
| #include <sys/wait.h> |
| |
| #include <cstdio> |
| #include <set> |
| |
| #include "talk/base/stringencode.h" |
| |
| namespace talk_base { |
| |
| static const char kCpuInfoFile[] = "/proc/cpuinfo"; |
| static const char kCpuMaxFreqFile[] = |
| "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; |
| |
| ProcCpuInfo::ProcCpuInfo() { |
| } |
| |
| ProcCpuInfo::~ProcCpuInfo() { |
| } |
| |
| bool ProcCpuInfo::LoadFromSystem() { |
| ConfigParser procfs; |
| if (!procfs.Open(kCpuInfoFile)) { |
| return false; |
| } |
| return procfs.Parse(§ions_); |
| }; |
| |
| bool ProcCpuInfo::GetSectionCount(size_t* count) { |
| if (sections_.empty()) { |
| return false; |
| } |
| if (count) { |
| *count = sections_.size(); |
| } |
| return true; |
| } |
| |
| bool ProcCpuInfo::GetNumCpus(int* num) { |
| if (sections_.empty()) { |
| return false; |
| } |
| int total_cpus = 0; |
| #if defined(__arm__) |
| // Count the number of blocks that have a "processor" key defined. On ARM, |
| // there may be extra blocks of information that aren't per-processor. |
| size_t section_count = sections_.size(); |
| for (size_t i = 0; i < section_count; ++i) { |
| int processor_id; |
| if (GetSectionIntValue(i, "processor", &processor_id)) { |
| ++total_cpus; |
| } |
| } |
| // Single core ARM systems don't include "processor" keys at all, so return |
| // that we have a single core if we didn't find any explicitly above. |
| if (total_cpus == 0) { |
| total_cpus = 1; |
| } |
| #else |
| // On X86, there is exactly one info section per processor. |
| total_cpus = static_cast<int>(sections_.size()); |
| #endif |
| if (num) { |
| *num = total_cpus; |
| } |
| return true; |
| } |
| |
| bool ProcCpuInfo::GetNumPhysicalCpus(int* num) { |
| if (sections_.empty()) { |
| return false; |
| } |
| // TODO: /proc/cpuinfo only reports cores that are currently |
| // _online_, so this may underreport the number of physical cores. |
| #if defined(__arm__) |
| // ARM (currently) has no hyperthreading, so just return the same value |
| // as GetNumCpus. |
| return GetNumCpus(num); |
| #else |
| int total_cores = 0; |
| std::set<int> physical_ids; |
| size_t section_count = sections_.size(); |
| for (size_t i = 0; i < section_count; ++i) { |
| int physical_id; |
| int cores; |
| // Count the cores for the physical id only if we have not counted the id. |
| if (GetSectionIntValue(i, "physical id", &physical_id) && |
| GetSectionIntValue(i, "cpu cores", &cores) && |
| physical_ids.find(physical_id) == physical_ids.end()) { |
| physical_ids.insert(physical_id); |
| total_cores += cores; |
| } |
| } |
| |
| if (num) { |
| *num = total_cores; |
| } |
| return true; |
| #endif |
| } |
| |
| bool ProcCpuInfo::GetCpuFamily(int* id) { |
| int cpu_family = 0; |
| |
| #if defined(__arm__) |
| // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But |
| // there is 'CPU Architecture' which can be used as 'cpu family'. |
| // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of |
| // ARM cpu families, architectures, and their mappings. |
| // There may be multiple sessions that aren't per-processor. We need to scan |
| // through each session until we find the first 'CPU architecture'. |
| size_t section_count = sections_.size(); |
| for (size_t i = 0; i < section_count; ++i) { |
| if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) { |
| // We returns the first one (if there are multiple entries). |
| break; |
| }; |
| } |
| #else |
| GetSectionIntValue(0, "cpu family", &cpu_family); |
| #endif |
| if (id) { |
| *id = cpu_family; |
| } |
| return true; |
| } |
| |
| bool ProcCpuInfo::GetSectionStringValue(size_t section_num, |
| const std::string& key, |
| std::string* result) { |
| if (section_num >= sections_.size()) { |
| return false; |
| } |
| ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); |
| if (iter == sections_[section_num].end()) { |
| return false; |
| } |
| *result = iter->second; |
| return true; |
| } |
| |
| bool ProcCpuInfo::GetSectionIntValue(size_t section_num, |
| const std::string& key, |
| int* result) { |
| if (section_num >= sections_.size()) { |
| return false; |
| } |
| ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key); |
| if (iter == sections_[section_num].end()) { |
| return false; |
| } |
| return FromString(iter->second, result); |
| } |
| |
| ConfigParser::ConfigParser() {} |
| |
| ConfigParser::~ConfigParser() {} |
| |
| bool ConfigParser::Open(const std::string& filename) { |
| FileStream* fs = new FileStream(); |
| if (!fs->Open(filename, "r", NULL)) { |
| return false; |
| } |
| instream_.reset(fs); |
| return true; |
| } |
| |
| void ConfigParser::Attach(StreamInterface* stream) { |
| instream_.reset(stream); |
| } |
| |
| bool ConfigParser::Parse(MapVector* key_val_pairs) { |
| // Parses the file and places the found key-value pairs into key_val_pairs. |
| SimpleMap section; |
| while (ParseSection(§ion)) { |
| key_val_pairs->push_back(section); |
| section.clear(); |
| } |
| return (!key_val_pairs->empty()); |
| } |
| |
| bool ConfigParser::ParseSection(SimpleMap* key_val_pair) { |
| // Parses the next section in the filestream and places the found key-value |
| // pairs into key_val_pair. |
| std::string key, value; |
| while (ParseLine(&key, &value)) { |
| (*key_val_pair)[key] = value; |
| } |
| return (!key_val_pair->empty()); |
| } |
| |
| bool ConfigParser::ParseLine(std::string* key, std::string* value) { |
| // Parses the next line in the filestream and places the found key-value |
| // pair into key and val. |
| std::string line; |
| if ((instream_->ReadLine(&line)) == EOF) { |
| return false; |
| } |
| std::vector<std::string> tokens; |
| if (2 != split(line, ':', &tokens)) { |
| return false; |
| } |
| // Removes whitespace at the end of Key name |
| size_t pos = tokens[0].length() - 1; |
| while ((pos > 0) && isspace(tokens[0][pos])) { |
| pos--; |
| } |
| tokens[0].erase(pos + 1); |
| // Removes whitespace at the start of value |
| pos = 0; |
| while (pos < tokens[1].length() && isspace(tokens[1][pos])) { |
| pos++; |
| } |
| tokens[1].erase(0, pos); |
| *key = tokens[0]; |
| *value = tokens[1]; |
| return true; |
| } |
| |
| static bool ExpectLineFromStream(FileStream* stream, |
| std::string* out) { |
| StreamResult res = stream->ReadLine(out); |
| if (res != SR_SUCCESS) { |
| if (res != SR_EOS) { |
| LOG(LS_ERROR) << "Error when reading from stream"; |
| } else { |
| LOG(LS_ERROR) << "Incorrect number of lines in stream"; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| static void ExpectEofFromStream(FileStream* stream) { |
| std::string unused; |
| StreamResult res = stream->ReadLine(&unused); |
| if (res == SR_SUCCESS) { |
| LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream"; |
| } else if (res != SR_EOS) { |
| LOG(LS_WARNING) << "Error when checking for extra lines from stream"; |
| } |
| } |
| |
| // For caching the lsb_release output (reading it invokes a sub-process and |
| // hence is somewhat expensive). |
| static std::string lsb_release_string; |
| static CriticalSection lsb_release_string_critsec; |
| |
| std::string ReadLinuxLsbRelease() { |
| CritScope cs(&lsb_release_string_critsec); |
| if (!lsb_release_string.empty()) { |
| // Have cached result from previous call. |
| return lsb_release_string; |
| } |
| // No cached result. Run lsb_release and parse output. |
| POpenStream lsb_release_output; |
| if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) { |
| LOG_ERR(LS_ERROR) << "Can't run lsb_release"; |
| return lsb_release_string; // empty |
| } |
| // Read in the command's output and build the string. |
| std::ostringstream sstr; |
| std::string line; |
| int wait_status; |
| |
| if (!ExpectLineFromStream(&lsb_release_output, &line)) { |
| return lsb_release_string; // empty |
| } |
| sstr << "DISTRIB_ID=" << line; |
| |
| if (!ExpectLineFromStream(&lsb_release_output, &line)) { |
| return lsb_release_string; // empty |
| } |
| sstr << " DISTRIB_DESCRIPTION=\"" << line << '"'; |
| |
| if (!ExpectLineFromStream(&lsb_release_output, &line)) { |
| return lsb_release_string; // empty |
| } |
| sstr << " DISTRIB_RELEASE=" << line; |
| |
| if (!ExpectLineFromStream(&lsb_release_output, &line)) { |
| return lsb_release_string; // empty |
| } |
| sstr << " DISTRIB_CODENAME=" << line; |
| |
| // Should not be anything left. |
| ExpectEofFromStream(&lsb_release_output); |
| |
| lsb_release_output.Close(); |
| wait_status = lsb_release_output.GetWaitStatus(); |
| if (wait_status == -1 || |
| !WIFEXITED(wait_status) || |
| WEXITSTATUS(wait_status) != 0) { |
| LOG(LS_WARNING) << "Unexpected exit status from lsb_release"; |
| } |
| |
| lsb_release_string = sstr.str(); |
| |
| return lsb_release_string; |
| } |
| |
| std::string ReadLinuxUname() { |
| struct utsname buf; |
| if (uname(&buf) < 0) { |
| LOG_ERR(LS_ERROR) << "Can't call uname()"; |
| return std::string(); |
| } |
| std::ostringstream sstr; |
| sstr << buf.sysname << " " |
| << buf.release << " " |
| << buf.version << " " |
| << buf.machine; |
| return sstr.str(); |
| } |
| |
| int ReadCpuMaxFreq() { |
| FileStream fs; |
| std::string str; |
| int freq = -1; |
| if (!fs.Open(kCpuMaxFreqFile, "r", NULL) || |
| SR_SUCCESS != fs.ReadLine(&str) || |
| !FromString(str, &freq)) { |
| return -1; |
| } |
| return freq; |
| } |
| |
| } // namespace talk_base |
| |
| #endif // defined(LINUX) || defined(ANDROID) |