• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2011, 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 #ifdef LINUX
29 #include "talk/base/linux.h"
30 
31 #include <errno.h>
32 #include <sys/utsname.h>
33 
34 #include <cstdio>
35 
36 #include "talk/base/stringencode.h"
37 
38 namespace talk_base {
39 
40 static const char kCpuInfoFile[] = "/proc/cpuinfo";
41 static const char kCpuMaxFreqFile[] =
42     "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
43 
ProcCpuInfo()44 ProcCpuInfo::ProcCpuInfo() {
45 }
46 
~ProcCpuInfo()47 ProcCpuInfo::~ProcCpuInfo() {
48 }
49 
LoadFromSystem()50 bool ProcCpuInfo::LoadFromSystem() {
51   ConfigParser procfs;
52   if (!procfs.Open(kCpuInfoFile)) {
53     return false;
54   }
55   return procfs.Parse(&cpu_info_);
56 };
57 
GetNumCpus(int * num)58 bool ProcCpuInfo::GetNumCpus(int *num) {
59   if (cpu_info_.size() == 0) {
60     return false;
61   }
62   *num = cpu_info_.size();
63   return true;
64 }
65 
GetNumPhysicalCpus(int * num)66 bool ProcCpuInfo::GetNumPhysicalCpus(int *num) {
67   if (cpu_info_.size() == 0) {
68     return false;
69   }
70   int total_cores = 0;
71   int physical_id_prev = -1;
72   int cpus = static_cast<int>(cpu_info_.size());
73   for (int i = 0; i < cpus; ++i) {
74     int physical_id;
75     if (GetCpuIntValue(i, "physical id", &physical_id)) {
76       if (physical_id != physical_id_prev) {
77         physical_id_prev = physical_id;
78         int cores;
79         if (GetCpuIntValue(i, "cpu cores", &cores)) {
80           total_cores += cores;
81         }
82       }
83     }
84   }
85   return total_cores;
86 }
87 
GetCpuStringValue(int cpu_id,const std::string & key,std::string * result)88 bool ProcCpuInfo::GetCpuStringValue(int cpu_id, const std::string& key,
89                                     std::string *result) {
90   if (cpu_id >= static_cast<int>(cpu_info_.size()))
91     return false;
92   ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
93   if (iter == cpu_info_[cpu_id].end()) {
94     return false;
95   }
96   *result = iter->second;
97   return true;
98 }
99 
GetCpuIntValue(int cpu_id,const std::string & key,int * result)100 bool ProcCpuInfo::GetCpuIntValue(int cpu_id, const std::string& key,
101                                  int *result) {
102   if (cpu_id >= static_cast<int>(cpu_info_.size())) {
103     return false;
104   }
105   ConfigParser::SimpleMap::iterator iter = cpu_info_[cpu_id].find(key);
106   if (iter == cpu_info_[cpu_id].end()) {
107     return false;
108   }
109   *result = atoi((iter->second).c_str());
110   return true;
111 }
112 
ConfigParser()113 ConfigParser::ConfigParser() {}
114 
~ConfigParser()115 ConfigParser::~ConfigParser() {}
116 
Open(const std::string & filename)117 bool ConfigParser::Open(const std::string& filename) {
118   FileStream *fs = new FileStream();
119   if (!fs->Open(filename, "r")) {
120     return false;
121   }
122   instream_.reset(fs);
123   return true;
124 }
125 
Attach(StreamInterface * stream)126 void ConfigParser::Attach(StreamInterface* stream) {
127   instream_.reset(stream);
128 }
129 
Parse(MapVector * key_val_pairs)130 bool ConfigParser::Parse(MapVector *key_val_pairs) {
131   // Parses the file and places the found key-value pairs into key_val_pairs.
132   SimpleMap section;
133   while (ParseSection(&section)) {
134     key_val_pairs->push_back(section);
135     section.clear();
136   }
137   return (!key_val_pairs->empty());
138 }
139 
ParseSection(SimpleMap * key_val_pair)140 bool ConfigParser::ParseSection(SimpleMap *key_val_pair) {
141   // Parses the next section in the filestream and places the found key-value
142   // pairs into key_val_pair.
143   std::string key, value;
144   while (ParseLine(&key, &value)) {
145     (*key_val_pair)[key] = value;
146   }
147   return (!key_val_pair->empty());
148 }
149 
ParseLine(std::string * key,std::string * value)150 bool ConfigParser::ParseLine(std::string *key, std::string *value) {
151   // Parses the next line in the filestream and places the found key-value
152   // pair into key and val.
153   std::string line;
154   if ((instream_->ReadLine(&line)) == EOF) {
155     return false;
156   }
157   std::vector<std::string> tokens;
158   if (2 != split(line, ':', &tokens)) {
159     return false;
160   }
161   // Removes whitespace at the end of Key name
162   size_t pos = tokens[0].length() - 1;
163   while ((pos > 0) && isspace(tokens[0][pos])) {
164     pos--;
165   }
166   tokens[0].erase(pos + 1);
167   // Removes whitespace at the start of value
168   pos = 0;
169   while (pos < tokens[1].length() && isspace(tokens[1][pos])) {
170     pos++;
171   }
172   tokens[1].erase(0, pos);
173   *key = tokens[0];
174   *value = tokens[1];
175   return true;
176 }
177 
ExpectLineFromStream(FileStream * stream,std::string * out)178 static bool ExpectLineFromStream(FileStream *stream,
179                                  std::string *out) {
180   StreamResult res = stream->ReadLine(out);
181   if (res != SR_SUCCESS) {
182     if (res != SR_EOS) {
183       LOG(LS_ERROR) << "Error when reading from stream";
184     } else {
185       LOG(LS_ERROR) << "Incorrect number of lines in stream";
186     }
187     return false;
188   }
189   return true;
190 }
191 
ExpectEofFromStream(FileStream * stream)192 static void ExpectEofFromStream(FileStream *stream) {
193   std::string unused;
194   StreamResult res = stream->ReadLine(&unused);
195   if (res == SR_SUCCESS) {
196     LOG(LS_WARNING) << "Ignoring unexpected extra lines from stream";
197   } else if (res != SR_EOS) {
198     LOG(LS_WARNING) << "Error when checking for extra lines from stream";
199   }
200 }
201 
202 // For caching the lsb_release output (reading it invokes a sub-process and
203 // hence is somewhat expensive).
204 static std::string lsb_release_string;
205 static CriticalSection lsb_release_string_critsec;
206 
ReadLinuxLsbRelease()207 std::string ReadLinuxLsbRelease() {
208   CritScope cs(&lsb_release_string_critsec);
209   if (!lsb_release_string.empty()) {
210     // Have cached result from previous call.
211     return lsb_release_string;
212   }
213   // No cached result. Run lsb_release and parse output.
214   POpenStream lsb_release_output;
215   if (!lsb_release_output.Open("lsb_release -idrcs", "r")) {
216     LOG_ERR(LS_ERROR) << "Can't run lsb_release";
217     return lsb_release_string;  // empty
218   }
219   // Read in the command's output and build the string.
220   std::ostringstream sstr;
221   std::string line;
222   int wait_status;
223 
224   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
225     return lsb_release_string;  // empty
226   }
227   sstr << "DISTRIB_ID=" << line;
228 
229   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
230     return lsb_release_string;  // empty
231   }
232   sstr << " DISTRIB_DESCRIPTION=\"" << line << '"';
233 
234   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
235     return lsb_release_string;  // empty
236   }
237   sstr << " DISTRIB_RELEASE=" << line;
238 
239   if (!ExpectLineFromStream(&lsb_release_output, &line)) {
240     return lsb_release_string;  // empty
241   }
242   sstr << " DISTRIB_CODENAME=" << line;
243 
244   // Should not be anything left.
245   ExpectEofFromStream(&lsb_release_output);
246 
247   lsb_release_output.Close();
248   wait_status = lsb_release_output.GetWaitStatus();
249   if (wait_status == -1 ||
250       !WIFEXITED(wait_status) ||
251       WEXITSTATUS(wait_status) != 0) {
252     LOG(LS_WARNING) << "Unexpected exit status from lsb_release";
253   }
254 
255   lsb_release_string = sstr.str();
256 
257   return lsb_release_string;
258 }
259 
ReadLinuxUname()260 std::string ReadLinuxUname() {
261   struct utsname buf;
262   if (uname(&buf) < 0) {
263     LOG_ERR(LS_ERROR) << "Can't call uname()";
264     return std::string();
265   }
266   std::ostringstream sstr;
267   sstr << buf.sysname << " "
268        << buf.release << " "
269        << buf.version << " "
270        << buf.machine;
271   return sstr.str();
272 }
273 
ReadCpuMaxFreq()274 int ReadCpuMaxFreq() {
275   FileStream fs;
276   std::string str;
277   if (!fs.Open(kCpuMaxFreqFile, "r") || SR_SUCCESS != fs.ReadLine(&str)) {
278     return -1;
279   }
280   return atoi(str.c_str());
281 }
282 
283 }  // namespace talk_base
284 
285 #endif  // LINUX
286