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