• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetProcessorInformation(int * physical_cpus,int * cache_size)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.
SystemInfo()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.
GetMaxCpus()213 int SystemInfo::GetMaxCpus() {
214   return logical_cpus_;
215 }
216 
217 // Return the number of cpu cores available to the system.
GetMaxPhysicalCpus()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.
GetCurCpus()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.
GetCpuArchitecture()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)
GetCpuVendor()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 
GetCpuCacheSize()274 int SystemInfo::GetCpuCacheSize() {
275   return cache_size_;
276 }
277 
278 // Return the "family" of this CPU.
GetCpuFamily()279 int SystemInfo::GetCpuFamily() {
280   return cpu_family_;
281 }
282 
283 // Return the "model" of this CPU.
GetCpuModel()284 int SystemInfo::GetCpuModel() {
285   return cpu_model_;
286 }
287 
288 // Return the "stepping" of this CPU.
GetCpuStepping()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.
GetMaxCpuSpeed()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
GetCurCpuSpeed()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.
GetMemorySize()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.
GetMachineModel()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.
SearchForProperty(io_service_t port,CFStringRef name)421 static CFTypeRef SearchForProperty(io_service_t port, CFStringRef name) {
422   return IORegistryEntrySearchCFProperty(port, kIOServicePlane,
423       name, kCFAllocatorDefault,
424       kIORegistryIterateRecursively | kIORegistryIterateParents);
425 }
426 
GetProperty(io_service_t port,CFStringRef name,int * value)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 
GetProperty(io_service_t port,CFStringRef name,std::string * value)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.
GetGpuInfo(GpuInfo * info)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