• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&sections_);
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(&section)) {
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