• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/notreached.h"
23 #include "base/numerics/safe_conversions.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/system/sys_info_internal.h"
28 #include "base/threading/scoped_blocking_call.h"
29 #include "build/build_config.h"
30 
31 #if BUILDFLAG(IS_ANDROID)
32 #include <sys/vfs.h>
33 #define statvfs statfs  // Android uses a statvfs-like statfs struct and call.
34 #else
35 #include <sys/statvfs.h>
36 #endif
37 
38 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
39 #include <linux/magic.h>
40 #include <sys/vfs.h>
41 #endif
42 
43 #if BUILDFLAG(IS_MAC)
44 #include "third_party/abseil-cpp/absl/types/optional.h"
45 #endif
46 
47 namespace {
48 
AmountOfVirtualMemory()49 uint64_t AmountOfVirtualMemory() {
50   struct rlimit limit;
51   int result = getrlimit(RLIMIT_DATA, &limit);
52   if (result != 0) {
53     NOTREACHED();
54     return 0;
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   absl::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       return 1;
156     }
157 
158     int num_cpus = static_cast<int>(res);
159 
160 #if BUILDFLAG(IS_LINUX)
161     // Restrict the CPU count based on the process's CPU affinity mask, if
162     // available.
163     cpu_set_t* cpu_set = CPU_ALLOC(num_cpus);
164     size_t cpu_set_size = CPU_ALLOC_SIZE(num_cpus);
165     int ret = sched_getaffinity(0, cpu_set_size, cpu_set);
166     if (ret == 0) {
167       num_cpus = CPU_COUNT_S(cpu_set_size, cpu_set);
168     }
169     CPU_FREE(cpu_set);
170 #endif  // BUILDFLAG(IS_LINUX)
171 
172     return num_cpus;
173   }();
174 
175   return cached_num_cpus;
176 }
177 #endif  // !BUILDFLAG(IS_OPENBSD)
178 
179 // static
AmountOfVirtualMemory()180 uint64_t SysInfo::AmountOfVirtualMemory() {
181   return g_lazy_virtual_memory.Get().value();
182 }
183 
184 // static
AmountOfFreeDiskSpace(const FilePath & path)185 int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
186   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
187                                                 base::BlockingType::MAY_BLOCK);
188 
189   int64_t available;
190   if (!GetDiskSpaceInfo(path, &available, nullptr))
191     return -1;
192   return available;
193 }
194 
195 // static
AmountOfTotalDiskSpace(const FilePath & path)196 int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& path) {
197   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
198                                                 base::BlockingType::MAY_BLOCK);
199 
200   int64_t total;
201   if (!GetDiskSpaceInfo(path, nullptr, &total))
202     return -1;
203   return total;
204 }
205 
206 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
207 // static
OperatingSystemName()208 std::string SysInfo::OperatingSystemName() {
209   struct utsname info;
210   if (uname(&info) < 0) {
211     NOTREACHED();
212     return std::string();
213   }
214   return std::string(info.sysname);
215 }
216 #endif  //! BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)
217 
218 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
219 // static
OperatingSystemVersion()220 std::string SysInfo::OperatingSystemVersion() {
221   struct utsname info;
222   if (uname(&info) < 0) {
223     NOTREACHED();
224     return std::string();
225   }
226   return std::string(info.release);
227 }
228 #endif
229 
230 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
231 // static
OperatingSystemVersionNumbers(int32_t * major_version,int32_t * minor_version,int32_t * bugfix_version)232 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
233                                             int32_t* minor_version,
234                                             int32_t* bugfix_version) {
235   struct utsname info;
236   if (uname(&info) < 0) {
237     NOTREACHED();
238     *major_version = 0;
239     *minor_version = 0;
240     *bugfix_version = 0;
241     return;
242   }
243   int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
244                         bugfix_version);
245   if (num_read < 1)
246     *major_version = 0;
247   if (num_read < 2)
248     *minor_version = 0;
249   if (num_read < 3)
250     *bugfix_version = 0;
251 }
252 #endif
253 
254 #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_IOS)
255 // static
OperatingSystemArchitecture()256 std::string SysInfo::OperatingSystemArchitecture() {
257   struct utsname info;
258   if (uname(&info) < 0) {
259     NOTREACHED();
260     return std::string();
261   }
262   std::string arch(info.machine);
263   if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
264     arch = "x86";
265   } else if (arch == "amd64") {
266     arch = "x86_64";
267   } else if (std::string(info.sysname) == "AIX") {
268     arch = "ppc64";
269   }
270   return arch;
271 }
272 #endif  // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_IOS)
273 
274 // static
VMAllocationGranularity()275 size_t SysInfo::VMAllocationGranularity() {
276   return checked_cast<size_t>(getpagesize());
277 }
278 
279 #if !BUILDFLAG(IS_APPLE)
280 // static
NumberOfEfficientProcessorsImpl()281 int SysInfo::NumberOfEfficientProcessorsImpl() {
282 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
283   // Try to guess the CPU architecture and cores of each cluster by comparing
284   // the maximum frequencies of the available (online and offline) cores.
285   int num_cpus = SysInfo::NumberOfProcessors();
286   DCHECK_GE(num_cpus, 0);
287   std::vector<uint32_t> max_core_frequencies_khz(static_cast<size_t>(num_cpus),
288                                                  0);
289   for (int core_index = 0; core_index < num_cpus; ++core_index) {
290     std::string content;
291     auto path = StringPrintf(
292         "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", core_index);
293     if (!ReadFileToStringNonBlocking(FilePath(path), &content))
294       return 0;
295     if (!StringToUint(
296             content,
297             &max_core_frequencies_khz[static_cast<size_t>(core_index)]))
298       return 0;
299   }
300 
301   auto [min_max_core_frequencies_khz_it, max_max_core_frequencies_khz_it] =
302       std::minmax_element(max_core_frequencies_khz.begin(),
303                           max_core_frequencies_khz.end());
304 
305   if (*min_max_core_frequencies_khz_it == *max_max_core_frequencies_khz_it)
306     return 0;
307 
308   return static_cast<int>(std::count(max_core_frequencies_khz.begin(),
309                                      max_core_frequencies_khz.end(),
310                                      *min_max_core_frequencies_khz_it));
311 #else
312   NOTIMPLEMENTED();
313   return 0;
314 #endif
315 }
316 #endif  // !BUILDFLAG(IS_APPLE)
317 
318 }  // namespace base
319