• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/system/sys_info.h"
11 
12 #include <windows.h>
13 
14 #include <stddef.h>
15 #include <stdint.h>
16 
17 #include <algorithm>
18 #include <bit>
19 #include <limits>
20 #include <type_traits>
21 #include <vector>
22 
23 #include "base/check.h"
24 #include "base/files/file_path.h"
25 #include "base/notreached.h"
26 #include "base/numerics/safe_conversions.h"
27 #include "base/process/process_metrics.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/strings/sys_string_conversions.h"
31 #include "base/strings/utf_string_conversions.h"
32 #include "base/threading/scoped_blocking_call.h"
33 #include "base/win/registry.h"
34 #include "base/win/windows_version.h"
35 #include "third_party/abseil-cpp/absl/container/inlined_vector.h"
36 
37 namespace {
38 
39 // Returns the power efficiency levels of physical cores or empty vector on
40 // failure. The BYTE value of the element is the relative efficiency rank among
41 // all physical cores, where 0 is the most efficient, 1 is the second most
42 // efficient, and so on.
GetCoreEfficiencyClasses()43 std::vector<BYTE> GetCoreEfficiencyClasses() {
44   const DWORD kReservedSize =
45       sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) * 64;
46   absl::InlinedVector<BYTE, kReservedSize> buffer;
47   buffer.resize(kReservedSize);
48   DWORD byte_length = kReservedSize;
49   if (!GetLogicalProcessorInformationEx(
50           RelationProcessorCore,
51           reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(
52               buffer.data()),
53           &byte_length)) {
54     DPCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
55     buffer.resize(byte_length);
56     if (!GetLogicalProcessorInformationEx(
57             RelationProcessorCore,
58             reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(
59                 buffer.data()),
60             &byte_length)) {
61       return {};
62     }
63   }
64 
65   std::vector<BYTE> efficiency_classes;
66   BYTE* byte_ptr = buffer.data();
67   while (byte_ptr < buffer.data() + byte_length) {
68     const auto* structure_ptr =
69         reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(byte_ptr);
70     DCHECK_EQ(structure_ptr->Relationship, RelationProcessorCore);
71     DCHECK_LE(&structure_ptr->Processor.EfficiencyClass +
72                   sizeof(structure_ptr->Processor.EfficiencyClass),
73               buffer.data() + byte_length);
74     efficiency_classes.push_back(structure_ptr->Processor.EfficiencyClass);
75     DCHECK_GE(
76         structure_ptr->Size,
77         offsetof(std::remove_pointer_t<decltype(structure_ptr)>, Processor) +
78             sizeof(structure_ptr->Processor));
79     byte_ptr = byte_ptr + structure_ptr->Size;
80   }
81 
82   return efficiency_classes;
83 }
84 
85 // Returns the physical cores to logical processor mapping masks by using the
86 // Windows API GetLogicalProcessorInformation(), or an empty vector on failure.
87 // When succeeded, the vector would be of same size to the number of physical
88 // cores, while each element is the bitmask of the logical processors that the
89 // physical core has.
GetCoreProcessorMasks()90 std::vector<uint64_t> GetCoreProcessorMasks() {
91   const DWORD kReservedSize = 64;
92   absl::InlinedVector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION, kReservedSize>
93       buffer;
94   buffer.resize(kReservedSize);
95   DWORD byte_length = sizeof(buffer[0]) * kReservedSize;
96   const BOOL result =
97       GetLogicalProcessorInformation(buffer.data(), &byte_length);
98   DWORD element_count = byte_length / sizeof(buffer[0]);
99   DCHECK_EQ(byte_length % sizeof(buffer[0]), 0u);
100   if (!result) {
101     DPCHECK(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
102     buffer.resize(element_count);
103     if (!GetLogicalProcessorInformation(buffer.data(), &byte_length)) {
104       return {};
105     }
106   }
107 
108   std::vector<uint64_t> processor_masks;
109   for (DWORD i = 0; i < element_count; i++) {
110     if (buffer[i].Relationship == RelationProcessorCore) {
111       processor_masks.push_back(buffer[i].ProcessorMask);
112     }
113   }
114 
115   return processor_masks;
116 }
117 
AmountOfMemory(DWORDLONG MEMORYSTATUSEX::* memory_field)118 uint64_t AmountOfMemory(DWORDLONG MEMORYSTATUSEX::*memory_field) {
119   MEMORYSTATUSEX memory_info;
120   memory_info.dwLength = sizeof(memory_info);
121   if (!GlobalMemoryStatusEx(&memory_info)) {
122     NOTREACHED();
123   }
124 
125   return memory_info.*memory_field;
126 }
127 
GetDiskSpaceInfo(const base::FilePath & path,int64_t * available_bytes,int64_t * total_bytes)128 bool GetDiskSpaceInfo(const base::FilePath& path,
129                       int64_t* available_bytes,
130                       int64_t* total_bytes) {
131   ULARGE_INTEGER available;
132   ULARGE_INTEGER total;
133   ULARGE_INTEGER free;
134   if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free))
135     return false;
136 
137   if (available_bytes) {
138     *available_bytes = static_cast<int64_t>(available.QuadPart);
139     if (*available_bytes < 0)
140       *available_bytes = std::numeric_limits<int64_t>::max();
141   }
142   if (total_bytes) {
143     *total_bytes = static_cast<int64_t>(total.QuadPart);
144     if (*total_bytes < 0)
145       *total_bytes = std::numeric_limits<int64_t>::max();
146   }
147   return true;
148 }
149 
150 }  // namespace
151 
152 namespace base {
153 
154 // static
NumberOfProcessors()155 int SysInfo::NumberOfProcessors() {
156   return win::OSInfo::GetInstance()->processors();
157 }
158 
159 // static
NumberOfEfficientProcessorsImpl()160 int SysInfo::NumberOfEfficientProcessorsImpl() {
161   std::vector<BYTE> efficiency_classes = GetCoreEfficiencyClasses();
162   if (efficiency_classes.empty())
163     return 0;
164 
165   auto [min_efficiency_class_it, max_efficiency_class_it] =
166       std::minmax_element(efficiency_classes.begin(), efficiency_classes.end());
167   if (*min_efficiency_class_it == *max_efficiency_class_it)
168     return 0;
169 
170   std::vector<uint64_t> processor_masks = GetCoreProcessorMasks();
171   if (processor_masks.empty())
172     return 0;
173 
174   DCHECK_EQ(efficiency_classes.size(), processor_masks.size());
175   int num_of_efficient_processors = 0;
176   for (size_t i = 0; i < efficiency_classes.size(); i++) {
177     if (efficiency_classes[i] == *min_efficiency_class_it) {
178       num_of_efficient_processors += std::popcount(processor_masks[i]);
179     }
180   }
181 
182   return num_of_efficient_processors;
183 }
184 
185 // static
AmountOfPhysicalMemoryImpl()186 uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
187   return AmountOfMemory(&MEMORYSTATUSEX::ullTotalPhys);
188 }
189 
190 // static
AmountOfAvailablePhysicalMemoryImpl()191 uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
192   SystemMemoryInfoKB info;
193   if (!GetSystemMemoryInfo(&info))
194     return 0;
195   return checked_cast<uint64_t>(info.avail_phys) * 1024;
196 }
197 
198 // static
AmountOfVirtualMemory()199 uint64_t SysInfo::AmountOfVirtualMemory() {
200   return AmountOfMemory(&MEMORYSTATUSEX::ullTotalVirtual);
201 }
202 
203 // static
AmountOfFreeDiskSpace(const FilePath & path)204 int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
205   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
206                                                 base::BlockingType::MAY_BLOCK);
207 
208   int64_t available;
209   if (!GetDiskSpaceInfo(path, &available, nullptr))
210     return -1;
211   return available;
212 }
213 
214 // static
AmountOfTotalDiskSpace(const FilePath & path)215 int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& path) {
216   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
217                                                 base::BlockingType::MAY_BLOCK);
218 
219   int64_t total;
220   if (!GetDiskSpaceInfo(path, nullptr, &total))
221     return -1;
222   return total;
223 }
224 
OperatingSystemName()225 std::string SysInfo::OperatingSystemName() {
226   return "Windows NT";
227 }
228 
229 // static
OperatingSystemVersion()230 std::string SysInfo::OperatingSystemVersion() {
231   win::OSInfo* os_info = win::OSInfo::GetInstance();
232   win::OSInfo::VersionNumber version_number = os_info->version_number();
233   std::string version(StringPrintf("%d.%d.%d", version_number.major,
234                                    version_number.minor, version_number.build));
235   win::OSInfo::ServicePack service_pack = os_info->service_pack();
236   if (service_pack.major != 0) {
237     version += StringPrintf(" SP%d", service_pack.major);
238     if (service_pack.minor != 0)
239       version += StringPrintf(".%d", service_pack.minor);
240   }
241   return version;
242 }
243 
244 // TODO: Implement OperatingSystemVersionComplete, which would include
245 // patchlevel/service pack number.
246 // See chrome/browser/feedback/feedback_util.h, FeedbackUtil::SetOSVersion.
247 
248 // static
OperatingSystemArchitecture()249 std::string SysInfo::OperatingSystemArchitecture() {
250   win::OSInfo::WindowsArchitecture arch = win::OSInfo::GetArchitecture();
251   switch (arch) {
252     case win::OSInfo::X86_ARCHITECTURE:
253       return "x86";
254     case win::OSInfo::X64_ARCHITECTURE:
255       return "x86_64";
256     case win::OSInfo::IA64_ARCHITECTURE:
257       return "ia64";
258     case win::OSInfo::ARM64_ARCHITECTURE:
259       return "arm64";
260     default:
261       return "";
262   }
263 }
264 
265 // static
CPUModelName()266 std::string SysInfo::CPUModelName() {
267   return win::OSInfo::GetInstance()->processor_model_name();
268 }
269 
270 // static
VMAllocationGranularity()271 size_t SysInfo::VMAllocationGranularity() {
272   return win::OSInfo::GetInstance()->allocation_granularity();
273 }
274 
275 // static
OperatingSystemVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)276 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
277                                             int32_t* minor_version,
278                                             int32_t* bugfix_version) {
279   win::OSInfo* os_info = win::OSInfo::GetInstance();
280   *major_version = static_cast<int32_t>(os_info->version_number().major);
281   *minor_version = static_cast<int32_t>(os_info->version_number().minor);
282   *bugfix_version = 0;
283 }
284 
285 // static
ReadHardwareInfoFromRegistry(const wchar_t * reg_value_name)286 std::string ReadHardwareInfoFromRegistry(const wchar_t* reg_value_name) {
287   // On some systems or VMs, the system information and some of the below
288   // locations may be missing info. Attempt to find the info from the below
289   // registry keys in the order provided.
290   static const wchar_t* const kSystemInfoRegKeyPaths[] = {
291       L"HARDWARE\\DESCRIPTION\\System\\BIOS",
292       L"SYSTEM\\CurrentControlSet\\Control\\SystemInformation",
293       L"SYSTEM\\HardwareConfig\\Current",
294   };
295 
296   std::wstring value;
297   for (const wchar_t* system_info_reg_key_path : kSystemInfoRegKeyPaths) {
298     base::win::RegKey system_information_key;
299     if (system_information_key.Open(HKEY_LOCAL_MACHINE,
300                                     system_info_reg_key_path,
301                                     KEY_READ) == ERROR_SUCCESS) {
302       if ((system_information_key.ReadValue(reg_value_name, &value) ==
303            ERROR_SUCCESS) &&
304           !value.empty()) {
305         break;
306       }
307     }
308   }
309 
310   return base::SysWideToUTF8(value);
311 }
312 
313 // static
GetHardwareInfoSync()314 SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() {
315   HardwareInfo info = {ReadHardwareInfoFromRegistry(L"SystemManufacturer"),
316                        SysInfo::HardwareModelName()};
317   return info;
318 }
319 
320 // static
HardwareModelName()321 std::string SysInfo::HardwareModelName() {
322   return ReadHardwareInfoFromRegistry(L"SystemProductName");
323 }
324 
325 }  // namespace base
326