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 44 ProcCpuInfo::ProcCpuInfo() { 45 } 46 47 ProcCpuInfo::~ProcCpuInfo() { 48 } 49 50 bool ProcCpuInfo::LoadFromSystem() { 51 ConfigParser procfs; 52 if (!procfs.Open(kCpuInfoFile)) { 53 return false; 54 } 55 return procfs.Parse(&cpu_info_); 56 }; 57 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 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 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 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 113 ConfigParser::ConfigParser() {} 114 115 ConfigParser::~ConfigParser() {} 116 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 126 void ConfigParser::Attach(StreamInterface* stream) { 127 instream_.reset(stream); 128 } 129 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 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 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 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 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 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 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 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