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(§ion)) {
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