Home | History | Annotate | Download | only in base
      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];
    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