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