blob: b958543532ed05a6bad6667e37ff99483835d349 [file] [log] [blame]
henrike@webrtc.org47be73b2014-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
236#if !defined(WEBRTC_CHROMIUM_BUILDs)
237static bool ExpectLineFromStream(FileStream* stream,
238 std::string* out) {
239 StreamResult res = stream->ReadLine(out);
240 if (res != SR_SUCCESS) {
241 if (res != SR_EOS) {
242 LOG(LS_ERROR) << "Error when reading from stream";
243 } else {
244 LOG(LS_ERROR) << "Incorrect number of lines in stream";
245 }
246 return false;
247 }
248 return true;
249}
250
251static void ExpectEofFromStream(FileStream* stream) {
252 std::string unused;
253 StreamResult res = stream->ReadLine(&unused);
254 if (res == SR_SUCCESS) {
255 LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
256 } else if (res != SR_EOS) {
257 LOG(LS_WARNING) << "Error when checking for extra lines from stream";
258 }
259}
260
261// For caching the lsb_release output (reading it invokes a sub-process and
262// hence is somewhat expensive).
263static std::string lsb_release_string;
264static CriticalSection lsb_release_string_critsec;
265
266std::string ReadLinuxLsbRelease() {
267 CritScope cs(&lsb_release_string_critsec);
268 if (!lsb_release_string.empty()) {
269 // Have cached result from previous call.
270 return lsb_release_string;
271 }
272 // No cached result. Run lsb_release and parse output.
273 POpenStream lsb_release_output;
274 if (!lsb_release_output.Open("lsb_release -idrcs", "r", NULL)) {
275 LOG_ERR(LS_ERROR) << "Can't run lsb_release";
276 return lsb_release_string; // empty
277 }
278 // Read in the command's output and build the string.
279 std::ostringstream sstr;
280 std::string line;
281 int wait_status;
282
283 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
284 return lsb_release_string; // empty
285 }
286 sstr << "DISTRIB_ID=" << line;
287
288 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
289 return lsb_release_string; // empty
290 }
291 sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
292
293 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
294 return lsb_release_string; // empty
295 }
296 sstr << " DISTRIB_RELEASE=" << line;
297
298 if (!ExpectLineFromStream(&lsb_release_output, &line)) {
299 return lsb_release_string; // empty
300 }
301 sstr << " DISTRIB_CODENAME=" << line;
302
303 // Should not be anything left.
304 ExpectEofFromStream(&lsb_release_output);
305
306 lsb_release_output.Close();
307 wait_status = lsb_release_output.GetWaitStatus();
308 if (wait_status == -1 ||
309 !WIFEXITED(wait_status) ||
310 WEXITSTATUS(wait_status) != 0) {
311 LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
312 }
313
314 lsb_release_string = sstr.str();
315
316 return lsb_release_string;
317}
318#endif
319
320std::string ReadLinuxUname() {
321 struct utsname buf;
322 if (uname(&buf) < 0) {
323 LOG_ERR(LS_ERROR) << "Can't call uname()";
324 return std::string();
325 }
326 std::ostringstream sstr;
327 sstr << buf.sysname << " "
328 << buf.release << " "
329 << buf.version << " "
330 << buf.machine;
331 return sstr.str();
332}
333
334int ReadCpuMaxFreq() {
335 FileStream fs;
336 std::string str;
337 int freq = -1;
338 if (!fs.Open(kCpuMaxFreqFile, "r", NULL) ||
339 SR_SUCCESS != fs.ReadLine(&str) ||
340 !FromString(str, &freq)) {
341 return -1;
342 }
343 return freq;
344}
345
346} // namespace rtc
347
348#endif // defined(WEBRTC_LINUX)