1 /*
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
25 namespace rtc {
26
27 static const char kCpuInfoFile[] = "/proc/cpuinfo";
28 static const char kCpuMaxFreqFile[] =
29 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
30
ProcCpuInfo()31 ProcCpuInfo::ProcCpuInfo() {
32 }
33
~ProcCpuInfo()34 ProcCpuInfo::~ProcCpuInfo() {
35 }
36
LoadFromSystem()37 bool ProcCpuInfo::LoadFromSystem() {
38 ConfigParser procfs;
39 if (!procfs.Open(kCpuInfoFile)) {
40 return false;
41 }
42 return procfs.Parse(§ions_);
43 };
44
GetSectionCount(size_t * count)45 bool 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
GetNumCpus(int * num)55 bool 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
GetNumPhysicalCpus(int * num)85 bool 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
GetCpuFamily(int * id)118 bool 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
GetSectionStringValue(size_t section_num,const std::string & key,std::string * result)144 bool 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
GetSectionIntValue(size_t section_num,const std::string & key,int * result)158 bool 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
ConfigParser()171 ConfigParser::ConfigParser() {}
172
~ConfigParser()173 ConfigParser::~ConfigParser() {}
174
Open(const std::string & filename)175 bool 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
Attach(StreamInterface * stream)184 void ConfigParser::Attach(StreamInterface* stream) {
185 instream_.reset(stream);
186 }
187
Parse(MapVector * key_val_pairs)188 bool 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(§ion)) {
192 key_val_pairs->push_back(section);
193 section.clear();
194 }
195 return (!key_val_pairs->empty());
196 }
197
ParseSection(SimpleMap * key_val_pair)198 bool 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
ParseLine(std::string * key,std::string * value)208 bool 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)
ExpectLineFromStream(FileStream * stream,std::string * out)237 static 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
ExpectEofFromStream(FileStream * stream)251 static 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).
263 static std::string lsb_release_string;
264 static CriticalSection lsb_release_string_critsec;
265
ReadLinuxLsbRelease()266 std::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
ReadLinuxUname()320 std::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
ReadCpuMaxFreq()334 int 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)
349