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 #include "talk/base/systeminfo.h" 29 30 #if defined(WIN32) 31 #include <winsock2.h> 32 #ifndef EXCLUDE_D3D9 33 #include <d3d9.h> 34 #endif 35 #include <intrin.h> // for __cpuid() 36 #elif defined(OSX) 37 #include <ApplicationServices/ApplicationServices.h> 38 #include <CoreServices/CoreServices.h> 39 #elif defined(LINUX) || defined(ANDROID) 40 #include <unistd.h> 41 #endif 42 #if defined(OSX) || defined(IOS) 43 #include <sys/sysctl.h> 44 #endif 45 46 #if defined(WIN32) 47 #include "talk/base/scoped_ptr.h" 48 #include "talk/base/win32.h" 49 #elif defined(OSX) 50 #include "talk/base/macconversion.h" 51 #elif defined(LINUX) || defined(ANDROID) 52 #include "talk/base/linux.h" 53 #endif 54 #include "talk/base/common.h" 55 #include "talk/base/logging.h" 56 #include "talk/base/stringutils.h" 57 58 namespace talk_base { 59 60 // See Also: http://msdn.microsoft.com/en-us/library/ms683194(v=vs.85).aspx 61 #if defined(WIN32) 62 typedef BOOL (WINAPI *LPFN_GLPI)( 63 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, 64 PDWORD); 65 66 static void GetProcessorInformation(int* physical_cpus, int* cache_size) { 67 // GetLogicalProcessorInformation() is available on Windows XP SP3 and beyond. 68 LPFN_GLPI glpi = reinterpret_cast<LPFN_GLPI>(GetProcAddress( 69 GetModuleHandle(L"kernel32"), 70 "GetLogicalProcessorInformation")); 71 if (NULL == glpi) { 72 return; 73 } 74 // Determine buffer size, allocate and get processor information. 75 // Size can change between calls (unlikely), so a loop is done. 76 DWORD return_length = 0; 77 scoped_ptr<SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]> infos; 78 while (!glpi(infos.get(), &return_length)) { 79 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 80 infos.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[ 81 return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)]); 82 } else { 83 return; 84 } 85 } 86 *physical_cpus = 0; 87 *cache_size = 0; 88 for (size_t i = 0; 89 i < return_length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i) { 90 if (infos[i].Relationship == RelationProcessorCore) { 91 ++*physical_cpus; 92 } else if (infos[i].Relationship == RelationCache) { 93 int next_cache_size = static_cast<int>(infos[i].Cache.Size); 94 if (next_cache_size >= *cache_size) { 95 *cache_size = next_cache_size; 96 } 97 } 98 } 99 return; 100 } 101 #else 102 // TODO(fbarchard): Use gcc 4.4 provided cpuid intrinsic 103 // 32 bit fpic requires ebx be preserved 104 #if (defined(__pic__) || defined(__APPLE__)) && defined(__i386__) 105 static inline void __cpuid(int cpu_info[4], int info_type) { 106 __asm__ volatile ( // NOLINT 107 "mov %%ebx, %%edi\n" 108 "cpuid\n" 109 "xchg %%edi, %%ebx\n" 110 : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) 111 : "a"(info_type) 112 ); // NOLINT 113 } 114 #elif defined(__i386__) || defined(__x86_64__) 115 static inline void __cpuid(int cpu_info[4], int info_type) { 116 __asm__ volatile ( // NOLINT 117 "cpuid\n" 118 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) 119 : "a"(info_type) 120 ); // NOLINT 121 } 122 #endif 123 #endif // WIN32 124 125 // Note(fbarchard): 126 // Family and model are extended family and extended model. 8 bits each. 127 SystemInfo::SystemInfo() 128 : physical_cpus_(1), logical_cpus_(1), cache_size_(0), 129 cpu_family_(0), cpu_model_(0), cpu_stepping_(0), 130 cpu_speed_(0), memory_(0) { 131 // Initialize the basic information. 132 #if defined(__arm__) || defined(_M_ARM) 133 cpu_arch_ = SI_ARCH_ARM; 134 #elif defined(__x86_64__) || defined(_M_X64) 135 cpu_arch_ = SI_ARCH_X64; 136 #elif defined(__i386__) || defined(_M_IX86) 137 cpu_arch_ = SI_ARCH_X86; 138 #else 139 cpu_arch_ = SI_ARCH_UNKNOWN; 140 #endif 141 142 #if defined(WIN32) 143 SYSTEM_INFO si; 144 GetSystemInfo(&si); 145 logical_cpus_ = si.dwNumberOfProcessors; 146 GetProcessorInformation(&physical_cpus_, &cache_size_); 147 if (physical_cpus_ <= 0) { 148 physical_cpus_ = logical_cpus_; 149 } 150 cpu_family_ = si.wProcessorLevel; 151 cpu_model_ = si.wProcessorRevision >> 8; 152 cpu_stepping_ = si.wProcessorRevision & 0xFF; 153 #elif defined(OSX) || defined(IOS) 154 uint32_t sysctl_value; 155 size_t length = sizeof(sysctl_value); 156 if (!sysctlbyname("hw.physicalcpu_max", &sysctl_value, &length, NULL, 0)) { 157 physical_cpus_ = static_cast<int>(sysctl_value); 158 } 159 length = sizeof(sysctl_value); 160 if (!sysctlbyname("hw.logicalcpu_max", &sysctl_value, &length, NULL, 0)) { 161 logical_cpus_ = static_cast<int>(sysctl_value); 162 } 163 uint64_t sysctl_value64; 164 length = sizeof(sysctl_value64); 165 if (!sysctlbyname("hw.l3cachesize", &sysctl_value64, &length, NULL, 0)) { 166 cache_size_ = static_cast<int>(sysctl_value64); 167 } 168 if (!cache_size_) { 169 length = sizeof(sysctl_value64); 170 if (!sysctlbyname("hw.l2cachesize", &sysctl_value64, &length, NULL, 0)) { 171 cache_size_ = static_cast<int>(sysctl_value64); 172 } 173 } 174 length = sizeof(sysctl_value); 175 if (!sysctlbyname("machdep.cpu.family", &sysctl_value, &length, NULL, 0)) { 176 cpu_family_ = static_cast<int>(sysctl_value); 177 } 178 length = sizeof(sysctl_value); 179 if (!sysctlbyname("machdep.cpu.model", &sysctl_value, &length, NULL, 0)) { 180 cpu_model_ = static_cast<int>(sysctl_value); 181 } 182 length = sizeof(sysctl_value); 183 if (!sysctlbyname("machdep.cpu.stepping", &sysctl_value, &length, NULL, 0)) { 184 cpu_stepping_ = static_cast<int>(sysctl_value); 185 } 186 #elif defined(__native_client__) 187 // TODO(ryanpetrie): Implement this via PPAPI when it's available. 188 #else // LINUX || ANDROID 189 ProcCpuInfo proc_info; 190 if (proc_info.LoadFromSystem()) { 191 proc_info.GetNumCpus(&logical_cpus_); 192 proc_info.GetNumPhysicalCpus(&physical_cpus_); 193 proc_info.GetCpuFamily(&cpu_family_); 194 #if defined(CPU_X86) 195 // These values only apply to x86 systems. 196 proc_info.GetSectionIntValue(0, "model", &cpu_model_); 197 proc_info.GetSectionIntValue(0, "stepping", &cpu_stepping_); 198 proc_info.GetSectionIntValue(0, "cpu MHz", &cpu_speed_); 199 proc_info.GetSectionIntValue(0, "cache size", &cache_size_); 200 cache_size_ *= 1024; 201 #endif 202 } 203 // ProcCpuInfo reads cpu speed from "cpu MHz" under /proc/cpuinfo. 204 // But that number is a moving target which can change on-the-fly according to 205 // many factors including system workload. 206 // See /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors. 207 // The one in /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq is more 208 // accurate. We use it as our cpu speed when it is available. 209 // cpuinfo_max_freq is measured in KHz and requires conversion to MHz. 210 int max_freq = talk_base::ReadCpuMaxFreq(); 211 if (max_freq > 0) { 212 cpu_speed_ = max_freq / 1000; 213 } 214 #endif 215 // For L2 CacheSize see also 216 // http://www.flounder.com/cpuid_explorer2.htm#CPUID(0x800000006) 217 #ifdef CPU_X86 218 if (cache_size_ == 0) { 219 int cpu_info[4]; 220 __cpuid(cpu_info, 0x80000000); // query maximum extended cpuid function. 221 if (static_cast<uint32>(cpu_info[0]) >= 0x80000006) { 222 __cpuid(cpu_info, 0x80000006); 223 cache_size_ = (cpu_info[2] >> 16) * 1024; 224 } 225 } 226 #endif 227 } 228 229 // Return the number of cpu threads available to the system. 230 int SystemInfo::GetMaxCpus() { 231 return logical_cpus_; 232 } 233 234 // Return the number of cpu cores available to the system. 235 int SystemInfo::GetMaxPhysicalCpus() { 236 return physical_cpus_; 237 } 238 239 // Return the number of cpus available to the process. Since affinity can be 240 // changed on the fly, do not cache this value. 241 // Can be affected by heat. 242 int SystemInfo::GetCurCpus() { 243 int cur_cpus; 244 #if defined(WIN32) 245 DWORD_PTR process_mask, system_mask; 246 ::GetProcessAffinityMask(::GetCurrentProcess(), &process_mask, &system_mask); 247 for (cur_cpus = 0; process_mask; ++cur_cpus) { 248 // Sparse-ones algorithm. There are slightly faster methods out there but 249 // they are unintuitive and won't make a difference on a single dword. 250 process_mask &= (process_mask - 1); 251 } 252 #elif defined(OSX) || defined(IOS) 253 uint32_t sysctl_value; 254 size_t length = sizeof(sysctl_value); 255 int error = sysctlbyname("hw.ncpu", &sysctl_value, &length, NULL, 0); 256 cur_cpus = !error ? static_cast<int>(sysctl_value) : 1; 257 #else 258 // Linux, Solaris, ANDROID 259 cur_cpus = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN)); 260 #endif 261 return cur_cpus; 262 } 263 264 // Return the type of this CPU. 265 SystemInfo::Architecture SystemInfo::GetCpuArchitecture() { 266 return cpu_arch_; 267 } 268 269 // Returns the vendor string from the cpu, e.g. "GenuineIntel", "AuthenticAMD". 270 // See "Intel Processor Identification and the CPUID Instruction" 271 // (Intel document number: 241618) 272 std::string SystemInfo::GetCpuVendor() { 273 if (cpu_vendor_.empty()) { 274 #if defined(CPU_X86) 275 int cpu_info[4]; 276 __cpuid(cpu_info, 0); 277 cpu_info[0] = cpu_info[1]; // Reorder output 278 cpu_info[1] = cpu_info[3]; 279 cpu_info[2] = cpu_info[2]; 280 cpu_info[3] = 0; 281 cpu_vendor_ = std::string(reinterpret_cast<char*>(&cpu_info[0])); 282 #elif defined(CPU_ARM) 283 cpu_vendor_ = std::string("ARM"); 284 #else 285 cpu_vendor_ = std::string("Undefined"); 286 #endif 287 } 288 return cpu_vendor_; 289 } 290 291 int SystemInfo::GetCpuCacheSize() { 292 return cache_size_; 293 } 294 295 // Return the "family" of this CPU. 296 int SystemInfo::GetCpuFamily() { 297 return cpu_family_; 298 } 299 300 // Return the "model" of this CPU. 301 int SystemInfo::GetCpuModel() { 302 return cpu_model_; 303 } 304 305 // Return the "stepping" of this CPU. 306 int SystemInfo::GetCpuStepping() { 307 return cpu_stepping_; 308 } 309 310 // Return the clockrate of the primary processor in Mhz. This value can be 311 // cached. Returns -1 on error. 312 int SystemInfo::GetMaxCpuSpeed() { 313 if (cpu_speed_) { 314 return cpu_speed_; 315 } 316 #if defined(WIN32) 317 HKEY key; 318 static const WCHAR keyName[] = 319 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; 320 321 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key) 322 == ERROR_SUCCESS) { 323 DWORD data, len; 324 len = sizeof(data); 325 326 if (RegQueryValueEx(key, L"~Mhz", 0, 0, reinterpret_cast<LPBYTE>(&data), 327 &len) == ERROR_SUCCESS) { 328 cpu_speed_ = data; 329 } else { 330 LOG(LS_WARNING) << "Failed to query registry value HKLM\\" << keyName 331 << "\\~Mhz"; 332 cpu_speed_ = -1; 333 } 334 335 RegCloseKey(key); 336 } else { 337 LOG(LS_WARNING) << "Failed to open registry key HKLM\\" << keyName; 338 cpu_speed_ = -1; 339 } 340 #elif defined(IOS) || defined(OSX) 341 uint64_t sysctl_value; 342 size_t length = sizeof(sysctl_value); 343 int error = sysctlbyname("hw.cpufrequency_max", &sysctl_value, &length, 344 NULL, 0); 345 cpu_speed_ = !error ? static_cast<int>(sysctl_value/1000000) : -1; 346 #else 347 // TODO(fbarchard): Implement using proc/cpuinfo 348 cpu_speed_ = 0; 349 #endif 350 return cpu_speed_; 351 } 352 353 // Dynamically check the current clockrate, which could be reduced because of 354 // powersaving profiles. Eventually for windows we want to query WMI for 355 // root\WMI::ProcessorPerformance.InstanceName="Processor_Number_0".frequency 356 int SystemInfo::GetCurCpuSpeed() { 357 #if defined(WIN32) 358 // TODO(fbarchard): Add WMI check, requires COM initialization 359 // NOTE(fbarchard): Testable on Sandy Bridge. 360 return GetMaxCpuSpeed(); 361 #elif defined(IOS) || defined(OSX) 362 uint64_t sysctl_value; 363 size_t length = sizeof(sysctl_value); 364 int error = sysctlbyname("hw.cpufrequency", &sysctl_value, &length, NULL, 0); 365 return !error ? static_cast<int>(sysctl_value/1000000) : GetMaxCpuSpeed(); 366 #else // LINUX || ANDROID 367 // TODO(fbarchard): Use proc/cpuinfo for Cur speed on Linux. 368 return GetMaxCpuSpeed(); 369 #endif 370 } 371 372 // Returns the amount of installed physical memory in Bytes. Cacheable. 373 // Returns -1 on error. 374 int64 SystemInfo::GetMemorySize() { 375 if (memory_) { 376 return memory_; 377 } 378 379 #if defined(WIN32) 380 MEMORYSTATUSEX status = {0}; 381 status.dwLength = sizeof(status); 382 383 if (GlobalMemoryStatusEx(&status)) { 384 memory_ = status.ullTotalPhys; 385 } else { 386 LOG_GLE(LS_WARNING) << "GlobalMemoryStatusEx failed."; 387 memory_ = -1; 388 } 389 390 #elif defined(OSX) || defined(IOS) 391 size_t len = sizeof(memory_); 392 int error = sysctlbyname("hw.memsize", &memory_, &len, NULL, 0); 393 if (error || memory_ == 0) { 394 memory_ = -1; 395 } 396 #else // LINUX || ANDROID 397 memory_ = static_cast<int64>(sysconf(_SC_PHYS_PAGES)) * 398 static_cast<int64>(sysconf(_SC_PAGESIZE)); 399 if (memory_ < 0) { 400 LOG(LS_WARNING) << "sysconf(_SC_PHYS_PAGES) failed." 401 << "sysconf(_SC_PHYS_PAGES) " << sysconf(_SC_PHYS_PAGES) 402 << "sysconf(_SC_PAGESIZE) " << sysconf(_SC_PAGESIZE); 403 memory_ = -1; 404 } 405 #endif 406 407 return memory_; 408 } 409 410 411 // Return the name of the machine model we are currently running on. 412 // This is a human readable string that consists of the name and version 413 // number of the hardware, i.e 'MacBookAir1,1'. Returns an empty string if 414 // model can not be determined. The string is cached for subsequent calls. 415 std::string SystemInfo::GetMachineModel() { 416 if (!machine_model_.empty()) { 417 return machine_model_; 418 } 419 420 #if defined(OSX) || defined(IOS) 421 char buffer[128]; 422 size_t length = sizeof(buffer); 423 int error = sysctlbyname("hw.model", buffer, &length, NULL, 0); 424 if (!error) { 425 machine_model_.assign(buffer, length - 1); 426 } else { 427 machine_model_.clear(); 428 } 429 #else 430 machine_model_ = "Not available"; 431 #endif 432 433 return machine_model_; 434 } 435 436 #ifdef OSX 437 // Helper functions to query IOKit for video hardware properties. 438 static CFTypeRef SearchForProperty(io_service_t port, CFStringRef name) { 439 return IORegistryEntrySearchCFProperty(port, kIOServicePlane, 440 name, kCFAllocatorDefault, 441 kIORegistryIterateRecursively | kIORegistryIterateParents); 442 } 443 444 static void GetProperty(io_service_t port, CFStringRef name, int* value) { 445 if (!value) return; 446 CFTypeRef ref = SearchForProperty(port, name); 447 if (ref) { 448 CFTypeID refType = CFGetTypeID(ref); 449 if (CFNumberGetTypeID() == refType) { 450 CFNumberRef number = reinterpret_cast<CFNumberRef>(ref); 451 p_convertCFNumberToInt(number, value); 452 } else if (CFDataGetTypeID() == refType) { 453 CFDataRef data = reinterpret_cast<CFDataRef>(ref); 454 if (CFDataGetLength(data) == sizeof(UInt32)) { 455 *value = *reinterpret_cast<const UInt32*>(CFDataGetBytePtr(data)); 456 } 457 } 458 CFRelease(ref); 459 } 460 } 461 462 static void GetProperty(io_service_t port, CFStringRef name, 463 std::string* value) { 464 if (!value) return; 465 CFTypeRef ref = SearchForProperty(port, name); 466 if (ref) { 467 CFTypeID refType = CFGetTypeID(ref); 468 if (CFStringGetTypeID() == refType) { 469 CFStringRef stringRef = reinterpret_cast<CFStringRef>(ref); 470 p_convertHostCFStringRefToCPPString(stringRef, *value); 471 } else if (CFDataGetTypeID() == refType) { 472 CFDataRef dataRef = reinterpret_cast<CFDataRef>(ref); 473 *value = std::string(reinterpret_cast<const char*>( 474 CFDataGetBytePtr(dataRef)), CFDataGetLength(dataRef)); 475 } 476 CFRelease(ref); 477 } 478 } 479 #endif 480 481 // Fills a struct with information on the graphics adapater and returns true 482 // iff successful. 483 bool SystemInfo::GetGpuInfo(GpuInfo *info) { 484 if (!info) return false; 485 #if defined(WIN32) && !defined(EXCLUDE_D3D9) 486 D3DADAPTER_IDENTIFIER9 identifier; 487 HRESULT hr = E_FAIL; 488 HINSTANCE d3d_lib = LoadLibrary(L"d3d9.dll"); 489 490 if (d3d_lib) { 491 typedef IDirect3D9* (WINAPI *D3DCreate9Proc)(UINT); 492 D3DCreate9Proc d3d_create_proc = reinterpret_cast<D3DCreate9Proc>( 493 GetProcAddress(d3d_lib, "Direct3DCreate9")); 494 if (d3d_create_proc) { 495 IDirect3D9* d3d = d3d_create_proc(D3D_SDK_VERSION); 496 if (d3d) { 497 hr = d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); 498 d3d->Release(); 499 } 500 } 501 FreeLibrary(d3d_lib); 502 } 503 504 if (hr != D3D_OK) { 505 LOG(LS_ERROR) << "Failed to access Direct3D9 information."; 506 return false; 507 } 508 509 info->device_name = identifier.DeviceName; 510 info->description = identifier.Description; 511 info->vendor_id = identifier.VendorId; 512 info->device_id = identifier.DeviceId; 513 info->driver = identifier.Driver; 514 // driver_version format: product.version.subversion.build 515 std::stringstream ss; 516 ss << HIWORD(identifier.DriverVersion.HighPart) << "." 517 << LOWORD(identifier.DriverVersion.HighPart) << "." 518 << HIWORD(identifier.DriverVersion.LowPart) << "." 519 << LOWORD(identifier.DriverVersion.LowPart); 520 info->driver_version = ss.str(); 521 return true; 522 #elif defined(OSX) 523 // We'll query the IOKit for the gpu of the main display. 524 io_service_t display_service_port = CGDisplayIOServicePort( 525 kCGDirectMainDisplay); 526 GetProperty(display_service_port, CFSTR("vendor-id"), &info->vendor_id); 527 GetProperty(display_service_port, CFSTR("device-id"), &info->device_id); 528 GetProperty(display_service_port, CFSTR("model"), &info->description); 529 return true; 530 #else // LINUX || ANDROID 531 // TODO(fbarchard): Implement this on Linux 532 return false; 533 #endif 534 } 535 } // namespace talk_base 536