// 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 #include #include #include #include #include #include #include #include "base/check.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" #include "third_party/abseil-cpp/absl/container/inlined_vector.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 GetCoreEfficiencyClasses() { const DWORD kReservedSize = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) * 64; absl::InlinedVector buffer; buffer.resize(kReservedSize); DWORD byte_length = kReservedSize; if (!GetLogicalProcessorInformationEx( RelationProcessorCore, reinterpret_cast( buffer.data()), &byte_length)) { DPCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER); buffer.resize(byte_length); if (!GetLogicalProcessorInformationEx( RelationProcessorCore, reinterpret_cast( buffer.data()), &byte_length)) { return {}; } } std::vector efficiency_classes; BYTE* byte_ptr = buffer.data(); while (byte_ptr < buffer.data() + byte_length) { const auto* structure_ptr = reinterpret_cast(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, 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 GetCoreProcessorMasks() { const DWORD kReservedSize = 64; absl::InlinedVector 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 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(available.QuadPart); if (*available_bytes < 0) *available_bytes = std::numeric_limits::max(); } if (total_bytes) { *total_bytes = static_cast(total.QuadPart); if (*total_bytes < 0) *total_bytes = std::numeric_limits::max(); } return true; } } // namespace namespace base { // static int SysInfo::NumberOfProcessors() { return win::OSInfo::GetInstance()->processors(); } // static int SysInfo::NumberOfEfficientProcessorsImpl() { std::vector 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 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(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(os_info->version_number().major); *minor_version = static_cast(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