3 * Copyright 2008 Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #if defined(LINUX) || defined(ANDROID)
29 #include "talk/base/linux.h"
34 #include <sys/utsname.h>
40 #include "talk/base/stringencode.h"
44 static const char kCpuInfoFile[] = "/proc/cpuinfo";
45 static const char kCpuMaxFreqFile[] =
46 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
48 ProcCpuInfo::ProcCpuInfo() {
51 ProcCpuInfo::~ProcCpuInfo() {
54 bool ProcCpuInfo::LoadFromSystem() {
56 if (!procfs.Open(kCpuInfoFile)) {
59 return procfs.Parse(§ions_);
62 bool ProcCpuInfo::GetSectionCount(size_t* count) {
63 if (sections_.empty()) {
67 *count = sections_.size();
72 bool ProcCpuInfo::GetNumCpus(int* num) {
73 if (sections_.empty()) {
78 // Count the number of blocks that have a "processor" key defined. On ARM,
79 // there may be extra blocks of information that aren't per-processor.
80 size_t section_count = sections_.size();
81 for (size_t i = 0; i < section_count; ++i) {
83 if (GetSectionIntValue(i, "processor", &processor_id)) {
87 // Single core ARM systems don't include "processor" keys at all, so return
88 // that we have a single core if we didn't find any explicitly above.
89 if (total_cpus == 0) {
93 // On X86, there is exactly one info section per processor.
94 total_cpus = static_cast<int>(sections_.size());
102 bool ProcCpuInfo::GetNumPhysicalCpus(int* num) {
103 if (sections_.empty()) {
106 // TODO: /proc/cpuinfo only reports cores that are currently
107 // _online_, so this may underreport the number of physical cores.
109 // ARM (currently) has no hyperthreading, so just return the same value
111 return GetNumCpus(num);
114 std::set<int> physical_ids;
115 size_t section_count = sections_.size();
116 for (size_t i = 0; i < section_count; ++i) {
119 // Count the cores for the physical id only if we have not counted the id.
120 if (GetSectionIntValue(i, "physical id", &physical_id) &&
121 GetSectionIntValue(i, "cpu cores", &cores) &&
122 physical_ids.find(physical_id) == physical_ids.end()) {
123 physical_ids.insert(physical_id);
124 total_cores += cores;
135 bool ProcCpuInfo::GetCpuFamily(int* id) {
139 // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But
140 // there is 'CPU Architecture' which can be used as 'cpu family'.
141 // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of
142 // ARM cpu families, architectures, and their mappings.
143 // There may be multiple sessions that aren't per-processor. We need to scan
144 // through each session until we find the first 'CPU architecture'.
145 size_t section_count = sections_.size();
146 for (size_t i = 0; i < section_count; ++i) {
147 if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) {
148 // We returns the first one (if there are multiple entries).
153 GetSectionIntValue(0, "cpu family", &cpu_family);
161 bool ProcCpuInfo::GetSectionStringValue(size_t section_num,
162 const std::string& key,
163 std::string* result) {
164 if (section_num >= sections_.size()) {
167 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
168 if (iter == sections_[section_num].end()) {
171 *result = iter->second;
175 bool ProcCpuInfo::GetSectionIntValue(size_t section_num,
176 const std::string& key,
178 if (section_num >= sections_.size()) {
181 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
182 if (iter == sections_[section_num].end()) {
185 return FromString(iter->second, result);
188 ConfigParser::ConfigParser() {}
190 ConfigParser::~ConfigParser() {}
192 bool ConfigParser::Open(const std::string& filename) {
193 FileStream* fs = new FileStream();
194 if (!fs->Open(filename, "r", NULL)) {
201 void ConfigParser::Attach(StreamInterface* stream) {
202 instream_.reset(stream);
205 bool ConfigParser::Parse(MapVector* key_val_pairs) {
206 // Parses the file and places the found key-value pairs into key_val_pairs.
208 while (ParseSection(§ion)) {
209 key_val_pairs->push_back(section);
212 return (!key_val_pairs->empty());
215 bool ConfigParser::ParseSection(SimpleMap* key_val_pair) {
216 // Parses the next section in the filestream and places the found key-value
217 // pairs into key_val_pair.
218 std::string key, value;
219 while (ParseLine(&key, &value)) {
220 (*key_val_pair)[key] = value;
222 return (!key_val_pair->empty());
225 bool ConfigParser::ParseLine(std::string* key, std::string* value) {
226 // Parses the next line in the filestream and places the found key-value
227 // pair into key and val.
229 if ((instream_->ReadLine(&line)) == SR_EOS) {
232 std::vector<std::string> tokens;
233 if (2 != split(line, ':', &tokens)) {
236 // Removes whitespace at the end of Key name
237 size_t pos = tokens[0].length() - 1;
238 while ((pos > 0) && isspace(tokens[0][pos])) {
241 tokens[0].erase(pos + 1);
242 // Removes whitespace at the start of value
244 while (pos < tokens[1].length() && isspace(tokens[1][pos])) {
247 tokens[1].erase(0, pos);
253 #if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD)
254 static bool ExpectLineFromStream(FileStream* stream,
256 StreamResult res = stream->ReadLine(out);
257 if (res != SR_SUCCESS) {
259 LOG(LS_ERROR) << "Error when reading from stream";
261 LOG(LS_ERROR) << "Incorrect number of lines in stream";
268 static void ExpectEofFromStream(FileStream* stream) {
270 StreamResult res = stream->ReadLine(&unused);
271 if (res == SR_SUCCESS) {
272 LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
273 } else if (res != SR_EOS) {
274 LOG(LS_WARNING) << "Error when checking for extra lines from stream";
278 // For caching the lsb_release output (reading it invokes a sub-process and
279 // hence is somewhat expensive).
280 static std::string lsb_release_string;
281 static CriticalSection lsb_release_string_critsec;
283 std::string ReadLinuxLsbRelease() {
284 CritScope cs(&lsb_release_string_critsec);
285 if (!lsb_release_string.empty()) {
286 // Have cached result from previous call.
287 return lsb_release_string;
289 // No cached result. Run lsb_release and parse output.
290 POpenStream lsb_release_output;
291 if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) {
292 LOG_ERR(LS_ERROR) << "Can't run lsb_release";
293 return lsb_release_string; // empty
295 // Read in the command's output and build the string.
296 std::ostringstream sstr;
300 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
301 return lsb_release_string; // empty
303 sstr << "DISTRIB_ID=" << line;
305 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
306 return lsb_release_string; // empty
308 sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
310 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
311 return lsb_release_string; // empty
313 sstr << " DISTRIB_RELEASE=" << line;
315 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
316 return lsb_release_string; // empty
318 sstr << " DISTRIB_CODENAME=" << line;
320 // Should not be anything left.
321 ExpectEofFromStream(&lsb_release_output);
323 lsb_release_output.Close();
324 wait_status = lsb_release_output.GetWaitStatus();
325 if (wait_status == -1 ||
326 !WIFEXITED(wait_status) ||
327 WEXITSTATUS(wait_status) != 0) {
328 LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
331 lsb_release_string = sstr.str();
333 return lsb_release_string;
337 std::string ReadLinuxUname() {
339 if (uname(&buf) < 0) {
340 LOG_ERR(LS_ERROR) << "Can't call uname()";
341 return std::string();
343 std::ostringstream sstr;
344 sstr << buf.sysname << " "
345 << buf.release << " "
346 << buf.version << " "
351 int ReadCpuMaxFreq() {
355 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) ||
356 SR_SUCCESS != fs.ReadLine(&str) ||
357 !FromString(str, &freq)) {
363 } // namespace talk_base
365 #endif // defined(LINUX) || defined(ANDROID)