sysinfo.cc
Go to the documentation of this file.
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 
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"
57 
58 namespace absl {
59 namespace base_internal {
60 
62 static int num_cpus = 0;
63 static double nominal_cpu_frequency = 1.0; // 0.0 might be dangerous.
64 
65 static 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 
78 static 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 
93 static 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.
107 static 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.
134 static 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 
148 class UnscaledCycleClockWrapperForInitializeFrequency {
149  public:
150  static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
151 };
152 
153 struct 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.
163 static 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();
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.
182 static 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.
197 static 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 
216 static 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.
262 static void InitializeSystemInfo() {
263  num_cpus = GetNumCPUs();
264  nominal_cpu_frequency = GetNominalCPUFrequency();
265 }
266 
267 int NumCPUs() {
268  base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
269  return num_cpus;
270 }
271 
273  base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
274  return nominal_cpu_frequency;
275 }
276 
277 #if defined(_WIN32)
278 
279 pid_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 
289 pid_t GetTID() {
290  return syscall(SYS_gettid);
291 }
292 
293 #elif defined(__akaros__)
294 
295 pid_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 
319 pid_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.
329 static pthread_key_t tid_key;
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().
336 static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr;
337 static constexpr int kBitsPerWord = 32; // tid_array is uint32_t.
338 
339 // Returns the TID to tid_array.
340 static 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));
345  assert(0 <= word && static_cast<size_t>(word) < tid_array->size());
346  (*tid_array)[word] &= mask;
347 }
348 
349 static 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.
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.
363 pid_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.
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
int v
Definition: variant_test.cc:81
static void FreeTID(void *v)
Definition: sysinfo.cc:340
Time Now()
Definition: clock.cc:37
void LowLevelCallOnce(absl::once_flag *flag, Callable &&fn, Args &&... args)
Definition: call_once.h:189
static constexpr int kBitsPerWord
Definition: sysinfo.cc:337
static double GetNominalCPUFrequency()
Definition: sysinfo.cc:216
double NominalCPUFrequency()
Definition: sysinfo.cc:272
static double nominal_cpu_frequency
Definition: sysinfo.cc:63
Definition: algorithm.h:29
void call_once(absl::once_flag &flag, Callable &&fn, Args &&... args)
Definition: call_once.h:202
size_t value
static char data[kDataSize]
Definition: city_test.cc:31
static void InitializeSystemInfo()
Definition: sysinfo.cc:262
static void InitGetTID()
Definition: sysinfo.cc:349
static pthread_key_t tid_key
Definition: sysinfo.cc:329
static absl::base_internal::SpinLock tid_lock(absl::base_internal::kLinkerInitialized)
uintptr_t size
static std::vector< uint32_t > *tid_array GUARDED_BY(tid_lock)
static once_flag tid_once
Definition: sysinfo.cc:328
static int num_cpus
Definition: sysinfo.cc:62
static int GetNumCPUs()
Definition: sysinfo.cc:65
static once_flag init_system_info_once
Definition: sysinfo.cc:61
static bool ReadLongFromFile(const char *file, long *value)
Definition: sysinfo.cc:107


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:20