| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/system/sys_info.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <windows.h> |
| |
| #include <algorithm> |
| #include <bit> |
| #include <limits> |
| #include <type_traits> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/containers/stack_container.h" |
| #include "base/files/file_path.h" |
| #include "base/notreached.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/process/process_metrics.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "base/win/registry.h" |
| #include "base/win/windows_version.h" |
| |
| namespace { |
| |
| // Returns the power efficiency levels of physical cores or empty vector on |
| // failure. The BYTE value of the element is the relative efficiency rank among |
| // all physical cores, where 0 is the most efficient, 1 is the second most |
| // efficient, and so on. |
| std::vector<BYTE> GetCoreEfficiencyClasses() { |
| const DWORD kReservedSize = |
| sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) * 64; |
| base::StackVector<BYTE, kReservedSize> buffer; |
| buffer->resize(kReservedSize); |
| DWORD byte_length = kReservedSize; |
| if (!GetLogicalProcessorInformationEx( |
| RelationProcessorCore, |
| reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>( |
| buffer->data()), |
| &byte_length)) { |
| DPCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER); |
| buffer->resize(byte_length); |
| if (!GetLogicalProcessorInformationEx( |
| RelationProcessorCore, |
| reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>( |
| buffer->data()), |
| &byte_length)) { |
| return {}; |
| } |
| } |
| |
| std::vector<BYTE> efficiency_classes; |
| BYTE* byte_ptr = buffer->data(); |
| while (byte_ptr < buffer->data() + byte_length) { |
| const auto* structure_ptr = |
| reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(byte_ptr); |
| DCHECK_EQ(structure_ptr->Relationship, RelationProcessorCore); |
| DCHECK_LE(&structure_ptr->Processor.EfficiencyClass + |
| sizeof(structure_ptr->Processor.EfficiencyClass), |
| buffer->data() + byte_length); |
| efficiency_classes.push_back(structure_ptr->Processor.EfficiencyClass); |
| DCHECK_GE( |
| structure_ptr->Size, |
| offsetof(std::remove_pointer_t<decltype(structure_ptr)>, Processor) + |
| sizeof(structure_ptr->Processor)); |
| byte_ptr = byte_ptr + structure_ptr->Size; |
| } |
| |
| return efficiency_classes; |
| } |
| |
| // Returns the physical cores to logical processor mapping masks by using the |
| // Windows API GetLogicalProcessorInformation(), or an empty vector on failure. |
| // When succeeded, the vector would be of same size to the number of physical |
| // cores, while each element is the bitmask of the logical processors that the |
| // physical core has. |
| std::vector<uint64_t> GetCoreProcessorMasks() { |
| const DWORD kReservedSize = 64; |
| base::StackVector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION, kReservedSize> buffer; |
| buffer->resize(kReservedSize); |
| DWORD byte_length = sizeof(buffer[0]) * kReservedSize; |
| const BOOL result = |
| GetLogicalProcessorInformation(buffer->data(), &byte_length); |
| DWORD element_count = byte_length / sizeof(buffer[0]); |
| DCHECK_EQ(byte_length % sizeof(buffer[0]), 0u); |
| if (!result) { |
| DPCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER); |
| buffer->resize(element_count); |
| if (!GetLogicalProcessorInformation(buffer->data(), &byte_length)) |
| return {}; |
| } |
| |
| std::vector<uint64_t> processor_masks; |
| for (DWORD i = 0; i < element_count; i++) { |
| if (buffer[i].Relationship == RelationProcessorCore) { |
| processor_masks.push_back(buffer[i].ProcessorMask); |
| } |
| } |
| |
| return processor_masks; |
| } |
| |
| uint64_t AmountOfMemory(DWORDLONG MEMORYSTATUSEX::*memory_field) { |
| MEMORYSTATUSEX memory_info; |
| memory_info.dwLength = sizeof(memory_info); |
| if (!GlobalMemoryStatusEx(&memory_info)) { |
| NOTREACHED(); |
| return 0; |
| } |
| |
| return memory_info.*memory_field; |
| } |
| |
| bool GetDiskSpaceInfo(const base::FilePath& path, |
| int64_t* available_bytes, |
| int64_t* total_bytes) { |
| ULARGE_INTEGER available; |
| ULARGE_INTEGER total; |
| ULARGE_INTEGER free; |
| if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free)) |
| return false; |
| |
| if (available_bytes) { |
| *available_bytes = static_cast<int64_t>(available.QuadPart); |
| if (*available_bytes < 0) |
| *available_bytes = std::numeric_limits<int64_t>::max(); |
| } |
| if (total_bytes) { |
| *total_bytes = static_cast<int64_t>(total.QuadPart); |
| if (*total_bytes < 0) |
| *total_bytes = std::numeric_limits<int64_t>::max(); |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| namespace base { |
| |
| // static |
| int SysInfo::NumberOfProcessors() { |
| return win::OSInfo::GetInstance()->processors(); |
| } |
| |
| // static |
| int SysInfo::NumberOfEfficientProcessorsImpl() { |
| std::vector<BYTE> efficiency_classes = GetCoreEfficiencyClasses(); |
| if (efficiency_classes.empty()) |
| return 0; |
| |
| auto [min_efficiency_class_it, max_efficiency_class_it] = |
| std::minmax_element(efficiency_classes.begin(), efficiency_classes.end()); |
| if (*min_efficiency_class_it == *max_efficiency_class_it) |
| return 0; |
| |
| std::vector<uint64_t> processor_masks = GetCoreProcessorMasks(); |
| if (processor_masks.empty()) |
| return 0; |
| |
| DCHECK_EQ(efficiency_classes.size(), processor_masks.size()); |
| int num_of_efficient_processors = 0; |
| for (size_t i = 0; i < efficiency_classes.size(); i++) { |
| if (efficiency_classes[i] == *min_efficiency_class_it) { |
| num_of_efficient_processors += std::popcount(processor_masks[i]); |
| } |
| } |
| |
| return num_of_efficient_processors; |
| } |
| |
| // static |
| uint64_t SysInfo::AmountOfPhysicalMemoryImpl() { |
| return AmountOfMemory(&MEMORYSTATUSEX::ullTotalPhys); |
| } |
| |
| // static |
| uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { |
| SystemMemoryInfoKB info; |
| if (!GetSystemMemoryInfo(&info)) |
| return 0; |
| return checked_cast<uint64_t>(info.avail_phys) * 1024; |
| } |
| |
| // static |
| uint64_t SysInfo::AmountOfVirtualMemory() { |
| return AmountOfMemory(&MEMORYSTATUSEX::ullTotalVirtual); |
| } |
| |
| // static |
| int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| int64_t available; |
| if (!GetDiskSpaceInfo(path, &available, nullptr)) |
| return -1; |
| return available; |
| } |
| |
| // static |
| int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& path) { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| int64_t total; |
| if (!GetDiskSpaceInfo(path, nullptr, &total)) |
| return -1; |
| return total; |
| } |
| |
| std::string SysInfo::OperatingSystemName() { |
| return "Windows NT"; |
| } |
| |
| // static |
| std::string SysInfo::OperatingSystemVersion() { |
| win::OSInfo* os_info = win::OSInfo::GetInstance(); |
| win::OSInfo::VersionNumber version_number = os_info->version_number(); |
| std::string version(StringPrintf("%d.%d.%d", version_number.major, |
| version_number.minor, version_number.build)); |
| win::OSInfo::ServicePack service_pack = os_info->service_pack(); |
| if (service_pack.major != 0) { |
| version += StringPrintf(" SP%d", service_pack.major); |
| if (service_pack.minor != 0) |
| version += StringPrintf(".%d", service_pack.minor); |
| } |
| return version; |
| } |
| |
| // TODO: Implement OperatingSystemVersionComplete, which would include |
| // patchlevel/service pack number. |
| // See chrome/browser/feedback/feedback_util.h, FeedbackUtil::SetOSVersion. |
| |
| // static |
| std::string SysInfo::OperatingSystemArchitecture() { |
| win::OSInfo::WindowsArchitecture arch = win::OSInfo::GetArchitecture(); |
| switch (arch) { |
| case win::OSInfo::X86_ARCHITECTURE: |
| return "x86"; |
| case win::OSInfo::X64_ARCHITECTURE: |
| return "x86_64"; |
| case win::OSInfo::IA64_ARCHITECTURE: |
| return "ia64"; |
| case win::OSInfo::ARM64_ARCHITECTURE: |
| return "arm64"; |
| default: |
| return ""; |
| } |
| } |
| |
| // static |
| std::string SysInfo::CPUModelName() { |
| return win::OSInfo::GetInstance()->processor_model_name(); |
| } |
| |
| // static |
| size_t SysInfo::VMAllocationGranularity() { |
| return win::OSInfo::GetInstance()->allocation_granularity(); |
| } |
| |
| // static |
| void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, |
| int32_t* minor_version, |
| int32_t* bugfix_version) { |
| win::OSInfo* os_info = win::OSInfo::GetInstance(); |
| *major_version = static_cast<int32_t>(os_info->version_number().major); |
| *minor_version = static_cast<int32_t>(os_info->version_number().minor); |
| *bugfix_version = 0; |
| } |
| |
| // static |
| std::string ReadHardwareInfoFromRegistry(const wchar_t* reg_value_name) { |
| // On some systems or VMs, the system information and some of the below |
| // locations may be missing info. Attempt to find the info from the below |
| // registry keys in the order provided. |
| static const wchar_t* const kSystemInfoRegKeyPaths[] = { |
| L"HARDWARE\\DESCRIPTION\\System\\BIOS", |
| L"SYSTEM\\CurrentControlSet\\Control\\SystemInformation", |
| L"SYSTEM\\HardwareConfig\\Current", |
| }; |
| |
| std::wstring value; |
| for (const wchar_t* system_info_reg_key_path : kSystemInfoRegKeyPaths) { |
| base::win::RegKey system_information_key; |
| if (system_information_key.Open(HKEY_LOCAL_MACHINE, |
| system_info_reg_key_path, |
| KEY_READ) == ERROR_SUCCESS) { |
| if ((system_information_key.ReadValue(reg_value_name, &value) == |
| ERROR_SUCCESS) && |
| !value.empty()) { |
| break; |
| } |
| } |
| } |
| |
| return base::SysWideToUTF8(value); |
| } |
| |
| // static |
| SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() { |
| HardwareInfo info = {ReadHardwareInfoFromRegistry(L"SystemManufacturer"), |
| SysInfo::HardwareModelName()}; |
| return info; |
| } |
| |
| // static |
| std::string SysInfo::HardwareModelName() { |
| return ReadHardwareInfoFromRegistry(L"SystemProductName"); |
| } |
| |
| } // namespace base |