1 // Copyright 2011 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 #include "base/system/sys_info.h"
6
7 #include <errno.h>
8 #include <sched.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <sys/param.h>
13 #include <sys/resource.h>
14 #include <sys/utsname.h>
15 #include <unistd.h>
16
17 #include <algorithm>
18
19 #include "base/check.h"
20 #include "base/files/file_util.h"
21 #include "base/lazy_instance.h"
22 #include "base/notimplemented.h"
23 #include "base/notreached.h"
24 #include "base/numerics/safe_conversions.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/system/sys_info_internal.h"
29 #include "base/threading/scoped_blocking_call.h"
30 #include "build/build_config.h"
31
32 #if BUILDFLAG(IS_ANDROID)
33 #include <sys/vfs.h>
34 #define statvfs statfs // Android uses a statvfs-like statfs struct and call.
35 #else
36 #include <sys/statvfs.h>
37 #endif
38
39 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
40 #include <linux/magic.h>
41 #include <sys/vfs.h>
42 #endif
43
44 #if BUILDFLAG(IS_MAC)
45 #include <optional>
46 #endif
47
48 namespace {
49
AmountOfVirtualMemory()50 uint64_t AmountOfVirtualMemory() {
51 struct rlimit limit;
52 int result = getrlimit(RLIMIT_DATA, &limit);
53 if (result != 0) {
54 NOTREACHED();
55 }
56 return limit.rlim_cur == RLIM_INFINITY ? 0 : limit.rlim_cur;
57 }
58
59 base::LazyInstance<
60 base::internal::LazySysInfoValue<uint64_t, AmountOfVirtualMemory>>::Leaky
61 g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER;
62
63 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
IsStatsZeroIfUnlimited(const base::FilePath & path)64 bool IsStatsZeroIfUnlimited(const base::FilePath& path) {
65 struct statfs stats;
66
67 if (HANDLE_EINTR(statfs(path.value().c_str(), &stats)) != 0)
68 return false;
69
70 // This static_cast is here because various libcs disagree about the size
71 // and signedness of statfs::f_type. In particular, glibc has it as either a
72 // signed long or a signed int depending on platform, and other libcs
73 // (following the statfs(2) man page) use unsigned int instead. To avoid
74 // either an unsigned -> signed cast, or a narrowing cast, we always upcast
75 // statfs::f_type to unsigned long. :(
76 switch (static_cast<unsigned long>(stats.f_type)) {
77 case TMPFS_MAGIC:
78 case HUGETLBFS_MAGIC:
79 case RAMFS_MAGIC:
80 return true;
81 }
82 return false;
83 }
84 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
85
GetDiskSpaceInfo(const base::FilePath & path,int64_t * available_bytes,int64_t * total_bytes)86 bool GetDiskSpaceInfo(const base::FilePath& path,
87 int64_t* available_bytes,
88 int64_t* total_bytes) {
89 struct statvfs stats;
90 if (HANDLE_EINTR(statvfs(path.value().c_str(), &stats)) != 0)
91 return false;
92
93 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
94 const bool zero_size_means_unlimited =
95 stats.f_blocks == 0 && IsStatsZeroIfUnlimited(path);
96 #else
97 const bool zero_size_means_unlimited = false;
98 #endif
99
100 if (available_bytes) {
101 *available_bytes =
102 zero_size_means_unlimited
103 ? std::numeric_limits<int64_t>::max()
104 : base::saturated_cast<int64_t>(stats.f_bavail * stats.f_frsize);
105 }
106
107 if (total_bytes) {
108 *total_bytes =
109 zero_size_means_unlimited
110 ? std::numeric_limits<int64_t>::max()
111 : base::saturated_cast<int64_t>(stats.f_blocks * stats.f_frsize);
112 }
113 return true;
114 }
115
116 } // namespace
117
118 namespace base {
119
120 #if !BUILDFLAG(IS_OPENBSD)
121 // static
NumberOfProcessors()122 int SysInfo::NumberOfProcessors() {
123 #if BUILDFLAG(IS_MAC)
124 std::optional<int> number_of_physical_cores =
125 internal::NumberOfProcessorsWhenCpuSecurityMitigationEnabled();
126 if (number_of_physical_cores.has_value()) {
127 return number_of_physical_cores.value();
128 }
129 #endif // BUILDFLAG(IS_MAC)
130
131 // This value is cached to avoid computing this value in the sandbox, which
132 // doesn't work on some platforms. The Mac-specific code above is not
133 // included because changing the value at runtime is the best way to unittest
134 // its behavior.
135 static int cached_num_cpus = [] {
136 // sysconf returns the number of "logical" (not "physical") processors on
137 // both Mac and Linux. So we get the number of max available "logical"
138 // processors.
139 //
140 // Note that the number of "currently online" processors may be fewer than
141 // the returned value of NumberOfProcessors(). On some platforms, the kernel
142 // may make some processors offline intermittently, to save power when
143 // system loading is low.
144 //
145 // One common use case that needs to know the processor count is to create
146 // optimal number of threads for optimization. It should make plan according
147 // to the number of "max available" processors instead of "currently online"
148 // ones. The kernel should be smart enough to make all processors online
149 // when it has sufficient number of threads waiting to run.
150 long res = sysconf(_SC_NPROCESSORS_CONF);
151 if (res == -1) {
152 // `res` can be -1 if this function is invoked under the sandbox, which
153 // should never happen.
154 NOTREACHED();
155 }
156
157 int num_cpus = static_cast<int>(res);
158
159 #if BUILDFLAG(IS_LINUX)
160 // Restrict the CPU count based on the process's CPU affinity mask, if
161 // available.
162 cpu_set_t* cpu_set = CPU_ALLOC(num_cpus);
163 size_t cpu_set_size = CPU_ALLOC_SIZE(num_cpus);
164 int ret = sched_getaffinity(0, cpu_set_size, cpu_set);
165 if (ret == 0) {
166 num_cpus = CPU_COUNT_S(cpu_set_size, cpu_set);
167 }
168 CPU_FREE(cpu_set);
169 #endif // BUILDFLAG(IS_LINUX)
170
171 return num_cpus;
172 }();
173
174 return cached_num_cpus;
175 }
176 #endif // !BUILDFLAG(IS_OPENBSD)
177
178 // static
AmountOfVirtualMemory()179 uint64_t SysInfo::AmountOfVirtualMemory() {
180 return g_lazy_virtual_memory.Get().value();
181 }
182
183 // static
AmountOfFreeDiskSpace(const FilePath & path)184 int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
185 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
186 base::BlockingType::MAY_BLOCK);
187
188 int64_t available;
189 if (!GetDiskSpaceInfo(path, &available, nullptr))
190 return -1;
191 return available;
192 }
193
194 // static
AmountOfTotalDiskSpace(const FilePath & path)195 int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& path) {
196 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
197 base::BlockingType::MAY_BLOCK);
198
199 int64_t total;
200 if (!GetDiskSpaceInfo(path, nullptr, &total))
201 return -1;
202 return total;
203 }
204
205 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
206 // static
OperatingSystemName()207 std::string SysInfo::OperatingSystemName() {
208 struct utsname info;
209 if (uname(&info) < 0) {
210 NOTREACHED();
211 }
212 return std::string(info.sysname);
213 }
214 #endif //! BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
215
216 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
217 // static
OperatingSystemVersion()218 std::string SysInfo::OperatingSystemVersion() {
219 struct utsname info;
220 if (uname(&info) < 0) {
221 NOTREACHED();
222 }
223 return std::string(info.release);
224 }
225 #endif
226
227 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
228 // static
OperatingSystemVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)229 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
230 int32_t* minor_version,
231 int32_t* bugfix_version) {
232 struct utsname info;
233 if (uname(&info) < 0) {
234 NOTREACHED();
235 }
236 int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
237 bugfix_version);
238 if (num_read < 1)
239 *major_version = 0;
240 if (num_read < 2)
241 *minor_version = 0;
242 if (num_read < 3)
243 *bugfix_version = 0;
244 }
245 #endif
246
247 #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_IOS)
248 // static
OperatingSystemArchitecture()249 std::string SysInfo::OperatingSystemArchitecture() {
250 struct utsname info;
251 if (uname(&info) < 0) {
252 NOTREACHED();
253 }
254 std::string arch(info.machine);
255 if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
256 arch = "x86";
257 } else if (arch == "amd64") {
258 arch = "x86_64";
259 } else if (std::string(info.sysname) == "AIX") {
260 arch = "ppc64";
261 }
262 return arch;
263 }
264 #endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_IOS)
265
266 // static
VMAllocationGranularity()267 size_t SysInfo::VMAllocationGranularity() {
268 return checked_cast<size_t>(getpagesize());
269 }
270
271 #if !BUILDFLAG(IS_APPLE)
272 // static
NumberOfEfficientProcessorsImpl()273 int SysInfo::NumberOfEfficientProcessorsImpl() {
274 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
275 // Try to guess the CPU architecture and cores of each cluster by comparing
276 // the maximum frequencies of the available (online and offline) cores.
277 int num_cpus = SysInfo::NumberOfProcessors();
278 DCHECK_GE(num_cpus, 0);
279 std::vector<uint32_t> max_core_frequencies_khz(static_cast<size_t>(num_cpus),
280 0);
281 for (int core_index = 0; core_index < num_cpus; ++core_index) {
282 std::string content;
283 auto path = StringPrintf(
284 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", core_index);
285 if (!ReadFileToStringNonBlocking(FilePath(path), &content))
286 return 0;
287 if (!StringToUint(
288 content,
289 &max_core_frequencies_khz[static_cast<size_t>(core_index)]))
290 return 0;
291 }
292
293 auto [min_max_core_frequencies_khz_it, max_max_core_frequencies_khz_it] =
294 std::minmax_element(max_core_frequencies_khz.begin(),
295 max_core_frequencies_khz.end());
296
297 if (*min_max_core_frequencies_khz_it == *max_max_core_frequencies_khz_it)
298 return 0;
299
300 return static_cast<int>(std::count(max_core_frequencies_khz.begin(),
301 max_core_frequencies_khz.end(),
302 *min_max_core_frequencies_khz_it));
303 #else
304 NOTIMPLEMENTED();
305 return 0;
306 #endif
307 }
308 #endif // !BUILDFLAG(IS_APPLE)
309
310 } // namespace base
311