// 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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/system/sys_info.h"
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <bit>
#include <limits>
#include <type_traits>
#include <vector>
#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<BYTE> GetCoreEfficiencyClasses() {
const DWORD kReservedSize =
sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) * 64;
absl::InlinedVector<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;
absl::InlinedVector<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 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