• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <cassert>
43 #include <cstdint>
44 #include <cstdio>
45 #include <cstdlib>
46 #include <ctime>
47 #include <limits>
48 #include <thread>  // NOLINT(build/c++11)
49 #include <utility>
50 #include <vector>
51 
52 #include "absl/base/call_once.h"
53 #include "absl/base/internal/raw_logging.h"
54 #include "absl/base/internal/spinlock.h"
55 #include "absl/base/internal/unscaledcycleclock.h"
56 
57 namespace absl {
58 ABSL_NAMESPACE_BEGIN
59 namespace base_internal {
60 
GetNumCPUs()61 static int GetNumCPUs() {
62 #if defined(__myriad2__)
63   return 1;
64 #else
65   // Other possibilities:
66   //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
67   //  - sysconf(_SC_NPROCESSORS_ONLN)
68   return std::thread::hardware_concurrency();
69 #endif
70 }
71 
72 #if defined(_WIN32)
73 
GetNominalCPUFrequency()74 static double GetNominalCPUFrequency() {
75 #pragma comment(lib, "advapi32.lib")  // For Reg* functions.
76   HKEY key;
77   // Use the Reg* functions rather than the SH functions because shlwapi.dll
78   // pulls in gdi32.dll which makes process destruction much more costly.
79   if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
80                     "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
81                     KEY_READ, &key) == ERROR_SUCCESS) {
82     DWORD type = 0;
83     DWORD data = 0;
84     DWORD data_size = sizeof(data);
85     auto result = RegQueryValueExA(key, "~MHz", 0, &type,
86                                    reinterpret_cast<LPBYTE>(&data), &data_size);
87     RegCloseKey(key);
88     if (result == ERROR_SUCCESS && type == REG_DWORD &&
89         data_size == sizeof(data)) {
90       return data * 1e6;  // Value is MHz.
91     }
92   }
93   return 1.0;
94 }
95 
96 #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
97 
GetNominalCPUFrequency()98 static double GetNominalCPUFrequency() {
99   unsigned freq;
100   size_t size = sizeof(freq);
101   int mib[2] = {CTL_HW, HW_CPU_FREQ};
102   if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
103     return static_cast<double>(freq);
104   }
105   return 1.0;
106 }
107 
108 #else
109 
110 // Helper function for reading a long from a file. Returns true if successful
111 // and the memory location pointed to by value is set to the value read.
ReadLongFromFile(const char * file,long * value)112 static bool ReadLongFromFile(const char *file, long *value) {
113   bool ret = false;
114   int fd = open(file, O_RDONLY);
115   if (fd != -1) {
116     char line[1024];
117     char *err;
118     memset(line, '\0', sizeof(line));
119     int len = read(fd, line, sizeof(line) - 1);
120     if (len <= 0) {
121       ret = false;
122     } else {
123       const long temp_value = strtol(line, &err, 10);
124       if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
125         *value = temp_value;
126         ret = true;
127       }
128     }
129     close(fd);
130   }
131   return ret;
132 }
133 
134 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
135 
136 // Reads a monotonic time source and returns a value in
137 // nanoseconds. The returned value uses an arbitrary epoch, not the
138 // Unix epoch.
ReadMonotonicClockNanos()139 static int64_t ReadMonotonicClockNanos() {
140   struct timespec t;
141 #ifdef CLOCK_MONOTONIC_RAW
142   int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
143 #else
144   int rc = clock_gettime(CLOCK_MONOTONIC, &t);
145 #endif
146   if (rc != 0) {
147     perror("clock_gettime() failed");
148     abort();
149   }
150   return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
151 }
152 
153 class UnscaledCycleClockWrapperForInitializeFrequency {
154  public:
Now()155   static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
156 };
157 
158 struct TimeTscPair {
159   int64_t time;  // From ReadMonotonicClockNanos().
160   int64_t tsc;   // From UnscaledCycleClock::Now().
161 };
162 
163 // Returns a pair of values (monotonic kernel time, TSC ticks) that
164 // approximately correspond to each other.  This is accomplished by
165 // doing several reads and picking the reading with the lowest
166 // latency.  This approach is used to minimize the probability that
167 // our thread was preempted between clock reads.
GetTimeTscPair()168 static TimeTscPair GetTimeTscPair() {
169   int64_t best_latency = std::numeric_limits<int64_t>::max();
170   TimeTscPair best;
171   for (int i = 0; i < 10; ++i) {
172     int64_t t0 = ReadMonotonicClockNanos();
173     int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
174     int64_t t1 = ReadMonotonicClockNanos();
175     int64_t latency = t1 - t0;
176     if (latency < best_latency) {
177       best_latency = latency;
178       best.time = t0;
179       best.tsc = tsc;
180     }
181   }
182   return best;
183 }
184 
185 // Measures and returns the TSC frequency by taking a pair of
186 // measurements approximately `sleep_nanoseconds` apart.
MeasureTscFrequencyWithSleep(int sleep_nanoseconds)187 static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
188   auto t0 = GetTimeTscPair();
189   struct timespec ts;
190   ts.tv_sec = 0;
191   ts.tv_nsec = sleep_nanoseconds;
192   while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
193   auto t1 = GetTimeTscPair();
194   double elapsed_ticks = t1.tsc - t0.tsc;
195   double elapsed_time = (t1.time - t0.time) * 1e-9;
196   return elapsed_ticks / elapsed_time;
197 }
198 
199 // Measures and returns the TSC frequency by calling
200 // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
201 // frequency measurement stabilizes.
MeasureTscFrequency()202 static double MeasureTscFrequency() {
203   double last_measurement = -1.0;
204   int sleep_nanoseconds = 1000000;  // 1 millisecond.
205   for (int i = 0; i < 8; ++i) {
206     double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
207     if (measurement * 0.99 < last_measurement &&
208         last_measurement < measurement * 1.01) {
209       // Use the current measurement if it is within 1% of the
210       // previous measurement.
211       return measurement;
212     }
213     last_measurement = measurement;
214     sleep_nanoseconds *= 2;
215   }
216   return last_measurement;
217 }
218 
219 #endif  // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
220 
GetNominalCPUFrequency()221 static double GetNominalCPUFrequency() {
222   long freq = 0;
223 
224   // Google's production kernel has a patch to export the TSC
225   // frequency through sysfs. If the kernel is exporting the TSC
226   // frequency use that. There are issues where cpuinfo_max_freq
227   // cannot be relied on because the BIOS may be exporting an invalid
228   // p-state (on x86) or p-states may be used to put the processor in
229   // a new mode (turbo mode). Essentially, those frequencies cannot
230   // always be relied upon. The same reasons apply to /proc/cpuinfo as
231   // well.
232   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
233     return freq * 1e3;  // Value is kHz.
234   }
235 
236 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
237   // On these platforms, the TSC frequency is the nominal CPU
238   // frequency.  But without having the kernel export it directly
239   // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
240   // other way to reliably get the TSC frequency, so we have to
241   // measure it ourselves.  Some CPUs abuse cpuinfo_max_freq by
242   // exporting "fake" frequencies for implementing new features. For
243   // example, Intel's turbo mode is enabled by exposing a p-state
244   // value with a higher frequency than that of the real TSC
245   // rate. Because of this, we prefer to measure the TSC rate
246   // ourselves on i386 and x86-64.
247   return MeasureTscFrequency();
248 #else
249 
250   // If CPU scaling is in effect, we want to use the *maximum*
251   // frequency, not whatever CPU speed some random processor happens
252   // to be using now.
253   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
254                        &freq)) {
255     return freq * 1e3;  // Value is kHz.
256   }
257 
258   return 1.0;
259 #endif  // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
260 }
261 
262 #endif
263 
264 ABSL_CONST_INIT static once_flag init_num_cpus_once;
265 ABSL_CONST_INIT static int num_cpus = 0;
266 
267 // NumCPUs() may be called before main() and before malloc is properly
268 // initialized, therefore this must not allocate memory.
NumCPUs()269 int NumCPUs() {
270   base_internal::LowLevelCallOnce(
271       &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
272   return num_cpus;
273 }
274 
275 // A default frequency of 0.0 might be dangerous if it is used in division.
276 ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
277 ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
278 
279 // NominalCPUFrequency() may be called before main() and before malloc is
280 // properly initialized, therefore this must not allocate memory.
NominalCPUFrequency()281 double NominalCPUFrequency() {
282   base_internal::LowLevelCallOnce(
283       &init_nominal_cpu_frequency_once,
284       []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
285   return nominal_cpu_frequency;
286 }
287 
288 #if defined(_WIN32)
289 
GetTID()290 pid_t GetTID() {
291   return pid_t{GetCurrentThreadId()};
292 }
293 
294 #elif defined(__linux__)
295 
296 #ifndef SYS_gettid
297 #define SYS_gettid __NR_gettid
298 #endif
299 
GetTID()300 pid_t GetTID() {
301   return syscall(SYS_gettid);
302 }
303 
304 #elif defined(__akaros__)
305 
GetTID()306 pid_t GetTID() {
307   // Akaros has a concept of "vcore context", which is the state the program
308   // is forced into when we need to make a user-level scheduling decision, or
309   // run a signal handler.  This is analogous to the interrupt context that a
310   // CPU might enter if it encounters some kind of exception.
311   //
312   // There is no current thread context in vcore context, but we need to give
313   // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
314   // Thread 0 always exists, so if we are in vcore context, we return that.
315   //
316   // Otherwise, we know (since we are using pthreads) that the uthread struct
317   // current_uthread is pointing to is the first element of a
318   // struct pthread_tcb, so we extract and return the thread ID from that.
319   //
320   // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
321   // structure at some point. We should modify this code to remove the cast
322   // when that happens.
323   if (in_vcore_context())
324     return 0;
325   return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
326 }
327 
328 #elif defined(__myriad2__)
329 
GetTID()330 pid_t GetTID() {
331   uint32_t tid;
332   rtems_task_ident(RTEMS_SELF, 0, &tid);
333   return tid;
334 }
335 
336 #else
337 
338 // Fallback implementation of GetTID using pthread_getspecific.
339 static once_flag tid_once;
340 static pthread_key_t tid_key;
341 static absl::base_internal::SpinLock tid_lock(
342     absl::base_internal::kLinkerInitialized);
343 
344 // We set a bit per thread in this array to indicate that an ID is in
345 // use. ID 0 is unused because it is the default value returned by
346 // pthread_getspecific().
347 static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr;
348 static constexpr int kBitsPerWord = 32;  // tid_array is uint32_t.
349 
350 // Returns the TID to tid_array.
FreeTID(void * v)351 static void FreeTID(void *v) {
352   intptr_t tid = reinterpret_cast<intptr_t>(v);
353   int word = tid / kBitsPerWord;
354   uint32_t mask = ~(1u << (tid % kBitsPerWord));
355   absl::base_internal::SpinLockHolder lock(&tid_lock);
356   assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
357   (*tid_array)[word] &= mask;
358 }
359 
InitGetTID()360 static void InitGetTID() {
361   if (pthread_key_create(&tid_key, FreeTID) != 0) {
362     // The logging system calls GetTID() so it can't be used here.
363     perror("pthread_key_create failed");
364     abort();
365   }
366 
367   // Initialize tid_array.
368   absl::base_internal::SpinLockHolder lock(&tid_lock);
369   tid_array = new std::vector<uint32_t>(1);
370   (*tid_array)[0] = 1;  // ID 0 is never-allocated.
371 }
372 
373 // Return a per-thread small integer ID from pthread's thread-specific data.
GetTID()374 pid_t GetTID() {
375   absl::call_once(tid_once, InitGetTID);
376 
377   intptr_t tid = reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
378   if (tid != 0) {
379     return tid;
380   }
381 
382   int bit;  // tid_array[word] = 1u << bit;
383   size_t word;
384   {
385     // Search for the first unused ID.
386     absl::base_internal::SpinLockHolder lock(&tid_lock);
387     // First search for a word in the array that is not all ones.
388     word = 0;
389     while (word < tid_array->size() && ~(*tid_array)[word] == 0) {
390       ++word;
391     }
392     if (word == tid_array->size()) {
393       tid_array->push_back(0);  // No space left, add kBitsPerWord more IDs.
394     }
395     // Search for a zero bit in the word.
396     bit = 0;
397     while (bit < kBitsPerWord && (((*tid_array)[word] >> bit) & 1) != 0) {
398       ++bit;
399     }
400     tid = (word * kBitsPerWord) + bit;
401     (*tid_array)[word] |= 1u << bit;  // Mark the TID as allocated.
402   }
403 
404   if (pthread_setspecific(tid_key, reinterpret_cast<void *>(tid)) != 0) {
405     perror("pthread_setspecific failed");
406     abort();
407   }
408 
409   return static_cast<pid_t>(tid);
410 }
411 
412 #endif
413 
414 }  // namespace base_internal
415 ABSL_NAMESPACE_END
416 }  // namespace absl
417