chromium/base/system/sys_info_win.cc

// 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