/* * 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 #include #include #include #include #include #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(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 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)) == SR_EOS) { return false; } std::vector 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; } #if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD) 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; } #endif 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)