sysinfo.cc
Go to the documentation of this file.
00001 // Copyright 2017 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "absl/base/internal/sysinfo.h"
00016 
00017 #include "absl/base/attributes.h"
00018 
00019 #ifdef _WIN32
00020 #include <shlwapi.h>
00021 #include <windows.h>
00022 #else
00023 #include <fcntl.h>
00024 #include <pthread.h>
00025 #include <sys/stat.h>
00026 #include <sys/types.h>
00027 #include <unistd.h>
00028 #endif
00029 
00030 #ifdef __linux__
00031 #include <sys/syscall.h>
00032 #endif
00033 
00034 #if defined(__APPLE__) || defined(__FreeBSD__)
00035 #include <sys/sysctl.h>
00036 #endif
00037 
00038 #if defined(__myriad2__)
00039 #include <rtems.h>
00040 #endif
00041 
00042 #include <string.h>
00043 #include <cassert>
00044 #include <cstdint>
00045 #include <cstdio>
00046 #include <cstdlib>
00047 #include <ctime>
00048 #include <limits>
00049 #include <thread>  // NOLINT(build/c++11)
00050 #include <utility>
00051 #include <vector>
00052 
00053 #include "absl/base/call_once.h"
00054 #include "absl/base/internal/raw_logging.h"
00055 #include "absl/base/internal/spinlock.h"
00056 #include "absl/base/internal/unscaledcycleclock.h"
00057 
00058 namespace absl {
00059 namespace base_internal {
00060 
00061 static once_flag init_system_info_once;
00062 static int num_cpus = 0;
00063 static double nominal_cpu_frequency = 1.0;  // 0.0 might be dangerous.
00064 
00065 static int GetNumCPUs() {
00066 #if defined(__myriad2__)
00067   return 1;
00068 #else
00069   // Other possibilities:
00070   //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
00071   //  - sysconf(_SC_NPROCESSORS_ONLN)
00072   return std::thread::hardware_concurrency();
00073 #endif
00074 }
00075 
00076 #if defined(_WIN32)
00077 
00078 static double GetNominalCPUFrequency() {
00079   DWORD data;
00080   DWORD data_size = sizeof(data);
00081   #pragma comment(lib, "shlwapi.lib")  // For SHGetValue().
00082   if (SUCCEEDED(
00083           SHGetValueA(HKEY_LOCAL_MACHINE,
00084                       "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
00085                       "~MHz", nullptr, &data, &data_size))) {
00086     return data * 1e6;  // Value is MHz.
00087   }
00088   return 1.0;
00089 }
00090 
00091 #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
00092 
00093 static double GetNominalCPUFrequency() {
00094   unsigned freq;
00095   size_t size = sizeof(freq);
00096   int mib[2] = {CTL_HW, HW_CPU_FREQ};
00097   if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
00098     return static_cast<double>(freq);
00099   }
00100   return 1.0;
00101 }
00102 
00103 #else
00104 
00105 // Helper function for reading a long from a file. Returns true if successful
00106 // and the memory location pointed to by value is set to the value read.
00107 static bool ReadLongFromFile(const char *file, long *value) {
00108   bool ret = false;
00109   int fd = open(file, O_RDONLY);
00110   if (fd != -1) {
00111     char line[1024];
00112     char *err;
00113     memset(line, '\0', sizeof(line));
00114     int len = read(fd, line, sizeof(line) - 1);
00115     if (len <= 0) {
00116       ret = false;
00117     } else {
00118       const long temp_value = strtol(line, &err, 10);
00119       if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
00120         *value = temp_value;
00121         ret = true;
00122       }
00123     }
00124     close(fd);
00125   }
00126   return ret;
00127 }
00128 
00129 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
00130 
00131 // Reads a monotonic time source and returns a value in
00132 // nanoseconds. The returned value uses an arbitrary epoch, not the
00133 // Unix epoch.
00134 static int64_t ReadMonotonicClockNanos() {
00135   struct timespec t;
00136 #ifdef CLOCK_MONOTONIC_RAW
00137   int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
00138 #else
00139   int rc = clock_gettime(CLOCK_MONOTONIC, &t);
00140 #endif
00141   if (rc != 0) {
00142     perror("clock_gettime() failed");
00143     abort();
00144   }
00145   return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
00146 }
00147 
00148 class UnscaledCycleClockWrapperForInitializeFrequency {
00149  public:
00150   static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
00151 };
00152 
00153 struct TimeTscPair {
00154   int64_t time;  // From ReadMonotonicClockNanos().
00155   int64_t tsc;   // From UnscaledCycleClock::Now().
00156 };
00157 
00158 // Returns a pair of values (monotonic kernel time, TSC ticks) that
00159 // approximately correspond to each other.  This is accomplished by
00160 // doing several reads and picking the reading with the lowest
00161 // latency.  This approach is used to minimize the probability that
00162 // our thread was preempted between clock reads.
00163 static TimeTscPair GetTimeTscPair() {
00164   int64_t best_latency = std::numeric_limits<int64_t>::max();
00165   TimeTscPair best;
00166   for (int i = 0; i < 10; ++i) {
00167     int64_t t0 = ReadMonotonicClockNanos();
00168     int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
00169     int64_t t1 = ReadMonotonicClockNanos();
00170     int64_t latency = t1 - t0;
00171     if (latency < best_latency) {
00172       best_latency = latency;
00173       best.time = t0;
00174       best.tsc = tsc;
00175     }
00176   }
00177   return best;
00178 }
00179 
00180 // Measures and returns the TSC frequency by taking a pair of
00181 // measurements approximately `sleep_nanoseconds` apart.
00182 static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
00183   auto t0 = GetTimeTscPair();
00184   struct timespec ts;
00185   ts.tv_sec = 0;
00186   ts.tv_nsec = sleep_nanoseconds;
00187   while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
00188   auto t1 = GetTimeTscPair();
00189   double elapsed_ticks = t1.tsc - t0.tsc;
00190   double elapsed_time = (t1.time - t0.time) * 1e-9;
00191   return elapsed_ticks / elapsed_time;
00192 }
00193 
00194 // Measures and returns the TSC frequency by calling
00195 // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
00196 // frequency measurement stabilizes.
00197 static double MeasureTscFrequency() {
00198   double last_measurement = -1.0;
00199   int sleep_nanoseconds = 1000000;  // 1 millisecond.
00200   for (int i = 0; i < 8; ++i) {
00201     double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
00202     if (measurement * 0.99 < last_measurement &&
00203         last_measurement < measurement * 1.01) {
00204       // Use the current measurement if it is within 1% of the
00205       // previous measurement.
00206       return measurement;
00207     }
00208     last_measurement = measurement;
00209     sleep_nanoseconds *= 2;
00210   }
00211   return last_measurement;
00212 }
00213 
00214 #endif  // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
00215 
00216 static double GetNominalCPUFrequency() {
00217   long freq = 0;
00218 
00219   // Google's production kernel has a patch to export the TSC
00220   // frequency through sysfs. If the kernel is exporting the TSC
00221   // frequency use that. There are issues where cpuinfo_max_freq
00222   // cannot be relied on because the BIOS may be exporting an invalid
00223   // p-state (on x86) or p-states may be used to put the processor in
00224   // a new mode (turbo mode). Essentially, those frequencies cannot
00225   // always be relied upon. The same reasons apply to /proc/cpuinfo as
00226   // well.
00227   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
00228     return freq * 1e3;  // Value is kHz.
00229   }
00230 
00231 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
00232   // On these platforms, the TSC frequency is the nominal CPU
00233   // frequency.  But without having the kernel export it directly
00234   // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
00235   // other way to reliably get the TSC frequency, so we have to
00236   // measure it ourselves.  Some CPUs abuse cpuinfo_max_freq by
00237   // exporting "fake" frequencies for implementing new features. For
00238   // example, Intel's turbo mode is enabled by exposing a p-state
00239   // value with a higher frequency than that of the real TSC
00240   // rate. Because of this, we prefer to measure the TSC rate
00241   // ourselves on i386 and x86-64.
00242   return MeasureTscFrequency();
00243 #else
00244 
00245   // If CPU scaling is in effect, we want to use the *maximum*
00246   // frequency, not whatever CPU speed some random processor happens
00247   // to be using now.
00248   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
00249                        &freq)) {
00250     return freq * 1e3;  // Value is kHz.
00251   }
00252 
00253   return 1.0;
00254 #endif  // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
00255 }
00256 
00257 #endif
00258 
00259 // InitializeSystemInfo() may be called before main() and before
00260 // malloc is properly initialized, therefore this must not allocate
00261 // memory.
00262 static void InitializeSystemInfo() {
00263   num_cpus = GetNumCPUs();
00264   nominal_cpu_frequency = GetNominalCPUFrequency();
00265 }
00266 
00267 int NumCPUs() {
00268   base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
00269   return num_cpus;
00270 }
00271 
00272 double NominalCPUFrequency() {
00273   base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
00274   return nominal_cpu_frequency;
00275 }
00276 
00277 #if defined(_WIN32)
00278 
00279 pid_t GetTID() {
00280   return GetCurrentThreadId();
00281 }
00282 
00283 #elif defined(__linux__)
00284 
00285 #ifndef SYS_gettid
00286 #define SYS_gettid __NR_gettid
00287 #endif
00288 
00289 pid_t GetTID() {
00290   return syscall(SYS_gettid);
00291 }
00292 
00293 #elif defined(__akaros__)
00294 
00295 pid_t GetTID() {
00296   // Akaros has a concept of "vcore context", which is the state the program
00297   // is forced into when we need to make a user-level scheduling decision, or
00298   // run a signal handler.  This is analogous to the interrupt context that a
00299   // CPU might enter if it encounters some kind of exception.
00300   //
00301   // There is no current thread context in vcore context, but we need to give
00302   // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
00303   // Thread 0 always exists, so if we are in vcore context, we return that.
00304   //
00305   // Otherwise, we know (since we are using pthreads) that the uthread struct
00306   // current_uthread is pointing to is the first element of a
00307   // struct pthread_tcb, so we extract and return the thread ID from that.
00308   //
00309   // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
00310   // structure at some point. We should modify this code to remove the cast
00311   // when that happens.
00312   if (in_vcore_context())
00313     return 0;
00314   return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
00315 }
00316 
00317 #elif defined(__myriad2__)
00318 
00319 pid_t GetTID() {
00320   uint32_t tid;
00321   rtems_task_ident(RTEMS_SELF, 0, &tid);
00322   return tid;
00323 }
00324 
00325 #else
00326 
00327 // Fallback implementation of GetTID using pthread_getspecific.
00328 static once_flag tid_once;
00329 static pthread_key_t tid_key;
00330 static absl::base_internal::SpinLock tid_lock(
00331     absl::base_internal::kLinkerInitialized);
00332 
00333 // We set a bit per thread in this array to indicate that an ID is in
00334 // use. ID 0 is unused because it is the default value returned by
00335 // pthread_getspecific().
00336 static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr;
00337 static constexpr int kBitsPerWord = 32;  // tid_array is uint32_t.
00338 
00339 // Returns the TID to tid_array.
00340 static void FreeTID(void *v) {
00341   intptr_t tid = reinterpret_cast<intptr_t>(v);
00342   int word = tid / kBitsPerWord;
00343   uint32_t mask = ~(1u << (tid % kBitsPerWord));
00344   absl::base_internal::SpinLockHolder lock(&tid_lock);
00345   assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
00346   (*tid_array)[word] &= mask;
00347 }
00348 
00349 static void InitGetTID() {
00350   if (pthread_key_create(&tid_key, FreeTID) != 0) {
00351     // The logging system calls GetTID() so it can't be used here.
00352     perror("pthread_key_create failed");
00353     abort();
00354   }
00355 
00356   // Initialize tid_array.
00357   absl::base_internal::SpinLockHolder lock(&tid_lock);
00358   tid_array = new std::vector<uint32_t>(1);
00359   (*tid_array)[0] = 1;  // ID 0 is never-allocated.
00360 }
00361 
00362 // Return a per-thread small integer ID from pthread's thread-specific data.
00363 pid_t GetTID() {
00364   absl::call_once(tid_once, InitGetTID);
00365 
00366   intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
00367   if (tid != 0) {
00368     return tid;
00369   }
00370 
00371   int bit;  // tid_array[word] = 1u << bit;
00372   size_t word;
00373   {
00374     // Search for the first unused ID.
00375     absl::base_internal::SpinLockHolder lock(&tid_lock);
00376     // First search for a word in the array that is not all ones.
00377     word = 0;
00378     while (word < tid_array->size() && ~(*tid_array)[word] == 0) {
00379       ++word;
00380     }
00381     if (word == tid_array->size()) {
00382       tid_array->push_back(0);  // No space left, add kBitsPerWord more IDs.
00383     }
00384     // Search for a zero bit in the word.
00385     bit = 0;
00386     while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) {
00387       ++bit;
00388     }
00389     tid = (word * kBitsPerWord) + bit;
00390     (*tid_array)[word] |= 1u << bit;  // Mark the TID as allocated.
00391   }
00392 
00393   if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) {
00394     perror("pthread_setspecific failed");
00395     abort();
00396   }
00397 
00398   return static_cast<pid_t>(tid);
00399 }
00400 
00401 #endif
00402 
00403 }  // namespace base_internal
00404 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15