blob: 0894d39c7716a50c5e20b1f75aef80ac25922a86 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2008 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_LINUX)
12#include "webrtc/base/linux.h"
13
14#include <ctype.h>
15
16#include <errno.h>
17#include <sys/utsname.h>
18#include <sys/wait.h>
19
20#include <cstdio>
21#include <set>
22
23#include "webrtc/base/stringencode.h"
24
25namespace rtc {
26
27static const char kCpuInfoFile[] = "/proc/cpuinfo";
28static const char kCpuMaxFreqFile[] =
29 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
30
31ProcCpuInfo::ProcCpuInfo() {
32}
33
34ProcCpuInfo::~ProcCpuInfo() {
35}
36
37bool ProcCpuInfo::LoadFromSystem() {
38 ConfigParser procfs;
39 if (!procfs.Open(kCpuInfoFile)) {
40 return false;
41 }
42 return procfs.Parse(&sections_);
43};
44
45bool ProcCpuInfo::GetSectionCount(size_t* count) {
46 if (sections_.empty()) {
47 return false;
48 }
49 if (count) {
50 *count = sections_.size();
51 }
52 return true;
53}
54
55bool ProcCpuInfo::GetNumCpus(int* num) {
56 if (sections_.empty()) {
57 return false;
58 }
59 int total_cpus = 0;
60#if defined(__arm__)
61 // Count the number of blocks that have a "processor" key defined. On ARM,
62 // there may be extra blocks of information that aren't per-processor.
63 size_t section_count = sections_.size();
64 for (size_t i = 0; i < section_count; ++i) {
65 int processor_id;
66 if (GetSectionIntValue(i, "processor", &processor_id)) {
67 ++total_cpus;
68 }
69 }
70 // Single core ARM systems don't include "processor" keys at all, so return
71 // that we have a single core if we didn't find any explicitly above.
72 if (total_cpus == 0) {
73 total_cpus = 1;
74 }
75#else
76 // On X86, there is exactly one info section per processor.
77 total_cpus = static_cast<int>(sections_.size());
78#endif
79 if (num) {
80 *num = total_cpus;
81 }
82 return true;
83}
84
85bool ProcCpuInfo::GetNumPhysicalCpus(int* num) {
86 if (sections_.empty()) {
87 return false;
88 }
89 // TODO: /proc/cpuinfo only reports cores that are currently
90 // _online_, so this may underreport the number of physical cores.
91#if defined(__arm__)
92 // ARM (currently) has no hyperthreading, so just return the same value
93 // as GetNumCpus.
94 return GetNumCpus(num);
95#else
96 int total_cores = 0;
97 std::set<int> physical_ids;
98 size_t section_count = sections_.size();
99 for (size_t i = 0; i < section_count; ++i) {
100 int physical_id;
101 int cores;
102 // Count the cores for the physical id only if we have not counted the id.
103 if (GetSectionIntValue(i, "physical id", &physical_id) &&
104 GetSectionIntValue(i, "cpu cores", &cores) &&
105 physical_ids.find(physical_id) == physical_ids.end()) {
106 physical_ids.insert(physical_id);
107 total_cores += cores;
108 }
109 }
110
111 if (num) {
112 *num = total_cores;
113 }
114 return true;
115#endif
116}
117
118bool ProcCpuInfo::GetCpuFamily(int* id) {
119 int cpu_family = 0;
120
121#if defined(__arm__)
122 // On some ARM platforms, there is no 'cpu family' in '/proc/cpuinfo'. But
123 // there is 'CPU Architecture' which can be used as 'cpu family'.
124 // See http://en.wikipedia.org/wiki/ARM_architecture for a good list of
125 // ARM cpu families, architectures, and their mappings.
126 // There may be multiple sessions that aren't per-processor. We need to scan
127 // through each session until we find the first 'CPU architecture'.
128 size_t section_count = sections_.size();
129 for (size_t i = 0; i < section_count; ++i) {
130 if (GetSectionIntValue(i, "CPU architecture", &cpu_family)) {
131 // We returns the first one (if there are multiple entries).
132 break;
133 };
134 }
135#else
136 GetSectionIntValue(0, "cpu family", &cpu_family);
137#endif
138 if (id) {
139 *id = cpu_family;
140 }
141 return true;
142}
143
144bool ProcCpuInfo::GetSectionStringValue(size_t section_num,
145 const std::string& key,
146 std::string* result) {
147 if (section_num >= sections_.size()) {
148 return false;
149 }
150 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
151 if (iter == sections_[section_num].end()) {
152 return false;
153 }
154 *result = iter->second;
155 return true;
156}
157
158bool ProcCpuInfo::GetSectionIntValue(size_t section_num,
159 const std::string& key,
160 int* result) {
161 if (section_num >= sections_.size()) {
162 return false;
163 }
164 ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
165 if (iter == sections_[section_num].end()) {
166 return false;
167 }
168 return FromString(iter->second, result);
169}
170
171ConfigParser::ConfigParser() {}
172
173ConfigParser::~ConfigParser() {}
174
175bool ConfigParser::Open(const std::string& filename) {
176 FileStream* fs = new FileStream();
177 if (!fs->Open(filename, "r", NULL)) {
178 return false;
179 }
180 instream_.reset(fs);
181 return true;
182}
183
184void ConfigParser::Attach(StreamInterface* stream) {
185 instream_.reset(stream);
186}
187
188bool ConfigParser::Parse(MapVector* key_val_pairs) {
189 // Parses the file and places the found key-value pairs into key_val_pairs.
190 SimpleMap section;
191 while (ParseSection(&section)) {
192 key_val_pairs->push_back(section);
193 section.clear();
194 }
195 return (!key_val_pairs->empty());
196}
197
198bool ConfigParser::ParseSection(SimpleMap* key_val_pair) {
199 // Parses the next section in the filestream and places the found key-value
200 // pairs into key_val_pair.
201 std::string key, value;
202 while (ParseLine(&key, &value)) {
203 (*key_val_pair)[key] = value;
204 }
205 return (!key_val_pair->empty());
206}
207
208bool ConfigParser::ParseLine(std::string* key, std::string* value) {
209 // Parses the next line in the filestream and places the found key-value
210 // pair into key and val.
211 std::string line;
212 if ((instream_->ReadLine(&line)) == SR_EOS) {
213 return false;
214 }
215 std::vector<std::string> tokens;
216 if (2 != split(line, ':', &tokens)) {
217 return false;
218 }
219 // Removes whitespace at the end of Key name
220 size_t pos = tokens[0].length() - 1;
221 while ((pos > 0) && isspace(tokens[0][pos])) {
222 pos--;
223 }
224 tokens[0].erase(pos + 1);
225 // Removes whitespace at the start of value
226 pos = 0;
227 while (pos < tokens[1].length() && isspace(tokens[1][pos])) {
228 pos++;
229 }
230 tokens[1].erase(0, pos);
231 *key = tokens[0];
232 *value = tokens[1];
233 return true;
234}
235
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000236std::string ReadLinuxUname() {
237 struct utsname buf;
238 if (uname(&buf) < 0) {
239 LOG_ERR(LS_ERROR) << "Can't call uname()";
240 return std::string();
241 }
242 std::ostringstream sstr;
243 sstr << buf.sysname << " "
244 << buf.release << " "
245 << buf.version << " "
246 << buf.machine;
247 return sstr.str();
248}
249
250int ReadCpuMaxFreq() {
251 FileStream fs;
252 std::string str;
253 int freq = -1;
254 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) ||
255 SR_SUCCESS != fs.ReadLine(&str) ||
256 !FromString(str, &freq)) {
257 return -1;
258 }
259 return freq;
260}
261
262} // namespace rtc
263
264#endif // defined(WEBRTC_LINUX)