xref: /aosp_15_r20/external/webrtc/third_party/abseil-cpp/absl/base/internal/sysinfo.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/base/internal/sysinfo.h"
16 
17 #include "absl/base/attributes.h"
18 
19 #ifdef _WIN32
20 #include <windows.h>
21 #else
22 #include <fcntl.h>
23 #include <pthread.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #endif
28 
29 #ifdef __linux__
30 #include <sys/syscall.h>
31 #endif
32 
33 #if defined(__APPLE__) || defined(__FreeBSD__)
34 #include <sys/sysctl.h>
35 #endif
36 
37 #if defined(__myriad2__)
38 #include <rtems.h>
39 #endif
40 
41 #include <string.h>
42 
43 #include <cassert>
44 #include <cstdint>
45 #include <cstdio>
46 #include <cstdlib>
47 #include <ctime>
48 #include <limits>
49 #include <thread>  // NOLINT(build/c++11)
50 #include <utility>
51 #include <vector>
52 
53 #include "absl/base/call_once.h"
54 #include "absl/base/config.h"
55 #include "absl/base/internal/raw_logging.h"
56 #include "absl/base/internal/spinlock.h"
57 #include "absl/base/internal/unscaledcycleclock.h"
58 #include "absl/base/thread_annotations.h"
59 
60 namespace absl {
61 ABSL_NAMESPACE_BEGIN
62 namespace base_internal {
63 
64 namespace {
65 
66 #if defined(_WIN32)
67 
68 // Returns number of bits set in `bitMask`
Win32CountSetBits(ULONG_PTR bitMask)69 DWORD Win32CountSetBits(ULONG_PTR bitMask) {
70   for (DWORD bitSetCount = 0; ; ++bitSetCount) {
71     if (bitMask == 0) return bitSetCount;
72     bitMask &= bitMask - 1;
73   }
74 }
75 
76 // Returns the number of logical CPUs using GetLogicalProcessorInformation(), or
77 // 0 if the number of processors is not available or can not be computed.
78 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
Win32NumCPUs()79 int Win32NumCPUs() {
80 #pragma comment(lib, "kernel32.lib")
81   using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
82 
83   DWORD info_size = sizeof(Info);
84   Info* info(static_cast<Info*>(malloc(info_size)));
85   if (info == nullptr) return 0;
86 
87   bool success = GetLogicalProcessorInformation(info, &info_size);
88   if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
89     free(info);
90     info = static_cast<Info*>(malloc(info_size));
91     if (info == nullptr) return 0;
92     success = GetLogicalProcessorInformation(info, &info_size);
93   }
94 
95   DWORD logicalProcessorCount = 0;
96   if (success) {
97     Info* ptr = info;
98     DWORD byteOffset = 0;
99     while (byteOffset + sizeof(Info) <= info_size) {
100       switch (ptr->Relationship) {
101         case RelationProcessorCore:
102           logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask);
103           break;
104 
105         case RelationNumaNode:
106         case RelationCache:
107         case RelationProcessorPackage:
108           // Ignore other entries
109           break;
110 
111         default:
112           // Ignore unknown entries
113           break;
114       }
115       byteOffset += sizeof(Info);
116       ptr++;
117     }
118   }
119   free(info);
120   return static_cast<int>(logicalProcessorCount);
121 }
122 
123 #endif
124 
125 }  // namespace
126 
GetNumCPUs()127 static int GetNumCPUs() {
128 #if defined(__myriad2__)
129   return 1;
130 #elif defined(_WIN32)
131   const int hardware_concurrency = Win32NumCPUs();
132   return hardware_concurrency ? hardware_concurrency : 1;
133 #elif defined(_AIX)
134   return sysconf(_SC_NPROCESSORS_ONLN);
135 #else
136   // Other possibilities:
137   //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
138   //  - sysconf(_SC_NPROCESSORS_ONLN)
139   return static_cast<int>(std::thread::hardware_concurrency());
140 #endif
141 }
142 
143 #if defined(_WIN32)
144 
GetNominalCPUFrequency()145 static double GetNominalCPUFrequency() {
146 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
147     !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
148   // UWP apps don't have access to the registry and currently don't provide an
149   // API informing about CPU nominal frequency.
150   return 1.0;
151 #else
152 #pragma comment(lib, "advapi32.lib")  // For Reg* functions.
153   HKEY key;
154   // Use the Reg* functions rather than the SH functions because shlwapi.dll
155   // pulls in gdi32.dll which makes process destruction much more costly.
156   if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
157                     "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
158                     KEY_READ, &key) == ERROR_SUCCESS) {
159     DWORD type = 0;
160     DWORD data = 0;
161     DWORD data_size = sizeof(data);
162     auto result = RegQueryValueExA(key, "~MHz", 0, &type,
163                                    reinterpret_cast<LPBYTE>(&data), &data_size);
164     RegCloseKey(key);
165     if (result == ERROR_SUCCESS && type == REG_DWORD &&
166         data_size == sizeof(data)) {
167       return data * 1e6;  // Value is MHz.
168     }
169   }
170   return 1.0;
171 #endif  // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
172 }
173 
174 #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
175 
GetNominalCPUFrequency()176 static double GetNominalCPUFrequency() {
177   unsigned freq;
178   size_t size = sizeof(freq);
179   int mib[2] = {CTL_HW, HW_CPU_FREQ};
180   if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
181     return static_cast<double>(freq);
182   }
183   return 1.0;
184 }
185 
186 #else
187 
188 // Helper function for reading a long from a file. Returns true if successful
189 // and the memory location pointed to by value is set to the value read.
ReadLongFromFile(const char * file,long * value)190 static bool ReadLongFromFile(const char *file, long *value) {
191   bool ret = false;
192   int fd = open(file, O_RDONLY | O_CLOEXEC);
193   if (fd != -1) {
194     char line[1024];
195     char *err;
196     memset(line, '\0', sizeof(line));
197     ssize_t len;
198     do {
199       len = read(fd, line, sizeof(line) - 1);
200     } while (len < 0 && errno == EINTR);
201     if (len <= 0) {
202       ret = false;
203     } else {
204       const long temp_value = strtol(line, &err, 10);
205       if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
206         *value = temp_value;
207         ret = true;
208       }
209     }
210     close(fd);
211   }
212   return ret;
213 }
214 
215 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
216 
217 // Reads a monotonic time source and returns a value in
218 // nanoseconds. The returned value uses an arbitrary epoch, not the
219 // Unix epoch.
ReadMonotonicClockNanos()220 static int64_t ReadMonotonicClockNanos() {
221   struct timespec t;
222 #ifdef CLOCK_MONOTONIC_RAW
223   int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
224 #else
225   int rc = clock_gettime(CLOCK_MONOTONIC, &t);
226 #endif
227   if (rc != 0) {
228     perror("clock_gettime() failed");
229     abort();
230   }
231   return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
232 }
233 
234 class UnscaledCycleClockWrapperForInitializeFrequency {
235  public:
Now()236   static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
237 };
238 
239 struct TimeTscPair {
240   int64_t time;  // From ReadMonotonicClockNanos().
241   int64_t tsc;   // From UnscaledCycleClock::Now().
242 };
243 
244 // Returns a pair of values (monotonic kernel time, TSC ticks) that
245 // approximately correspond to each other.  This is accomplished by
246 // doing several reads and picking the reading with the lowest
247 // latency.  This approach is used to minimize the probability that
248 // our thread was preempted between clock reads.
GetTimeTscPair()249 static TimeTscPair GetTimeTscPair() {
250   int64_t best_latency = std::numeric_limits<int64_t>::max();
251   TimeTscPair best;
252   for (int i = 0; i < 10; ++i) {
253     int64_t t0 = ReadMonotonicClockNanos();
254     int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
255     int64_t t1 = ReadMonotonicClockNanos();
256     int64_t latency = t1 - t0;
257     if (latency < best_latency) {
258       best_latency = latency;
259       best.time = t0;
260       best.tsc = tsc;
261     }
262   }
263   return best;
264 }
265 
266 // Measures and returns the TSC frequency by taking a pair of
267 // measurements approximately `sleep_nanoseconds` apart.
MeasureTscFrequencyWithSleep(int sleep_nanoseconds)268 static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
269   auto t0 = GetTimeTscPair();
270   struct timespec ts;
271   ts.tv_sec = 0;
272   ts.tv_nsec = sleep_nanoseconds;
273   while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
274   auto t1 = GetTimeTscPair();
275   double elapsed_ticks = t1.tsc - t0.tsc;
276   double elapsed_time = (t1.time - t0.time) * 1e-9;
277   return elapsed_ticks / elapsed_time;
278 }
279 
280 // Measures and returns the TSC frequency by calling
281 // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
282 // frequency measurement stabilizes.
MeasureTscFrequency()283 static double MeasureTscFrequency() {
284   double last_measurement = -1.0;
285   int sleep_nanoseconds = 1000000;  // 1 millisecond.
286   for (int i = 0; i < 8; ++i) {
287     double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
288     if (measurement * 0.99 < last_measurement &&
289         last_measurement < measurement * 1.01) {
290       // Use the current measurement if it is within 1% of the
291       // previous measurement.
292       return measurement;
293     }
294     last_measurement = measurement;
295     sleep_nanoseconds *= 2;
296   }
297   return last_measurement;
298 }
299 
300 #endif  // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
301 
GetNominalCPUFrequency()302 static double GetNominalCPUFrequency() {
303   long freq = 0;
304 
305   // Google's production kernel has a patch to export the TSC
306   // frequency through sysfs. If the kernel is exporting the TSC
307   // frequency use that. There are issues where cpuinfo_max_freq
308   // cannot be relied on because the BIOS may be exporting an invalid
309   // p-state (on x86) or p-states may be used to put the processor in
310   // a new mode (turbo mode). Essentially, those frequencies cannot
311   // always be relied upon. The same reasons apply to /proc/cpuinfo as
312   // well.
313   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
314     return freq * 1e3;  // Value is kHz.
315   }
316 
317 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
318   // On these platforms, the TSC frequency is the nominal CPU
319   // frequency.  But without having the kernel export it directly
320   // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
321   // other way to reliably get the TSC frequency, so we have to
322   // measure it ourselves.  Some CPUs abuse cpuinfo_max_freq by
323   // exporting "fake" frequencies for implementing new features. For
324   // example, Intel's turbo mode is enabled by exposing a p-state
325   // value with a higher frequency than that of the real TSC
326   // rate. Because of this, we prefer to measure the TSC rate
327   // ourselves on i386 and x86-64.
328   return MeasureTscFrequency();
329 #else
330 
331   // If CPU scaling is in effect, we want to use the *maximum*
332   // frequency, not whatever CPU speed some random processor happens
333   // to be using now.
334   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
335                        &freq)) {
336     return freq * 1e3;  // Value is kHz.
337   }
338 
339   return 1.0;
340 #endif  // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
341 }
342 
343 #endif
344 
345 ABSL_CONST_INIT static once_flag init_num_cpus_once;
346 ABSL_CONST_INIT static int num_cpus = 0;
347 
348 // NumCPUs() may be called before main() and before malloc is properly
349 // initialized, therefore this must not allocate memory.
NumCPUs()350 int NumCPUs() {
351   base_internal::LowLevelCallOnce(
352       &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
353   return num_cpus;
354 }
355 
356 // A default frequency of 0.0 might be dangerous if it is used in division.
357 ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
358 ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
359 
360 // NominalCPUFrequency() may be called before main() and before malloc is
361 // properly initialized, therefore this must not allocate memory.
NominalCPUFrequency()362 double NominalCPUFrequency() {
363   base_internal::LowLevelCallOnce(
364       &init_nominal_cpu_frequency_once,
365       []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
366   return nominal_cpu_frequency;
367 }
368 
369 #if defined(_WIN32)
370 
GetTID()371 pid_t GetTID() {
372   return pid_t{GetCurrentThreadId()};
373 }
374 
375 #elif defined(__linux__)
376 
377 #ifndef SYS_gettid
378 #define SYS_gettid __NR_gettid
379 #endif
380 
GetTID()381 pid_t GetTID() {
382   return static_cast<pid_t>(syscall(SYS_gettid));
383 }
384 
385 #elif defined(__akaros__)
386 
GetTID()387 pid_t GetTID() {
388   // Akaros has a concept of "vcore context", which is the state the program
389   // is forced into when we need to make a user-level scheduling decision, or
390   // run a signal handler.  This is analogous to the interrupt context that a
391   // CPU might enter if it encounters some kind of exception.
392   //
393   // There is no current thread context in vcore context, but we need to give
394   // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
395   // Thread 0 always exists, so if we are in vcore context, we return that.
396   //
397   // Otherwise, we know (since we are using pthreads) that the uthread struct
398   // current_uthread is pointing to is the first element of a
399   // struct pthread_tcb, so we extract and return the thread ID from that.
400   //
401   // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
402   // structure at some point. We should modify this code to remove the cast
403   // when that happens.
404   if (in_vcore_context())
405     return 0;
406   return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
407 }
408 
409 #elif defined(__myriad2__)
410 
GetTID()411 pid_t GetTID() {
412   uint32_t tid;
413   rtems_task_ident(RTEMS_SELF, 0, &tid);
414   return tid;
415 }
416 
417 #else
418 
419 // Fallback implementation of GetTID using pthread_getspecific.
420 ABSL_CONST_INIT static once_flag tid_once;
421 ABSL_CONST_INIT static pthread_key_t tid_key;
422 ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock(
423     absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
424 
425 // We set a bit per thread in this array to indicate that an ID is in
426 // use. ID 0 is unused because it is the default value returned by
427 // pthread_getspecific().
428 ABSL_CONST_INIT static std::vector<uint32_t> *tid_array
429     ABSL_GUARDED_BY(tid_lock) = nullptr;
430 static constexpr int kBitsPerWord = 32;  // tid_array is uint32_t.
431 
432 // Returns the TID to tid_array.
FreeTID(void * v)433 static void FreeTID(void *v) {
434   intptr_t tid = reinterpret_cast<intptr_t>(v);
435   intptr_t word = tid / kBitsPerWord;
436   uint32_t mask = ~(1u << (tid % kBitsPerWord));
437   absl::base_internal::SpinLockHolder lock(&tid_lock);
438   assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
439   (*tid_array)[static_cast<size_t>(word)] &= mask;
440 }
441 
InitGetTID()442 static void InitGetTID() {
443   if (pthread_key_create(&tid_key, FreeTID) != 0) {
444     // The logging system calls GetTID() so it can't be used here.
445     perror("pthread_key_create failed");
446     abort();
447   }
448 
449   // Initialize tid_array.
450   absl::base_internal::SpinLockHolder lock(&tid_lock);
451   tid_array = new std::vector<uint32_t>(1);
452   (*tid_array)[0] = 1;  // ID 0 is never-allocated.
453 }
454 
455 // Return a per-thread small integer ID from pthread's thread-specific data.
GetTID()456 pid_t GetTID() {
457   absl::call_once(tid_once, InitGetTID);
458 
459   intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
460   if (tid != 0) {
461     return static_cast<pid_t>(tid);
462   }
463 
464   int bit;  // tid_array[word] = 1u << bit;
465   size_t word;
466   {
467     // Search for the first unused ID.
468     absl::base_internal::SpinLockHolder lock(&tid_lock);
469     // First search for a word in the array that is not all ones.
470     word = 0;
471     while (word < tid_array->size() && ~(*tid_array)[word] == 0) {
472       ++word;
473     }
474     if (word == tid_array->size()) {
475       tid_array->push_back(0);  // No space left, add kBitsPerWord more IDs.
476     }
477     // Search for a zero bit in the word.
478     bit = 0;
479     while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) {
480       ++bit;
481     }
482     tid =
483         static_cast<intptr_t>((word * kBitsPerWord) + static_cast<size_t>(bit));
484     (*tid_array)[word] |= 1u << bit;  // Mark the TID as allocated.
485   }
486 
487   if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) {
488     perror("pthread_setspecific failed");
489     abort();
490   }
491 
492   return static_cast<pid_t>(tid);
493 }
494 
495 #endif
496 
497 // GetCachedTID() caches the thread ID in thread-local storage (which is a
498 // userspace construct) to avoid unnecessary system calls. Without this caching,
499 // it can take roughly 98ns, while it takes roughly 1ns with this caching.
GetCachedTID()500 pid_t GetCachedTID() {
501 #ifdef ABSL_HAVE_THREAD_LOCAL
502   static thread_local pid_t thread_id = GetTID();
503   return thread_id;
504 #else
505   return GetTID();
506 #endif  // ABSL_HAVE_THREAD_LOCAL
507 }
508 
509 }  // namespace base_internal
510 ABSL_NAMESPACE_END
511 }  // namespace absl
512