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