67ee6b9a62
Review URL: https://webrtc-codereview.appspot.com/7909004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5475 4adac7df-926f-26a2-2b94-8c16560cd09d
366 lines
10 KiB
C++
366 lines
10 KiB
C++
/*
|
|
* 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)) == SR_EOS) {
|
|
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;
|
|
}
|
|
|
|
#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)
|