Coverage Report

Created: 2026-10-28 06:49

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