• 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 
43 #include <cassert>
44 #include <cerrno>
45 #include <cstdint>
46 #include <cstdio>
47 #include <cstdlib>
48 #include <ctime>
49 #include <limits>
50 #include <thread>  // NOLINT(build/c++11)
51 #include <utility>
52 #include <vector>
53 
54 #include "absl/base/call_once.h"
55 #include "absl/base/config.h"
56 #include "absl/base/internal/raw_logging.h"
57 #include "absl/base/internal/spinlock.h"
58 #include "absl/base/internal/unscaledcycleclock.h"
59 #include "absl/base/thread_annotations.h"
60 
61 namespace absl {
62 ABSL_NAMESPACE_BEGIN
63 namespace base_internal {
64 
65 namespace {
66 
67 #if defined(_WIN32)
68 
69 // Returns number of bits set in `bitMask`
Win32CountSetBits(ULONG_PTR bitMask)70 DWORD Win32CountSetBits(ULONG_PTR bitMask) {
71   for (DWORD bitSetCount = 0; ; ++bitSetCount) {
72     if (bitMask == 0) return bitSetCount;
73     bitMask &= bitMask - 1;
74   }
75 }
76 
77 // Returns the number of logical CPUs using GetLogicalProcessorInformation(), or
78 // 0 if the number of processors is not available or can not be computed.
79 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation
Win32NumCPUs()80 int Win32NumCPUs() {
81 #pragma comment(lib, "kernel32.lib")
82   using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
83 
84   DWORD info_size = sizeof(Info);
85   Info* info(static_cast<Info*>(malloc(info_size)));
86   if (info == nullptr) return 0;
87 
88   bool success = GetLogicalProcessorInformation(info, &info_size);
89   if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
90     free(info);
91     info = static_cast<Info*>(malloc(info_size));
92     if (info == nullptr) return 0;
93     success = GetLogicalProcessorInformation(info, &info_size);
94   }
95 
96   DWORD logicalProcessorCount = 0;
97   if (success) {
98     Info* ptr = info;
99     DWORD byteOffset = 0;
100     while (byteOffset + sizeof(Info) <= info_size) {
101       switch (ptr->Relationship) {
102         case RelationProcessorCore:
103           logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask);
104           break;
105 
106         case RelationNumaNode:
107         case RelationCache:
108         case RelationProcessorPackage:
109           // Ignore other entries
110           break;
111 
112         default:
113           // Ignore unknown entries
114           break;
115       }
116       byteOffset += sizeof(Info);
117       ptr++;
118     }
119   }
120   free(info);
121   return static_cast<int>(logicalProcessorCount);
122 }
123 
124 #endif
125 
126 }  // namespace
127 
GetNumCPUs()128 static int GetNumCPUs() {
129 #if defined(__myriad2__)
130   return 1;
131 #elif defined(_WIN32)
132   const int hardware_concurrency = Win32NumCPUs();
133   return hardware_concurrency ? hardware_concurrency : 1;
134 #elif defined(_AIX)
135   return sysconf(_SC_NPROCESSORS_ONLN);
136 #else
137   // Other possibilities:
138   //  - Read /sys/devices/system/cpu/online and use cpumask_parse()
139   //  - sysconf(_SC_NPROCESSORS_ONLN)
140   return static_cast<int>(std::thread::hardware_concurrency());
141 #endif
142 }
143 
144 #if defined(_WIN32)
145 
GetNominalCPUFrequency()146 static double GetNominalCPUFrequency() {
147 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
148     !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
149   // UWP apps don't have access to the registry and currently don't provide an
150   // API informing about CPU nominal frequency.
151   return 1.0;
152 #else
153 #pragma comment(lib, "advapi32.lib")  // For Reg* functions.
154   HKEY key;
155   // Use the Reg* functions rather than the SH functions because shlwapi.dll
156   // pulls in gdi32.dll which makes process destruction much more costly.
157   if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
158                     "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
159                     KEY_READ, &key) == ERROR_SUCCESS) {
160     DWORD type = 0;
161     DWORD data = 0;
162     DWORD data_size = sizeof(data);
163     auto result = RegQueryValueExA(key, "~MHz", nullptr, &type,
164                                    reinterpret_cast<LPBYTE>(&data), &data_size);
165     RegCloseKey(key);
166     if (result == ERROR_SUCCESS && type == REG_DWORD &&
167         data_size == sizeof(data)) {
168       return data * 1e6;  // Value is MHz.
169     }
170   }
171   return 1.0;
172 #endif  // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
173 }
174 
175 #elif defined(CTL_HW) && defined(HW_CPU_FREQ)
176 
GetNominalCPUFrequency()177 static double GetNominalCPUFrequency() {
178   unsigned freq;
179   size_t size = sizeof(freq);
180   int mib[2] = {CTL_HW, HW_CPU_FREQ};
181   if (sysctl(mib, 2, &freq, &size, nullptr, 0) == 0) {
182     return static_cast<double>(freq);
183   }
184   return 1.0;
185 }
186 
187 #else
188 
189 // Helper function for reading a long from a file. Returns true if successful
190 // and the memory location pointed to by value is set to the value read.
ReadLongFromFile(const char * file,long * value)191 static bool ReadLongFromFile(const char *file, long *value) {
192   bool ret = false;
193 #if defined(_POSIX_C_SOURCE)
194   const int file_mode = (O_RDONLY | O_CLOEXEC);
195 #else
196   const int file_mode = O_RDONLY;
197 #endif
198 
199   int fd = open(file, file_mode);
200   if (fd != -1) {
201     char line[1024];
202     char *err;
203     memset(line, '\0', sizeof(line));
204     ssize_t len;
205     do {
206       len = read(fd, line, sizeof(line) - 1);
207     } while (len < 0 && errno == EINTR);
208     if (len <= 0) {
209       ret = false;
210     } else {
211       const long temp_value = strtol(line, &err, 10);
212       if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
213         *value = temp_value;
214         ret = true;
215       }
216     }
217     close(fd);
218   }
219   return ret;
220 }
221 
222 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
223 
224 // Reads a monotonic time source and returns a value in
225 // nanoseconds. The returned value uses an arbitrary epoch, not the
226 // Unix epoch.
ReadMonotonicClockNanos()227 static int64_t ReadMonotonicClockNanos() {
228   struct timespec t;
229 #ifdef CLOCK_MONOTONIC_RAW
230   int rc = clock_gettime(CLOCK_MONOTONIC_RAW, &t);
231 #else
232   int rc = clock_gettime(CLOCK_MONOTONIC, &t);
233 #endif
234   if (rc != 0) {
235     ABSL_INTERNAL_LOG(
236         FATAL, "clock_gettime() failed: (" + std::to_string(errno) + ")");
237   }
238   return int64_t{t.tv_sec} * 1000000000 + t.tv_nsec;
239 }
240 
241 class UnscaledCycleClockWrapperForInitializeFrequency {
242  public:
Now()243   static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
244 };
245 
246 struct TimeTscPair {
247   int64_t time;  // From ReadMonotonicClockNanos().
248   int64_t tsc;   // From UnscaledCycleClock::Now().
249 };
250 
251 // Returns a pair of values (monotonic kernel time, TSC ticks) that
252 // approximately correspond to each other.  This is accomplished by
253 // doing several reads and picking the reading with the lowest
254 // latency.  This approach is used to minimize the probability that
255 // our thread was preempted between clock reads.
GetTimeTscPair()256 static TimeTscPair GetTimeTscPair() {
257   int64_t best_latency = std::numeric_limits<int64_t>::max();
258   TimeTscPair best;
259   for (int i = 0; i < 10; ++i) {
260     int64_t t0 = ReadMonotonicClockNanos();
261     int64_t tsc = UnscaledCycleClockWrapperForInitializeFrequency::Now();
262     int64_t t1 = ReadMonotonicClockNanos();
263     int64_t latency = t1 - t0;
264     if (latency < best_latency) {
265       best_latency = latency;
266       best.time = t0;
267       best.tsc = tsc;
268     }
269   }
270   return best;
271 }
272 
273 // Measures and returns the TSC frequency by taking a pair of
274 // measurements approximately `sleep_nanoseconds` apart.
MeasureTscFrequencyWithSleep(int sleep_nanoseconds)275 static double MeasureTscFrequencyWithSleep(int sleep_nanoseconds) {
276   auto t0 = GetTimeTscPair();
277   struct timespec ts;
278   ts.tv_sec = 0;
279   ts.tv_nsec = sleep_nanoseconds;
280   while (nanosleep(&ts, &ts) != 0 && errno == EINTR) {}
281   auto t1 = GetTimeTscPair();
282   double elapsed_ticks = t1.tsc - t0.tsc;
283   double elapsed_time = (t1.time - t0.time) * 1e-9;
284   return elapsed_ticks / elapsed_time;
285 }
286 
287 // Measures and returns the TSC frequency by calling
288 // MeasureTscFrequencyWithSleep(), doubling the sleep interval until the
289 // frequency measurement stabilizes.
MeasureTscFrequency()290 static double MeasureTscFrequency() {
291   double last_measurement = -1.0;
292   int sleep_nanoseconds = 1000000;  // 1 millisecond.
293   for (int i = 0; i < 8; ++i) {
294     double measurement = MeasureTscFrequencyWithSleep(sleep_nanoseconds);
295     if (measurement * 0.99 < last_measurement &&
296         last_measurement < measurement * 1.01) {
297       // Use the current measurement if it is within 1% of the
298       // previous measurement.
299       return measurement;
300     }
301     last_measurement = measurement;
302     sleep_nanoseconds *= 2;
303   }
304   return last_measurement;
305 }
306 
307 #endif  // ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
308 
GetNominalCPUFrequency()309 static double GetNominalCPUFrequency() {
310   long freq = 0;
311 
312   // Google's production kernel has a patch to export the TSC
313   // frequency through sysfs. If the kernel is exporting the TSC
314   // frequency use that. There are issues where cpuinfo_max_freq
315   // cannot be relied on because the BIOS may be exporting an invalid
316   // p-state (on x86) or p-states may be used to put the processor in
317   // a new mode (turbo mode). Essentially, those frequencies cannot
318   // always be relied upon. The same reasons apply to /proc/cpuinfo as
319   // well.
320   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
321     return freq * 1e3;  // Value is kHz.
322   }
323 
324 #if defined(ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY)
325   // On these platforms, the TSC frequency is the nominal CPU
326   // frequency.  But without having the kernel export it directly
327   // though /sys/devices/system/cpu/cpu0/tsc_freq_khz, there is no
328   // other way to reliably get the TSC frequency, so we have to
329   // measure it ourselves.  Some CPUs abuse cpuinfo_max_freq by
330   // exporting "fake" frequencies for implementing new features. For
331   // example, Intel's turbo mode is enabled by exposing a p-state
332   // value with a higher frequency than that of the real TSC
333   // rate. Because of this, we prefer to measure the TSC rate
334   // ourselves on i386 and x86-64.
335   return MeasureTscFrequency();
336 #else
337 
338   // If CPU scaling is in effect, we want to use the *maximum*
339   // frequency, not whatever CPU speed some random processor happens
340   // to be using now.
341   if (ReadLongFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
342                        &freq)) {
343     return freq * 1e3;  // Value is kHz.
344   }
345 
346   return 1.0;
347 #endif  // !ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY
348 }
349 
350 #endif
351 
352 ABSL_CONST_INIT static once_flag init_num_cpus_once;
353 ABSL_CONST_INIT static int num_cpus = 0;
354 
355 // NumCPUs() may be called before main() and before malloc is properly
356 // initialized, therefore this must not allocate memory.
NumCPUs()357 int NumCPUs() {
358   base_internal::LowLevelCallOnce(
359       &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
360   return num_cpus;
361 }
362 
363 // A default frequency of 0.0 might be dangerous if it is used in division.
364 ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
365 ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
366 
367 // NominalCPUFrequency() may be called before main() and before malloc is
368 // properly initialized, therefore this must not allocate memory.
NominalCPUFrequency()369 double NominalCPUFrequency() {
370   base_internal::LowLevelCallOnce(
371       &init_nominal_cpu_frequency_once,
372       []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
373   return nominal_cpu_frequency;
374 }
375 
376 #if defined(_WIN32)
377 
GetTID()378 pid_t GetTID() {
379   return pid_t{GetCurrentThreadId()};
380 }
381 
382 #elif defined(__linux__)
383 
384 #ifndef SYS_gettid
385 #define SYS_gettid __NR_gettid
386 #endif
387 
GetTID()388 pid_t GetTID() {
389   return static_cast<pid_t>(syscall(SYS_gettid));
390 }
391 
392 #elif defined(__akaros__)
393 
GetTID()394 pid_t GetTID() {
395   // Akaros has a concept of "vcore context", which is the state the program
396   // is forced into when we need to make a user-level scheduling decision, or
397   // run a signal handler.  This is analogous to the interrupt context that a
398   // CPU might enter if it encounters some kind of exception.
399   //
400   // There is no current thread context in vcore context, but we need to give
401   // a reasonable answer if asked for a thread ID (e.g., in a signal handler).
402   // Thread 0 always exists, so if we are in vcore context, we return that.
403   //
404   // Otherwise, we know (since we are using pthreads) that the uthread struct
405   // current_uthread is pointing to is the first element of a
406   // struct pthread_tcb, so we extract and return the thread ID from that.
407   //
408   // TODO(dcross): Akaros anticipates moving the thread ID to the uthread
409   // structure at some point. We should modify this code to remove the cast
410   // when that happens.
411   if (in_vcore_context())
412     return 0;
413   return reinterpret_cast<struct pthread_tcb *>(current_uthread)->id;
414 }
415 
416 #elif defined(__myriad2__)
417 
GetTID()418 pid_t GetTID() {
419   uint32_t tid;
420   rtems_task_ident(RTEMS_SELF, 0, &tid);
421   return tid;
422 }
423 
424 #elif defined(__APPLE__)
425 
GetTID()426 pid_t GetTID() {
427   uint64_t tid;
428   // `nullptr` here implies this thread.  This only fails if the specified
429   // thread is invalid or the pointer-to-tid is null, so we needn't worry about
430   // it.
431   pthread_threadid_np(nullptr, &tid);
432   return static_cast<pid_t>(tid);
433 }
434 
435 #elif defined(__native_client__)
436 
GetTID()437 pid_t GetTID() {
438   auto* thread = pthread_self();
439   static_assert(sizeof(pid_t) == sizeof(thread),
440                 "In NaCL int expected to be the same size as a pointer");
441   return reinterpret_cast<pid_t>(thread);
442 }
443 
444 #else
445 
446 // Fallback implementation of `GetTID` using `pthread_self`.
GetTID()447 pid_t GetTID() {
448   // `pthread_t` need not be arithmetic per POSIX; platforms where it isn't
449   // should be handled above.
450   return static_cast<pid_t>(pthread_self());
451 }
452 
453 #endif
454 
455 // GetCachedTID() caches the thread ID in thread-local storage (which is a
456 // userspace construct) to avoid unnecessary system calls. Without this caching,
457 // it can take roughly 98ns, while it takes roughly 1ns with this caching.
GetCachedTID()458 pid_t GetCachedTID() {
459 #ifdef ABSL_HAVE_THREAD_LOCAL
460   static thread_local pid_t thread_id = GetTID();
461   return thread_id;
462 #else
463   return GetTID();
464 #endif  // ABSL_HAVE_THREAD_LOCAL
465 }
466 
467 }  // namespace base_internal
468 ABSL_NAMESPACE_END
469 }  // namespace absl
470