Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| // 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 "absl/base/attributes.h" | |
| #ifdef _WIN32 | |
| #include <shlwapi.h> | |
| #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 | |
| #if defined(__myriad2__) | |
| #include <rtems.h> | |
| #endif | |
| #include <string.h> | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <ctime> | |
| #include <limits> | |
| #include <thread> // NOLINT(build/c++11) | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/base/call_once.h" | |
| #include "absl/base/internal/raw_logging.h" | |
| #include "absl/base/internal/spinlock.h" | |
| #include "absl/base/internal/unscaledcycleclock.h" | |
| namespace absl { | |
| namespace base_internal { | |
| static once_flag init_system_info_once; | |
| static int num_cpus = 0; | |
| static double nominal_cpu_frequency = 1.0; // 0.0 might be dangerous. | |
| static int GetNumCPUs() { | |
| #if defined(__myriad2__) | |
| return 1; | |
| #else | |
| // Other possibilities: | |
| // - Read /sys/devices/system/cpu/online and use cpumask_parse() | |
| // - sysconf(_SC_NPROCESSORS_ONLN) | |
| return std::thread::hardware_concurrency(); | |
| #endif | |
| } | |
| #if defined(_WIN32) | |
| static double GetNominalCPUFrequency() { | |
| DWORD data; | |
| DWORD data_size = sizeof(data); | |
| #pragma comment(lib, "shlwapi.lib") // For SHGetValue(). | |
| if (SUCCEEDED( | |
| SHGetValueA(HKEY_LOCAL_MACHINE, | |
| "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", | |
| "~MHz", nullptr, &data, &data_size))) { | |
| return data * 1e6; // Value is MHz. | |
| } | |
| return 1.0; | |
| } | |
| #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; | |
| int fd = open(file, O_RDONLY); | |
| if (fd != -1) { | |
| char line[1024]; | |
| char *err; | |
| memset(line, '\0', sizeof(line)); | |
| int len = read(fd, line, sizeof(line) - 1); | |
| 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) { | |
| perror("clock_gettime() failed"); | |
| abort(); | |
| } | |
| 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 | |
| // InitializeSystemInfo() may be called before main() and before | |
| // malloc is properly initialized, therefore this must not allocate | |
| // memory. | |
| static void InitializeSystemInfo() { | |
| num_cpus = GetNumCPUs(); | |
| nominal_cpu_frequency = GetNominalCPUFrequency(); | |
| } | |
| int NumCPUs() { | |
| base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo); | |
| return num_cpus; | |
| } | |
| double NominalCPUFrequency() { | |
| base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo); | |
| return nominal_cpu_frequency; | |
| } | |
| #if defined(_WIN32) | |
| pid_t GetTID() { | |
| return GetCurrentThreadId(); | |
| } | |
| #elif defined(__linux__) | |
| #ifndef SYS_gettid | |
| #define SYS_gettid __NR_gettid | |
| #endif | |
| pid_t GetTID() { | |
| return syscall(SYS_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; | |
| } | |
| #else | |
| // Fallback implementation of GetTID using pthread_getspecific. | |
| static once_flag tid_once; | |
| static pthread_key_t tid_key; | |
| static absl::base_internal::SpinLock tid_lock( | |
| absl::base_internal::kLinkerInitialized); | |
| // We set a bit per thread in this array to indicate that an ID is in | |
| // use. ID 0 is unused because it is the default value returned by | |
| // pthread_getspecific(). | |
| static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr; | |
| static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. | |
| // Returns the TID to tid_array. | |
| static void FreeTID(void *v) { | |
| intptr_t tid = reinterpret_cast<intptr_t>(v); | |
| int word = tid / kBitsPerWord; | |
| uint32_t mask = ~(1u << (tid % kBitsPerWord)); | |
| absl::base_internal::SpinLockHolder lock(&tid_lock); | |
| assert(0 <= word && static_cast<size_t>(word) < tid_array->size()); | |
| (*tid_array)[word] &= mask; | |
| } | |
| static void InitGetTID() { | |
| if (pthread_key_create(&tid_key, FreeTID) != 0) { | |
| // The logging system calls GetTID() so it can't be used here. | |
| perror("pthread_key_create failed"); | |
| abort(); | |
| } | |
| // Initialize tid_array. | |
| absl::base_internal::SpinLockHolder lock(&tid_lock); | |
| tid_array = new std::vector<uint32_t>(1); | |
| (*tid_array)[0] = 1; // ID 0 is never-allocated. | |
| } | |
| // Return a per-thread small integer ID from pthread's thread-specific data. | |
| pid_t GetTID() { | |
| absl::call_once(tid_once, InitGetTID); | |
| intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key)); | |
| if (tid != 0) { | |
| return tid; | |
| } | |
| int bit; // tid_array[word] = 1u << bit; | |
| size_t word; | |
| { | |
| // Search for the first unused ID. | |
| absl::base_internal::SpinLockHolder lock(&tid_lock); | |
| // First search for a word in the array that is not all ones. | |
| word = 0; | |
| while (word < tid_array->size() && ~(*tid_array)[word] == 0) { | |
| ++word; | |
| } | |
| if (word == tid_array->size()) { | |
| tid_array->push_back(0); // No space left, add kBitsPerWord more IDs. | |
| } | |
| // Search for a zero bit in the word. | |
| bit = 0; | |
| while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) { | |
| ++bit; | |
| } | |
| tid = (word * kBitsPerWord) + bit; | |
| (*tid_array)[word] |= 1u << bit; // Mark the TID as allocated. | |
| } | |
| if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) { | |
| perror("pthread_setspecific failed"); | |
| abort(); | |
| } | |
| return static_cast<pid_t>(tid); | |
| } | |
| #endif | |
| } // namespace base_internal | |
| } // namespace absl |