| // Copyright 2017 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "absl/base/internal/sysinfo.h" | |
| #include <string.h> | |
| #include <cassert> | |
| #include <cerrno> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <ctime> | |
| #include <limits> | |
| #include <thread> // NOLINT(build/c++11) | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/base/attributes.h" | |
| #include "absl/base/call_once.h" | |
| #include "absl/base/config.h" | |
| #include "absl/base/internal/raw_logging.h" | |
| #include "absl/base/internal/spinlock.h" | |
| #include "absl/base/internal/unscaledcycleclock.h" | |
| #include "absl/base/thread_annotations.h" | |
| #ifdef _WIN32 | |
| #include <windows.h> | |
| #else | |
| #include <fcntl.h> | |
| #include <pthread.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #endif | |
| #ifdef __linux__ | |
| #include <sys/syscall.h> | |
| #endif | |
| #if defined(__APPLE__) || defined(__FreeBSD__) | |
| #include <sys/sysctl.h> | |
| #endif | |
| #ifdef __FreeBSD__ | |
| #include <pthread_np.h> | |
| #endif | |
| #ifdef __NetBSD__ | |
| #include <lwp.h> | |
| #endif | |
| #if defined(__myriad2__) | |
| #include <rtems.h> | |
| #endif | |
| #if defined(__Fuchsia__) | |
| #include <zircon/process.h> | |
| #endif | |
| #if defined(__FREERTOS__) | |
| #include <task.h> | |
| #endif | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace base_internal { | |
| namespace { | |
| #if defined(_WIN32) | |
| // Returns number of bits set in `bitMask` | |
| DWORD Win32CountSetBits(ULONG_PTR bitMask) { | |
| for (DWORD bitSetCount = 0; ; ++bitSetCount) { | |
| if (bitMask == 0) return bitSetCount; | |
| bitMask &= bitMask - 1; | |
| } | |
| } | |
| // Returns the number of logical CPUs using GetLogicalProcessorInformation(), or | |
| // 0 if the number of processors is not available or can not be computed. | |
| // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation | |
| int Win32NumCPUs() { | |
| #pragma comment(lib, "kernel32.lib") | |
| using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; | |
| DWORD info_size = sizeof(Info); | |
| Info* info(static_cast<Info*>(malloc(info_size))); | |
| if (info == nullptr) return 0; | |
| bool success = GetLogicalProcessorInformation(info, &info_size); | |
| if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { | |
| free(info); | |
| info = static_cast<Info*>(malloc(info_size)); | |
| if (info == nullptr) return 0; | |
| success = GetLogicalProcessorInformation(info, &info_size); | |
| } | |
| DWORD logicalProcessorCount = 0; | |
| if (success) { | |
| Info* ptr = info; | |
| DWORD byteOffset = 0; | |
| while (byteOffset + sizeof(Info) <= info_size) { | |
| switch (ptr->Relationship) { | |
| case RelationProcessorCore: | |
| logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask); | |
| break; | |
| case RelationNumaNode: | |
| case RelationCache: | |
| case RelationProcessorPackage: | |
| // Ignore other entries | |
| break; | |
| default: | |
| // Ignore unknown entries | |
| break; | |
| } | |
| byteOffset += sizeof(Info); | |
| ptr++; | |
| } | |
| } | |
| free(info); | |
| return static_cast<int>(logicalProcessorCount); | |
| } | |
| #endif | |
| } // namespace | |
| static int GetNumCPUs() { | |
| #if defined(__myriad2__) | |
| return 1; | |
| #elif defined(_WIN32) | |
| const int hardware_concurrency = Win32NumCPUs(); | |
| return hardware_concurrency ? hardware_concurrency : 1; | |
| #elif defined(_AIX) | |
| return sysconf(_SC_NPROCESSORS_ONLN); | |
| #else | |
| // Other possibilities: | |
| // - Read /sys/devices/system/cpu/online and use cpumask_parse() | |
| // - sysconf(_SC_NPROCESSORS_ONLN) | |
| return static_cast<int>(std::thread::hardware_concurrency()); | |
| #endif | |
| } | |
| #if defined(_WIN32) | |
| static double GetNominalCPUFrequency() { | |
| #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ | |
| !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) | |
| // UWP apps don't have access to the registry and currently don't provide an | |
| // API informing about CPU nominal frequency. | |
| return 1.0; | |
| #else | |
| #pragma comment(lib, "advapi32.lib") // For Reg* functions. | |
| HKEY key; | |
| // Use the Reg* functions rather than the SH functions because shlwapi.dll | |
| // pulls in gdi32.dll which makes process destruction much more costly. | |
| if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, | |
| "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, | |
| KEY_READ, &key) == ERROR_SUCCESS) { | |
| DWORD type = 0; | |
| DWORD data = 0; | |
| DWORD data_size = sizeof(data); | |
| auto result = RegQueryValueExA(key, "~MHz", nullptr, &type, | |
| reinterpret_cast<LPBYTE>(&data), &data_size); | |
| RegCloseKey(key); | |
| if (result == ERROR_SUCCESS && type == REG_DWORD && | |
| data_size == sizeof(data)) { | |
| return data * 1e6; // Value is MHz. | |
| } | |
| } | |
| return 1.0; | |
| #endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP | |
| } | |
| #elif defined(CTL_HW) && defined(HW_CPU_FREQ) | |
| static double GetNominalCPUFrequency() { | |
| unsigned freq; | |
| size_t size = sizeof(freq); | |
| int mib[2] = {CTL_HW, HW_CPU_FREQ}; | |
| if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) { | |
| return static_cast<double>(freq); | |
| } | |
| return 1.0; | |
| } | |
| #else | |
| // Helper function for reading a long from a file. Returns true if successful | |
| // and the memory location pointed to by value is set to the value read. | |
| static bool ReadLongFromFile(const char *file, long *value) { | |
| bool ret = false; | |
| #if defined(_POSIX_C_SOURCE) | |
| const int file_mode = (O_RDONLY | O_CLOEXEC); | |
| #else | |
| const int file_mode = O_RDONLY; | |
| #endif | |
| int fd = open(file, file_mode); | |
| if (fd != -1) { | |
| char line[1024]; | |
| char *err; | |
| memset(line, '\0', sizeof(line)); | |
| ssize_t len; | |
| do { | |
| len = read(fd, line, sizeof(line) - 1); | |
| } while (len < 0 && errno == EINTR); | |
| if (len <= 0) { | |
| ret = false; | |
| } else { | |
| const long temp_value = strtol(line, &err, 10); | |
| if (line[0] != '\0' && (*err == '\n' || *err == '\0')) { | |
| *value = temp_value; | |
| ret = true; | |
| } | |
| } | |
| close(fd); | |
| } | |
| return ret; | |
| } | |
| #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY) | |
| // Reads a monotonic time source and returns a value in | |
| // nanoseconds. The returned value uses an arbitrary epoch, not the | |
| // Unix epoch. | |
| static int64_t ReadMonotonicClockNanos() { | |
| struct timespec t; | |
| #ifdef CLOCK_MONOTONIC_RAW | |
| int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t); | |
| #else | |
| int rc = clock_gettime(CLOCK_MONOTONIC, &t); | |
| #endif | |
| if (rc != 0) { | |
| ABSL_RAW_LOG(FATAL, "clock_gettime() failed: (%d)", errno); | |
| } | |
| return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec; | |
| } | |
| class UnscaledCycleClockWrapperForInitializeFrequency { | |
| public: | |
| static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); } | |
| }; | |
| struct TimeTscPair { | |
| int64_t time; // From ReadMonotonicClockNanos(). | |
| int64_t tsc; // From UnscaledCycleClock::Now(). | |
| }; | |
| // Returns a pair of values (monotonic kernel time, TSC ticks) that | |
| // approximately correspond to each other. This is accomplished by | |
| // doing several reads and picking the reading with the lowest | |
| // latency. This approach is used to minimize the probability that | |
| // our thread was preempted between clock reads. | |
| static TimeTscPair GetTimeTscPair() { | |
| int64_t best_latency = std::numeric_limits<int64_t>::max(); | |
| TimeTscPair best; | |
| for (int i = 0; i < 10; ++i) { | |
| int64_t t0 = ReadMonotonicClockNanos(); | |
| int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now(); | |
| int64_t t1 = ReadMonotonicClockNanos(); | |
| int64_t latency = t1 - t0; | |
| if (latency < best_latency) { | |
| best_latency = latency; | |
| best.time = t0; | |
| best.tsc = tsc; | |
| } | |
| } | |
| return best; | |
| } | |
| // Measures and returns the TSC frequency by taking a pair of | |
| // measurements approximately `sleep_nanoseconds` apart. | |
| static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) { | |
| auto t0 = GetTimeTscPair(); | |
| struct timespec ts; | |
| ts.tv_sec = 0; | |
| ts.tv_nsec = sleep_nanoseconds; | |
| while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {} | |
| auto t1 = GetTimeTscPair(); | |
| double elapsed_ticks = t1.tsc - t0.tsc; | |
| double elapsed_time = (t1.time - t0.time) * 1e-9; | |
| return elapsed_ticks / elapsed_time; | |
| } | |
| // Measures and returns the TSC frequency by calling | |
| // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the | |
| // frequency measurement stabilizes. | |
| static double MeasureTscFrequency() { | |
| double last_measurement = -1.0; | |
| int sleep_nanoseconds = 1000000; // 1 millisecond. | |
| for (int i = 0; i < 8; ++i) { | |
| double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds); | |
| if (measurement * 0.99 < last_measurement && | |
| last_measurement < measurement * 1.01) { | |
| // Use the current measurement if it is within 1% of the | |
| // previous measurement. | |
| return measurement; | |
| } | |
| last_measurement = measurement; | |
| sleep_nanoseconds *= 2; | |
| } | |
| return last_measurement; | |
| } | |
| #endif // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY | |
| static double GetNominalCPUFrequency() { | |
| long freq = 0; | |
| // Google's production kernel has a patch to export the TSC | |
| // frequency through sysfs. If the kernel is exporting the TSC | |
| // frequency use that. There are issues where cpuinfo_max_freq | |
| // cannot be relied on because the BIOS may be exporting an invalid | |
| // p-state (on x86) or p-states may be used to put the processor in | |
| // a new mode (turbo mode). Essentially, those frequencies cannot | |
| // always be relied upon. The same reasons apply to /proc/cpuinfo as | |
| // well. | |
| if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) { | |
| return freq * 1e3; // Value is kHz. | |
| } | |
| #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY) | |
| // On these platforms, the TSC frequency is the nominal CPU | |
| // frequency. But without having the kernel export it directly | |
| // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no | |
| // other way to reliably get the TSC frequency, so we have to | |
| // measure it ourselves. Some CPUs abuse cpuinfo_max_freq by | |
| // exporting "fake" frequencies for implementing new features. For | |
| // example, Intel's turbo mode is enabled by exposing a p-state | |
| // value with a higher frequency than that of the real TSC | |
| // rate. Because of this, we prefer to measure the TSC rate | |
| // ourselves on i386 and x86-64. | |
| return MeasureTscFrequency(); | |
| #else | |
| // If CPU scaling is in effect, we want to use the *maximum* | |
| // frequency, not whatever CPU speed some random processor happens | |
| // to be using now. | |
| if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", | |
| &freq)) { | |
| return freq * 1e3; // Value is kHz. | |
| } | |
| return 1.0; | |
| #endif // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY | |
| } | |
| #endif | |
| ABSL_CONST_INIT static once_flag init_num_cpus_once; | |
| ABSL_CONST_INIT static int num_cpus = 0; | |
| // NumCPUs() may be called before main() and before malloc is properly | |
| // initialized, therefore this must not allocate memory. | |
| int NumCPUs() { | |
| base_internal::LowLevelCallOnce( | |
| &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); }); | |
| return num_cpus; | |
| } | |
| // A default frequency of 0.0 might be dangerous if it is used in division. | |
| ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once; | |
| ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0; | |
| // NominalCPUFrequency() may be called before main() and before malloc is | |
| // properly initialized, therefore this must not allocate memory. | |
| double NominalCPUFrequency() { | |
| base_internal::LowLevelCallOnce(&init_nominal_cpu_frequency_once, []() { | |
| nominal_cpu_frequency = GetNominalCPUFrequency(); | |
| }); | |
| return nominal_cpu_frequency; | |
| } | |
| #if defined(_WIN32) | |
| pid_t GetTID() { return pid_t{GetCurrentThreadId()}; } | |
| #elif defined(__linux__) | |
| #ifdef __ANDROID__ | |
| #if __ANDROID_API__ >= 21 | |
| #define ABSL_INTERNAL_HAVE_GETTID 1 | |
| #endif | |
| #endif | |
| #ifdef ABSL_INTERNAL_HAVE_GETTID | |
| pid_t GetTID() { | |
| return static_cast<pid_t>(gettid()); | |
| } | |
| #else | |
| #ifndef SYS_gettid | |
| #define SYS_gettid __NR_gettid | |
| #endif | |
| pid_t GetTID() { return static_cast<pid_t>(syscall(SYS_gettid)); } | |
| #endif | |
| #undef ABSL_INTERNAL_HAVE_GETTID | |
| #elif defined(__akaros__) | |
| pid_t GetTID() { | |
| // Akaros has a concept of "vcore context", which is the state the program | |
| // is forced into when we need to make a user-level scheduling decision, or | |
| // run a signal handler. This is analogous to the interrupt context that a | |
| // CPU might enter if it encounters some kind of exception. | |
| // | |
| // There is no current thread context in vcore context, but we need to give | |
| // a reasonable answer if asked for a thread ID (e.g., in a signal handler). | |
| // Thread 0 always exists, so if we are in vcore context, we return that. | |
| // | |
| // Otherwise, we know (since we are using pthreads) that the uthread struct | |
| // current_uthread is pointing to is the first element of a | |
| // struct pthread_tcb, so we extract and return the thread ID from that. | |
| // | |
| // TODO(dcross): Akaros anticipates moving the thread ID to the uthread | |
| // structure at some point. We should modify this code to remove the cast | |
| // when that happens. | |
| if (in_vcore_context()) return 0; | |
| return reinterpret_cast<struct pthread_tcb*>(current_uthread)->id; | |
| } | |
| #elif defined(__myriad2__) | |
| pid_t GetTID() { | |
| uint32_t tid; | |
| rtems_task_ident(RTEMS_SELF, 0, &tid); | |
| return tid; | |
| } | |
| #elif defined(__APPLE__) | |
| pid_t GetTID() { | |
| uint64_t tid; | |
| // `nullptr` here implies this thread. This only fails if the specified | |
| // thread is invalid or the pointer-to-tid is null, so we needn't worry about | |
| // it. | |
| pthread_threadid_np(nullptr, &tid); | |
| return static_cast<pid_t>(tid); | |
| } | |
| #elif defined(__FreeBSD__) | |
| pid_t GetTID() { return static_cast<pid_t>(pthread_getthreadid_np()); } | |
| #elif defined(__OpenBSD__) | |
| pid_t GetTID() { return getthrid(); } | |
| #elif defined(__NetBSD__) | |
| pid_t GetTID() { return static_cast<pid_t>(_lwp_self()); } | |
| #elif defined(__Fuchsia__) | |
| pid_t GetTID() { | |
| // Use our thread handle as the TID, which should be unique within this | |
| // process (but may not be globally unique). The handle value was chosen over | |
| // a kernel object ID (KOID) because zx_handle_t (32-bits) can be cast to a | |
| // pid_t type without loss of precision, but a zx_koid_t (64-bits) cannot. | |
| return static_cast<pid_t>(zx_thread_self()); | |
| } | |
| #elif defined(__FREERTOS__) | |
| pid_t GetTID() { | |
| return static_cast<pid_t>(uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle())); | |
| } | |
| #else | |
| // Fallback implementation of `GetTID` using `pthread_self`. | |
| pid_t GetTID() { | |
| // `pthread_t` need not be arithmetic per POSIX; platforms where it isn't | |
| // should be handled above. | |
| return static_cast<pid_t>(pthread_self()); | |
| } | |
| #endif | |
| // GetCachedTID() caches the thread ID in thread-local storage (which is a | |
| // userspace construct) to avoid unnecessary system calls. Without this caching, | |
| // it can take roughly 98ns, while it takes roughly 1ns with this caching. | |
| pid_t GetCachedTID() { | |
| #ifdef __ANDROID__ | |
| // NDK defaults to emulated TLS for API < 29, and native ELF TLS for API >= 29. | |
| // Emulated TLS is slower than bionic's internal caching. | |
| #if __ANDROID_API__ < 29 | |
| #define ABSL_INTERNAL_USING_EMULATED_TLS 1 | |
| #endif | |
| #endif | |
| #if defined(ABSL_HAVE_THREAD_LOCAL) && \ | |
| !defined(ABSL_INTERNAL_USING_EMULATED_TLS) | |
| static thread_local pid_t thread_id = GetTID(); | |
| return thread_id; | |
| #else | |
| return GetTID(); | |
| #endif // defined(ABSL_HAVE_THREAD_LOCAL) && | |
| // !defined(ABSL_INTERNAL_USING_EMULATED_TLS) | |
| #undef ABSL_INTERNAL_USING_EMULATED_TLS | |
| } | |
| } // namespace base_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl |