Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / linux.cc
1 /*
2  * libjingle
3  * Copyright 2008 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
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.
15  *
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.
26  */
27
28 #if defined(LINUX) || defined(ANDROID)
29 #include "talk/base/linux.h"
30
31 #include <ctype.h>
32
33 #include <errno.h>
34 #include <sys/utsname.h>
35 #include <sys/wait.h>
36
37 #include <cstdio>
38 #include <set>
39
40 #include "talk/base/stringencode.h"
41
42 namespace talk_base {
43
44 static const char kCpuInfoFile[] = "/proc/cpuinfo";
45 static const char kCpuMaxFreqFile[] =
46     "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
47
48 ProcCpuInfo::ProcCpuInfo() {
49 }
50
51 ProcCpuInfo::~ProcCpuInfo() {
52 }
53
54 bool ProcCpuInfo::LoadFromSystem() {
55   ConfigParser procfs;
56   if (!procfs.Open(kCpuInfoFile)) {
57     return false;
58   }
59   return procfs.Parse(&sections_);
60 };
61
62 bool ProcCpuInfo::GetSectionCount(size_t* count) {
63   if (sections_.empty()) {
64     return false;
65   }
66   if (count) {
67     *count = sections_.size();
68   }
69   return true;
70 }
71
72 bool ProcCpuInfo::GetNumCpus(int* num) {
73   if (sections_.empty()) {
74     return false;
75   }
76   int total_cpus = 0;
77 #if defined(__arm__)
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) {
82     int processor_id;
83     if (GetSectionIntValue(i, "processor", &processor_id)) {
84       ++total_cpus;
85     }
86   }
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) {
90     total_cpus = 1;
91   }
92 #else
93   // On X86, there is exactly one info section per processor.
94   total_cpus = static_cast<int>(sections_.size());
95 #endif
96   if (num) {
97     *num = total_cpus;
98   }
99   return true;
100 }
101
102 bool ProcCpuInfo::GetNumPhysicalCpus(int* num) {
103   if (sections_.empty()) {
104     return false;
105   }
106   // TODO: /proc/cpuinfo only reports cores that are currently
107   // _online_, so this may underreport the number of physical cores.
108 #if defined(__arm__)
109   // ARM (currently) has no hyperthreading, so just return the same value
110   // as GetNumCpus.
111   return GetNumCpus(num);
112 #else
113   int total_cores = 0;
114   std::set<int> physical_ids;
115   size_t section_count = sections_.size();
116   for (size_t i = 0; i < section_count; ++i) {
117     int physical_id;
118     int cores;
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;
125     }
126   }
127
128   if (num) {
129     *num = total_cores;
130   }
131   return true;
132 #endif
133 }
134
135 bool ProcCpuInfo::GetCpuFamily(int* id) {
136   int cpu_family = 0;
137
138 #if defined(__arm__)
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).
149       break;
150     };
151   }
152 #else
153   GetSectionIntValue(0, "cpu family", &cpu_family);
154 #endif
155   if (id) {
156     *id = cpu_family;
157   }
158   return true;
159 }
160
161 bool ProcCpuInfo::GetSectionStringValue(size_t section_num,
162                                         const std::string& key,
163                                         std::string* result) {
164   if (section_num >= sections_.size()) {
165     return false;
166   }
167   ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
168   if (iter == sections_[section_num].end()) {
169     return false;
170   }
171   *result = iter->second;
172   return true;
173 }
174
175 bool ProcCpuInfo::GetSectionIntValue(size_t section_num,
176                                      const std::string& key,
177                                      int* result) {
178   if (section_num >= sections_.size()) {
179     return false;
180   }
181   ConfigParser::SimpleMap::iterator iter = sections_[section_num].find(key);
182   if (iter == sections_[section_num].end()) {
183     return false;
184   }
185   return FromString(iter->second, result);
186 }
187
188 ConfigParser::ConfigParser() {}
189
190 ConfigParser::~ConfigParser() {}
191
192 bool ConfigParser::Open(const std::string& filename) {
193   FileStream* fs = new FileStream();
194   if (!fs->Open(filename, "r", NULL)) {
195     return false;
196   }
197   instream_.reset(fs);
198   return true;
199 }
200
201 void ConfigParser::Attach(StreamInterface* stream) {
202   instream_.reset(stream);
203 }
204
205 bool ConfigParser::Parse(MapVector* key_val_pairs) {
206   // Parses the file and places the found key-value pairs into key_val_pairs.
207   SimpleMap section;
208   while (ParseSection(&section)) {
209     key_val_pairs->push_back(section);
210     section.clear();
211   }
212   return (!key_val_pairs->empty());
213 }
214
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;
221   }
222   return (!key_val_pair->empty());
223 }
224
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.
228   std::string line;
229   if ((instream_->ReadLine(&line)) == SR_EOS) {
230     return false;
231   }
232   std::vector<std::string> tokens;
233   if (2 != split(line, ':', &tokens)) {
234     return false;
235   }
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])) {
239     pos--;
240   }
241   tokens[0].erase(pos + 1);
242   // Removes whitespace at the start of value
243   pos = 0;
244   while (pos < tokens[1].length() && isspace(tokens[1][pos])) {
245     pos++;
246   }
247   tokens[1].erase(0, pos);
248   *key = tokens[0];
249   *value = tokens[1];
250   return true;
251 }
252
253 #if !defined(GOOGLE_CHROME_BUILD) && !defined(CHROMIUM_BUILD)
254 static bool ExpectLineFromStream(FileStream* stream,
255                                  std::string* out) {
256   StreamResult res = stream->ReadLine(out);
257   if (res != SR_SUCCESS) {
258     if (res != SR_EOS) {
259       LOG(LS_ERROR) << "Error when reading from stream";
260     } else {
261       LOG(LS_ERROR) << "Incorrect number of lines in stream";
262     }
263     return false;
264   }
265   return true;
266 }
267
268 static void ExpectEofFromStream(FileStream* stream) {
269   std::string unused;
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";
275   }
276 }
277
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;
282
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;
288   }
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
294   }
295   // Read in the command's output and build the string.
296   std::ostringstream sstr;
297   std::string line;
298   int wait_status;
299
300   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
301     return lsb_release_string;  // empty
302   }
303   sstr << "DISTRIB_ID=" << line;
304
305   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
306     return lsb_release_string;  // empty
307   }
308   sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
309
310   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
311     return lsb_release_string;  // empty
312   }
313   sstr << " DISTRIB_RELEASE=" << line;
314
315   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
316     return lsb_release_string;  // empty
317   }
318   sstr << " DISTRIB_CODENAME=" << line;
319
320   // Should not be anything left.
321   ExpectEofFromStream(&lsb_release_output);
322
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";
329   }
330
331   lsb_release_string = sstr.str();
332
333   return lsb_release_string;
334 }
335 #endif
336
337 std::string ReadLinuxUname() {
338   struct utsname buf;
339   if (uname(&buf) < 0) {
340     LOG_ERR(LS_ERROR) << "Can't call uname()";
341     return std::string();
342   }
343   std::ostringstream sstr;
344   sstr << buf.sysname << " "
345        << buf.release << " "
346        << buf.version << " "
347        << buf.machine;
348   return sstr.str();
349 }
350
351 int ReadCpuMaxFreq() {
352   FileStream fs;
353   std::string str;
354   int freq = -1;
355   if (!fs.Open(kCpuMaxFreqFile, "r", NULL) ||
356       SR_SUCCESS != fs.ReadLine(&str) ||
357       !FromString(str, &freq)) {
358     return -1;
359   }
360   return freq;
361 }
362
363 }  // namespace talk_base
364
365 #endif  // defined(LINUX) || defined(ANDROID)